From 54ade5df94eb133486bcc688b45989da847fbab5 Mon Sep 17 00:00:00 2001 From: merlokk <807634+merlokk@users.noreply.github.com> Date: Wed, 20 Feb 2019 21:12:06 +0200 Subject: [PATCH 01/21] add read sector code --- client/mifarehost.c | 31 +++++++++++++++++++++++++++++++ client/mifarehost.h | 2 +- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/client/mifarehost.c b/client/mifarehost.c index 31ed8e39d..817f99c93 100644 --- a/client/mifarehost.c +++ b/client/mifarehost.c @@ -414,6 +414,37 @@ out: return -4; } +// MIFARE +int mfReadSector(uint8_t sectorNo, uint8_t keyType, uint8_t *key, uint8_t *data) { + + UsbCommand c = {CMD_MIFARE_READSC, {sectorNo, keyType, 0}}; + memcpy(c.d.asBytes, key, 6); + clearCommandBuffer(); + SendCommand(&c); + + UsbCommand resp; + if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { + uint8_t isOK = resp.arg[0] & 0xff; + + if (isOK) { + memcpy(data, resp.d.asBytes, mfNumBlocksPerSector(sectorNo) * 16); + for (int i = 0; i < (sectorNo<32?3:15); i++) { + PrintAndLogEx(NORMAL, "data : %s", sprint_hex(data + i * 16, 16)); + } + PrintAndLogEx(NORMAL, "trailer: %s", sprint_hex(data + (sectorNo<32?3:15) * 16, 16)); + + return 0; + } else { + return 1; + } + } else { + PrintAndLogEx(ERR, "Command execute timeout"); + return 2; + } + + return 0; +} + // EMULATOR int mfEmlGetMem(uint8_t *data, int blockNum, int blocksCount) { UsbCommand c = {CMD_MIFARE_EML_MEMGET, {blockNum, blocksCount, 0}}; diff --git a/client/mifarehost.h b/client/mifarehost.h index 8f0f3f3bf..69458fdfa 100644 --- a/client/mifarehost.h +++ b/client/mifarehost.h @@ -76,7 +76,7 @@ extern int mfCheckKeys_fast( uint8_t sectorsCnt, uint8_t firstChunk, uint8_t las uint8_t strategy, uint32_t size, uint8_t *keyBlock, sector_t *e_sector, bool use_flashmemory); extern int mfKeyBrute(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint64_t *resultkey); - +extern int mfReadSector(uint8_t sectorNo, uint8_t keyType, uint8_t *key, uint8_t *data); extern int mfEmlGetMem(uint8_t *data, int blockNum, int blocksCount); extern int mfEmlSetMem(uint8_t *data, int blockNum, int blocksCount); From f2b600e6e96f34fa5112ffadeb6816b89191b267 Mon Sep 17 00:00:00 2001 From: merlokk <807634+merlokk@users.noreply.github.com> Date: Wed, 20 Feb 2019 21:12:28 +0200 Subject: [PATCH 02/21] add keys for mad and ndef --- client/mifaredefault.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/client/mifaredefault.h b/client/mifaredefault.h index 400463eaf..db9c0329d 100644 --- a/client/mifaredefault.h +++ b/client/mifaredefault.h @@ -31,7 +31,7 @@ static const uint64_t g_mifare_default_keys[] = 0xabcdef123456, 0x4d3a99c351dd, 0x1a982c7e459a, - 0xd3f7d3f7d3f7, + 0xd3f7d3f7d3f7, // NDEF public key 0x714c5c886e97, 0x587ee5f9350f, 0xa0478cc39091, @@ -42,4 +42,7 @@ static const uint64_t g_mifare_default_keys[] = 0x96a301bce267 }; +static const uint8_t g_mifare_mad_key[] = {0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5}; +static const uint8_t g_mifare_ndef_key[] = {0xd3, 0xf7, 0xd3, 0xf7, 0xd3, 0xf7}; + #endif From 583b73226810d2f0aa8a72a5e01aeef352ebe8cd Mon Sep 17 00:00:00 2001 From: merlokk <807634+merlokk@users.noreply.github.com> Date: Wed, 20 Feb 2019 21:12:50 +0200 Subject: [PATCH 03/21] add `hf mf mad` command --- client/cmdhfmf.c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/client/cmdhfmf.c b/client/cmdhfmf.c index 47646852e..811fa2430 100644 --- a/client/cmdhfmf.c +++ b/client/cmdhfmf.c @@ -3198,6 +3198,39 @@ int CmdHF14AMfAuth4(const char *cmd) { return MifareAuth4(NULL, keyn, key, true, false, true); } +// https://www.nxp.com/docs/en/application-note/AN10787.pdf +int CmdHF14AMfMAD(const char *cmd) { + + CLIParserInit("hf mf mad", + "Checks and prints Mifare Application Directory (MAD)", + "Usage:\n\thf mf mad -> shows MAD if exists\n"); + + void* argtable[] = { + arg_param_begin, + arg_lit0("vV", "verbose", "show technical data"), + arg_param_end + }; + CLIExecWithReturn(cmd, argtable, true); + bool verbose = arg_get_lit(1); + + CLIParserFree(); + + uint8_t sector[16 * 4] = {0}; + if (mfReadSector(0, 0, (uint8_t *)g_mifare_mad_key, sector)) { + PrintAndLogEx(ERR, "read sector 0 error. card don't have MAD or don't have MAD on default keys."); + return 2; + } + + if (verbose) { + for(int i = 0; i < 3; i ++) + PrintAndLogEx(NORMAL, "[i] %s", sprint_hex(§or[i * 16], 16)); + } + +// MADDecodeAndPrint(sector, verbose); + + return 0; +} + static command_t CommandTable[] = { {"help", CmdHelp, 1, "This help"}, {"darkside", CmdHF14ADarkside, 0, "Darkside attack. read parity error messages."}, @@ -3234,6 +3267,9 @@ static command_t CommandTable[] = { {"cgetsc", CmdHF14AMfCGetSc, 0, "Read sector - Magic Chinese card"}, {"cload", CmdHF14AMfCLoad, 0, "Load dump into magic Chinese card"}, {"csave", CmdHF14AMfCSave, 0, "Save dump from magic Chinese card into file or emulator"}, + {"-----------", CmdHelp, 1, ""}, + {"mad", CmdHF14AMfMAD, 0, "Checks and prints MAD"}, +// {"ndef", CmdHF14AMfHDEF, 0, "Checks and prints NDEF records from card"}, {"ice", CmdHF14AMfice, 0, "collect Mifare Classic nonces to file"}, {NULL, NULL, 0, NULL} From 1675b10c009646fddf4b8a384ca3a7bf5f18fb12 Mon Sep 17 00:00:00 2001 From: merlokk <807634+merlokk@users.noreply.github.com> Date: Wed, 20 Feb 2019 21:15:44 +0200 Subject: [PATCH 04/21] small fix --- client/mifarehost.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/mifarehost.c b/client/mifarehost.c index 817f99c93..ba5081441 100644 --- a/client/mifarehost.c +++ b/client/mifarehost.c @@ -435,7 +435,7 @@ int mfReadSector(uint8_t sectorNo, uint8_t keyType, uint8_t *key, uint8_t *data) return 0; } else { - return 1; + return isOK; } } else { PrintAndLogEx(ERR, "Command execute timeout"); From 3b21b175099649f91572953cee1e65100385269e Mon Sep 17 00:00:00 2001 From: merlokk <807634+merlokk@users.noreply.github.com> Date: Thu, 21 Feb 2019 19:15:46 +0200 Subject: [PATCH 05/21] move mifare stuff to its folder --- client/Makefile | 6 +- client/cmdanalyse.h | 6 +- client/cmdhf14a.h | 2 +- client/cmdhflist.h | 4 +- client/cmdhfmf.c | 22 +- client/cmdhfmf.h | 14 +- client/cmdhfmfp.c | 2 +- client/cmdhfmfp.h | 2 +- client/loclass/fileutils.h | 2 +- client/{ => mifare}/mfkey.c | 0 client/{ => mifare}/mfkey.h | 0 client/{ => mifare}/mifare4.c | 0 client/{ => mifare}/mifare4.h | 0 client/{ => mifare}/mifaredefault.h | 0 client/{ => mifare}/mifarehost.c | 2033 +++++++++++++-------------- client/{ => mifare}/mifarehost.h | 204 +-- client/obj/mifare/.dummy | 0 client/scripting.h | 2 +- include/mifare.h | 6 + 19 files changed, 1158 insertions(+), 1147 deletions(-) rename client/{ => mifare}/mfkey.c (100%) rename client/{ => mifare}/mfkey.h (100%) rename client/{ => mifare}/mifare4.c (100%) rename client/{ => mifare}/mifare4.h (100%) rename client/{ => mifare}/mifaredefault.h (100%) rename client/{ => mifare}/mifarehost.c (95%) rename client/{ => mifare}/mifarehost.h (97%) create mode 100644 client/obj/mifare/.dummy diff --git a/client/Makefile b/client/Makefile index 0b19d4599..9f8055f8e 100644 --- a/client/Makefile +++ b/client/Makefile @@ -108,7 +108,7 @@ CORESRCS = uart_posix.c \ CMDSRCS = crapto1/crapto1.c \ crapto1/crypto1.c \ - mfkey.c \ + mifare/mfkey.c \ tea.c \ fido/additional_ca.c \ fido/cose.c \ @@ -126,7 +126,7 @@ CMDSRCS = crapto1/crapto1.c \ loclass/elite_crack.c \ loclass/fileutils.c \ whereami.c \ - mifarehost.c \ + mifare/mifarehost.c \ parity.c \ crc.c \ crc16.c \ @@ -156,7 +156,7 @@ CMDSRCS = crapto1/crapto1.c \ emv/test/cda_test.c\ emv/cmdemv.c \ emv/emv_roca.c \ - mifare4.c \ + mifare/mifare4.c \ cmdanalyse.c \ cmdhf.c \ cmdhflist.c \ diff --git a/client/cmdanalyse.h b/client/cmdanalyse.h index f834c717d..ab7606f5c 100644 --- a/client/cmdanalyse.h +++ b/client/cmdanalyse.h @@ -16,15 +16,15 @@ #include #include "cmdmain.h" #include "proxmark3.h" -#include "ui.h" // PrintAndLog +#include "ui.h" // PrintAndLog #include "util.h" #include "crc.h" #include "crc16.h" // crc16 ccitt #include "tea.h" #include "legic_prng.h" #include "loclass/elite_crack.h" -#include "mfkey.h" //nonce2key -#include "util_posix.h" // msclock +#include "mifare/mfkey.h" //nonce2key +#include "util_posix.h" // msclock int usage_analyse_lcr(void); diff --git a/client/cmdhf14a.h b/client/cmdhf14a.h index 723c31100..3fe04d26c 100644 --- a/client/cmdhf14a.h +++ b/client/cmdhf14a.h @@ -29,7 +29,7 @@ #include "cmdhfmf.h" #include "cmdhfmfu.h" #include "cmdhf.h" // list cmd -#include "mifarehost.h" +#include "mifare/mifarehost.h" #include "emv/apduinfo.h" #include "emv/emvcore.h" diff --git a/client/cmdhflist.h b/client/cmdhflist.h index 1955119f2..60a0a0dfd 100644 --- a/client/cmdhflist.h +++ b/client/cmdhflist.h @@ -31,8 +31,8 @@ #include "emv/cmdemv.h" // EMV #include "protocols.h" #include "crapto1/crapto1.h" -#include "mifarehost.h" -#include "mifaredefault.h" +#include "mifare/mifarehost.h" +#include "mifare/mifaredefault.h" #include "parity.h" // oddparity #include "iso15693tools.h" // ISO15693 crc diff --git a/client/cmdhfmf.c b/client/cmdhfmf.c index 811fa2430..a12bc34c2 100644 --- a/client/cmdhfmf.c +++ b/client/cmdhfmf.c @@ -9,7 +9,7 @@ //----------------------------------------------------------------------------- #include "cmdhfmf.h" -#include "mifare4.h" +#include "mifare/mifare4.h" #define MIFARE_4K_MAXBLOCK 256 #define MIFARE_2K_MAXBLOCK 128 @@ -3216,18 +3216,28 @@ int CmdHF14AMfMAD(const char *cmd) { CLIParserFree(); uint8_t sector[16 * 4] = {0}; - if (mfReadSector(0, 0, (uint8_t *)g_mifare_mad_key, sector)) { + if (mfReadSector(MF_MAD1_SECTOR, MF_KEY_A, (uint8_t *)g_mifare_mad_key, sector)) { PrintAndLogEx(ERR, "read sector 0 error. card don't have MAD or don't have MAD on default keys."); return 2; } if (verbose) { - for(int i = 0; i < 3; i ++) - PrintAndLogEx(NORMAL, "[i] %s", sprint_hex(§or[i * 16], 16)); + for(int i = 0; i < 4; i ++) + PrintAndLogEx(NORMAL, "[%d] %s", i, sprint_hex(§or[i * 16], 16)); } + /* + bool haveMAD2 = false; + MAD1DecodeAndPrint(sector, verbose, &haveMAD2); -// MADDecodeAndPrint(sector, verbose); - + if (haveMAD2) { + if (mfReadSector(MF_MAD2_SECTOR, MF_KEY_A, (uint8_t *)g_mifare_mad_key, sector)) { + PrintAndLogEx(ERR, "read sector 0 error. card don't have MAD or don't have MAD on default keys."); + return 2; + } + + MAD2DecodeAndPrint(sector, verbose); + } + */ return 0; } diff --git a/client/cmdhfmf.h b/client/cmdhfmf.h index eeb7a74f6..bdd817e41 100644 --- a/client/cmdhfmf.h +++ b/client/cmdhfmf.h @@ -23,14 +23,14 @@ #include "cmdparser.h" #include "common.h" #include "util.h" -#include "mifare.h" // nonces_t struct -#include "mfkey.h" // mfkey32_moebious +#include "mifare.h" // nonces_t struct +#include "mifare/mfkey.h" // mfkey32_moebious #include "cmdhfmfhard.h" -#include "mifarehost.h" // icesector_t, sector_t -#include "util_posix.h" // msclock -#include "mifaredefault.h" // mifare default key array -#include "cmdhf14a.h" // dropfield -#include "cliparser/cliparser.h" // argtable +#include "mifare/mifarehost.h" // icesector_t, sector_t +#include "util_posix.h" // msclock +#include "mifare/mifaredefault.h" // mifare default key array +#include "cmdhf14a.h" // dropfield +#include "cliparser/cliparser.h" // argtable #include "hardnested/hardnested_bf_core.h" // SetSIMDInstr extern int CmdHFMF(const char *Cmd); diff --git a/client/cmdhfmfp.c b/client/cmdhfmfp.c index 0d7a623d4..0917728be 100644 --- a/client/cmdhfmfp.c +++ b/client/cmdhfmfp.c @@ -22,7 +22,7 @@ #include "ui.h" #include "cmdhf14a.h" #include "mifare.h" -#include "mifare4.h" +#include "mifare/mifare4.h" #include "cliparser/cliparser.h" #include "crypto/libpcrypto.h" diff --git a/client/cmdhfmfp.h b/client/cmdhfmfp.h index b1ac7c349..a5cacb518 100644 --- a/client/cmdhfmfp.h +++ b/client/cmdhfmfp.h @@ -10,7 +10,7 @@ #ifndef CMDHFMFP_H__ #define CMDHFMFP_H__ -#include "mifaredefault.h" +#include "mifare/mifaredefault.h" extern int CmdHFMFP(const char *Cmd); diff --git a/client/loclass/fileutils.h b/client/loclass/fileutils.h index 3fc781d2e..9485707f1 100644 --- a/client/loclass/fileutils.h +++ b/client/loclass/fileutils.h @@ -48,7 +48,7 @@ #include #include "../ui.h" #include "../emv/emvjson.h" -#include "mifare4.h" +#include "mifare/mifare4.h" #include "cmdhfmfu.h" typedef enum { diff --git a/client/mfkey.c b/client/mifare/mfkey.c similarity index 100% rename from client/mfkey.c rename to client/mifare/mfkey.c diff --git a/client/mfkey.h b/client/mifare/mfkey.h similarity index 100% rename from client/mfkey.h rename to client/mifare/mfkey.h diff --git a/client/mifare4.c b/client/mifare/mifare4.c similarity index 100% rename from client/mifare4.c rename to client/mifare/mifare4.c diff --git a/client/mifare4.h b/client/mifare/mifare4.h similarity index 100% rename from client/mifare4.h rename to client/mifare/mifare4.h diff --git a/client/mifaredefault.h b/client/mifare/mifaredefault.h similarity index 100% rename from client/mifaredefault.h rename to client/mifare/mifaredefault.h diff --git a/client/mifarehost.c b/client/mifare/mifarehost.c similarity index 95% rename from client/mifarehost.c rename to client/mifare/mifarehost.c index ba5081441..cd239d585 100644 --- a/client/mifarehost.c +++ b/client/mifare/mifarehost.c @@ -1,1020 +1,1015 @@ -// Merlok, 2011, 2012 -// people from mifare@nethemba.com, 2010 -// -// 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. -//----------------------------------------------------------------------------- -// mifare commands -//----------------------------------------------------------------------------- -#include "mifarehost.h" -#include "cmdmain.h" - -int mfDarkside(uint8_t blockno, uint8_t key_type, uint64_t *key) { - uint32_t uid = 0; - uint32_t nt = 0, nr = 0, ar = 0; - uint64_t par_list = 0, ks_list = 0; - uint64_t *keylist = NULL, *last_keylist = NULL; - uint32_t keycount = 0; - int16_t isOK = 0; - - UsbCommand c = {CMD_READER_MIFARE, {true, blockno, key_type}}; - - // message - PrintAndLogEx(NORMAL, "--------------------------------------------------------------------------------\n"); - PrintAndLogEx(NORMAL, "executing Darkside attack. Expected execution time: 25sec on average"); - PrintAndLogEx(NORMAL, "press pm3-button on the proxmark3 device to abort both proxmark3 and client."); - PrintAndLogEx(NORMAL, "--------------------------------------------------------------------------------\n"); - - while (true) { - clearCommandBuffer(); - SendCommand(&c); - - //flush queue - while (ukbhit()) { - int gc = getchar(); (void)gc; - return -5; - } - - // wait cycle - while (true) { - printf("."); fflush(stdout); - if (ukbhit()) { - int gc = getchar(); (void)gc; - return -5; - } - - UsbCommand resp; - if (WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { - isOK = resp.arg[0]; - if (isOK < 0) - return isOK; - - uid = (uint32_t)bytes_to_num(resp.d.asBytes + 0, 4); - nt = (uint32_t)bytes_to_num(resp.d.asBytes + 4, 4); - par_list = bytes_to_num(resp.d.asBytes + 8, 8); - ks_list = bytes_to_num(resp.d.asBytes + 16, 8); - nr = (uint32_t)bytes_to_num(resp.d.asBytes + 24, 4); - ar = (uint32_t)bytes_to_num(resp.d.asBytes + 28, 4); - break; - } - } - PrintAndLogEx(NORMAL, "\n"); - - if (par_list == 0 && c.arg[0] == true) { - PrintAndLogEx(SUCCESS, "Parity is all zero. Most likely this card sends NACK on every authentication."); - } - c.arg[0] = false; - - keycount = nonce2key(uid, nt, nr, ar, par_list, ks_list, &keylist); - - if (keycount == 0) { - PrintAndLogEx(FAILED, "key not found (lfsr_common_prefix list is null). Nt=%08x", nt); - PrintAndLogEx(FAILED, "this is expected to happen in 25%% of all cases. Trying again with a different reader nonce..."); - continue; - } - - // only parity zero attack - if (par_list == 0 ) { - qsort(keylist, keycount, sizeof(*keylist), compare_uint64); - keycount = intersection(last_keylist, keylist); - if (keycount == 0) { - free(last_keylist); - last_keylist = keylist; - PrintAndLogEx(FAILED, "no candidates found, trying again"); - continue; - } - } - - PrintAndLogEx(SUCCESS, "found %u candidate key%s\n", keycount, (keycount > 1) ? "s." : "."); - - *key = -1; - uint8_t keyBlock[USB_CMD_DATA_SIZE]; - int max_keys = USB_CMD_DATA_SIZE / 6; - for (int i = 0; i < keycount; i += max_keys) { - - int size = keycount - i > max_keys ? max_keys : keycount - i; - for (int j = 0; j < size; j++) { - if (par_list == 0) { - num_to_bytes(last_keylist[i*max_keys + j], 6, keyBlock+(j*6)); - } else { - num_to_bytes(keylist[i*max_keys + j], 6, keyBlock+(j*6)); - } - } - - if (!mfCheckKeys(blockno, key_type - 0x60, false, size, keyBlock, key)) { - break; - } - } - - if (*key != -1) { - break; - } else { - PrintAndLogEx(FAILED, "all candidate keys failed. Restarting darkside attack"); - free(last_keylist); - last_keylist = keylist; - c.arg[0] = true; - } - } - free(last_keylist); - free(keylist); - return 0; -} -int mfCheckKeys(uint8_t blockNo, uint8_t keyType, bool clear_trace, uint8_t keycnt, uint8_t * keyBlock, uint64_t * key){ - *key = -1; - UsbCommand c = {CMD_MIFARE_CHKKEYS, { (blockNo | (keyType << 8)), clear_trace, keycnt}}; - memcpy(c.d.asBytes, keyBlock, 6 * keycnt); - clearCommandBuffer(); - SendCommand(&c); - UsbCommand resp; - if (!WaitForResponseTimeout(CMD_ACK, &resp, 2500)) return 1; - if ((resp.arg[0] & 0xff) != 0x01) return 2; - *key = bytes_to_num(resp.d.asBytes, 6); - return 0; -} - -// Sends chunks of keys to device. -// 0 == ok all keys found -// 1 == -// 2 == Time-out, aborting -int mfCheckKeys_fast( uint8_t sectorsCnt, uint8_t firstChunk, uint8_t lastChunk, uint8_t strategy, - uint32_t size, uint8_t *keyBlock, sector_t *e_sector, bool use_flashmemory) { - - uint64_t t2 = msclock(); - uint32_t timeout = 0; - - // send keychunk - UsbCommand c = {CMD_MIFARE_CHKKEYS_FAST, { (sectorsCnt | (firstChunk << 8) | (lastChunk << 12) ), ((use_flashmemory << 8) | strategy), size}}; - memcpy(c.d.asBytes, keyBlock, 6 * size); - clearCommandBuffer(); - SendCommand(&c); - UsbCommand resp; - - while ( !WaitForResponseTimeout(CMD_ACK, &resp, 2000) ) { - timeout++; - printf("."); fflush(stdout); - // max timeout for one chunk of 85keys, 60*3sec = 180seconds - // s70 with 40*2 keys to check, 80*85 = 6800 auth. - // takes about 97s, still some margin before abort - if (timeout > 180) { - PrintAndLogEx(WARNING, "\nno response from Proxmark. Aborting..."); - return 2; - } - } - t2 = msclock() - t2; - - // time to convert the returned data. - uint8_t curr_keys = resp.arg[0]; - - PrintAndLogEx(SUCCESS, "\nChunk: %.1fs | found %u/%u keys (%u)", (float)(t2/1000.0), curr_keys, (sectorsCnt<<1), size); - - // all keys? - if ( curr_keys == sectorsCnt*2 || lastChunk ) { - - // success array. each byte is status of key - uint8_t arr[80]; - uint64_t foo = 0; - uint16_t bar = 0; - foo = bytes_to_num(resp.d.asBytes+480, 8); - bar = (resp.d.asBytes[489] << 8 | resp.d.asBytes[488]); - - for (uint8_t i = 0; i < 64; i++) - arr[i] = (foo >> i) & 0x1; - - for (uint8_t i = 0; i < 16; i++) - arr[i+64] = (bar >> i) & 0x1; - - // initialize storage for found keys - icesector_t *tmp = calloc(sectorsCnt, sizeof(icesector_t)); - if (tmp == NULL) - return 1; - memcpy(tmp, resp.d.asBytes, sectorsCnt * sizeof(icesector_t) ); - - for ( int i = 0; i < sectorsCnt; i++) { - // key A - if ( !e_sector[i].foundKey[0] ) { - e_sector[i].Key[0] = bytes_to_num( tmp[i].keyA, 6); - e_sector[i].foundKey[0] = arr[ (i*2) ]; - } - // key B - if ( !e_sector[i].foundKey[1] ) { - e_sector[i].Key[1] = bytes_to_num( tmp[i].keyB, 6); - e_sector[i].foundKey[1] = arr[ (i*2) + 1 ]; - } - } - free(tmp); - - if ( curr_keys == sectorsCnt*2 ) - return 0; - if ( lastChunk ) - return 1; - } - return 1; -} - -// PM3 imp of J-Run mf_key_brute (part 2) -// ref: https://github.com/J-Run/mf_key_brute -int mfKeyBrute(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint64_t *resultkey){ - - #define KEYS_IN_BLOCK 85 - #define KEYBLOCK_SIZE 510 - #define CANDIDATE_SIZE 0xFFFF * 6 - uint8_t found = false; - uint64_t key64 = 0; - uint8_t candidates[CANDIDATE_SIZE] = {0x00}; - uint8_t keyBlock[KEYBLOCK_SIZE] = {0x00}; - - memset(candidates, 0, sizeof(candidates)); - memset(keyBlock, 0, sizeof(keyBlock)); - - // Generate all possible keys for the first two unknown bytes. - for (uint16_t i = 0; i < 0xFFFF; ++i) { - uint32_t j = i * 6; - candidates[0 + j] = i >> 8; - candidates[1 + j] = i; - candidates[2 + j] = key[2]; - candidates[3 + j] = key[3]; - candidates[4 + j] = key[4]; - candidates[5 + j] = key[5]; - } - uint32_t counter, i; - for ( i = 0, counter = 1; i < CANDIDATE_SIZE; i += KEYBLOCK_SIZE, ++counter){ - - key64 = 0; - - // copy candidatekeys to test key block - memcpy(keyBlock, candidates + i, KEYBLOCK_SIZE); - - // check a block of generated candidate keys. - if (!mfCheckKeys(blockNo, keyType, true, KEYS_IN_BLOCK, keyBlock, &key64)) { - *resultkey = key64; - found = true; - break; - } - - // progress - if ( counter % 20 == 0 ) - PrintAndLogEx(SUCCESS, "tried : %s.. \t %u keys", sprint_hex(candidates + i, 6), counter * KEYS_IN_BLOCK ); - } - return found; -} - -// Compare 16 Bits out of cryptostate -int Compare16Bits(const void * a, const void * b) { - if ((*(uint64_t*)b & 0x00ff000000ff0000) == (*(uint64_t*)a & 0x00ff000000ff0000)) return 0; - if ((*(uint64_t*)b & 0x00ff000000ff0000) > (*(uint64_t*)a & 0x00ff000000ff0000)) return 1; - return -1; -} - -// wrapper function for multi-threaded lfsr_recovery32 -void -#ifdef __has_attribute -#if __has_attribute(force_align_arg_pointer) -__attribute__((force_align_arg_pointer)) -#endif -#endif -*nested_worker_thread(void *arg) { - struct Crypto1State *p1; - StateList_t *statelist = arg; - statelist->head.slhead = lfsr_recovery32(statelist->ks1, statelist->nt ^ statelist->uid); - - for (p1 = statelist->head.slhead; *(uint64_t *)p1 != 0; p1++) {}; - - statelist->len = p1 - statelist->head.slhead; - statelist->tail.sltail = --p1; - qsort(statelist->head.slhead, statelist->len, sizeof(uint64_t), Compare16Bits); - - return statelist->head.slhead; -} - -int mfnested(uint8_t blockNo, uint8_t keyType, uint8_t * key, uint8_t trgBlockNo, uint8_t trgKeyType, uint8_t * resultKey, bool calibrate) { - uint16_t i; - uint32_t uid; - UsbCommand resp; - StateList_t statelists[2]; - struct Crypto1State *p1, *p2, *p3, *p4; - - UsbCommand c = {CMD_MIFARE_NESTED, {blockNo + keyType * 0x100, trgBlockNo + trgKeyType * 0x100, calibrate}}; - memcpy(c.d.asBytes, key, 6); - clearCommandBuffer(); - SendCommand(&c); - if (!WaitForResponseTimeout(CMD_ACK, &resp, 1500)) return -1; - - // error during nested - if (resp.arg[0]) return resp.arg[0]; - - memcpy(&uid, resp.d.asBytes, 4); - - for (i = 0; i < 2; i++) { - statelists[i].blockNo = resp.arg[2] & 0xff; - statelists[i].keyType = (resp.arg[2] >> 8) & 0xff; - statelists[i].uid = uid; - memcpy(&statelists[i].nt, (void *)(resp.d.asBytes + 4 + i * 8 + 0), 4); - memcpy(&statelists[i].ks1, (void *)(resp.d.asBytes + 4 + i * 8 + 4), 4); - } - - // calc keys - pthread_t thread_id[2]; - - // create and run worker threads - for (i = 0; i < 2; i++) - pthread_create(thread_id + i, NULL, nested_worker_thread, &statelists[i]); - - // wait for threads to terminate: - for (i = 0; i < 2; i++) - pthread_join(thread_id[i], (void*)&statelists[i].head.slhead); - - // the first 16 Bits of the cryptostate already contain part of our key. - // Create the intersection of the two lists based on these 16 Bits and - // roll back the cryptostate - p1 = p3 = statelists[0].head.slhead; - p2 = p4 = statelists[1].head.slhead; - - while (p1 <= statelists[0].tail.sltail && p2 <= statelists[1].tail.sltail) { - if (Compare16Bits(p1, p2) == 0) { - - struct Crypto1State savestate, *savep = &savestate; - savestate = *p1; - while(Compare16Bits(p1, savep) == 0 && p1 <= statelists[0].tail.sltail) { - *p3 = *p1; - lfsr_rollback_word(p3, statelists[0].nt ^ statelists[0].uid, 0); - p3++; - p1++; - } - savestate = *p2; - while(Compare16Bits(p2, savep) == 0 && p2 <= statelists[1].tail.sltail) { - *p4 = *p2; - lfsr_rollback_word(p4, statelists[1].nt ^ statelists[1].uid, 0); - p4++; - p2++; - } - } - else { - while (Compare16Bits(p1, p2) == -1) p1++; - while (Compare16Bits(p1, p2) == 1) p2++; - } - } - - *(uint64_t*)p3 = -1; - *(uint64_t*)p4 = -1; - statelists[0].len = p3 - statelists[0].head.slhead; - statelists[1].len = p4 - statelists[1].head.slhead; - statelists[0].tail.sltail = --p3; - statelists[1].tail.sltail = --p4; - - // the statelists now contain possible keys. The key we are searching for must be in the - // intersection of both lists - qsort(statelists[0].head.keyhead, statelists[0].len, sizeof(uint64_t), compare_uint64); - qsort(statelists[1].head.keyhead, statelists[1].len, sizeof(uint64_t), compare_uint64); - // Create the intersection - statelists[0].len = intersection(statelists[0].head.keyhead, statelists[1].head.keyhead); - - //statelists[0].tail.keytail = --p7; - uint32_t keycnt = statelists[0].len; - if ( keycnt == 0 ) goto out; - - memset(resultKey, 0, 6); - uint64_t key64 = -1; - - // The list may still contain several key candidates. Test each of them with mfCheckKeys - uint32_t max_keys = keycnt > (USB_CMD_DATA_SIZE/6) ? (USB_CMD_DATA_SIZE/6) : keycnt; - uint8_t keyBlock[USB_CMD_DATA_SIZE] = {0x00}; - - for (int i = 0; i < keycnt; i += max_keys) { - - int size = keycnt - i > max_keys ? max_keys : keycnt - i; - - for (int j = 0; j < size; j++) { - crypto1_get_lfsr(statelists[0].head.slhead + i, &key64); - num_to_bytes(key64, 6, keyBlock + i * 6); - } - - if (!mfCheckKeys(statelists[0].blockNo, statelists[0].keyType, false, size, keyBlock, &key64)) { - free(statelists[0].head.slhead); - free(statelists[1].head.slhead); - num_to_bytes(key64, 6, resultKey); - - PrintAndLogEx(SUCCESS, "target block:%3u key type: %c -- found valid key [%012" PRIx64 "]", - (uint16_t)resp.arg[2] & 0xff, - (resp.arg[2] >> 8) ? 'B' : 'A', - key64 - ); - return -5; - } - } - -out: - PrintAndLogEx(SUCCESS, "target block:%3u key type: %c", - (uint16_t)resp.arg[2] & 0xff, - (resp.arg[2] >> 8) ? 'B' : 'A' - ); - - free(statelists[0].head.slhead); - free(statelists[1].head.slhead); - return -4; -} - -// MIFARE -int mfReadSector(uint8_t sectorNo, uint8_t keyType, uint8_t *key, uint8_t *data) { - - UsbCommand c = {CMD_MIFARE_READSC, {sectorNo, keyType, 0}}; - memcpy(c.d.asBytes, key, 6); - clearCommandBuffer(); - SendCommand(&c); - - UsbCommand resp; - if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { - uint8_t isOK = resp.arg[0] & 0xff; - - if (isOK) { - memcpy(data, resp.d.asBytes, mfNumBlocksPerSector(sectorNo) * 16); - for (int i = 0; i < (sectorNo<32?3:15); i++) { - PrintAndLogEx(NORMAL, "data : %s", sprint_hex(data + i * 16, 16)); - } - PrintAndLogEx(NORMAL, "trailer: %s", sprint_hex(data + (sectorNo<32?3:15) * 16, 16)); - - return 0; - } else { - return isOK; - } - } else { - PrintAndLogEx(ERR, "Command execute timeout"); - return 2; - } - - return 0; -} - -// EMULATOR -int mfEmlGetMem(uint8_t *data, int blockNum, int blocksCount) { - UsbCommand c = {CMD_MIFARE_EML_MEMGET, {blockNum, blocksCount, 0}}; - clearCommandBuffer(); - SendCommand(&c); - UsbCommand resp; - if (!WaitForResponseTimeout(CMD_ACK, &resp, 1500)) return 1; - memcpy(data, resp.d.asBytes, blocksCount * 16); - return 0; -} - -int mfEmlSetMem(uint8_t *data, int blockNum, int blocksCount) { - return mfEmlSetMem_xt(data, blockNum, blocksCount, 16); -} - -int mfEmlSetMem_xt(uint8_t *data, int blockNum, int blocksCount, int blockBtWidth) { - UsbCommand c = {CMD_MIFARE_EML_MEMSET, {blockNum, blocksCount, blockBtWidth}}; - memcpy(c.d.asBytes, data, blocksCount * blockBtWidth); - clearCommandBuffer(); - SendCommand(&c); - return 0; -} - -// "MAGIC" CARD -int mfCSetUID(uint8_t *uid, uint8_t *atqa, uint8_t *sak, uint8_t *oldUID, uint8_t wipecard) { - - uint8_t params = MAGIC_SINGLE; - uint8_t block0[16]; - memset(block0, 0x00, sizeof(block0)); - - int old = mfCGetBlock(0, block0, params); - if (old == 0) - PrintAndLogEx(SUCCESS, "old block 0: %s", sprint_hex(block0, sizeof(block0))); - else - PrintAndLogEx(FAILED, "couldn't get old data. Will write over the last bytes of Block 0."); - - // fill in the new values - // UID - memcpy(block0, uid, 4); - // Mifare UID BCC - block0[4] = block0[0] ^ block0[1] ^ block0[2] ^ block0[3]; - // mifare classic SAK(byte 5) and ATQA(byte 6 and 7, reversed) - if ( sak != NULL ) - block0[5] = sak[0]; - - if ( atqa != NULL ) { - block0[6] = atqa[1]; - block0[7] = atqa[0]; - } - PrintAndLogEx(SUCCESS, "new block 0: %s", sprint_hex(block0,16)); - - if ( wipecard ) params |= MAGIC_WIPE; - if ( oldUID == NULL) params |= MAGIC_UID; - - return mfCSetBlock(0, block0, oldUID, params); -} - -int mfCSetBlock(uint8_t blockNo, uint8_t *data, uint8_t *uid, uint8_t params) { - - uint8_t isOK = 0; - UsbCommand c = {CMD_MIFARE_CSETBLOCK, {params, blockNo, 0}}; - memcpy(c.d.asBytes, data, 16); - clearCommandBuffer(); - SendCommand(&c); - UsbCommand resp; - if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { - isOK = resp.arg[0] & 0xff; - if (uid != NULL) - memcpy(uid, resp.d.asBytes, 4); - if (!isOK) - return 2; - } else { - PrintAndLogEx(WARNING, "command execute timeout"); - return 1; - } - return 0; -} - -int mfCGetBlock(uint8_t blockNo, uint8_t *data, uint8_t params) { - uint8_t isOK = 0; - UsbCommand c = {CMD_MIFARE_CGETBLOCK, {params, blockNo, 0}}; - clearCommandBuffer(); - SendCommand(&c); - UsbCommand resp; - if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { - isOK = resp.arg[0] & 0xff; - if (!isOK) - return 2; - memcpy(data, resp.d.asBytes, 16); - } else { - PrintAndLogEx(WARNING, "command execute timeout"); - return 1; - } - return 0; -} - -// SNIFFER -// [iceman] so many global variables.... - -// constants -static uint8_t trailerAccessBytes[4] = {0x08, 0x77, 0x8F, 0x00}; - -// variables -char logHexFileName[FILE_PATH_SIZE] = {0x00}; -static uint8_t traceCard[4096] = {0x00}; -static char traceFileName[FILE_PATH_SIZE] = {0x00}; -static int traceState = TRACE_IDLE; -static uint8_t traceCurBlock = 0; -static uint8_t traceCurKey = 0; - -struct Crypto1State *traceCrypto1 = NULL; -struct Crypto1State *revstate = NULL; -uint64_t key = 0; -uint32_t ks2 = 0; -uint32_t ks3 = 0; - -uint32_t cuid = 0; // uid part used for crypto1. -uint32_t nt = 0; // tag challenge -uint32_t nr_enc = 0; // encrypted reader challenge -uint32_t ar_enc = 0; // encrypted reader response -uint32_t at_enc = 0; // encrypted tag response - -int isTraceCardEmpty(void) { - return ((traceCard[0] == 0) && (traceCard[1] == 0) && (traceCard[2] == 0) && (traceCard[3] == 0)); -} - -int isBlockEmpty(int blockN) { - for (int i = 0; i < 16; i++) - if (traceCard[blockN * 16 + i] != 0) return 0; - - return 1; -} - -int isBlockTrailer(int blockN) { - return ((blockN & 0x03) == 0x03); -} - -int loadTraceCard(uint8_t *tuid, uint8_t uidlen) { - FILE * f; - char buf[64] = {0x00}; - uint8_t buf8[64] = {0x00}; - int i, blockNum; - uint32_t tmp; - - if (!isTraceCardEmpty()) - saveTraceCard(); - - memset(traceCard, 0x00, 4096); - memcpy(traceCard, tuid, uidlen); - - FillFileNameByUID(traceFileName, tuid, ".eml", uidlen); - - f = fopen(traceFileName, "r"); - if (!f) return 1; - - blockNum = 0; - - while (!feof(f)){ - - memset(buf, 0, sizeof(buf)); - if (fgets(buf, sizeof(buf), f) == NULL) { - PrintAndLogEx(FAILED, "No trace file found or reading error."); - if (f) { - fclose(f); - } - return 2; - } - - if (strlen(buf) < 32){ - if (feof(f)) break; - PrintAndLogEx(FAILED, "File content error. Block data must include 32 HEX symbols"); - if (f) { - fclose(f); - } - return 2; - } - for (i = 0; i < 32; i += 2) { - sscanf(&buf[i], "%02X", &tmp); - buf8[i / 2] = tmp & 0xFF; - } - - memcpy(traceCard + blockNum * 16, buf8, 16); - - blockNum++; - } - if (f) { - fclose(f); - } - return 0; -} - -int saveTraceCard(void) { - - if ((!strlen(traceFileName)) || (isTraceCardEmpty())) return 0; - - FILE * f; - f = fopen(traceFileName, "w+"); - if ( !f ) return 1; - - // given 4096 tracecard size, these loop will only match a 1024, 1kb card memory - // 4086/16 == 256blocks. - for (uint16_t i = 0; i < 256; i++) { // blocks - for (uint8_t j = 0; j < 16; j++) // bytes - fprintf(f, "%02X", *(traceCard + i * 16 + j)); - - // no extra line in the end - if ( i < 255 ) - fprintf(f, "\n"); - } - fflush(f); - fclose(f); - return 0; -} -// -int mfTraceInit(uint8_t *tuid, uint8_t uidlen, uint8_t *atqa, uint8_t sak, bool wantSaveToEmlFile) { - - if (traceCrypto1) - crypto1_destroy(traceCrypto1); - - traceCrypto1 = NULL; - - if (wantSaveToEmlFile) - loadTraceCard(tuid, uidlen); - - traceCard[4] = traceCard[0] ^ traceCard[1] ^ traceCard[2] ^ traceCard[3]; - traceCard[5] = sak; - memcpy(&traceCard[6], atqa, 2); - traceCurBlock = 0; - cuid = bytes_to_num(tuid + (uidlen-4), 4); - traceState = TRACE_IDLE; - return 0; -} - -void mf_crypto1_decrypt(struct Crypto1State *pcs, uint8_t *data, int len, bool isEncrypted){ - uint8_t bt = 0; - int i; - - if (len != 1) { - for (i = 0; i < len; i++) - data[i] = crypto1_byte(pcs, 0x00, isEncrypted) ^ data[i]; - } else { - bt = 0; - bt |= (crypto1_bit(pcs, 0, isEncrypted) ^ BIT(data[0], 0)) << 0; - bt |= (crypto1_bit(pcs, 0, isEncrypted) ^ BIT(data[0], 1)) << 1; - bt |= (crypto1_bit(pcs, 0, isEncrypted) ^ BIT(data[0], 2)) << 2; - bt |= (crypto1_bit(pcs, 0, isEncrypted) ^ BIT(data[0], 3)) << 3; - data[0] = bt; - } -} - -int mfTraceDecode(uint8_t *data_src, int len, bool wantSaveToEmlFile) { - - if (traceState == TRACE_ERROR) - return 1; - - if (len > 255) { - traceState = TRACE_ERROR; - return 1; - } - - uint8_t data[255]; - memset(data, 0x00, sizeof(data)); - - memcpy(data, data_src, len); - - if ((traceCrypto1) && ((traceState == TRACE_IDLE) || (traceState > TRACE_AUTH_OK))) { - mf_crypto1_decrypt(traceCrypto1, data, len, 0); - PrintAndLogEx(NORMAL, "DEC| %s", sprint_hex(data, len)); - AddLogHex(logHexFileName, "DEC| ", data, len); - } - - switch (traceState) { - case TRACE_IDLE: - // check packet crc16! - if ((len >= 4) && (!check_crc(CRC_14443_A, data, len))) { - PrintAndLogEx(NORMAL, "DEC| CRC ERROR!!!"); - AddLogLine(logHexFileName, "DEC| ", "CRC ERROR!!!"); - traceState = TRACE_ERROR; // do not decrypt the next commands - return 1; - } - - // AUTHENTICATION - if ((len == 4) && ((data[0] == MIFARE_AUTH_KEYA) || (data[0] == MIFARE_AUTH_KEYB))) { - traceState = TRACE_AUTH1; - traceCurBlock = data[1]; - traceCurKey = data[0] == 60 ? 1:0; - return 0; - } - - // READ - if ((len == 4) && ((data[0] == ISO14443A_CMD_READBLOCK))) { - traceState = TRACE_READ_DATA; - traceCurBlock = data[1]; - return 0; - } - - // WRITE - if ((len == 4) && ((data[0] == ISO14443A_CMD_WRITEBLOCK))) { - traceState = TRACE_WRITE_OK; - traceCurBlock = data[1]; - return 0; - } - - // HALT - if ((len == 4) && ((data[0] == ISO14443A_CMD_HALT) && (data[1] == 0x00))) { - traceState = TRACE_ERROR; // do not decrypt the next commands - return 0; - } - return 0; - - case TRACE_READ_DATA: - if (len == 18) { - traceState = TRACE_IDLE; - - if (isBlockTrailer(traceCurBlock)) { - memcpy(traceCard + traceCurBlock * 16 + 6, data + 6, 4); - } else { - memcpy(traceCard + traceCurBlock * 16, data, 16); - } - if (wantSaveToEmlFile) saveTraceCard(); - return 0; - } else { - traceState = TRACE_ERROR; - return 1; - } - break; - case TRACE_WRITE_OK: - if ((len == 1) && (data[0] == 0x0a)) { - traceState = TRACE_WRITE_DATA; - return 0; - } else { - traceState = TRACE_ERROR; - return 1; - } - break; - case TRACE_WRITE_DATA: - if (len == 18) { - traceState = TRACE_IDLE; - memcpy(traceCard + traceCurBlock * 16, data, 16); - if (wantSaveToEmlFile) saveTraceCard(); - return 0; - } else { - traceState = TRACE_ERROR; - return 1; - } - break; - case TRACE_AUTH1: - if (len == 4) { - traceState = TRACE_AUTH2; - nt = bytes_to_num(data, 4); - return 0; - } else { - traceState = TRACE_ERROR; - return 1; - } - break; - case TRACE_AUTH2: - if (len == 8) { - traceState = TRACE_AUTH_OK; - nr_enc = bytes_to_num(data, 4); - ar_enc = bytes_to_num(data + 4, 4); - return 0; - } else { - traceState = TRACE_ERROR; - return 1; - } - break; - case TRACE_AUTH_OK: - if (len == 4) { - traceState = TRACE_IDLE; - at_enc = bytes_to_num(data, 4); - - // mfkey64 recover key. - ks2 = ar_enc ^ prng_successor(nt, 64); - ks3 = at_enc ^ prng_successor(nt, 96); - revstate = lfsr_recovery64(ks2, ks3); - lfsr_rollback_word(revstate, 0, 0); - lfsr_rollback_word(revstate, 0, 0); - lfsr_rollback_word(revstate, nr_enc, 1); - lfsr_rollback_word(revstate, cuid ^ nt, 0); - crypto1_get_lfsr(revstate, &key); - PrintAndLogEx(SUCCESS, "found Key: [%012" PRIx64 "]", key); - - //if ( tryMfk64(cuid, nt, nr_enc, ar_enc, at_enc, &key) ) - AddLogUint64(logHexFileName, "Found Key: ", key); - - int blockShift = ((traceCurBlock & 0xFC) + 3) * 16; - if (isBlockEmpty((traceCurBlock & 0xFC) + 3)) - memcpy(traceCard + blockShift + 6, trailerAccessBytes, 4); - - // keytype A/B - if (traceCurKey) - num_to_bytes(key, 6, traceCard + blockShift + 10); - else - num_to_bytes(key, 6, traceCard + blockShift); - - if (wantSaveToEmlFile) - saveTraceCard(); - - if (traceCrypto1) - crypto1_destroy(traceCrypto1); - - // set cryptosystem state - traceCrypto1 = lfsr_recovery64(ks2, ks3); - - } else { - PrintAndLogEx(NORMAL, "[!] nested key recovery not implemented!\n"); - at_enc = bytes_to_num(data, 4); - crypto1_destroy(traceCrypto1); - traceState = TRACE_ERROR; - } - break; - default: - traceState = TRACE_ERROR; - return 1; - } - return 0; -} - -int tryDecryptWord(uint32_t nt, uint32_t ar_enc, uint32_t at_enc, uint8_t *data, int len){ - PrintAndLogEx(SUCCESS, "\nencrypted data: [%s]", sprint_hex(data, len) ); - struct Crypto1State *s; - ks2 = ar_enc ^ prng_successor(nt, 64); - ks3 = at_enc ^ prng_successor(nt, 96); - s = lfsr_recovery64(ks2, ks3); - mf_crypto1_decrypt(s, data, len, false); - PrintAndLogEx(SUCCESS, "decrypted data: [%s]", sprint_hex(data, len) ); - crypto1_destroy(s); - return 0; -} - -/* Detect Tag Prng, -* function performs a partial AUTH, where it tries to authenticate against block0, key A, but only collects tag nonce. -* the tag nonce is check to see if it has a predictable PRNG. -* @returns -* TRUE if tag uses WEAK prng (ie Now the NACK bug also needs to be present for Darkside attack) -* FALSE is tag uses HARDEND prng (ie hardnested attack possible, with known key) -*/ -int detect_classic_prng(void){ - - UsbCommand resp, respA; - uint8_t cmd[] = {MIFARE_AUTH_KEYA, 0x00}; - uint32_t flags = ISO14A_CONNECT | ISO14A_RAW | ISO14A_APPEND_CRC | ISO14A_NO_RATS; - - UsbCommand c = {CMD_READER_ISO_14443a, {flags, sizeof(cmd), 0}}; - memcpy(c.d.asBytes, cmd, sizeof(cmd)); - - clearCommandBuffer(); - SendCommand(&c); - - if (!WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { - PrintAndLogEx(WARNING, "PRNG UID: Reply timeout."); - return -1; - } - - // if select tag failed. - if ( resp.arg[0] == 0 ) { - PrintAndLogEx(WARNING, "error: selecting tag failed, can't detect prng\n"); - return -2; - } - if (!WaitForResponseTimeout(CMD_ACK, &respA, 2500)) { - PrintAndLogEx(WARNING, "PRNG data: Reply timeout."); - return -3; - } - - // check respA - if (respA.arg[0] != 4) { - PrintAndLogEx(WARNING, "PRNG data error: Wrong length: %d", respA.arg[0]); - return -4; - } - - uint32_t nonce = bytes_to_num(respA.d.asBytes, respA.arg[0]); - return validate_prng_nonce(nonce); -} -/* Detect Mifare Classic NACK bug - -returns: -0 = error during test / aborted -1 = has nack bug -2 = has not nack bug -3 = always leak nacks (clones) -*/ -int detect_classic_nackbug(bool verbose){ - - UsbCommand c = {CMD_MIFARE_NACK_DETECT, {0, 0, 0}}; - clearCommandBuffer(); - SendCommand(&c); - UsbCommand resp; - - if ( verbose ) - PrintAndLogEx(SUCCESS, "press pm3-button on the proxmark3 device to abort both proxmark3 and client.\n"); - - // for nice animation - bool term = !isatty(STDIN_FILENO); -#if defined(__linux__) || (__APPLE__) - char star[] = {'-', '\\', '|', '/'}; - uint8_t staridx = 0; -#endif - - while (true) { - - if (term) { - printf("."); - } else { - printf( - #if defined(__linux__) || (__APPLE__) - "\e[32m\e[s%c\e[u\e[0m", star[ (staridx++ % 4) ] - #else - "." - #endif - ); - } - fflush(stdout); - if (ukbhit()) { - int gc = getchar(); (void)gc; - return -1; - break; - } - - if (WaitForResponseTimeout(CMD_ACK, &resp, 500)) { - int32_t ok = resp.arg[0]; - uint32_t nacks = resp.arg[1]; - uint32_t auths = resp.arg[2]; - PrintAndLogEx(NORMAL, ""); - - if ( verbose ) { - PrintAndLogEx(SUCCESS, "num of auth requests : %u", auths); - PrintAndLogEx(SUCCESS, "num of received NACK : %u", nacks); - } - switch( ok ) { - case 99 : PrintAndLogEx(WARNING, "button pressed. Aborted."); return 0; - case 96 : - case 98 : { - if (verbose) - PrintAndLogEx(FAILED, "card random number generator is not predictable."); - PrintAndLogEx(WARNING, "detection failed"); - return 2; - } - case 97 : { - if (verbose) { - PrintAndLogEx(FAILED, "card random number generator seems to be based on the well-known generating polynomial"); - PrintAndLogEx(NORMAL, "[- ]with 16 effective bits only, but shows unexpected behavior, try again."); - return 0; - } - } - case 2 : PrintAndLogEx(SUCCESS, "always leak NACK detected"); return 3; - case 1 : PrintAndLogEx(SUCCESS, "NACK bug detected"); return 1; - case 0 : PrintAndLogEx(SUCCESS, "No NACK bug detected"); return 2; - default : PrintAndLogEx(WARNING, "errorcode from device [%i]", ok); return 0; - } - break; - } - } - return 0; -} -/* try to see if card responses to "chinese magic backdoor" commands. */ -void detect_classic_magic(void) { - - uint8_t isGeneration = 0; - UsbCommand resp; - UsbCommand c = {CMD_MIFARE_CIDENT, {0, 0, 0}}; - clearCommandBuffer(); - SendCommand(&c); - if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) - isGeneration = resp.arg[0] & 0xff; - - switch( isGeneration ){ - case 1: PrintAndLogEx(SUCCESS, "Answers to magic commands (GEN 1a): YES"); break; - case 2: PrintAndLogEx(SUCCESS, "Answers to magic commands (GEN 1b): YES"); break; - //case 4: PrintAndLogEx(SUCCESS, "Answers to magic commands (GEN 2): YES"); break; - default: PrintAndLogEx(INFO, "Answers to magic commands: NO"); break; - } +// Merlok, 2011, 2012 +// people from mifare@nethemba.com, 2010 +// +// 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. +//----------------------------------------------------------------------------- +// mifare commands +//----------------------------------------------------------------------------- +#include "mifarehost.h" +#include "cmdmain.h" + +int mfDarkside(uint8_t blockno, uint8_t key_type, uint64_t *key) { + uint32_t uid = 0; + uint32_t nt = 0, nr = 0, ar = 0; + uint64_t par_list = 0, ks_list = 0; + uint64_t *keylist = NULL, *last_keylist = NULL; + uint32_t keycount = 0; + int16_t isOK = 0; + + UsbCommand c = {CMD_READER_MIFARE, {true, blockno, key_type}}; + + // message + PrintAndLogEx(NORMAL, "--------------------------------------------------------------------------------\n"); + PrintAndLogEx(NORMAL, "executing Darkside attack. Expected execution time: 25sec on average"); + PrintAndLogEx(NORMAL, "press pm3-button on the proxmark3 device to abort both proxmark3 and client."); + PrintAndLogEx(NORMAL, "--------------------------------------------------------------------------------\n"); + + while (true) { + clearCommandBuffer(); + SendCommand(&c); + + //flush queue + while (ukbhit()) { + int gc = getchar(); (void)gc; + return -5; + } + + // wait cycle + while (true) { + printf("."); fflush(stdout); + if (ukbhit()) { + int gc = getchar(); (void)gc; + return -5; + } + + UsbCommand resp; + if (WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { + isOK = resp.arg[0]; + if (isOK < 0) + return isOK; + + uid = (uint32_t)bytes_to_num(resp.d.asBytes + 0, 4); + nt = (uint32_t)bytes_to_num(resp.d.asBytes + 4, 4); + par_list = bytes_to_num(resp.d.asBytes + 8, 8); + ks_list = bytes_to_num(resp.d.asBytes + 16, 8); + nr = (uint32_t)bytes_to_num(resp.d.asBytes + 24, 4); + ar = (uint32_t)bytes_to_num(resp.d.asBytes + 28, 4); + break; + } + } + PrintAndLogEx(NORMAL, "\n"); + + if (par_list == 0 && c.arg[0] == true) { + PrintAndLogEx(SUCCESS, "Parity is all zero. Most likely this card sends NACK on every authentication."); + } + c.arg[0] = false; + + keycount = nonce2key(uid, nt, nr, ar, par_list, ks_list, &keylist); + + if (keycount == 0) { + PrintAndLogEx(FAILED, "key not found (lfsr_common_prefix list is null). Nt=%08x", nt); + PrintAndLogEx(FAILED, "this is expected to happen in 25%% of all cases. Trying again with a different reader nonce..."); + continue; + } + + // only parity zero attack + if (par_list == 0 ) { + qsort(keylist, keycount, sizeof(*keylist), compare_uint64); + keycount = intersection(last_keylist, keylist); + if (keycount == 0) { + free(last_keylist); + last_keylist = keylist; + PrintAndLogEx(FAILED, "no candidates found, trying again"); + continue; + } + } + + PrintAndLogEx(SUCCESS, "found %u candidate key%s\n", keycount, (keycount > 1) ? "s." : "."); + + *key = -1; + uint8_t keyBlock[USB_CMD_DATA_SIZE]; + int max_keys = USB_CMD_DATA_SIZE / 6; + for (int i = 0; i < keycount; i += max_keys) { + + int size = keycount - i > max_keys ? max_keys : keycount - i; + for (int j = 0; j < size; j++) { + if (par_list == 0) { + num_to_bytes(last_keylist[i*max_keys + j], 6, keyBlock+(j*6)); + } else { + num_to_bytes(keylist[i*max_keys + j], 6, keyBlock+(j*6)); + } + } + + if (!mfCheckKeys(blockno, key_type - 0x60, false, size, keyBlock, key)) { + break; + } + } + + if (*key != -1) { + break; + } else { + PrintAndLogEx(FAILED, "all candidate keys failed. Restarting darkside attack"); + free(last_keylist); + last_keylist = keylist; + c.arg[0] = true; + } + } + free(last_keylist); + free(keylist); + return 0; +} +int mfCheckKeys(uint8_t blockNo, uint8_t keyType, bool clear_trace, uint8_t keycnt, uint8_t * keyBlock, uint64_t * key){ + *key = -1; + UsbCommand c = {CMD_MIFARE_CHKKEYS, { (blockNo | (keyType << 8)), clear_trace, keycnt}}; + memcpy(c.d.asBytes, keyBlock, 6 * keycnt); + clearCommandBuffer(); + SendCommand(&c); + UsbCommand resp; + if (!WaitForResponseTimeout(CMD_ACK, &resp, 2500)) return 1; + if ((resp.arg[0] & 0xff) != 0x01) return 2; + *key = bytes_to_num(resp.d.asBytes, 6); + return 0; +} + +// Sends chunks of keys to device. +// 0 == ok all keys found +// 1 == +// 2 == Time-out, aborting +int mfCheckKeys_fast( uint8_t sectorsCnt, uint8_t firstChunk, uint8_t lastChunk, uint8_t strategy, + uint32_t size, uint8_t *keyBlock, sector_t *e_sector, bool use_flashmemory) { + + uint64_t t2 = msclock(); + uint32_t timeout = 0; + + // send keychunk + UsbCommand c = {CMD_MIFARE_CHKKEYS_FAST, { (sectorsCnt | (firstChunk << 8) | (lastChunk << 12) ), ((use_flashmemory << 8) | strategy), size}}; + memcpy(c.d.asBytes, keyBlock, 6 * size); + clearCommandBuffer(); + SendCommand(&c); + UsbCommand resp; + + while ( !WaitForResponseTimeout(CMD_ACK, &resp, 2000) ) { + timeout++; + printf("."); fflush(stdout); + // max timeout for one chunk of 85keys, 60*3sec = 180seconds + // s70 with 40*2 keys to check, 80*85 = 6800 auth. + // takes about 97s, still some margin before abort + if (timeout > 180) { + PrintAndLogEx(WARNING, "\nno response from Proxmark. Aborting..."); + return 2; + } + } + t2 = msclock() - t2; + + // time to convert the returned data. + uint8_t curr_keys = resp.arg[0]; + + PrintAndLogEx(SUCCESS, "\nChunk: %.1fs | found %u/%u keys (%u)", (float)(t2/1000.0), curr_keys, (sectorsCnt<<1), size); + + // all keys? + if ( curr_keys == sectorsCnt*2 || lastChunk ) { + + // success array. each byte is status of key + uint8_t arr[80]; + uint64_t foo = 0; + uint16_t bar = 0; + foo = bytes_to_num(resp.d.asBytes+480, 8); + bar = (resp.d.asBytes[489] << 8 | resp.d.asBytes[488]); + + for (uint8_t i = 0; i < 64; i++) + arr[i] = (foo >> i) & 0x1; + + for (uint8_t i = 0; i < 16; i++) + arr[i+64] = (bar >> i) & 0x1; + + // initialize storage for found keys + icesector_t *tmp = calloc(sectorsCnt, sizeof(icesector_t)); + if (tmp == NULL) + return 1; + memcpy(tmp, resp.d.asBytes, sectorsCnt * sizeof(icesector_t) ); + + for ( int i = 0; i < sectorsCnt; i++) { + // key A + if ( !e_sector[i].foundKey[0] ) { + e_sector[i].Key[0] = bytes_to_num( tmp[i].keyA, 6); + e_sector[i].foundKey[0] = arr[ (i*2) ]; + } + // key B + if ( !e_sector[i].foundKey[1] ) { + e_sector[i].Key[1] = bytes_to_num( tmp[i].keyB, 6); + e_sector[i].foundKey[1] = arr[ (i*2) + 1 ]; + } + } + free(tmp); + + if ( curr_keys == sectorsCnt*2 ) + return 0; + if ( lastChunk ) + return 1; + } + return 1; +} + +// PM3 imp of J-Run mf_key_brute (part 2) +// ref: https://github.com/J-Run/mf_key_brute +int mfKeyBrute(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint64_t *resultkey){ + + #define KEYS_IN_BLOCK 85 + #define KEYBLOCK_SIZE 510 + #define CANDIDATE_SIZE 0xFFFF * 6 + uint8_t found = false; + uint64_t key64 = 0; + uint8_t candidates[CANDIDATE_SIZE] = {0x00}; + uint8_t keyBlock[KEYBLOCK_SIZE] = {0x00}; + + memset(candidates, 0, sizeof(candidates)); + memset(keyBlock, 0, sizeof(keyBlock)); + + // Generate all possible keys for the first two unknown bytes. + for (uint16_t i = 0; i < 0xFFFF; ++i) { + uint32_t j = i * 6; + candidates[0 + j] = i >> 8; + candidates[1 + j] = i; + candidates[2 + j] = key[2]; + candidates[3 + j] = key[3]; + candidates[4 + j] = key[4]; + candidates[5 + j] = key[5]; + } + uint32_t counter, i; + for ( i = 0, counter = 1; i < CANDIDATE_SIZE; i += KEYBLOCK_SIZE, ++counter){ + + key64 = 0; + + // copy candidatekeys to test key block + memcpy(keyBlock, candidates + i, KEYBLOCK_SIZE); + + // check a block of generated candidate keys. + if (!mfCheckKeys(blockNo, keyType, true, KEYS_IN_BLOCK, keyBlock, &key64)) { + *resultkey = key64; + found = true; + break; + } + + // progress + if ( counter % 20 == 0 ) + PrintAndLogEx(SUCCESS, "tried : %s.. \t %u keys", sprint_hex(candidates + i, 6), counter * KEYS_IN_BLOCK ); + } + return found; +} + +// Compare 16 Bits out of cryptostate +int Compare16Bits(const void * a, const void * b) { + if ((*(uint64_t*)b & 0x00ff000000ff0000) == (*(uint64_t*)a & 0x00ff000000ff0000)) return 0; + if ((*(uint64_t*)b & 0x00ff000000ff0000) > (*(uint64_t*)a & 0x00ff000000ff0000)) return 1; + return -1; +} + +// wrapper function for multi-threaded lfsr_recovery32 +void +#ifdef __has_attribute +#if __has_attribute(force_align_arg_pointer) +__attribute__((force_align_arg_pointer)) +#endif +#endif +*nested_worker_thread(void *arg) { + struct Crypto1State *p1; + StateList_t *statelist = arg; + statelist->head.slhead = lfsr_recovery32(statelist->ks1, statelist->nt ^ statelist->uid); + + for (p1 = statelist->head.slhead; *(uint64_t *)p1 != 0; p1++) {}; + + statelist->len = p1 - statelist->head.slhead; + statelist->tail.sltail = --p1; + qsort(statelist->head.slhead, statelist->len, sizeof(uint64_t), Compare16Bits); + + return statelist->head.slhead; +} + +int mfnested(uint8_t blockNo, uint8_t keyType, uint8_t * key, uint8_t trgBlockNo, uint8_t trgKeyType, uint8_t * resultKey, bool calibrate) { + uint16_t i; + uint32_t uid; + UsbCommand resp; + StateList_t statelists[2]; + struct Crypto1State *p1, *p2, *p3, *p4; + + UsbCommand c = {CMD_MIFARE_NESTED, {blockNo + keyType * 0x100, trgBlockNo + trgKeyType * 0x100, calibrate}}; + memcpy(c.d.asBytes, key, 6); + clearCommandBuffer(); + SendCommand(&c); + if (!WaitForResponseTimeout(CMD_ACK, &resp, 1500)) return -1; + + // error during nested + if (resp.arg[0]) return resp.arg[0]; + + memcpy(&uid, resp.d.asBytes, 4); + + for (i = 0; i < 2; i++) { + statelists[i].blockNo = resp.arg[2] & 0xff; + statelists[i].keyType = (resp.arg[2] >> 8) & 0xff; + statelists[i].uid = uid; + memcpy(&statelists[i].nt, (void *)(resp.d.asBytes + 4 + i * 8 + 0), 4); + memcpy(&statelists[i].ks1, (void *)(resp.d.asBytes + 4 + i * 8 + 4), 4); + } + + // calc keys + pthread_t thread_id[2]; + + // create and run worker threads + for (i = 0; i < 2; i++) + pthread_create(thread_id + i, NULL, nested_worker_thread, &statelists[i]); + + // wait for threads to terminate: + for (i = 0; i < 2; i++) + pthread_join(thread_id[i], (void*)&statelists[i].head.slhead); + + // the first 16 Bits of the cryptostate already contain part of our key. + // Create the intersection of the two lists based on these 16 Bits and + // roll back the cryptostate + p1 = p3 = statelists[0].head.slhead; + p2 = p4 = statelists[1].head.slhead; + + while (p1 <= statelists[0].tail.sltail && p2 <= statelists[1].tail.sltail) { + if (Compare16Bits(p1, p2) == 0) { + + struct Crypto1State savestate, *savep = &savestate; + savestate = *p1; + while(Compare16Bits(p1, savep) == 0 && p1 <= statelists[0].tail.sltail) { + *p3 = *p1; + lfsr_rollback_word(p3, statelists[0].nt ^ statelists[0].uid, 0); + p3++; + p1++; + } + savestate = *p2; + while(Compare16Bits(p2, savep) == 0 && p2 <= statelists[1].tail.sltail) { + *p4 = *p2; + lfsr_rollback_word(p4, statelists[1].nt ^ statelists[1].uid, 0); + p4++; + p2++; + } + } + else { + while (Compare16Bits(p1, p2) == -1) p1++; + while (Compare16Bits(p1, p2) == 1) p2++; + } + } + + *(uint64_t*)p3 = -1; + *(uint64_t*)p4 = -1; + statelists[0].len = p3 - statelists[0].head.slhead; + statelists[1].len = p4 - statelists[1].head.slhead; + statelists[0].tail.sltail = --p3; + statelists[1].tail.sltail = --p4; + + // the statelists now contain possible keys. The key we are searching for must be in the + // intersection of both lists + qsort(statelists[0].head.keyhead, statelists[0].len, sizeof(uint64_t), compare_uint64); + qsort(statelists[1].head.keyhead, statelists[1].len, sizeof(uint64_t), compare_uint64); + // Create the intersection + statelists[0].len = intersection(statelists[0].head.keyhead, statelists[1].head.keyhead); + + //statelists[0].tail.keytail = --p7; + uint32_t keycnt = statelists[0].len; + if ( keycnt == 0 ) goto out; + + memset(resultKey, 0, 6); + uint64_t key64 = -1; + + // The list may still contain several key candidates. Test each of them with mfCheckKeys + uint32_t max_keys = keycnt > (USB_CMD_DATA_SIZE/6) ? (USB_CMD_DATA_SIZE/6) : keycnt; + uint8_t keyBlock[USB_CMD_DATA_SIZE] = {0x00}; + + for (int i = 0; i < keycnt; i += max_keys) { + + int size = keycnt - i > max_keys ? max_keys : keycnt - i; + + for (int j = 0; j < size; j++) { + crypto1_get_lfsr(statelists[0].head.slhead + i, &key64); + num_to_bytes(key64, 6, keyBlock + i * 6); + } + + if (!mfCheckKeys(statelists[0].blockNo, statelists[0].keyType, false, size, keyBlock, &key64)) { + free(statelists[0].head.slhead); + free(statelists[1].head.slhead); + num_to_bytes(key64, 6, resultKey); + + PrintAndLogEx(SUCCESS, "target block:%3u key type: %c -- found valid key [%012" PRIx64 "]", + (uint16_t)resp.arg[2] & 0xff, + (resp.arg[2] >> 8) ? 'B' : 'A', + key64 + ); + return -5; + } + } + +out: + PrintAndLogEx(SUCCESS, "target block:%3u key type: %c", + (uint16_t)resp.arg[2] & 0xff, + (resp.arg[2] >> 8) ? 'B' : 'A' + ); + + free(statelists[0].head.slhead); + free(statelists[1].head.slhead); + return -4; +} + +// MIFARE +int mfReadSector(uint8_t sectorNo, uint8_t keyType, uint8_t *key, uint8_t *data) { + + UsbCommand c = {CMD_MIFARE_READSC, {sectorNo, keyType, 0}}; + memcpy(c.d.asBytes, key, 6); + clearCommandBuffer(); + SendCommand(&c); + + UsbCommand resp; + if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { + uint8_t isOK = resp.arg[0] & 0xff; + + if (isOK) { + memcpy(data, resp.d.asBytes, mfNumBlocksPerSector(sectorNo) * 16); + return 0; + } else { + return 1; + } + } else { + PrintAndLogEx(ERR, "Command execute timeout"); + return 2; + } + + return 0; +} + +// EMULATOR +int mfEmlGetMem(uint8_t *data, int blockNum, int blocksCount) { + UsbCommand c = {CMD_MIFARE_EML_MEMGET, {blockNum, blocksCount, 0}}; + clearCommandBuffer(); + SendCommand(&c); + UsbCommand resp; + if (!WaitForResponseTimeout(CMD_ACK, &resp, 1500)) return 1; + memcpy(data, resp.d.asBytes, blocksCount * 16); + return 0; +} + +int mfEmlSetMem(uint8_t *data, int blockNum, int blocksCount) { + return mfEmlSetMem_xt(data, blockNum, blocksCount, 16); +} + +int mfEmlSetMem_xt(uint8_t *data, int blockNum, int blocksCount, int blockBtWidth) { + UsbCommand c = {CMD_MIFARE_EML_MEMSET, {blockNum, blocksCount, blockBtWidth}}; + memcpy(c.d.asBytes, data, blocksCount * blockBtWidth); + clearCommandBuffer(); + SendCommand(&c); + return 0; +} + +// "MAGIC" CARD +int mfCSetUID(uint8_t *uid, uint8_t *atqa, uint8_t *sak, uint8_t *oldUID, uint8_t wipecard) { + + uint8_t params = MAGIC_SINGLE; + uint8_t block0[16]; + memset(block0, 0x00, sizeof(block0)); + + int old = mfCGetBlock(0, block0, params); + if (old == 0) + PrintAndLogEx(SUCCESS, "old block 0: %s", sprint_hex(block0, sizeof(block0))); + else + PrintAndLogEx(FAILED, "couldn't get old data. Will write over the last bytes of Block 0."); + + // fill in the new values + // UID + memcpy(block0, uid, 4); + // Mifare UID BCC + block0[4] = block0[0] ^ block0[1] ^ block0[2] ^ block0[3]; + // mifare classic SAK(byte 5) and ATQA(byte 6 and 7, reversed) + if ( sak != NULL ) + block0[5] = sak[0]; + + if ( atqa != NULL ) { + block0[6] = atqa[1]; + block0[7] = atqa[0]; + } + PrintAndLogEx(SUCCESS, "new block 0: %s", sprint_hex(block0,16)); + + if ( wipecard ) params |= MAGIC_WIPE; + if ( oldUID == NULL) params |= MAGIC_UID; + + return mfCSetBlock(0, block0, oldUID, params); +} + +int mfCSetBlock(uint8_t blockNo, uint8_t *data, uint8_t *uid, uint8_t params) { + + uint8_t isOK = 0; + UsbCommand c = {CMD_MIFARE_CSETBLOCK, {params, blockNo, 0}}; + memcpy(c.d.asBytes, data, 16); + clearCommandBuffer(); + SendCommand(&c); + UsbCommand resp; + if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { + isOK = resp.arg[0] & 0xff; + if (uid != NULL) + memcpy(uid, resp.d.asBytes, 4); + if (!isOK) + return 2; + } else { + PrintAndLogEx(WARNING, "command execute timeout"); + return 1; + } + return 0; +} + +int mfCGetBlock(uint8_t blockNo, uint8_t *data, uint8_t params) { + uint8_t isOK = 0; + UsbCommand c = {CMD_MIFARE_CGETBLOCK, {params, blockNo, 0}}; + clearCommandBuffer(); + SendCommand(&c); + UsbCommand resp; + if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { + isOK = resp.arg[0] & 0xff; + if (!isOK) + return 2; + memcpy(data, resp.d.asBytes, 16); + } else { + PrintAndLogEx(WARNING, "command execute timeout"); + return 1; + } + return 0; +} + +// SNIFFER +// [iceman] so many global variables.... + +// constants +static uint8_t trailerAccessBytes[4] = {0x08, 0x77, 0x8F, 0x00}; + +// variables +char logHexFileName[FILE_PATH_SIZE] = {0x00}; +static uint8_t traceCard[4096] = {0x00}; +static char traceFileName[FILE_PATH_SIZE] = {0x00}; +static int traceState = TRACE_IDLE; +static uint8_t traceCurBlock = 0; +static uint8_t traceCurKey = 0; + +struct Crypto1State *traceCrypto1 = NULL; +struct Crypto1State *revstate = NULL; +uint64_t key = 0; +uint32_t ks2 = 0; +uint32_t ks3 = 0; + +uint32_t cuid = 0; // uid part used for crypto1. +uint32_t nt = 0; // tag challenge +uint32_t nr_enc = 0; // encrypted reader challenge +uint32_t ar_enc = 0; // encrypted reader response +uint32_t at_enc = 0; // encrypted tag response + +int isTraceCardEmpty(void) { + return ((traceCard[0] == 0) && (traceCard[1] == 0) && (traceCard[2] == 0) && (traceCard[3] == 0)); +} + +int isBlockEmpty(int blockN) { + for (int i = 0; i < 16; i++) + if (traceCard[blockN * 16 + i] != 0) return 0; + + return 1; +} + +int isBlockTrailer(int blockN) { + return ((blockN & 0x03) == 0x03); +} + +int loadTraceCard(uint8_t *tuid, uint8_t uidlen) { + FILE * f; + char buf[64] = {0x00}; + uint8_t buf8[64] = {0x00}; + int i, blockNum; + uint32_t tmp; + + if (!isTraceCardEmpty()) + saveTraceCard(); + + memset(traceCard, 0x00, 4096); + memcpy(traceCard, tuid, uidlen); + + FillFileNameByUID(traceFileName, tuid, ".eml", uidlen); + + f = fopen(traceFileName, "r"); + if (!f) return 1; + + blockNum = 0; + + while (!feof(f)){ + + memset(buf, 0, sizeof(buf)); + if (fgets(buf, sizeof(buf), f) == NULL) { + PrintAndLogEx(FAILED, "No trace file found or reading error."); + if (f) { + fclose(f); + } + return 2; + } + + if (strlen(buf) < 32){ + if (feof(f)) break; + PrintAndLogEx(FAILED, "File content error. Block data must include 32 HEX symbols"); + if (f) { + fclose(f); + } + return 2; + } + for (i = 0; i < 32; i += 2) { + sscanf(&buf[i], "%02X", &tmp); + buf8[i / 2] = tmp & 0xFF; + } + + memcpy(traceCard + blockNum * 16, buf8, 16); + + blockNum++; + } + if (f) { + fclose(f); + } + return 0; +} + +int saveTraceCard(void) { + + if ((!strlen(traceFileName)) || (isTraceCardEmpty())) return 0; + + FILE * f; + f = fopen(traceFileName, "w+"); + if ( !f ) return 1; + + // given 4096 tracecard size, these loop will only match a 1024, 1kb card memory + // 4086/16 == 256blocks. + for (uint16_t i = 0; i < 256; i++) { // blocks + for (uint8_t j = 0; j < 16; j++) // bytes + fprintf(f, "%02X", *(traceCard + i * 16 + j)); + + // no extra line in the end + if ( i < 255 ) + fprintf(f, "\n"); + } + fflush(f); + fclose(f); + return 0; +} +// +int mfTraceInit(uint8_t *tuid, uint8_t uidlen, uint8_t *atqa, uint8_t sak, bool wantSaveToEmlFile) { + + if (traceCrypto1) + crypto1_destroy(traceCrypto1); + + traceCrypto1 = NULL; + + if (wantSaveToEmlFile) + loadTraceCard(tuid, uidlen); + + traceCard[4] = traceCard[0] ^ traceCard[1] ^ traceCard[2] ^ traceCard[3]; + traceCard[5] = sak; + memcpy(&traceCard[6], atqa, 2); + traceCurBlock = 0; + cuid = bytes_to_num(tuid + (uidlen-4), 4); + traceState = TRACE_IDLE; + return 0; +} + +void mf_crypto1_decrypt(struct Crypto1State *pcs, uint8_t *data, int len, bool isEncrypted){ + uint8_t bt = 0; + int i; + + if (len != 1) { + for (i = 0; i < len; i++) + data[i] = crypto1_byte(pcs, 0x00, isEncrypted) ^ data[i]; + } else { + bt = 0; + bt |= (crypto1_bit(pcs, 0, isEncrypted) ^ BIT(data[0], 0)) << 0; + bt |= (crypto1_bit(pcs, 0, isEncrypted) ^ BIT(data[0], 1)) << 1; + bt |= (crypto1_bit(pcs, 0, isEncrypted) ^ BIT(data[0], 2)) << 2; + bt |= (crypto1_bit(pcs, 0, isEncrypted) ^ BIT(data[0], 3)) << 3; + data[0] = bt; + } +} + +int mfTraceDecode(uint8_t *data_src, int len, bool wantSaveToEmlFile) { + + if (traceState == TRACE_ERROR) + return 1; + + if (len > 255) { + traceState = TRACE_ERROR; + return 1; + } + + uint8_t data[255]; + memset(data, 0x00, sizeof(data)); + + memcpy(data, data_src, len); + + if ((traceCrypto1) && ((traceState == TRACE_IDLE) || (traceState > TRACE_AUTH_OK))) { + mf_crypto1_decrypt(traceCrypto1, data, len, 0); + PrintAndLogEx(NORMAL, "DEC| %s", sprint_hex(data, len)); + AddLogHex(logHexFileName, "DEC| ", data, len); + } + + switch (traceState) { + case TRACE_IDLE: + // check packet crc16! + if ((len >= 4) && (!check_crc(CRC_14443_A, data, len))) { + PrintAndLogEx(NORMAL, "DEC| CRC ERROR!!!"); + AddLogLine(logHexFileName, "DEC| ", "CRC ERROR!!!"); + traceState = TRACE_ERROR; // do not decrypt the next commands + return 1; + } + + // AUTHENTICATION + if ((len == 4) && ((data[0] == MIFARE_AUTH_KEYA) || (data[0] == MIFARE_AUTH_KEYB))) { + traceState = TRACE_AUTH1; + traceCurBlock = data[1]; + traceCurKey = data[0] == 60 ? 1:0; + return 0; + } + + // READ + if ((len == 4) && ((data[0] == ISO14443A_CMD_READBLOCK))) { + traceState = TRACE_READ_DATA; + traceCurBlock = data[1]; + return 0; + } + + // WRITE + if ((len == 4) && ((data[0] == ISO14443A_CMD_WRITEBLOCK))) { + traceState = TRACE_WRITE_OK; + traceCurBlock = data[1]; + return 0; + } + + // HALT + if ((len == 4) && ((data[0] == ISO14443A_CMD_HALT) && (data[1] == 0x00))) { + traceState = TRACE_ERROR; // do not decrypt the next commands + return 0; + } + return 0; + + case TRACE_READ_DATA: + if (len == 18) { + traceState = TRACE_IDLE; + + if (isBlockTrailer(traceCurBlock)) { + memcpy(traceCard + traceCurBlock * 16 + 6, data + 6, 4); + } else { + memcpy(traceCard + traceCurBlock * 16, data, 16); + } + if (wantSaveToEmlFile) saveTraceCard(); + return 0; + } else { + traceState = TRACE_ERROR; + return 1; + } + break; + case TRACE_WRITE_OK: + if ((len == 1) && (data[0] == 0x0a)) { + traceState = TRACE_WRITE_DATA; + return 0; + } else { + traceState = TRACE_ERROR; + return 1; + } + break; + case TRACE_WRITE_DATA: + if (len == 18) { + traceState = TRACE_IDLE; + memcpy(traceCard + traceCurBlock * 16, data, 16); + if (wantSaveToEmlFile) saveTraceCard(); + return 0; + } else { + traceState = TRACE_ERROR; + return 1; + } + break; + case TRACE_AUTH1: + if (len == 4) { + traceState = TRACE_AUTH2; + nt = bytes_to_num(data, 4); + return 0; + } else { + traceState = TRACE_ERROR; + return 1; + } + break; + case TRACE_AUTH2: + if (len == 8) { + traceState = TRACE_AUTH_OK; + nr_enc = bytes_to_num(data, 4); + ar_enc = bytes_to_num(data + 4, 4); + return 0; + } else { + traceState = TRACE_ERROR; + return 1; + } + break; + case TRACE_AUTH_OK: + if (len == 4) { + traceState = TRACE_IDLE; + at_enc = bytes_to_num(data, 4); + + // mfkey64 recover key. + ks2 = ar_enc ^ prng_successor(nt, 64); + ks3 = at_enc ^ prng_successor(nt, 96); + revstate = lfsr_recovery64(ks2, ks3); + lfsr_rollback_word(revstate, 0, 0); + lfsr_rollback_word(revstate, 0, 0); + lfsr_rollback_word(revstate, nr_enc, 1); + lfsr_rollback_word(revstate, cuid ^ nt, 0); + crypto1_get_lfsr(revstate, &key); + PrintAndLogEx(SUCCESS, "found Key: [%012" PRIx64 "]", key); + + //if ( tryMfk64(cuid, nt, nr_enc, ar_enc, at_enc, &key) ) + AddLogUint64(logHexFileName, "Found Key: ", key); + + int blockShift = ((traceCurBlock & 0xFC) + 3) * 16; + if (isBlockEmpty((traceCurBlock & 0xFC) + 3)) + memcpy(traceCard + blockShift + 6, trailerAccessBytes, 4); + + // keytype A/B + if (traceCurKey) + num_to_bytes(key, 6, traceCard + blockShift + 10); + else + num_to_bytes(key, 6, traceCard + blockShift); + + if (wantSaveToEmlFile) + saveTraceCard(); + + if (traceCrypto1) + crypto1_destroy(traceCrypto1); + + // set cryptosystem state + traceCrypto1 = lfsr_recovery64(ks2, ks3); + + } else { + PrintAndLogEx(NORMAL, "[!] nested key recovery not implemented!\n"); + at_enc = bytes_to_num(data, 4); + crypto1_destroy(traceCrypto1); + traceState = TRACE_ERROR; + } + break; + default: + traceState = TRACE_ERROR; + return 1; + } + return 0; +} + +int tryDecryptWord(uint32_t nt, uint32_t ar_enc, uint32_t at_enc, uint8_t *data, int len){ + PrintAndLogEx(SUCCESS, "\nencrypted data: [%s]", sprint_hex(data, len) ); + struct Crypto1State *s; + ks2 = ar_enc ^ prng_successor(nt, 64); + ks3 = at_enc ^ prng_successor(nt, 96); + s = lfsr_recovery64(ks2, ks3); + mf_crypto1_decrypt(s, data, len, false); + PrintAndLogEx(SUCCESS, "decrypted data: [%s]", sprint_hex(data, len) ); + crypto1_destroy(s); + return 0; +} + +/* Detect Tag Prng, +* function performs a partial AUTH, where it tries to authenticate against block0, key A, but only collects tag nonce. +* the tag nonce is check to see if it has a predictable PRNG. +* @returns +* TRUE if tag uses WEAK prng (ie Now the NACK bug also needs to be present for Darkside attack) +* FALSE is tag uses HARDEND prng (ie hardnested attack possible, with known key) +*/ +int detect_classic_prng(void){ + + UsbCommand resp, respA; + uint8_t cmd[] = {MIFARE_AUTH_KEYA, 0x00}; + uint32_t flags = ISO14A_CONNECT | ISO14A_RAW | ISO14A_APPEND_CRC | ISO14A_NO_RATS; + + UsbCommand c = {CMD_READER_ISO_14443a, {flags, sizeof(cmd), 0}}; + memcpy(c.d.asBytes, cmd, sizeof(cmd)); + + clearCommandBuffer(); + SendCommand(&c); + + if (!WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { + PrintAndLogEx(WARNING, "PRNG UID: Reply timeout."); + return -1; + } + + // if select tag failed. + if ( resp.arg[0] == 0 ) { + PrintAndLogEx(WARNING, "error: selecting tag failed, can't detect prng\n"); + return -2; + } + if (!WaitForResponseTimeout(CMD_ACK, &respA, 2500)) { + PrintAndLogEx(WARNING, "PRNG data: Reply timeout."); + return -3; + } + + // check respA + if (respA.arg[0] != 4) { + PrintAndLogEx(WARNING, "PRNG data error: Wrong length: %d", respA.arg[0]); + return -4; + } + + uint32_t nonce = bytes_to_num(respA.d.asBytes, respA.arg[0]); + return validate_prng_nonce(nonce); +} +/* Detect Mifare Classic NACK bug + +returns: +0 = error during test / aborted +1 = has nack bug +2 = has not nack bug +3 = always leak nacks (clones) +*/ +int detect_classic_nackbug(bool verbose){ + + UsbCommand c = {CMD_MIFARE_NACK_DETECT, {0, 0, 0}}; + clearCommandBuffer(); + SendCommand(&c); + UsbCommand resp; + + if ( verbose ) + PrintAndLogEx(SUCCESS, "press pm3-button on the proxmark3 device to abort both proxmark3 and client.\n"); + + // for nice animation + bool term = !isatty(STDIN_FILENO); +#if defined(__linux__) || (__APPLE__) + char star[] = {'-', '\\', '|', '/'}; + uint8_t staridx = 0; +#endif + + while (true) { + + if (term) { + printf("."); + } else { + printf( + #if defined(__linux__) || (__APPLE__) + "\e[32m\e[s%c\e[u\e[0m", star[ (staridx++ % 4) ] + #else + "." + #endif + ); + } + fflush(stdout); + if (ukbhit()) { + int gc = getchar(); (void)gc; + return -1; + break; + } + + if (WaitForResponseTimeout(CMD_ACK, &resp, 500)) { + int32_t ok = resp.arg[0]; + uint32_t nacks = resp.arg[1]; + uint32_t auths = resp.arg[2]; + PrintAndLogEx(NORMAL, ""); + + if ( verbose ) { + PrintAndLogEx(SUCCESS, "num of auth requests : %u", auths); + PrintAndLogEx(SUCCESS, "num of received NACK : %u", nacks); + } + switch( ok ) { + case 99 : PrintAndLogEx(WARNING, "button pressed. Aborted."); return 0; + case 96 : + case 98 : { + if (verbose) + PrintAndLogEx(FAILED, "card random number generator is not predictable."); + PrintAndLogEx(WARNING, "detection failed"); + return 2; + } + case 97 : { + if (verbose) { + PrintAndLogEx(FAILED, "card random number generator seems to be based on the well-known generating polynomial"); + PrintAndLogEx(NORMAL, "[- ]with 16 effective bits only, but shows unexpected behavior, try again."); + return 0; + } + } + case 2 : PrintAndLogEx(SUCCESS, "always leak NACK detected"); return 3; + case 1 : PrintAndLogEx(SUCCESS, "NACK bug detected"); return 1; + case 0 : PrintAndLogEx(SUCCESS, "No NACK bug detected"); return 2; + default : PrintAndLogEx(WARNING, "errorcode from device [%i]", ok); return 0; + } + break; + } + } + return 0; +} +/* try to see if card responses to "chinese magic backdoor" commands. */ +void detect_classic_magic(void) { + + uint8_t isGeneration = 0; + UsbCommand resp; + UsbCommand c = {CMD_MIFARE_CIDENT, {0, 0, 0}}; + clearCommandBuffer(); + SendCommand(&c); + if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) + isGeneration = resp.arg[0] & 0xff; + + switch( isGeneration ){ + case 1: PrintAndLogEx(SUCCESS, "Answers to magic commands (GEN 1a): YES"); break; + case 2: PrintAndLogEx(SUCCESS, "Answers to magic commands (GEN 1b): YES"); break; + //case 4: PrintAndLogEx(SUCCESS, "Answers to magic commands (GEN 2): YES"); break; + default: PrintAndLogEx(INFO, "Answers to magic commands: NO"); break; + } } \ No newline at end of file diff --git a/client/mifarehost.h b/client/mifare/mifarehost.h similarity index 97% rename from client/mifarehost.h rename to client/mifare/mifarehost.h index 69458fdfa..c0633bd35 100644 --- a/client/mifarehost.h +++ b/client/mifare/mifarehost.h @@ -1,103 +1,103 @@ -// Merlok, 2011 -// people from mifare@nethemba.com, 2010 -// -// 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 ISO14443A commands -//----------------------------------------------------------------------------- -#ifndef __MIFARE_HOST_H -#define __MIFARE_HOST_H - -#include -#include -#include -#include -#include - -#include "proxmark3.h" // time_t -#include "common.h" -#include "util.h" // FILE_PATH_SIZE -#include "ui.h" // PrintAndLog... -#include "crapto1/crapto1.h" -#include "crc16.h" -#include "protocols.h" -#include "mifare.h" -#include "mfkey.h" -#include "util_posix.h" // msclock - -#define MIFARE_SECTOR_RETRY 10 - -// mifare tracer flags -#define TRACE_IDLE 0x00 -#define TRACE_AUTH1 0x01 -#define TRACE_AUTH2 0x02 -#define TRACE_AUTH_OK 0x03 -#define TRACE_READ_DATA 0x04 -#define TRACE_WRITE_OK 0x05 -#define TRACE_WRITE_DATA 0x06 -#define TRACE_ERROR 0xFF - -typedef struct { - union { - struct Crypto1State *slhead; - uint64_t *keyhead; - } head; - union { - struct Crypto1State *sltail; - uint64_t *keytail; - } tail; - uint32_t len; - uint32_t uid; - uint32_t blockNo; - uint32_t keyType; - uint32_t nt; - uint32_t ks1; -} StateList_t; - -typedef struct { - uint64_t Key[2]; - uint8_t foundKey[2]; -} sector_t; - -typedef struct { - uint8_t keyA[6]; - uint8_t keyB[6]; - //uint8_t foundKey[2]; -} icesector_t; - -extern char logHexFileName[FILE_PATH_SIZE]; - -extern int mfDarkside(uint8_t blockno, uint8_t key_type, uint64_t *key); -extern int mfnested(uint8_t blockNo, uint8_t keyType, uint8_t * key, uint8_t trgBlockNo, uint8_t trgKeyType, uint8_t * ResultKeys, bool calibrate); -extern int mfCheckKeys (uint8_t blockNo, uint8_t keyType, bool clear_trace, uint8_t keycnt, uint8_t * keyBlock, uint64_t * key); -extern int mfCheckKeys_fast( uint8_t sectorsCnt, uint8_t firstChunk, uint8_t lastChunk, - uint8_t strategy, uint32_t size, uint8_t *keyBlock, sector_t *e_sector, bool use_flashmemory); -extern int mfKeyBrute(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint64_t *resultkey); - -extern int mfReadSector(uint8_t sectorNo, uint8_t keyType, uint8_t *key, uint8_t *data); - -extern int mfEmlGetMem(uint8_t *data, int blockNum, int blocksCount); -extern int mfEmlSetMem(uint8_t *data, int blockNum, int blocksCount); -extern int mfEmlSetMem_xt(uint8_t *data, int blockNum, int blocksCount, int blockBtWidth); - -extern int mfCSetUID(uint8_t *uid, uint8_t *atqa, uint8_t *sak, uint8_t *oldUID, uint8_t wipecard); -extern int mfCSetBlock(uint8_t blockNo, uint8_t *data, uint8_t *uid, uint8_t params); -extern int mfCGetBlock(uint8_t blockNo, uint8_t *data, uint8_t params); - -extern int mfTraceInit(uint8_t *tuid, uint8_t uidlen, uint8_t *atqa, uint8_t sak, bool wantSaveToEmlFile); -extern int mfTraceDecode(uint8_t *data_src, int len, bool wantSaveToEmlFile); - -extern int isTraceCardEmpty(void); -extern int isBlockEmpty(int blockN); -extern int isBlockTrailer(int blockN); -extern int loadTraceCard(uint8_t *tuid, uint8_t uidlen); -extern int saveTraceCard(void); -extern int tryDecryptWord(uint32_t nt, uint32_t ar_enc, uint32_t at_enc, uint8_t *data, int len); - -extern int detect_classic_prng(void); -extern int detect_classic_nackbug(bool verbose); -extern void detect_classic_magic(void); -extern void mf_crypto1_decrypt(struct Crypto1State *pcs, uint8_t *data, int len, bool isEncrypted); +// Merlok, 2011 +// people from mifare@nethemba.com, 2010 +// +// 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 ISO14443A commands +//----------------------------------------------------------------------------- +#ifndef __MIFARE_HOST_H +#define __MIFARE_HOST_H + +#include +#include +#include +#include +#include + +#include "proxmark3.h" // time_t +#include "common.h" +#include "util.h" // FILE_PATH_SIZE +#include "ui.h" // PrintAndLog... +#include "crapto1/crapto1.h" +#include "crc16.h" +#include "protocols.h" +#include "mifare.h" +#include "mfkey.h" +#include "util_posix.h" // msclock + +#define MIFARE_SECTOR_RETRY 10 + +// mifare tracer flags +#define TRACE_IDLE 0x00 +#define TRACE_AUTH1 0x01 +#define TRACE_AUTH2 0x02 +#define TRACE_AUTH_OK 0x03 +#define TRACE_READ_DATA 0x04 +#define TRACE_WRITE_OK 0x05 +#define TRACE_WRITE_DATA 0x06 +#define TRACE_ERROR 0xFF + +typedef struct { + union { + struct Crypto1State *slhead; + uint64_t *keyhead; + } head; + union { + struct Crypto1State *sltail; + uint64_t *keytail; + } tail; + uint32_t len; + uint32_t uid; + uint32_t blockNo; + uint32_t keyType; + uint32_t nt; + uint32_t ks1; +} StateList_t; + +typedef struct { + uint64_t Key[2]; + uint8_t foundKey[2]; +} sector_t; + +typedef struct { + uint8_t keyA[6]; + uint8_t keyB[6]; + //uint8_t foundKey[2]; +} icesector_t; + +extern char logHexFileName[FILE_PATH_SIZE]; + +extern int mfDarkside(uint8_t blockno, uint8_t key_type, uint64_t *key); +extern int mfnested(uint8_t blockNo, uint8_t keyType, uint8_t * key, uint8_t trgBlockNo, uint8_t trgKeyType, uint8_t * ResultKeys, bool calibrate); +extern int mfCheckKeys (uint8_t blockNo, uint8_t keyType, bool clear_trace, uint8_t keycnt, uint8_t * keyBlock, uint64_t * key); +extern int mfCheckKeys_fast( uint8_t sectorsCnt, uint8_t firstChunk, uint8_t lastChunk, + uint8_t strategy, uint32_t size, uint8_t *keyBlock, sector_t *e_sector, bool use_flashmemory); +extern int mfKeyBrute(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint64_t *resultkey); + +extern int mfReadSector(uint8_t sectorNo, uint8_t keyType, uint8_t *key, uint8_t *data); + +extern int mfEmlGetMem(uint8_t *data, int blockNum, int blocksCount); +extern int mfEmlSetMem(uint8_t *data, int blockNum, int blocksCount); +extern int mfEmlSetMem_xt(uint8_t *data, int blockNum, int blocksCount, int blockBtWidth); + +extern int mfCSetUID(uint8_t *uid, uint8_t *atqa, uint8_t *sak, uint8_t *oldUID, uint8_t wipecard); +extern int mfCSetBlock(uint8_t blockNo, uint8_t *data, uint8_t *uid, uint8_t params); +extern int mfCGetBlock(uint8_t blockNo, uint8_t *data, uint8_t params); + +extern int mfTraceInit(uint8_t *tuid, uint8_t uidlen, uint8_t *atqa, uint8_t sak, bool wantSaveToEmlFile); +extern int mfTraceDecode(uint8_t *data_src, int len, bool wantSaveToEmlFile); + +extern int isTraceCardEmpty(void); +extern int isBlockEmpty(int blockN); +extern int isBlockTrailer(int blockN); +extern int loadTraceCard(uint8_t *tuid, uint8_t uidlen); +extern int saveTraceCard(void); +extern int tryDecryptWord(uint32_t nt, uint32_t ar_enc, uint32_t at_enc, uint8_t *data, int len); + +extern int detect_classic_prng(void); +extern int detect_classic_nackbug(bool verbose); +extern void detect_classic_magic(void); +extern void mf_crypto1_decrypt(struct Crypto1State *pcs, uint8_t *data, int len, bool isEncrypted); #endif \ No newline at end of file diff --git a/client/obj/mifare/.dummy b/client/obj/mifare/.dummy new file mode 100644 index 000000000..e69de29bb diff --git a/client/scripting.h b/client/scripting.h index 4d39a6c6f..81b95cbe0 100644 --- a/client/scripting.h +++ b/client/scripting.h @@ -19,7 +19,7 @@ #include "cmdmain.h" #include "comms.h" #include "util.h" -#include "mifarehost.h" +#include "mifare/mifarehost.h" #include "crc.h" #include "crc16.h" #include "crc64.h" diff --git a/include/mifare.h b/include/mifare.h index 14c0e7e27..90077f60d 100644 --- a/include/mifare.h +++ b/include/mifare.h @@ -13,6 +13,12 @@ #include "common.h" +#define MF_KEY_A 0 +#define MF_KEY_B 1 + +#define MF_MAD1_SECTOR 0x00 +#define MF_MAD2_SECTOR 0x10 + //----------------------------------------------------------------------------- // ISO 14443A //----------------------------------------------------------------------------- From db7580203b648be8cb472dc86718dba59318f38f Mon Sep 17 00:00:00 2001 From: merlokk <807634+merlokk@users.noreply.github.com> Date: Thu, 21 Feb 2019 19:34:12 +0200 Subject: [PATCH 06/21] create mad.c/h --- client/Makefile | 1 + client/cmdhfmf.c | 5 +++-- client/mifare/mad.c | 31 +++++++++++++++++++++++++++++++ client/mifare/mad.h | 26 ++++++++++++++++++++++++++ 4 files changed, 61 insertions(+), 2 deletions(-) create mode 100644 client/mifare/mad.c create mode 100644 client/mifare/mad.h diff --git a/client/Makefile b/client/Makefile index 9f8055f8e..ebbce92dd 100644 --- a/client/Makefile +++ b/client/Makefile @@ -157,6 +157,7 @@ CMDSRCS = crapto1/crapto1.c \ emv/cmdemv.c \ emv/emv_roca.c \ mifare/mifare4.c \ + mifare/mad.c \ cmdanalyse.c \ cmdhf.c \ cmdhflist.c \ diff --git a/client/cmdhfmf.c b/client/cmdhfmf.c index a12bc34c2..edc3a662f 100644 --- a/client/cmdhfmf.c +++ b/client/cmdhfmf.c @@ -10,6 +10,7 @@ #include "cmdhfmf.h" #include "mifare/mifare4.h" +#include "mifare/mad.h" #define MIFARE_4K_MAXBLOCK 256 #define MIFARE_2K_MAXBLOCK 128 @@ -3225,7 +3226,7 @@ int CmdHF14AMfMAD(const char *cmd) { for(int i = 0; i < 4; i ++) PrintAndLogEx(NORMAL, "[%d] %s", i, sprint_hex(§or[i * 16], 16)); } - /* + bool haveMAD2 = false; MAD1DecodeAndPrint(sector, verbose, &haveMAD2); @@ -3237,7 +3238,7 @@ int CmdHF14AMfMAD(const char *cmd) { MAD2DecodeAndPrint(sector, verbose); } - */ + return 0; } diff --git a/client/mifare/mad.c b/client/mifare/mad.c new file mode 100644 index 000000000..88fc2c31b --- /dev/null +++ b/client/mifare/mad.c @@ -0,0 +1,31 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2019 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. +//----------------------------------------------------------------------------- +// MIFARE Application Directory (MAD) functions +//----------------------------------------------------------------------------- + +#include "mad.h" +#include "ui.h" + +madAIDDescr madKnownAIDs[] = { + {0x0000, "free"}, + {0x0001, "defect, e.g. access keys are destroyed or unknown"}, + {0x0002, "reserved"}, + {0x0003, "contains additional directory info"}, + {0x0004, "contains card holder information in ASCII format."}, + {0x0005, "not applicable (above memory size)}"} +}; + +int MAD1DecodeAndPrint(uint8_t *sector, bool verbose, bool *haveMAD2) { + + return 0; +}; + +int MAD2DecodeAndPrint(uint8_t *sector, bool verbose) { + + return 0; +}; diff --git a/client/mifare/mad.h b/client/mifare/mad.h new file mode 100644 index 000000000..dc2b2e3c0 --- /dev/null +++ b/client/mifare/mad.h @@ -0,0 +1,26 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2019 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. +//----------------------------------------------------------------------------- +// MIFARE Application Directory (MAD) functions +//----------------------------------------------------------------------------- + +#ifndef _MAD_H_ +#define _MAD_H_ + +#include +#include + +typedef struct { + uint16_t AID; + const char *Description; +} madAIDDescr; + +int MAD1DecodeAndPrint(uint8_t *sector, bool verbose, bool *haveMAD2); +int MAD2DecodeAndPrint(uint8_t *sector, bool verbose); + + +#endif // _MAD_H_ From b9dc841bf923ef131866c8582e93d31b9839940d Mon Sep 17 00:00:00 2001 From: merlokk <807634+merlokk@users.noreply.github.com> Date: Thu, 21 Feb 2019 19:44:07 +0200 Subject: [PATCH 07/21] MAD. print GPB --- client/mifare/mad.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/client/mifare/mad.c b/client/mifare/mad.c index 88fc2c31b..03b5b3f77 100644 --- a/client/mifare/mad.c +++ b/client/mifare/mad.c @@ -22,6 +22,37 @@ madAIDDescr madKnownAIDs[] = { int MAD1DecodeAndPrint(uint8_t *sector, bool verbose, bool *haveMAD2) { + uint8_t GPB = sector[3 * 16 + 9]; + PrintAndLogEx(NORMAL, "GPB: 0x%02x", GPB); + + // DA (MAD available) + if (!(GPB & 0x80)) { + PrintAndLogEx(ERR, "DA=0! MAD not available."); + return 1; + } + + // MA (multi-application card) + if (GPB & 0x40) + PrintAndLogEx(NORMAL, "Multi application card."); + else + PrintAndLogEx(NORMAL, "Single application card."); + + uint8_t MADVer = GPB & 0x03; + + // MAD version + if ((MADVer != 0x01) && (MADVer != 0x02)) { + PrintAndLogEx(ERR, "Wrong MAD version: 0x%02x", MADVer); + return 2; + }; + + if (haveMAD2) + *haveMAD2 = (MADVer == 2); + + + + + + return 0; }; From 01c9325469e0b0a7c75c8652202be17f25fa2b40 Mon Sep 17 00:00:00 2001 From: merlokk <807634+merlokk@users.noreply.github.com> Date: Thu, 21 Feb 2019 20:06:53 +0200 Subject: [PATCH 08/21] check MAD crc --- client/mifare/mad.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/client/mifare/mad.c b/client/mifare/mad.c index 03b5b3f77..2fbc23e38 100644 --- a/client/mifare/mad.c +++ b/client/mifare/mad.c @@ -10,6 +10,7 @@ #include "mad.h" #include "ui.h" +#include "crc.h" madAIDDescr madKnownAIDs[] = { {0x0000, "free"}, @@ -48,7 +49,11 @@ int MAD1DecodeAndPrint(uint8_t *sector, bool verbose, bool *haveMAD2) { if (haveMAD2) *haveMAD2 = (MADVer == 2); - + uint8_t crc = CRC8Mad(§or[16 + 1], 31); + if (crc != sector[16]) { + PrintAndLogEx(ERR, "Wrong MAD CRC. Calculated crc: 0x%02x, from sector: 0x%02x", crc, sector[16]); + return 3; + }; From 0632918a477a90c3985c76843ce5e5d835866167 Mon Sep 17 00:00:00 2001 From: merlokk <807634+merlokk@users.noreply.github.com> Date: Thu, 21 Feb 2019 20:07:17 +0200 Subject: [PATCH 09/21] fix MAD crc calculation (was wrong) --- common/crc.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/common/crc.c b/common/crc.c index b710404cc..4d28d120e 100644 --- a/common/crc.c +++ b/common/crc.c @@ -99,14 +99,15 @@ uint32_t CRC8Maxim(uint8_t *buff, size_t size) { crc_update2(&crc, buff[i], 8); return crc_finish(&crc); } -// width=8 poly=0x1d, reversed poly=0x?? init=0xe3 refin=true refout=true xorout=0x0000 check=0xC6 name="CRC-8/MAD" +// width=8 poly=0x1d, init=0xc7 (0xe3 - WRONG! but it mentioned in MAD datasheet) refin=true refout=true xorout=0x00 name="CRC-8/MAD" // the CRC needs to be reversed before returned. +// init c7, poly 1d, final 0x00. uint32_t CRC8Mad(uint8_t *buff, size_t size) { crc_t crc; - crc_init_ref(&crc, 8, 0x1d, 0xe3, 0, true, true); + crc_init_ref(&crc, 8, 0x1d, 0xc7, 0, false, false); for ( int i = 0; i < size; ++i) crc_update2(&crc, buff[i], 8); - return reflect8(crc_finish(&crc)); + return crc_finish(&crc); } // width=4 poly=0xC, reversed poly=0x7 init=0x5 refin=true refout=true xorout=0x0000 check= name="CRC-4/LEGIC" uint32_t CRC4Legic(uint8_t *cmd, size_t size) { From c5a1b06f25a68d00ef9eb4009e9195f71ac03e83 Mon Sep 17 00:00:00 2001 From: merlokk <807634+merlokk@users.noreply.github.com> Date: Fri, 22 Feb 2019 16:58:49 +0200 Subject: [PATCH 10/21] mad comment fix --- common/crc.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/common/crc.c b/common/crc.c index 4d28d120e..77bea0b47 100644 --- a/common/crc.c +++ b/common/crc.c @@ -99,9 +99,7 @@ uint32_t CRC8Maxim(uint8_t *buff, size_t size) { crc_update2(&crc, buff[i], 8); return crc_finish(&crc); } -// width=8 poly=0x1d, init=0xc7 (0xe3 - WRONG! but it mentioned in MAD datasheet) refin=true refout=true xorout=0x00 name="CRC-8/MAD" -// the CRC needs to be reversed before returned. -// init c7, poly 1d, final 0x00. +// width=8 poly=0x1d, init=0xc7 (0xe3 - WRONG! but it mentioned in MAD datasheet) refin=false refout=false xorout=0x00 name="CRC-8/MIFARE-MAD" uint32_t CRC8Mad(uint8_t *buff, size_t size) { crc_t crc; crc_init_ref(&crc, 8, 0x1d, 0xc7, 0, false, false); From 8010f2dc5e77b3135037ba2645ca2d52de979265 Mon Sep 17 00:00:00 2001 From: merlokk <807634+merlokk@users.noreply.github.com> Date: Fri, 22 Feb 2019 18:05:45 +0200 Subject: [PATCH 11/21] MAD1 OK --- client/mifare/mad.c | 131 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 121 insertions(+), 10 deletions(-) diff --git a/client/mifare/mad.c b/client/mifare/mad.c index 2fbc23e38..e7a611384 100644 --- a/client/mifare/mad.c +++ b/client/mifare/mad.c @@ -11,16 +11,119 @@ #include "mad.h" #include "ui.h" #include "crc.h" +#include "util.h" -madAIDDescr madKnownAIDs[] = { +static madAIDDescr madKnownAIDs[] = { {0x0000, "free"}, {0x0001, "defect, e.g. access keys are destroyed or unknown"}, {0x0002, "reserved"}, {0x0003, "contains additional directory info"}, {0x0004, "contains card holder information in ASCII format."}, - {0x0005, "not applicable (above memory size)}"} + {0x0005, "not applicable (above memory size)"}, + + {0x03e1, "NDEF"}, }; +static madAIDDescr madKnownClusterCodes[] = { + {0x00, "cluster: card administration"}, + {0x01, "cluster: miscellaneous applications"}, + {0x02, "cluster: miscellaneous applications"}, + {0x03, "cluster: miscellaneous applications"}, + {0x04, "cluster: miscellaneous applications"}, + {0x05, "cluster: miscellaneous applications"}, + {0x06, "cluster: miscellaneous applications"}, + {0x07, "cluster: miscellaneous applications"}, + {0x08, "cluster: airlines"}, + {0x09, "cluster: ferry traffic"}, + {0x10, "cluster: railway services"}, + {0x11, "cluster: miscellaneous applications"}, + {0x12, "cluster: transport"}, + {0x14, "cluster: security solutions"}, + {0x18, "cluster: city traffic"}, + {0x19, "cluster: Czech Railways"}, + {0x20, "cluster: bus services"}, + {0x21, "cluster: multi modal transit"}, + {0x28, "cluster: taxi"}, + {0x30, "cluster: road toll"}, + {0x31, "cluster: generic transport"}, + {0x38, "cluster: company services"}, + {0x40, "cluster: city card services"}, + {0x47, "cluster: access control & security"}, + {0x48, "cluster: access control & security"}, + {0x49, "cluster: VIGIK"}, + {0x4A, "cluster: Ministry of Defence, Netherlands"}, + {0x4B, "cluster: Bosch Telecom, Germany"}, + {0x4C, "cluster: European Union Institutions"}, + {0x50, "cluster: ski ticketing"}, + {0x51, "cluster: access control & security"}, + {0x52, "cluster: access control & security"}, + {0x53, "cluster: access control & security"}, + {0x54, "cluster: access control & security"}, + {0x55, "cluster: SOAA standard for offline access standard"}, + {0x56, "cluster: access control & security"}, + {0x58, "cluster: academic services"}, + {0x60, "cluster: food"}, + {0x68, "cluster: non-food trade"}, + {0x70, "cluster: hotel"}, + {0x71, "cluster: loyalty"}, + {0x75, "cluster: airport services"}, + {0x78, "cluster: car rental"}, + {0x79, "cluster: Dutch government"}, + {0x80, "cluster: administration services"}, + {0x88, "cluster: electronic purse"}, + {0x90, "cluster: television"}, + {0x91, "cluster: cruise ship"}, + {0x95, "cluster: IOPTA"}, + {0x97, "cluster: metering"}, + {0x98, "cluster: telephone"}, + {0xA0, "cluster: health services"}, + {0xA8, "cluster: warehouse"}, + {0xB0, "cluster: electronic trade"}, + {0xB8, "cluster: banking"}, + {0xC0, "cluster: entertainment & sports"}, + {0xC8, "cluster: car parking"}, + {0xC9, "cluster: fleet management"}, + {0xD0, "cluster: fuel, gasoline"}, + {0xD8, "cluster: info services"}, + {0xE0, "cluster: press"}, + {0xE1, "cluster: NFC Forum"}, + {0xE8, "cluster: computer"}, + {0xF0, "cluster: mail"}, + {0xF8, "cluster: miscellaneous applications"}, +}; + +static const char unknownAID[] = ""; + +static const char *GetAIDDescription(uint16_t AID) { + for(int i = 0; i < ARRAYLEN(madKnownAIDs); i++) + if (madKnownAIDs[i].AID == AID) + return madKnownAIDs[i].Description; + + for(int i = 0; i < ARRAYLEN(madKnownClusterCodes); i++) + if (madKnownClusterCodes[i].AID == AID) + return madKnownClusterCodes[i].Description; + + return unknownAID; +} + +int madCRCCheck(uint8_t *sector, bool verbose, int MADver) { + if (MADver == 1) { + uint8_t crc = CRC8Mad(§or[16 + 1], 31); + if (crc != sector[16]) { + if (verbose) + PrintAndLogEx(ERR, "Wrong MAD%d CRC. Calculated: 0x%02x, from card: 0x%02x", MADver, crc, sector[16]); + return 3; + }; + } else { + } + + return 0; +} + +uint16_t madGetAID(uint8_t *sector, int MADver, int sectorNo) { + return (sector[16 + 2 + (sectorNo - 1) * 2] << 8) + (sector[16 + 2 + (sectorNo - 1) * 2 + 1]); +} + int MAD1DecodeAndPrint(uint8_t *sector, bool verbose, bool *haveMAD2) { uint8_t GPB = sector[3 * 16 + 9]; @@ -39,6 +142,7 @@ int MAD1DecodeAndPrint(uint8_t *sector, bool verbose, bool *haveMAD2) { PrintAndLogEx(NORMAL, "Single application card."); uint8_t MADVer = GPB & 0x03; + PrintAndLogEx(NORMAL, "MAD version: %d", MADVer); // MAD version if ((MADVer != 0x01) && (MADVer != 0x02)) { @@ -48,20 +152,27 @@ int MAD1DecodeAndPrint(uint8_t *sector, bool verbose, bool *haveMAD2) { if (haveMAD2) *haveMAD2 = (MADVer == 2); + + int res = madCRCCheck(sector, true, 1); + if (res) + return res; - uint8_t crc = CRC8Mad(§or[16 + 1], 31); - if (crc != sector[16]) { - PrintAndLogEx(ERR, "Wrong MAD CRC. Calculated crc: 0x%02x, from sector: 0x%02x", crc, sector[16]); - return 3; + if (verbose) + PrintAndLogEx(NORMAL, "CRC8-MAD OK."); + + for(int i = 1; i < 16; i++) { + uint16_t AID = madGetAID(sector, 1, i); + PrintAndLogEx(NORMAL, "%02d [%04X] %s", i, AID, GetAIDDescription(AID)); }; - - - - + return 0; }; int MAD2DecodeAndPrint(uint8_t *sector, bool verbose) { + int res = madCRCCheck(sector, true, 2); + if (res) + return res; + return 0; }; From 91ed51c673fffb26375e48e02a98bf95c2adad60 Mon Sep 17 00:00:00 2001 From: merlokk <807634+merlokk@users.noreply.github.com> Date: Fri, 22 Feb 2019 18:06:37 +0200 Subject: [PATCH 12/21] add comment --- client/mifare/mad.c | 1 + 1 file changed, 1 insertion(+) diff --git a/client/mifare/mad.c b/client/mifare/mad.c index e7a611384..2b6033588 100644 --- a/client/mifare/mad.c +++ b/client/mifare/mad.c @@ -13,6 +13,7 @@ #include "crc.h" #include "util.h" +// https://www.nxp.com/docs/en/application-note/AN10787.pdf static madAIDDescr madKnownAIDs[] = { {0x0000, "free"}, {0x0001, "defect, e.g. access keys are destroyed or unknown"}, From 5122e1289d584bb7deb9dc46b3febeabe9c1b6a5 Mon Sep 17 00:00:00 2001 From: merlokk <807634+merlokk@users.noreply.github.com> Date: Fri, 22 Feb 2019 18:24:22 +0200 Subject: [PATCH 13/21] info-byte --- client/mifare/mad.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/client/mifare/mad.c b/client/mifare/mad.c index 2b6033588..421b4844f 100644 --- a/client/mifare/mad.c +++ b/client/mifare/mad.c @@ -161,6 +161,17 @@ int MAD1DecodeAndPrint(uint8_t *sector, bool verbose, bool *haveMAD2) { if (verbose) PrintAndLogEx(NORMAL, "CRC8-MAD OK."); + // info byte + uint8_t InfoByte = sector[16 + 1] & 0x3f; + if (InfoByte) { + PrintAndLogEx(NORMAL, "Card publisher sector: 0x%02x", InfoByte); + } else { + if (verbose) + PrintAndLogEx(NORMAL, "Card publisher sector not present."); + } + if (InfoByte == 0x10 || InfoByte >= 0x28) + PrintAndLogEx(WARNING, "Info byte error"); + for(int i = 1; i < 16; i++) { uint16_t AID = madGetAID(sector, 1, i); PrintAndLogEx(NORMAL, "%02d [%04X] %s", i, AID, GetAIDDescription(AID)); From 20be5c5d9f030abae2d122edda8a94947d48ea51 Mon Sep 17 00:00:00 2001 From: merlokk <807634+merlokk@users.noreply.github.com> Date: Fri, 22 Feb 2019 19:58:50 +0200 Subject: [PATCH 14/21] some of MAD2 code --- client/cmdhfmf.c | 6 +++--- client/mifare/mad.c | 10 +++++++++- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/client/cmdhfmf.c b/client/cmdhfmf.c index edc3a662f..a7966bee8 100644 --- a/client/cmdhfmf.c +++ b/client/cmdhfmf.c @@ -3230,9 +3230,9 @@ int CmdHF14AMfMAD(const char *cmd) { bool haveMAD2 = false; MAD1DecodeAndPrint(sector, verbose, &haveMAD2); - if (haveMAD2) { - if (mfReadSector(MF_MAD2_SECTOR, MF_KEY_A, (uint8_t *)g_mifare_mad_key, sector)) { - PrintAndLogEx(ERR, "read sector 0 error. card don't have MAD or don't have MAD on default keys."); + if (haveMAD2) { // MF_MAD2_SECTOR + if (mfReadSector(1, MF_KEY_A, (uint8_t *)g_mifare_mad_key, sector)) { + PrintAndLogEx(ERR, "read sector 0x10 error. card don't have MAD or don't have MAD on default keys."); return 2; } diff --git a/client/mifare/mad.c b/client/mifare/mad.c index 421b4844f..9f28de394 100644 --- a/client/mifare/mad.c +++ b/client/mifare/mad.c @@ -122,7 +122,10 @@ int madCRCCheck(uint8_t *sector, bool verbose, int MADver) { } uint16_t madGetAID(uint8_t *sector, int MADver, int sectorNo) { - return (sector[16 + 2 + (sectorNo - 1) * 2] << 8) + (sector[16 + 2 + (sectorNo - 1) * 2 + 1]); + if (MADver == 1) + return (sector[16 + 2 + (sectorNo - 1) * 2] << 8) + (sector[16 + 2 + (sectorNo - 1) * 2 + 1]); + else + return (sector[2 + (sectorNo - 1) * 2] << 8) + (sector[2 + (sectorNo - 1) * 2 + 1]); } int MAD1DecodeAndPrint(uint8_t *sector, bool verbose, bool *haveMAD2) { @@ -185,6 +188,11 @@ int MAD2DecodeAndPrint(uint8_t *sector, bool verbose) { if (res) return res; + for(int i = 1; i < 8 + 8 + 7; i++) { + uint16_t AID = madGetAID(sector, 2, i); + PrintAndLogEx(NORMAL, "%02d [%04X] %s", i + 15, AID, GetAIDDescription(AID)); + }; + return 0; }; From d73c501ffa92ac7df553a3950725a6c8d340bce7 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Tue, 26 Feb 2019 22:27:33 +0100 Subject: [PATCH 15/21] another --- client/default_keys.dic | 1 + 1 file changed, 1 insertion(+) diff --git a/client/default_keys.dic b/client/default_keys.dic index ffbdb3cbd..cd23d9a0d 100644 --- a/client/default_keys.dic +++ b/client/default_keys.dic @@ -98,6 +98,7 @@ fc00018778f7,--VästtrafikenKeyA, RKFÖstgötaTrafikenKeyA 314B49474956,--VIGIK1KeyA 564c505f4d41,--VIGIK1KeyB ba5b895da162,--VIGIK1KeyB +4143414F5250, # # Data from: http://irq5.io/2013/04/13/decoding-bcard-conference-badges/ f4a9ef2afc6d,--BCARD KeyB From 4d79ced1a9bbeb282664efa622089243be144410 Mon Sep 17 00:00:00 2001 From: merlokk <807634+merlokk@users.noreply.github.com> Date: Fri, 1 Mar 2019 18:49:53 +0200 Subject: [PATCH 16/21] =?UTF-8?q?=D0=B0=D1=88=D1=87=D1=83=D1=8B=20=D1=88?= =?UTF-8?q?=D1=82=20=D1=8C=D1=84=D0=B2=D1=8E=D1=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/mifare/mad.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/client/mifare/mad.c b/client/mifare/mad.c index 9f28de394..3973116b4 100644 --- a/client/mifare/mad.c +++ b/client/mifare/mad.c @@ -101,7 +101,7 @@ static const char *GetAIDDescription(uint16_t AID) { return madKnownAIDs[i].Description; for(int i = 0; i < ARRAYLEN(madKnownClusterCodes); i++) - if (madKnownClusterCodes[i].AID == AID) + if (madKnownClusterCodes[i].AID == (AID >> 8)) // high byte - cluster code return madKnownClusterCodes[i].Description; return unknownAID; @@ -175,6 +175,7 @@ int MAD1DecodeAndPrint(uint8_t *sector, bool verbose, bool *haveMAD2) { if (InfoByte == 0x10 || InfoByte >= 0x28) PrintAndLogEx(WARNING, "Info byte error"); + PrintAndLogEx(NORMAL, "00 MAD1"); for(int i = 1; i < 16; i++) { uint16_t AID = madGetAID(sector, 1, i); PrintAndLogEx(NORMAL, "%02d [%04X] %s", i, AID, GetAIDDescription(AID)); @@ -184,15 +185,19 @@ int MAD1DecodeAndPrint(uint8_t *sector, bool verbose, bool *haveMAD2) { }; int MAD2DecodeAndPrint(uint8_t *sector, bool verbose) { + PrintAndLogEx(NORMAL, "16 MAD2"); + int res = madCRCCheck(sector, true, 2); if (res) return res; - for(int i = 1; i < 8 + 8 + 7; i++) { + uint8_t InfoByte = sector[1] & 0x3f; + PrintAndLogEx(NORMAL, "MAD2 Card publisher sector: 0x%02x", InfoByte); + + for(int i = 1; i < 8 + 8 + 7 + 1; i++) { uint16_t AID = madGetAID(sector, 2, i); - PrintAndLogEx(NORMAL, "%02d [%04X] %s", i + 15, AID, GetAIDDescription(AID)); - }; - + PrintAndLogEx(NORMAL, "%02d [%04X] %s", i + 16, AID, GetAIDDescription(AID)); + }; return 0; }; From 6208a6869cdb21b9b4d7330d4b1e0179a72389b8 Mon Sep 17 00:00:00 2001 From: merlokk <807634+merlokk@users.noreply.github.com> Date: Fri, 1 Mar 2019 18:50:25 +0200 Subject: [PATCH 17/21] added mad decode for mfp --- client/cmdhfmfp.c | 108 ++++++++++++++++++++++++++++++++++ client/mifare/mifaredefault.h | 1 + 2 files changed, 109 insertions(+) diff --git a/client/cmdhfmfp.c b/client/cmdhfmfp.c index 0917728be..bcbe323a9 100644 --- a/client/cmdhfmfp.c +++ b/client/cmdhfmfp.c @@ -23,6 +23,7 @@ #include "cmdhf14a.h" #include "mifare.h" #include "mifare/mifare4.h" +#include "mifare/mad.h" #include "cliparser/cliparser.h" #include "crypto/libpcrypto.h" @@ -733,6 +734,112 @@ int CmdHFMFPWrbl(const char *cmd) { return 0; } +int mfpReadSector(uint8_t sectorNo, uint8_t keyType, uint8_t *key, uint8_t *dataout, bool verbose){ + uint8_t keyn[2] = {0}; + bool plain = false; + + uint16_t uKeyNum = 0x4000 + sectorNo * 2 + (keyType ? 1 : 0); + keyn[0] = uKeyNum >> 8; + keyn[1] = uKeyNum & 0xff; + if (verbose) + PrintAndLogEx(INFO, "--sector[%d]:%02x key:%04x", mfNumBlocksPerSector(sectorNo), sectorNo, uKeyNum); + + mf4Session session; + int res = MifareAuth4(&session, keyn, key, true, true, verbose); + if (res) { + PrintAndLogEx(ERR, "Sector %d authentication error: %d", sectorNo, res); + return res; + } + + uint8_t data[250] = {0}; + int datalen = 0; + uint8_t mac[8] = {0}; + uint8_t firstBlockNo = mfFirstBlockOfSector(sectorNo); + for(int n = firstBlockNo; n < firstBlockNo + mfNumBlocksPerSector(sectorNo); n++) { + res = MFPReadBlock(&session, plain, n & 0xff, 1, false, true, data, sizeof(data), &datalen, mac); + if (res) { + PrintAndLogEx(ERR, "Sector %d read error: %d", sectorNo, res); + DropField(); + return res; + } + + if (datalen && data[0] != 0x90) { + PrintAndLogEx(ERR, "Sector %d card read error: %02x %s", sectorNo, data[0], GetErrorDescription(data[0])); + DropField(); + return 5; + } + if (datalen != 1 + 16 + 8 + 2) { + PrintAndLogEx(ERR, "Sector %d error returned data length:%d", sectorNo, datalen); + DropField(); + return 6; + } + + memcpy(&dataout[(n - firstBlockNo) * 16], &data[1], 16); + + if (verbose) + PrintAndLogEx(INFO, "data[%03d]: %s", n, sprint_hex(&data[1], 16)); + + if (memcmp(&data[1 + 16], mac, 8)) { + PrintAndLogEx(WARNING, "WARNING: mac on block %d not equal...", n); + PrintAndLogEx(WARNING, "MAC card: %s", sprint_hex(&data[1 + 16], 8)); + PrintAndLogEx(WARNING, "MAC reader: %s", sprint_hex(mac, 8)); + + if (!verbose) + return 7; + } else { + if(verbose) + PrintAndLogEx(INFO, "MAC: %s", sprint_hex(&data[1 + 16], 8)); + } + } + DropField(); + + return 0; +} + +int CmdHFMFPMAD(const char *cmd) { + + CLIParserInit("hf mfp mad", + "Checks and prints Mifare Application Directory (MAD)", + "Usage:\n\thf mfp mad -> shows MAD if exists\n"); + + void* argtable[] = { + arg_param_begin, + arg_lit0("vV", "verbose", "show technical data"), + arg_param_end + }; + CLIExecWithReturn(cmd, argtable, true); + bool verbose = arg_get_lit(1); + + CLIParserFree(); + + uint8_t sector[16 * 4] = {0}; + if (mfpReadSector(MF_MAD1_SECTOR, MF_KEY_A, (uint8_t *)g_mifarep_mad_key, sector, verbose)) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(ERR, "read sector 0 error. card don't have MAD or don't have MAD on default keys."); + return 2; + } + + if (verbose) { + for(int i = 0; i < 4; i ++) + PrintAndLogEx(NORMAL, "[%d] %s", i, sprint_hex(§or[i * 16], 16)); + } + + bool haveMAD2 = false; + MAD1DecodeAndPrint(sector, verbose, &haveMAD2); + + if (haveMAD2) { + if (mfpReadSector(MF_MAD2_SECTOR, MF_KEY_A, (uint8_t *)g_mifarep_mad_key, sector, verbose)) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(ERR, "read sector 0x10 error. card don't have MAD or don't have MAD on default keys."); + return 2; + } + + MAD2DecodeAndPrint(sector, verbose); + } + + return 0; +} + static command_t CommandTable[] = { {"help", CmdHelp, 1, "This help"}, @@ -744,6 +851,7 @@ static command_t CommandTable[] = {"rdbl", CmdHFMFPRdbl, 0, "Read blocks"}, {"rdsc", CmdHFMFPRdsc, 0, "Read sectors"}, {"wrbl", CmdHFMFPWrbl, 0, "Write blocks"}, + {"mad", CmdHFMFPMAD, 0, "Checks and prints MAD"}, {NULL, NULL, 0, NULL} }; diff --git a/client/mifare/mifaredefault.h b/client/mifare/mifaredefault.h index db9c0329d..e7218cbe7 100644 --- a/client/mifare/mifaredefault.h +++ b/client/mifare/mifaredefault.h @@ -44,5 +44,6 @@ static const uint64_t g_mifare_default_keys[] = static const uint8_t g_mifare_mad_key[] = {0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5}; static const uint8_t g_mifare_ndef_key[] = {0xd3, 0xf7, 0xd3, 0xf7, 0xd3, 0xf7}; +static const uint8_t g_mifarep_mad_key[] = {0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7}; #endif From 5cc4bea4e3f44a322902ab4f3553a8490508804b Mon Sep 17 00:00:00 2001 From: merlokk <807634+merlokk@users.noreply.github.com> Date: Fri, 1 Mar 2019 19:05:51 +0200 Subject: [PATCH 18/21] mfp refactoring --- client/cmdhfmfp.c | 181 +++------------------------------------- client/mifare/mifare4.c | 157 ++++++++++++++++++++++++++++++++++ client/mifare/mifare4.h | 9 ++ 3 files changed, 178 insertions(+), 169 deletions(-) diff --git a/client/cmdhfmfp.c b/client/cmdhfmfp.c index bcbe323a9..43c8d645d 100644 --- a/client/cmdhfmfp.c +++ b/client/cmdhfmfp.c @@ -29,103 +29,8 @@ static const uint8_t DefaultKey[16] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; -typedef struct { - uint8_t Code; - const char *Description; -} PlusErrorsElm; - -static const PlusErrorsElm PlusErrors[] = { - {0xFF, ""}, - {0x00, "Transfer cannot be granted within the current authentication."}, - {0x06, "Access Conditions not fulfilled. Block does not exist, block is not a value block."}, - {0x07, "Too many read or write commands in the session or in the transaction."}, - {0x08, "Invalid MAC in command or response"}, - {0x09, "Block Number is not valid"}, - {0x0a, "Invalid block number, not existing block number"}, - {0x0b, "The current command code not available at the current card state."}, - {0x0c, "Length error"}, - {0x0f, "General Manipulation Error. Failure in the operation of the PICC (cannot write to the data block), etc."}, - {0x90, "OK"}, -}; -int PlusErrorsLen = sizeof(PlusErrors) / sizeof(PlusErrorsElm); - -const char * GetErrorDescription(uint8_t errorCode) { - for(int i = 0; i < PlusErrorsLen; i++) - if (errorCode == PlusErrors[i].Code) - return PlusErrors[i].Description; - - return PlusErrors[0].Description; -} - static int CmdHelp(const char *Cmd); -static bool VerboseMode = false; -void SetVerboseMode(bool verbose) { - VerboseMode = verbose; -} - -int intExchangeRAW14aPlus(uint8_t *datain, int datainlen, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen) { - if(VerboseMode) - PrintAndLogEx(INFO, ">>> %s", sprint_hex(datain, datainlen)); - - int res = ExchangeRAW14a(datain, datainlen, activateField, leaveSignalON, dataout, maxdataoutlen, dataoutlen); - - if(VerboseMode) - PrintAndLogEx(INFO, "<<< %s", sprint_hex(dataout, *dataoutlen)); - - return res; -} - -int MFPWritePerso(uint8_t *keyNum, uint8_t *key, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen) { - uint8_t rcmd[3 + 16] = {0xa8, keyNum[1], keyNum[0], 0x00}; - memmove(&rcmd[3], key, 16); - - return intExchangeRAW14aPlus(rcmd, sizeof(rcmd), activateField, leaveSignalON, dataout, maxdataoutlen, dataoutlen); -} - -int MFPCommitPerso(bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen) { - uint8_t rcmd[1] = {0xaa}; - - return intExchangeRAW14aPlus(rcmd, sizeof(rcmd), activateField, leaveSignalON, dataout, maxdataoutlen, dataoutlen); -} - -int MFPReadBlock(mf4Session *session, bool plain, uint8_t blockNum, uint8_t blockCount, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen, uint8_t *mac) { - uint8_t rcmd[4 + 8] = {(plain?(0x37):(0x33)), blockNum, 0x00, blockCount}; - if (!plain && session) - CalculateMAC(session, mtypReadCmd, blockNum, blockCount, rcmd, 4, &rcmd[4], VerboseMode); - - int res = intExchangeRAW14aPlus(rcmd, plain?4:sizeof(rcmd), activateField, leaveSignalON, dataout, maxdataoutlen, dataoutlen); - if(res) - return res; - - if (session) - session->R_Ctr++; - - if(session && mac && *dataoutlen > 11) - CalculateMAC(session, mtypReadResp, blockNum, blockCount, dataout, *dataoutlen - 8 - 2, mac, VerboseMode); - - return 0; -} - -int MFPWriteBlock(mf4Session *session, uint8_t blockNum, uint8_t *data, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen, uint8_t *mac) { - uint8_t rcmd[1 + 2 + 16 + 8] = {0xA3, blockNum, 0x00}; - memmove(&rcmd[3], data, 16); - if (session) - CalculateMAC(session, mtypWriteCmd, blockNum, 1, rcmd, 19, &rcmd[19], VerboseMode); - - int res = intExchangeRAW14aPlus(rcmd, sizeof(rcmd), activateField, leaveSignalON, dataout, maxdataoutlen, dataoutlen); - if(res) - return res; - - if (session) - session->W_Ctr++; - - if(session && mac && *dataoutlen > 3) - CalculateMAC(session, mtypWriteResp, blockNum, 1, dataout, *dataoutlen, mac, VerboseMode); - - return 0; -} - int CmdHFMFPInfo(const char *cmd) { if (cmd && strlen(cmd) > 0) @@ -230,7 +135,7 @@ int CmdHFMFPWritePerso(const char *cmd) { CLIGetHexWithReturn(3, key, &keyLen); CLIParserFree(); - SetVerboseMode(verbose); + mfpSetVerboseMode(verbose); if (!keyLen) { memmove(key, DefaultKey, 16); @@ -261,7 +166,7 @@ int CmdHFMFPWritePerso(const char *cmd) { } if (data[0] != 0x90) { - PrintAndLogEx(ERR, "Command error: %02x %s", data[0], GetErrorDescription(data[0])); + PrintAndLogEx(ERR, "Command error: %02x %s", data[0], mfpGetErrorDescription(data[0])); return 1; } PrintAndLogEx(INFO, "Write OK."); @@ -305,7 +210,7 @@ int CmdHFMFPInitPerso(const char *cmd) { if (!keyLen) memmove(key, DefaultKey, 16); - SetVerboseMode(verbose2); + mfpSetVerboseMode(verbose2); for (uint16_t sn = 0x4000; sn < 0x4050; sn++) { keyNum[0] = sn >> 8; keyNum[1] = sn & 0xff; @@ -320,7 +225,7 @@ int CmdHFMFPInitPerso(const char *cmd) { } } - SetVerboseMode(verbose); + mfpSetVerboseMode(verbose); for (int i = 0; i < sizeof(CardAddresses) / 2; i++) { keyNum[0] = CardAddresses[i] >> 8; keyNum[1] = CardAddresses[i] & 0xff; @@ -361,7 +266,7 @@ int CmdHFMFPCommitPerso(const char *cmd) { bool verbose = arg_get_lit(1); CLIParserFree(); - SetVerboseMode(verbose); + mfpSetVerboseMode(verbose); uint8_t data[250] = {0}; int datalen = 0; @@ -378,7 +283,7 @@ int CmdHFMFPCommitPerso(const char *cmd) { } if (data[0] != 0x90) { - PrintAndLogEx(ERR, "Command error: %02x %s", data[0], GetErrorDescription(data[0])); + PrintAndLogEx(ERR, "Command error: %02x %s", data[0], mfpGetErrorDescription(data[0])); return 1; } PrintAndLogEx(INFO, "Switch level OK."); @@ -454,7 +359,7 @@ int CmdHFMFPRdbl(const char *cmd) { CLIGetHexWithReturn(6, key, &keylen); CLIParserFree(); - SetVerboseMode(verbose); + mfpSetVerboseMode(verbose); if (!keylen) { memmove(key, DefaultKey, 16); @@ -505,7 +410,7 @@ int CmdHFMFPRdbl(const char *cmd) { } if (datalen && data[0] != 0x90) { - PrintAndLogEx(ERR, "Card read error: %02x %s", data[0], GetErrorDescription(data[0])); + PrintAndLogEx(ERR, "Card read error: %02x %s", data[0], mfpGetErrorDescription(data[0])); return 6; } @@ -564,7 +469,7 @@ int CmdHFMFPRdsc(const char *cmd) { CLIGetHexWithReturn(5, key, &keylen); CLIParserFree(); - SetVerboseMode(verbose); + mfpSetVerboseMode(verbose); if (!keylen) { memmove(key, DefaultKey, 16); @@ -606,7 +511,7 @@ int CmdHFMFPRdsc(const char *cmd) { } if (datalen && data[0] != 0x90) { - PrintAndLogEx(ERR, "Card read error: %02x %s", data[0], GetErrorDescription(data[0])); + PrintAndLogEx(ERR, "Card read error: %02x %s", data[0], mfpGetErrorDescription(data[0])); DropField(); return 6; } @@ -662,7 +567,7 @@ int CmdHFMFPWrbl(const char *cmd) { CLIGetHexWithReturn(5, key, &keylen); CLIParserFree(); - SetVerboseMode(verbose); + mfpSetVerboseMode(verbose); if (!keylen) { memmove(key, DefaultKey, 16); @@ -715,7 +620,7 @@ int CmdHFMFPWrbl(const char *cmd) { } if (datalen && data[0] != 0x90) { - PrintAndLogEx(ERR, "Card write error: %02x %s", data[0], GetErrorDescription(data[0])); + PrintAndLogEx(ERR, "Card write error: %02x %s", data[0], mfpGetErrorDescription(data[0])); DropField(); return 6; } @@ -734,68 +639,6 @@ int CmdHFMFPWrbl(const char *cmd) { return 0; } -int mfpReadSector(uint8_t sectorNo, uint8_t keyType, uint8_t *key, uint8_t *dataout, bool verbose){ - uint8_t keyn[2] = {0}; - bool plain = false; - - uint16_t uKeyNum = 0x4000 + sectorNo * 2 + (keyType ? 1 : 0); - keyn[0] = uKeyNum >> 8; - keyn[1] = uKeyNum & 0xff; - if (verbose) - PrintAndLogEx(INFO, "--sector[%d]:%02x key:%04x", mfNumBlocksPerSector(sectorNo), sectorNo, uKeyNum); - - mf4Session session; - int res = MifareAuth4(&session, keyn, key, true, true, verbose); - if (res) { - PrintAndLogEx(ERR, "Sector %d authentication error: %d", sectorNo, res); - return res; - } - - uint8_t data[250] = {0}; - int datalen = 0; - uint8_t mac[8] = {0}; - uint8_t firstBlockNo = mfFirstBlockOfSector(sectorNo); - for(int n = firstBlockNo; n < firstBlockNo + mfNumBlocksPerSector(sectorNo); n++) { - res = MFPReadBlock(&session, plain, n & 0xff, 1, false, true, data, sizeof(data), &datalen, mac); - if (res) { - PrintAndLogEx(ERR, "Sector %d read error: %d", sectorNo, res); - DropField(); - return res; - } - - if (datalen && data[0] != 0x90) { - PrintAndLogEx(ERR, "Sector %d card read error: %02x %s", sectorNo, data[0], GetErrorDescription(data[0])); - DropField(); - return 5; - } - if (datalen != 1 + 16 + 8 + 2) { - PrintAndLogEx(ERR, "Sector %d error returned data length:%d", sectorNo, datalen); - DropField(); - return 6; - } - - memcpy(&dataout[(n - firstBlockNo) * 16], &data[1], 16); - - if (verbose) - PrintAndLogEx(INFO, "data[%03d]: %s", n, sprint_hex(&data[1], 16)); - - if (memcmp(&data[1 + 16], mac, 8)) { - PrintAndLogEx(WARNING, "WARNING: mac on block %d not equal...", n); - PrintAndLogEx(WARNING, "MAC card: %s", sprint_hex(&data[1 + 16], 8)); - PrintAndLogEx(WARNING, "MAC reader: %s", sprint_hex(mac, 8)); - - if (!verbose) - return 7; - } else { - if(verbose) - PrintAndLogEx(INFO, "MAC: %s", sprint_hex(&data[1 + 16], 8)); - } - } - DropField(); - - return 0; -} - int CmdHFMFPMAD(const char *cmd) { CLIParserInit("hf mfp mad", diff --git a/client/mifare/mifare4.c b/client/mifare/mifare4.c index 0c53a1f9b..3fe67077f 100644 --- a/client/mifare/mifare4.c +++ b/client/mifare/mifare4.c @@ -17,6 +17,39 @@ #include "ui.h" #include "crypto/libpcrypto.h" +static bool VerboseMode = false; +void mfpSetVerboseMode(bool verbose) { + VerboseMode = verbose; +} + +typedef struct { + uint8_t Code; + const char *Description; +} PlusErrorsElm; + +static const PlusErrorsElm PlusErrors[] = { + {0xFF, ""}, + {0x00, "Transfer cannot be granted within the current authentication."}, + {0x06, "Access Conditions not fulfilled. Block does not exist, block is not a value block."}, + {0x07, "Too many read or write commands in the session or in the transaction."}, + {0x08, "Invalid MAC in command or response"}, + {0x09, "Block Number is not valid"}, + {0x0a, "Invalid block number, not existing block number"}, + {0x0b, "The current command code not available at the current card state."}, + {0x0c, "Length error"}, + {0x0f, "General Manipulation Error. Failure in the operation of the PICC (cannot write to the data block), etc."}, + {0x90, "OK"}, +}; +int PlusErrorsLen = sizeof(PlusErrors) / sizeof(PlusErrorsElm); + +const char * mfpGetErrorDescription(uint8_t errorCode) { + for(int i = 0; i < PlusErrorsLen; i++) + if (errorCode == PlusErrors[i].Code) + return PlusErrors[i].Description; + + return PlusErrors[0].Description; +} + AccessConditions_t MFAccessConditions[] = { {0x00, "rdAB wrAB incAB dectrAB"}, {0x01, "rdAB dectrAB"}, @@ -274,6 +307,130 @@ int MifareAuth4(mf4Session *session, uint8_t *keyn, uint8_t *key, bool activateF return 0; } +int intExchangeRAW14aPlus(uint8_t *datain, int datainlen, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen) { + if(VerboseMode) + PrintAndLogEx(INFO, ">>> %s", sprint_hex(datain, datainlen)); + + int res = ExchangeRAW14a(datain, datainlen, activateField, leaveSignalON, dataout, maxdataoutlen, dataoutlen); + + if(VerboseMode) + PrintAndLogEx(INFO, "<<< %s", sprint_hex(dataout, *dataoutlen)); + + return res; +} + +int MFPWritePerso(uint8_t *keyNum, uint8_t *key, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen) { + uint8_t rcmd[3 + 16] = {0xa8, keyNum[1], keyNum[0], 0x00}; + memmove(&rcmd[3], key, 16); + + return intExchangeRAW14aPlus(rcmd, sizeof(rcmd), activateField, leaveSignalON, dataout, maxdataoutlen, dataoutlen); +} + +int MFPCommitPerso(bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen) { + uint8_t rcmd[1] = {0xaa}; + + return intExchangeRAW14aPlus(rcmd, sizeof(rcmd), activateField, leaveSignalON, dataout, maxdataoutlen, dataoutlen); +} + +int MFPReadBlock(mf4Session *session, bool plain, uint8_t blockNum, uint8_t blockCount, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen, uint8_t *mac) { + uint8_t rcmd[4 + 8] = {(plain?(0x37):(0x33)), blockNum, 0x00, blockCount}; + if (!plain && session) + CalculateMAC(session, mtypReadCmd, blockNum, blockCount, rcmd, 4, &rcmd[4], VerboseMode); + + int res = intExchangeRAW14aPlus(rcmd, plain?4:sizeof(rcmd), activateField, leaveSignalON, dataout, maxdataoutlen, dataoutlen); + if(res) + return res; + + if (session) + session->R_Ctr++; + + if(session && mac && *dataoutlen > 11) + CalculateMAC(session, mtypReadResp, blockNum, blockCount, dataout, *dataoutlen - 8 - 2, mac, VerboseMode); + + return 0; +} + +int MFPWriteBlock(mf4Session *session, uint8_t blockNum, uint8_t *data, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen, uint8_t *mac) { + uint8_t rcmd[1 + 2 + 16 + 8] = {0xA3, blockNum, 0x00}; + memmove(&rcmd[3], data, 16); + if (session) + CalculateMAC(session, mtypWriteCmd, blockNum, 1, rcmd, 19, &rcmd[19], VerboseMode); + + int res = intExchangeRAW14aPlus(rcmd, sizeof(rcmd), activateField, leaveSignalON, dataout, maxdataoutlen, dataoutlen); + if(res) + return res; + + if (session) + session->W_Ctr++; + + if(session && mac && *dataoutlen > 3) + CalculateMAC(session, mtypWriteResp, blockNum, 1, dataout, *dataoutlen, mac, VerboseMode); + + return 0; +} + +int mfpReadSector(uint8_t sectorNo, uint8_t keyType, uint8_t *key, uint8_t *dataout, bool verbose){ + uint8_t keyn[2] = {0}; + bool plain = false; + + uint16_t uKeyNum = 0x4000 + sectorNo * 2 + (keyType ? 1 : 0); + keyn[0] = uKeyNum >> 8; + keyn[1] = uKeyNum & 0xff; + if (verbose) + PrintAndLogEx(INFO, "--sector[%d]:%02x key:%04x", mfNumBlocksPerSector(sectorNo), sectorNo, uKeyNum); + + mf4Session session; + int res = MifareAuth4(&session, keyn, key, true, true, verbose); + if (res) { + PrintAndLogEx(ERR, "Sector %d authentication error: %d", sectorNo, res); + return res; + } + + uint8_t data[250] = {0}; + int datalen = 0; + uint8_t mac[8] = {0}; + uint8_t firstBlockNo = mfFirstBlockOfSector(sectorNo); + for(int n = firstBlockNo; n < firstBlockNo + mfNumBlocksPerSector(sectorNo); n++) { + res = MFPReadBlock(&session, plain, n & 0xff, 1, false, true, data, sizeof(data), &datalen, mac); + if (res) { + PrintAndLogEx(ERR, "Sector %d read error: %d", sectorNo, res); + DropField(); + return res; + } + + if (datalen && data[0] != 0x90) { + PrintAndLogEx(ERR, "Sector %d card read error: %02x %s", sectorNo, data[0], mfpGetErrorDescription(data[0])); + DropField(); + return 5; + } + if (datalen != 1 + 16 + 8 + 2) { + PrintAndLogEx(ERR, "Sector %d error returned data length:%d", sectorNo, datalen); + DropField(); + return 6; + } + + memcpy(&dataout[(n - firstBlockNo) * 16], &data[1], 16); + + if (verbose) + PrintAndLogEx(INFO, "data[%03d]: %s", n, sprint_hex(&data[1], 16)); + + if (memcmp(&data[1 + 16], mac, 8)) { + PrintAndLogEx(WARNING, "WARNING: mac on block %d not equal...", n); + PrintAndLogEx(WARNING, "MAC card: %s", sprint_hex(&data[1 + 16], 8)); + PrintAndLogEx(WARNING, "MAC reader: %s", sprint_hex(mac, 8)); + + if (!verbose) + return 7; + } else { + if(verbose) + PrintAndLogEx(INFO, "MAC: %s", sprint_hex(&data[1 + 16], 8)); + } + } + DropField(); + + return 0; +} + // Mifare Memory Structure: up to 32 Sectors with 4 blocks each (1k and 2k cards), // plus evtl. 8 sectors with 16 blocks each (4k cards) uint8_t mfNumBlocksPerSector(uint8_t sectorNo) { diff --git a/client/mifare/mifare4.h b/client/mifare/mifare4.h index 011567a1e..e4bf6e386 100644 --- a/client/mifare/mifare4.h +++ b/client/mifare/mifare4.h @@ -43,9 +43,18 @@ typedef struct { char *description; } AccessConditions_t; +extern void mfpSetVerboseMode(bool verbose); +extern const char * mfpGetErrorDescription(uint8_t errorCode); + extern int CalculateMAC(mf4Session *session, MACType_t mtype, uint8_t blockNum, uint8_t blockCount, uint8_t *data, int datalen, uint8_t *mac, bool verbose); extern int MifareAuth4(mf4Session *session, uint8_t *keyn, uint8_t *key, bool activateField, bool leaveSignalON, bool verbose); +extern int MFPWritePerso(uint8_t *keyNum, uint8_t *key, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen); +extern int MFPCommitPerso(bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen); +extern int MFPReadBlock(mf4Session *session, bool plain, uint8_t blockNum, uint8_t blockCount, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen, uint8_t *mac); +extern int MFPWriteBlock(mf4Session *session, uint8_t blockNum, uint8_t *data, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen, uint8_t *mac); +extern int mfpReadSector(uint8_t sectorNo, uint8_t keyType, uint8_t *key, uint8_t *dataout, bool verbose); + extern char *mfGetAccessConditionsDesc(uint8_t blockn, uint8_t *data); extern uint8_t mfNumBlocksPerSector(uint8_t sectorNo); From 979aa678ad7c70ea62541bf2019e060f359f23ae Mon Sep 17 00:00:00 2001 From: merlokk <807634+merlokk@users.noreply.github.com> Date: Fri, 1 Mar 2019 19:07:34 +0200 Subject: [PATCH 19/21] small fix --- client/cmdhfmf.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/cmdhfmf.c b/client/cmdhfmf.c index a7966bee8..eea26f170 100644 --- a/client/cmdhfmf.c +++ b/client/cmdhfmf.c @@ -3230,8 +3230,8 @@ int CmdHF14AMfMAD(const char *cmd) { bool haveMAD2 = false; MAD1DecodeAndPrint(sector, verbose, &haveMAD2); - if (haveMAD2) { // MF_MAD2_SECTOR - if (mfReadSector(1, MF_KEY_A, (uint8_t *)g_mifare_mad_key, sector)) { + if (haveMAD2) { + if (mfReadSector(MF_MAD2_SECTOR, MF_KEY_A, (uint8_t *)g_mifare_mad_key, sector)) { PrintAndLogEx(ERR, "read sector 0x10 error. card don't have MAD or don't have MAD on default keys."); return 2; } From 3a98c9fcd32cf9b4c6c10705d25df46fe7b57061 Mon Sep 17 00:00:00 2001 From: merlokk <807634+merlokk@users.noreply.github.com> Date: Fri, 1 Mar 2019 19:13:59 +0200 Subject: [PATCH 20/21] fix mad2 crc8 check --- client/mifare/mad.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/client/mifare/mad.c b/client/mifare/mad.c index 3973116b4..ff6991f22 100644 --- a/client/mifare/mad.c +++ b/client/mifare/mad.c @@ -109,13 +109,19 @@ static const char *GetAIDDescription(uint16_t AID) { int madCRCCheck(uint8_t *sector, bool verbose, int MADver) { if (MADver == 1) { - uint8_t crc = CRC8Mad(§or[16 + 1], 31); + uint8_t crc = CRC8Mad(§or[16 + 1], 15 + 16); if (crc != sector[16]) { if (verbose) PrintAndLogEx(ERR, "Wrong MAD%d CRC. Calculated: 0x%02x, from card: 0x%02x", MADver, crc, sector[16]); return 3; }; } else { + uint8_t crc = CRC8Mad(§or[1], 15 + 16 + 16); + if (crc != sector[0]) { + if (verbose) + PrintAndLogEx(ERR, "Wrong MAD%d CRC. Calculated: 0x%02x, from card: 0x%02x", MADver, crc, sector[16]); + return 3; + }; } return 0; @@ -191,6 +197,9 @@ int MAD2DecodeAndPrint(uint8_t *sector, bool verbose) { if (res) return res; + if (verbose) + PrintAndLogEx(NORMAL, "CRC8-MAD OK."); + uint8_t InfoByte = sector[1] & 0x3f; PrintAndLogEx(NORMAL, "MAD2 Card publisher sector: 0x%02x", InfoByte); From e8c0a49d076519b37f05cbe0f0c1d0a3dd91ceb2 Mon Sep 17 00:00:00 2001 From: merlokk <807634+merlokk@users.noreply.github.com> Date: Fri, 1 Mar 2019 19:39:17 +0200 Subject: [PATCH 21/21] changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 673b25f8b..a99c8c522 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -102,6 +102,7 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - Added `hf emv` `gpo`, `readrec`, `genac`, `challenge`, `intauth` - separate commands from `hf emc exec` (@merlokk) - Added `hf fido` `assert` and `make` commands from fido2 protocol (authenticatorMakeCredential and authenticatorGetAssertion) (@merlokk) - Added trailer block decoding to `hf mf rdbl` and `hf mf cgetbl` (@merlokk) + - Added `hf mf mad` and `hf mfp mad` MAD decode, check and print commands (@merlokk) ### Fixed - Changed driver file proxmark3.inf to support both old and new Product/Vendor IDs (piwi)