diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c3781798..41d6b5eec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ All notable changes to this project will be documented in this file. This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log... ## [unreleased][unreleased] + - Added support to demodulate Electra tags and column parity check for EM410x (@doegox) - Fix demod plot for various demodulations (@doegox) - Fix `lf t55xx detect/rdbl/dump` - to override if user set `lf config` and use default values during operation (@iceman1001) - Added `hf iclass encode --wiegand/--fc/--cn` - direct fmt/fc/cn support (@bettse) diff --git a/armsrc/Standalone/lf_icehid.c b/armsrc/Standalone/lf_icehid.c index 0b1e6cbfe..01b164afd 100644 --- a/armsrc/Standalone/lf_icehid.c +++ b/armsrc/Standalone/lf_icehid.c @@ -96,8 +96,9 @@ static uint32_t IceEM410xdemod(void) { return PM3_ESOFT; } - errCnt = Em410xDecode(dest, &size, &idx, &hi, &lo); - if (errCnt != 1) { + int type = Em410xDecode(dest, &size, &idx, &hi, &lo); + // Did we find a Short EM or a Long EM? + if ((type & (0x1 | 0x2)) == 0) { BigBuf_free(); return PM3_ESOFT; } diff --git a/armsrc/lfops.c b/armsrc/lfops.c index 10ee379cd..2f5fb47e4 100644 --- a/armsrc/lfops.c +++ b/armsrc/lfops.c @@ -1476,31 +1476,52 @@ int lf_em410x_watch(int findone, uint32_t *high, uint64_t *low) { WDT_HIT(); - errCnt = Em410xDecode(dest, &size, &idx, &hi, &lo); - if (errCnt == 1) { - if (size == 128) { - Dbprintf("EM XL TAG ID: " _GREEN_("%06x%08x%08x") " - ( %05d_%03d_%08d )", - hi, - (uint32_t)(lo >> 32), - (uint32_t)lo, - (uint32_t)(lo & 0xFFFF), - (uint32_t)((lo >> 16LL) & 0xFF), - (uint32_t)(lo & 0xFFFFFF)); + int type = Em410xDecode(dest, &size, &idx, &hi, &lo); + if (type & 0x1) { + Dbprintf("EM TAG ID: " _GREEN_("%02x%08x") " - ( %05d_%03d_%08d )", + (uint32_t)(lo >> 32), + (uint32_t)lo, + (uint32_t)(lo & 0xFFFF), + (uint32_t)((lo >> 16LL) & 0xFF), + (uint32_t)(lo & 0xFFFFFF)); + } + if (type & 0x2) { + Dbprintf("EM XL TAG ID: " _GREEN_("%06x%08x%08x") " - ( %05d_%03d_%08d )", + hi, + (uint32_t)(lo >> 32), + (uint32_t)lo, + (uint32_t)(lo & 0xFFFF), + (uint32_t)((lo >> 16LL) & 0xFF), + (uint32_t)(lo & 0xFFFFFF)); + } + if (type & 0x4) { + uint64_t data = (lo << 20) >> 20; + // Convert back to Short ID + uint64_t id = ((uint64_t)hi << 16) | (lo >> 48); + if ((data & 0xFFFFFFFF) == 0) { + Dbprintf("EM TAG ID: " _GREEN_("%02x%08x") " - ( %05d_%03d_%08d ) Electra "_GREEN_("%i"), + (uint32_t)(id >> 32), + (uint32_t)id, + (uint32_t)(id & 0xFFFF), + (uint32_t)((id >> 16LL) & 0xFF), + (uint32_t)(id & 0xFFFFFF), + (uint32_t)(data >> 32)); } else { - Dbprintf("EM TAG ID: " _GREEN_("%02x%08x") " - ( %05d_%03d_%08d )", - (uint32_t)(lo >> 32), - (uint32_t)lo, - (uint32_t)(lo & 0xFFFF), - (uint32_t)((lo >> 16LL) & 0xFF), - (uint32_t)(lo & 0xFFFFFF)); - } - - if (findone) { - *high = hi; - *low = lo; - break; + Dbprintf("EM TAG ID: " _GREEN_("%02x%08x") " - ( %05d_%03d_%08d ) on 128b frame with data "_GREEN_("%03x%08x"), + (uint32_t)(id >> 32), + (uint32_t)id, + (uint32_t)(id & 0xFFFF), + (uint32_t)((id >> 16LL) & 0xFF), + (uint32_t)(id & 0xFFFFFF), + (uint32_t)(data >> 32), + (uint32_t)data); } } + if ((type > 0) && findone) { + *high = hi; + *low = lo; + break; + } hi = lo = size = idx = 0; clk = invert = 0; } diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 21ebf9efd..64dd713a3 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -222,6 +222,7 @@ set (TARGET_SOURCES ${PM3_ROOT}/client/src/loclass/hash1_brute.c ${PM3_ROOT}/client/src/loclass/ikeys.c ${PM3_ROOT}/client/src/mifare/mad.c + ${PM3_ROOT}/client/src/mifare/aiddesfire.c ${PM3_ROOT}/client/src/mifare/mfkey.c ${PM3_ROOT}/client/src/mifare/mifare4.c ${PM3_ROOT}/client/src/mifare/mifaredefault.c @@ -236,7 +237,6 @@ set (TARGET_SOURCES ${PM3_ROOT}/client/src/uart/uart_win32.c ${PM3_ROOT}/client/src/ui/overlays.ui ${PM3_ROOT}/client/src/ui/image.ui - ${PM3_ROOT}/client/src/aiddesfire.c ${PM3_ROOT}/client/src/aidsearch.c ${PM3_ROOT}/client/src/cmdanalyse.c ${PM3_ROOT}/client/src/cmdcrc.c diff --git a/client/Makefile b/client/Makefile index 51f7cb2df..bcdfb7140 100644 --- a/client/Makefile +++ b/client/Makefile @@ -474,7 +474,7 @@ POSTCOMPILE = $(MV) -f $(OBJDIR)/$*.Td $(OBJDIR)/$*.d && $(TOUCH) $@ # enumerations # ################ -SRCS = aiddesfire.c \ +SRCS = mifare/aiddesfire.c \ aidsearch.c \ cmdanalyse.c \ cmdcrc.c \ diff --git a/client/resources/mad.json b/client/resources/mad.json index 8b1c4193c..9ea7aeaea 100644 --- a/client/resources/mad.json +++ b/client/resources/mad.json @@ -7875,6 +7875,13 @@ "service_provider": "Atelei Engineering", "system_integrator": "Atelei Engineering" }, + { + "application": "Access control", + "company": "LEAF Identity", + "mad": "0x51CD", + "service_provider": "", + "system_integrator": "" + }, { "application": "Access & biometrics application", "company": "Manufacture Francaise des Pneumatiques MICHELIN", diff --git a/client/src/aiddesfire.c b/client/src/aiddesfire.c deleted file mode 100644 index b628ea072..000000000 --- a/client/src/aiddesfire.c +++ /dev/null @@ -1,133 +0,0 @@ -//----------------------------------------------------------------------------- -// 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. -//----------------------------------------------------------------------------- -// AID DESFire functions -//----------------------------------------------------------------------------- - -#include "aiddesfire.h" -#include "pm3_cmd.h" -#include "fileutils.h" -#include "jansson.h" - -static json_t *df_known_aids = NULL; - -static int open_aiddf_file(json_t **root, bool verbose) { - - char *path; - int res = searchFile(&path, RESOURCES_SUBDIR, "aid_desfire", ".json", true); - if (res != PM3_SUCCESS) { - return PM3_EFILE; - } - - int retval = PM3_SUCCESS; - json_error_t error; - - *root = json_load_file(path, 0, &error); - if (!*root) { - PrintAndLogEx(ERR, "json (%s) error on line %d: %s", path, error.line, error.text); - retval = PM3_ESOFT; - goto out; - } - - if (!json_is_array(*root)) { - PrintAndLogEx(ERR, "Invalid json (%s) format. root must be an array.", path); - retval = PM3_ESOFT; - goto out; - } - - if (verbose) - PrintAndLogEx(SUCCESS, "Loaded file " _YELLOW_("`%s`") " (%s) %zu records.", path, _GREEN_("ok"), json_array_size(*root)); -out: - free(path); - return retval; -} - -static int close_aiddf_file(json_t *root) { - json_decref(root); - return PM3_SUCCESS; -} - -static const char *aiddf_json_get_str(json_t *data, const char *name) { - - json_t *jstr = json_object_get(data, name); - if (jstr == NULL) - return NULL; - - if (!json_is_string(jstr)) { - PrintAndLogEx(WARNING, _YELLOW_("`%s`") " is not a string", name); - return NULL; - } - - const char *cstr = json_string_value(jstr); - if (strlen(cstr) == 0) - return NULL; - - return cstr; -} - -static int print_aiddf_description(json_t *root, uint8_t aid[3], char *fmt, bool verbose) { - char laid[7] = {0}; - sprintf(laid, "%02x%02x%02x", aid[2], aid[1], aid[0]); // must be lowercase - - json_t *elm = NULL; - - for (uint32_t idx = 0; idx < json_array_size(root); idx++) { - json_t *data = json_array_get(root, idx); - if (!json_is_object(data)) { - PrintAndLogEx(ERR, "data [%d] is not an object\n", idx); - continue; - } - const char *faid = aiddf_json_get_str(data, "AID"); - char lfaid[strlen(faid) + 1]; - strcpy(lfaid, faid); - str_lower(lfaid); - if (strcmp(laid, lfaid) == 0) { - elm = data; - break; - } - } - - if (elm == NULL) { - PrintAndLogEx(INFO, fmt, " (unknown)"); - return PM3_ENODATA; - } - const char *vaid = aiddf_json_get_str(elm, "AID"); - const char *vendor = aiddf_json_get_str(elm, "Vendor"); - const char *country = aiddf_json_get_str(elm, "Country"); - const char *name = aiddf_json_get_str(elm, "Name"); - const char *description = aiddf_json_get_str(elm, "Description"); - const char *type = aiddf_json_get_str(elm, "Type"); - - if (name && vendor) { - char result[5 + strlen(name) + strlen(vendor)]; - sprintf(result, " %s [%s]", name, vendor); - PrintAndLogEx(INFO, fmt, result); - } - - if (verbose) { - PrintAndLogEx(SUCCESS, " AID: %s", vaid); - if (name) - PrintAndLogEx(SUCCESS, " Name: %s", name); - if (description) - PrintAndLogEx(SUCCESS, " Description: %s", description); - if (type) - PrintAndLogEx(SUCCESS, " Type: %s", type); - if (vendor) - PrintAndLogEx(SUCCESS, " Vendor: %s", vendor); - if (country) - PrintAndLogEx(SUCCESS, " Country: %s", country); - } - return PM3_SUCCESS; -} - -int AIDDFDecodeAndPrint(uint8_t aid[3]) { - open_aiddf_file(&df_known_aids, false); - - char fmt[80]; - sprintf(fmt, " DF AID Function %02X%02X%02X :" _YELLOW_("%s"), aid[2], aid[1], aid[0], "%s"); - print_aiddf_description(df_known_aids, aid, fmt, false); - close_aiddf_file(df_known_aids); - return PM3_SUCCESS; -} diff --git a/client/src/cmddata.c b/client/src/cmddata.c index 32e2600de..2e8ba515c 100644 --- a/client/src/cmddata.c +++ b/client/src/cmddata.c @@ -592,10 +592,11 @@ static int Cmdmandecoderaw(const char *Cmd) { uint64_t id = 0; uint32_t hi = 0; size_t idx = 0; - if (Em410xDecode(bits, &size, &idx, &hi, &id) == 1) { + int res = Em410xDecode(bits, &size, &idx, &hi, &id); + if (res > 0) { //need to adjust to set bitstream back to manchester encoded data //setDemodBuff(bits, size, idx); - printEM410x(hi, id, false); + printEM410x(hi, id, false, res); } } setDemodBuff(bits, size, 0); diff --git a/client/src/cmdhfmfdes.c b/client/src/cmdhfmfdes.c index e93531403..3d6aa95c9 100644 --- a/client/src/cmdhfmfdes.c +++ b/client/src/cmdhfmfdes.c @@ -38,7 +38,7 @@ #include "nfc/ndef.h" // NDEF #include "mifare/mad.h" #include "generator.h" -#include "aiddesfire.h" +#include "mifare/aiddesfire.h" #include "util.h" #define MAX_KEY_LEN 24 @@ -146,202 +146,6 @@ typedef enum { MFDES_VALUE_FILE } MFDES_FILE_TYPE_T; -// NXP Appnote AN10787 - Application Directory (MAD) -typedef enum { - CL_ADMIN = 0, - CL_MISC1, - CL_MISC2, - CL_MISC3, - CL_MISC4, - CL_MISC5, - CL_MISC6, - CL_MISC7, - CL_AIRLINES = 8, - CL_FERRY, - CL_RAIL, - CL_MISC, - CL_TRANSPORT, - CL_SECURITY = 0x14, - CL_CITYTRAFFIC = 0x18, - CL_CZECH_RAIL, - CL_BUS, - CL_MMT, - CL_TAXI = 0x28, - CL_TOLL = 0x30, - CL_GENERIC_TRANS, - CL_COMPANY_SERVICES = 0x38, - CL_CITYCARD = 0x40, - CL_ACCESS_CONTROL_1 = 0x47, - CL_ACCESS_CONTROL_2, - CL_VIGIK = 0x49, - CL_NED_DEFENCE = 0x4A, - CL_BOSCH_TELECOM = 0x4B, - CL_EU = 0x4C, - CL_SKI_TICKET = 0x50, - CL_SOAA = 0x55, - CL_ACCESS2 = 0x56, - CL_FOOD = 0x60, - CL_NONFOOD = 0x68, - CL_HOTEL = 0x70, - CL_LOYALTY = 0x71, - CL_AIRPORT = 0x75, - CL_CAR_RENTAL = 0x78, - CL_NED_GOV = 0x79, - CL_ADMIN2 = 0x80, - CL_PURSE = 0x88, - CL_TV = 0x90, - CL_CRUISESHIP = 0x91, - CL_IOPTA = 0x95, - CL_METERING = 0x97, - CL_TELEPHONE = 0x98, - CL_HEALTH = 0xA0, - CL_WAREHOUSE = 0xA8, - CL_BANKING = 0xB8, - CL_ENTERTAIN = 0xC0, - CL_PARKING = 0xC8, - CL_FLEET = 0xC9, - CL_FUEL = 0xD0, - CL_INFO = 0xD8, - CL_PRESS = 0xE0, - CL_NFC = 0xE1, - CL_COMPUTER = 0xE8, - CL_MAIL = 0xF0, - CL_AMISC = 0xF8, - CL_AMISC1 = 0xF9, - CL_AMISC2 = 0xFA, - CL_AMISC3 = 0xFB, - CL_AMISC4 = 0xFC, - CL_AMISC5 = 0xFD, - CL_AMISC6 = 0xFE, - CL_AMISC7 = 0xFF, -} aidcluster_h; - -static const char *cluster_to_text(uint8_t cluster) { - switch (cluster) { - case CL_ADMIN: - return "card administration"; - case CL_MISC1: - case CL_MISC2: - case CL_MISC3: - case CL_MISC4: - case CL_MISC5: - case CL_MISC6: - case CL_MISC7: - return "miscellaneous applications"; - case CL_AIRLINES: - return "airlines"; - case CL_FERRY: - return "ferry traffic"; - case CL_RAIL: - return "railway services"; - case CL_MISC: - return "miscellaneous applications"; - case CL_TRANSPORT: - return "transport"; - case CL_SECURITY: - return "security solutions"; - case CL_CITYTRAFFIC: - return "city traffic"; - case CL_CZECH_RAIL: - return "Czech Railways"; - case CL_BUS: - return "bus services"; - case CL_MMT: - return "multi modal transit"; - case CL_TAXI: - return "taxi"; - case CL_TOLL: - return "road toll"; - case CL_GENERIC_TRANS: - return "generic transport"; - case CL_COMPANY_SERVICES: - return "company services"; - case CL_CITYCARD: - return "city card services"; - case CL_ACCESS_CONTROL_1: - case CL_ACCESS_CONTROL_2: - return "access control & security"; - case CL_VIGIK: - return "VIGIK"; - case CL_NED_DEFENCE: - return "Ministry of Defence, Netherlands"; - case CL_BOSCH_TELECOM: - return "Bosch Telecom, Germany"; - case CL_EU: - return "European Union Institutions"; - case CL_SKI_TICKET: - return "ski ticketing"; - case CL_SOAA: - return "SOAA standard for offline access standard"; - case CL_ACCESS2: - return "access control & security"; - case CL_FOOD: - return "food"; - case CL_NONFOOD: - return "non-food trade"; - case CL_HOTEL: - return "hotel"; - case CL_LOYALTY: - return "loyalty"; - case CL_AIRPORT: - return "airport services"; - case CL_CAR_RENTAL: - return "car rental"; - case CL_NED_GOV: - return "Dutch government"; - case CL_ADMIN2: - return "administration services"; - case CL_PURSE: - return "electronic purse"; - case CL_TV: - return "television"; - case CL_CRUISESHIP: - return "cruise ship"; - case CL_IOPTA: - return "IOPTA"; - case CL_METERING: - return "metering"; - case CL_TELEPHONE: - return "telephone"; - case CL_HEALTH: - return "health services"; - case CL_WAREHOUSE: - return "warehouse"; - case CL_BANKING: - return "banking"; - case CL_ENTERTAIN: - return "entertainment & sports"; - case CL_PARKING: - return "car parking"; - case CL_FLEET: - return "fleet management"; - case CL_FUEL: - return "fuel, gasoline"; - case CL_INFO: - return "info services"; - case CL_PRESS: - return "press"; - case CL_NFC: - return "NFC Forum"; - case CL_COMPUTER: - return "computer"; - case CL_MAIL: - return "mail"; - case CL_AMISC: - case CL_AMISC1: - case CL_AMISC2: - case CL_AMISC3: - case CL_AMISC4: - case CL_AMISC5: - case CL_AMISC6: - case CL_AMISC7: - return "miscellaneous applications"; - default: - break; - } - return "reserved"; -} - typedef enum { DESFIRE_UNKNOWN = 0, DESFIRE_MF3ICD40, @@ -916,87 +720,6 @@ static int handler_desfire_auth(mfdes_authinput_t *payload, mfdes_auth_res_t *rp return PM3_SUCCESS; } -// -- test if card supports 0x0A -static int test_desfire_authenticate(void) { - uint8_t data[] = {0x00}; - sAPDU apdu = {0x90, MFDES_AUTHENTICATE, 0x00, 0x00, 0x01, data}; // 0x0A, KEY 0 - uint32_t recv_len = 0; - uint16_t sw = 0; - int res = send_desfire_cmd(&apdu, true, NULL, &recv_len, &sw, 0, false); - if (res == PM3_SUCCESS) - if (sw == status(MFDES_ADDITIONAL_FRAME)) { - DropFieldDesfire(); - return res; - } - return res; -} - -// -- test if card supports 0x1A -static int test_desfire_authenticate_iso(void) { - uint8_t data[] = {0x00}; - sAPDU apdu = {0x90, MFDES_AUTHENTICATE_ISO, 0x00, 0x00, 0x01, data}; // 0x1A, KEY 0 - uint32_t recv_len = 0; - uint16_t sw = 0; - int res = send_desfire_cmd(&apdu, true, NULL, &recv_len, &sw, 0, false); - if (res == PM3_SUCCESS) - if (sw == status(MFDES_ADDITIONAL_FRAME)) { - DropFieldDesfire(); - return res; - } - return res; -} - -// -- test if card supports 0xAA -static int test_desfire_authenticate_aes(void) { - uint8_t data[] = {0x00}; - sAPDU apdu = {0x90, MFDES_AUTHENTICATE_AES, 0x00, 0x00, 0x01, data}; // 0xAA, KEY 0 - uint32_t recv_len = 0; - uint16_t sw = 0; - int res = send_desfire_cmd(&apdu, true, NULL, &recv_len, &sw, 0, false); - if (res == PM3_SUCCESS) - if (sw == status(MFDES_ADDITIONAL_FRAME)) { - DropFieldDesfire(); - return res; - } - return res; -} - -// --- GET FREE MEM -static int desfire_print_freemem(uint32_t free_mem) { - PrintAndLogEx(SUCCESS, " Available free memory on card : " _GREEN_("%d bytes"), free_mem); - return PM3_SUCCESS; -} - -static int handler_desfire_freemem(uint32_t *free_mem) { - if (free_mem == NULL) return PM3_EINVARG; - - uint8_t data[] = {0x00}; - sAPDU apdu = {0x90, MFDES_GET_FREE_MEMORY, 0x00, 0x00, 0x00, data}; // 0x6E - *free_mem = 0; - uint32_t recv_len = 0; - uint16_t sw = 0; - uint8_t fmem[4] = {0}; - - size_t plen = apdu.Lc; - uint8_t *p = mifare_cryto_preprocess_data(tag, (uint8_t *)apdu.data, &plen, 0, MDCM_PLAIN | CMAC_COMMAND); - apdu.Lc = (uint8_t)plen; - apdu.data = p; - - int res = send_desfire_cmd(&apdu, true, fmem, &recv_len, &sw, 0, true); - - if (res != PM3_SUCCESS) - return res; - - size_t dlen = recv_len; - p = mifare_cryto_postprocess_data(tag, apdu.data, &dlen, MDCM_PLAIN | CMAC_COMMAND | CMAC_VERIFY); - (void)p; - if (sw != status(MFDES_S_OPERATION_OK)) - return PM3_ESOFT; - - *free_mem = le24toh(fmem); - return res; -} - /*static int mifare_desfire_change_key(uint8_t key_no, uint8_t *new_key, uint8_t new_algo, uint8_t *old_key, uint8_t old_algo, uint8_t aes_version) { if (new_key == NULL || old_key == NULL) { @@ -1289,155 +1012,6 @@ static int desfire_print_signature(uint8_t *uid, uint8_t uidlen, uint8_t *signat return PM3_SUCCESS; } -static int handler_desfire_signature(uint8_t *signature, size_t *signature_len) { - - if (signature == NULL) { - PrintAndLogEx(DEBUG, "SIGNATURE=NULL"); - return PM3_EINVARG; - } - if (signature_len == NULL) { - PrintAndLogEx(DEBUG, "SIGNATURE_LEN=NULL"); - return PM3_EINVARG; - } - - uint8_t c[] = {0x00}; - sAPDU apdu = {0x90, MFDES_READSIG, 0x00, 0x00, sizeof(c), c}; // 0x3C - - uint32_t recv_len = 0; - uint16_t sw = 0; - int res = send_desfire_cmd(&apdu, true, signature, &recv_len, &sw, 0, true); - if (res == PM3_SUCCESS) { - if (recv_len != 56) { - *signature_len = 0; - res = PM3_ESOFT; - } else { - *signature_len = recv_len; - } - } - DropFieldDesfire(); - return res; -} - -// --- KEY VERSION -static int desfire_print_keyversion(uint8_t key_idx, uint8_t key_version) { - PrintAndLogEx(SUCCESS, " Key [%u] Version : %d (0x%02x)", key_idx, key_version, key_version); - return PM3_SUCCESS; -} - -static int handler_desfire_keyversion(uint8_t curr_key, uint8_t *num_versions) { - if (num_versions == NULL) { - PrintAndLogEx(DEBUG, "NUM_VERSIONS=NULL"); - return PM3_EINVARG; - } - sAPDU apdu = {0x90, MFDES_GET_KEY_VERSION, 0x00, 0x00, 0x01, &curr_key}; //0x64 - uint32_t recv_len = 0; - uint16_t sw = 0; - int res = send_desfire_cmd(&apdu, false, num_versions, &recv_len, &sw, 0, true); - - if (res != PM3_SUCCESS) - return res; - - if (sw != status(MFDES_S_OPERATION_OK)) - return PM3_ESOFT; - - return res; -} - -// --- KEY SETTING Application Master Key -static int desfire_print_amk_keysetting(uint8_t key_settings, uint8_t num_keys, int algo) { - PrintAndLogEx(SUCCESS, " AID Key settings : 0x%02x", key_settings); - // 2 MSB denotes - const char *str = " Max key number and type : %d, " _YELLOW_("%s"); - - if (algo == MFDES_ALGO_DES) - PrintAndLogEx(SUCCESS, str, num_keys & 0x3F, "(3)DES"); - else if (algo == MFDES_ALGO_AES) - PrintAndLogEx(SUCCESS, str, num_keys & 0x3F, "AES"); - else if (algo == MFDES_ALGO_3K3DES) - PrintAndLogEx(SUCCESS, str, num_keys & 0x3F, "3K3DES"); - - //PrintAndLogEx(SUCCESS, " Max number of keys in AID : %d", num_keys & 0x3F); - PrintAndLogEx(INFO, "-------------------------------------------------------------"); - PrintAndLogEx(SUCCESS, " Changekey Access rights"); - - // Access rights. - uint8_t rights = ((key_settings >> 4) & 0x0F); - switch (rights) { - case 0x0: - PrintAndLogEx(SUCCESS, " -- AMK authentication is necessary to change any key (default)"); - break; - case 0xE: - PrintAndLogEx(SUCCESS, " -- Authentication with the key to be changed (same KeyNo) is necessary to change a key"); - break; - case 0xF: - PrintAndLogEx(SUCCESS, " -- All keys (except AMK,see Bit0) within this application are frozen"); - break; - default: - PrintAndLogEx(SUCCESS, - " -- Authentication with the specified key is necessary to change any key.\n" - "A change key and a PICC master key (CMK) can only be changed after authentication with the master key.\n" - "For keys other then the master or change key, an authentication with the same key is needed." - ); - break; - } - - PrintAndLogEx(SUCCESS, " [%c...] AMK Configuration changeable : %s", (key_settings & (1 << 3)) ? '1' : '0', (key_settings & (1 << 3)) ? _GREEN_("YES") : "NO (frozen)"); - PrintAndLogEx(SUCCESS, " [.%c..] AMK required for create/delete : %s", (key_settings & (1 << 2)) ? '1' : '0', (key_settings & (1 << 2)) ? "NO" : "YES"); - PrintAndLogEx(SUCCESS, " [..%c.] Directory list access with AMK : %s", (key_settings & (1 << 1)) ? '1' : '0', (key_settings & (1 << 1)) ? "NO" : "YES"); - PrintAndLogEx(SUCCESS, " [...%c] AMK is changeable : %s", (key_settings & (1 << 0)) ? '1' : '0', (key_settings & (1 << 0)) ? _GREEN_("YES") : "NO (frozen)"); - return PM3_SUCCESS; -} - -// --- KEY SETTING PICC Master Key (CMK) -static int desfire_print_piccmk_keysetting(uint8_t key_settings, uint8_t num_keys, int algo) { - //PrintAndLogEx(INFO, "--- " _CYAN_("PICC Master Key (CMK) settings")); - // number of Master keys (0x01) - PrintAndLogEx(SUCCESS, " Number of Masterkeys : " _YELLOW_("%u"), (num_keys & 0x3F)); - const char *str = " Operation of PICC master key : " _YELLOW_("%s"); - - if (algo == MFDES_ALGO_DES) - PrintAndLogEx(SUCCESS, str, "(3)DES"); - else if (algo == MFDES_ALGO_AES) - PrintAndLogEx(SUCCESS, str, "AES"); - else if (algo == MFDES_ALGO_3K3DES) - PrintAndLogEx(SUCCESS, str, "3K3DES"); - - uint8_t cmk_num_versions = 0; - if (handler_desfire_keyversion(0, &cmk_num_versions) == PM3_SUCCESS) { - PrintAndLogEx(SUCCESS, " PICC Master key Version : " _YELLOW_("%d (0x%02x)"), cmk_num_versions, cmk_num_versions); - } - - PrintAndLogEx(INFO, " ----------------------------------------------------------"); - - // Authentication tests - int res = test_desfire_authenticate(); - if (res == PM3_SUCCESS) - PrintAndLogEx(SUCCESS, " [0x0A] Authenticate : %s", (res == PM3_SUCCESS) ? _YELLOW_("YES") : "NO"); - - res = test_desfire_authenticate_iso(); - if (res == PM3_SUCCESS) - PrintAndLogEx(SUCCESS, " [0x1A] Authenticate ISO : %s", (res == PM3_SUCCESS) ? _YELLOW_("YES") : "NO"); - - res = test_desfire_authenticate_aes(); - if (res == PM3_SUCCESS) - PrintAndLogEx(SUCCESS, " [0xAA] Authenticate AES : %s", (res == PM3_SUCCESS) ? _YELLOW_("YES") : "NO"); - - PrintAndLogEx(INFO, "-------------------------------------------------------------"); - PrintAndLogEx(INFO, " Key setting: 0x%02X [%c%c%c%c]", - key_settings, - (key_settings & (1 << 3)) ? '1' : '0', - (key_settings & (1 << 2)) ? '1' : '0', - (key_settings & (1 << 1)) ? '1' : '0', - (key_settings & (1 << 0)) ? '1' : '0' - ); - - PrintAndLogEx(SUCCESS, " [%c...] CMK Configuration changeable : %s", (key_settings & (1 << 3)) ? '1' : '0', (key_settings & (1 << 3)) ? _GREEN_("YES") : "NO (frozen)"); - PrintAndLogEx(SUCCESS, " [.%c..] CMK required for create/delete : %s", (key_settings & (1 << 2)) ? '1' : '0', (key_settings & (1 << 2)) ? _GREEN_("NO") : "YES"); - PrintAndLogEx(SUCCESS, " [..%c.] Directory list access with CMK : %s", (key_settings & (1 << 1)) ? '1' : '0', (key_settings & (1 << 1)) ? _GREEN_("NO") : "YES"); - PrintAndLogEx(SUCCESS, " [...%c] CMK is changeable : %s", (key_settings & (1 << 0)) ? '1' : '0', (key_settings & (1 << 0)) ? _GREEN_("YES") : "NO (frozen)"); - return PM3_SUCCESS; -} - static int handler_desfire_getkeysettings(uint8_t *key_settings, uint8_t *num_keys) { if (key_settings == NULL) { PrintAndLogEx(DEBUG, "KEY_SETTINGS=NULL"); @@ -1490,33 +1064,6 @@ static int handler_desfire_appids(uint8_t *dest, uint32_t *app_ids_len) { return res; } -// --- GET DF NAMES -static int handler_desfire_dfnames(dfname_t *dest, uint8_t *dfname_count) { - - if (g_debugMode > 1) { - if (dest == NULL) PrintAndLogEx(ERR, "DEST = NULL"); - if (dfname_count == NULL) PrintAndLogEx(ERR, "DFNAME_COUNT = NULL"); - } - - if (dest == NULL || dfname_count == NULL) - return PM3_EINVARG; - - *dfname_count = 0; - sAPDU apdu = {0x90, MFDES_GET_DF_NAMES, 0x00, 0x00, 0x00, NULL}; //0x6d - uint32_t recv_len = 0; - uint16_t sw = 0; - int res = send_desfire_cmd(&apdu, true, (uint8_t *)dest, &recv_len, &sw, sizeof(dfname_t), true); - if (res != PM3_SUCCESS) { - return res; - } - - if (sw != status(MFDES_S_OPERATION_OK)) - return PM3_ESOFT; - - *dfname_count = recv_len; - return res; -} - static int handler_desfire_select_application(uint8_t *aid) { if (g_debugMode > 1) { if (aid == NULL) { @@ -1545,28 +1092,6 @@ static int handler_desfire_select_application(uint8_t *aid) { return PM3_SUCCESS; } -static int key_setting_to_algo(uint8_t aid[3], uint8_t *key_setting, mifare_des_authalgo_t *algo, uint8_t *num_keys) { - int res = handler_desfire_select_application(aid); - if (res != PM3_SUCCESS) return res; - - *num_keys = 0; - res = handler_desfire_getkeysettings(key_setting, num_keys); - if (res == PM3_SUCCESS) { - switch (*num_keys >> 6) { - case 0: - *algo = MFDES_ALGO_DES; - break; - case 1: - *algo = MFDES_ALGO_3K3DES; - break; - case 2: - *algo = MFDES_ALGO_AES; - break; - } - } - return res; -} - static int handler_desfire_fileids(uint8_t *dest, uint32_t *file_ids_len) { if (g_debugMode > 1) { if (dest == NULL) PrintAndLogEx(ERR, "DEST=NULL"); @@ -1605,70 +1130,6 @@ static int handler_desfire_filesettings(uint8_t file_id, uint8_t *dest, uint32_t return res; } -static int getKeySettings(uint8_t *aid) { - if (aid == NULL) return PM3_EINVARG; - - uint8_t num_keys = 0; - uint8_t key_setting = 0; - int res = 0; - if (memcmp(aid, "\x00\x00\x00", 3) == 0) { - - // CARD MASTER KEY - //PrintAndLogEx(INFO, "--- " _CYAN_("CMK - PICC, Card Master Key settings")); - - // KEY Settings - AMK - mifare_des_authalgo_t algo = MFDES_ALGO_DES; - res = key_setting_to_algo(aid, &key_setting, &algo, &num_keys); - - if (res == PM3_SUCCESS) { - desfire_print_piccmk_keysetting(key_setting, num_keys, algo); - } else { - PrintAndLogEx(WARNING, _RED_(" Can't read PICC Master key settings")); - } - - } else { - - // AID - APPLICATION MASTER KEYS - //PrintAndLogEx(SUCCESS, "--- " _CYAN_("AMK - Application Master Key settings")); - res = handler_desfire_select_application(aid); - if (res != PM3_SUCCESS) return res; - - // KEY Settings - AMK - mifare_des_authalgo_t algo = MFDES_ALGO_DES; - res = key_setting_to_algo(aid, &key_setting, &algo, &num_keys); - if (res == PM3_SUCCESS) { - desfire_print_amk_keysetting(key_setting, num_keys, algo); - } else { - PrintAndLogEx(WARNING, _RED_(" Can't read Application Master key settings")); - } - - // KEY VERSION - AMK - uint8_t num_version = 0; - if (handler_desfire_keyversion(0, &num_version) == PM3_SUCCESS) { - PrintAndLogEx(INFO, "-------------------------------------------------------------"); - PrintAndLogEx(INFO, " Application keys"); - desfire_print_keyversion(0, num_version); - } else { - PrintAndLogEx(WARNING, " Can't read AID master key version. Trying all keys"); - } - - // From 0x01 to numOfKeys. We already got 0x00. (AMK) - num_keys &= 0x3F; - if (num_keys > 1) { - for (uint8_t i = 0x01; i < num_keys; ++i) { - if (handler_desfire_keyversion(i, &num_version) == PM3_SUCCESS) { - desfire_print_keyversion(i, num_version); - } else { - PrintAndLogEx(WARNING, " Can't read key %d (0x%02x) version", i, i); - } - } - } - } - - DropFieldDesfire(); - return PM3_SUCCESS; -} - static void swap24(uint8_t *data) { if (data == NULL) return; uint8_t tmp = data[0]; @@ -1794,7 +1255,8 @@ static int CmdHF14ADesInfo(const char *Cmd) { CLIExecWithReturn(ctx, Cmd, argtable, true); CLIParserFree(ctx); - DropFieldDesfire(); + SetAPDULogging(false); + DropField(); mfdes_info_res_t info; int res = mfdes_get_info(&info); @@ -1809,8 +1271,7 @@ static int CmdHF14ADesInfo(const char *Cmd) { } PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "--- " _CYAN_("Tag Information") " ---------------------------"); - PrintAndLogEx(INFO, "-------------------------------------------------------------"); + PrintAndLogEx(INFO, "---------------------------------- " _CYAN_("Tag Information") " ----------------------------------"); PrintAndLogEx(SUCCESS, " UID: " _GREEN_("%s"), sprint_hex(info.uid, info.uidlen)); PrintAndLogEx(SUCCESS, " Batch number: " _GREEN_("%s"), sprint_hex(info.details + 7, 5)); PrintAndLogEx(SUCCESS, " Production date: week " _GREEN_("%02x") " / " _GREEN_("20%02x"), info.details[12], info.details[13]); @@ -1835,7 +1296,7 @@ static int CmdHF14ADesInfo(const char *Cmd) { PrintAndLogEx(INFO, " Protocol: %s", getProtocolStr(info.versionSW[6], false)); PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "--- " _CYAN_("Card capabilities")); + PrintAndLogEx(INFO, "--------------------------------- " _CYAN_("Card capabilities") " ---------------------------------"); uint8_t major = info.versionSW[3]; uint8_t minor = info.versionSW[4]; if (major == 0 && minor == 4) @@ -1856,39 +1317,66 @@ static int CmdHF14ADesInfo(const char *Cmd) { if (major == 0 && minor == 2) PrintAndLogEx(INFO, "\t0.2 - DESFire Light, Originality check, "); + DesfireContext dctx = {0}; + dctx.commMode = DCMPlain; + dctx.cmdSet = DCCNative; + res = DesfireSelectAIDHex(&dctx, 0x000000, false, 0); + if (res != PM3_SUCCESS) + return res; + + PICCInfoS PICCInfo = {0}; + + uint8_t aidbuf[250] = {0}; + size_t aidbuflen = 0; + res = DesfireGetAIDList(&dctx, aidbuf, &aidbuflen); + if (res == PM3_SUCCESS) { + PICCInfo.appCount = aidbuflen / 3; + } + if (cardtype == DESFIRE_EV2 || cardtype == DESFIRE_LIGHT || cardtype == DESFIRE_EV3 || cardtype == NTAG413DNA) { // Signature originality check - uint8_t signature[56] = {0}; + uint8_t signature[250] = {0}; // must be 56 size_t signature_len = 0; PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "--- " _CYAN_("Tag Signature")); - if (handler_desfire_signature(signature, &signature_len) == PM3_SUCCESS) { - desfire_print_signature(info.uid, info.uidlen, signature, signature_len, cardtype); + res = DesfireReadSignature(&dctx, 0x00, signature, &signature_len); + if (res == PM3_SUCCESS) { + if (signature_len == 56) + desfire_print_signature(info.uid, info.uidlen, signature, signature_len, cardtype); + else + PrintAndLogEx(WARNING, "--- GetSignature returned wrong signature length: %zu", signature_len); } else { PrintAndLogEx(WARNING, "--- Card doesn't support GetSignature cmd"); } } - // Master Key settings - uint8_t master_aid[3] = {0x00, 0x00, 0x00}; - getKeySettings(master_aid); + if (aidbuflen > 2) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(SUCCESS, "--- " _CYAN_("AID list")); + PrintAndLogEx(SUCCESS, "AIDs: " NOLF); + for (int i = 0; i < aidbuflen; i += 3) + PrintAndLogEx(NORMAL, "%s %06x" NOLF, (i == 0) ? "" : ",", DesfireAIDByteToUint(&aidbuf[i])); + PrintAndLogEx(NORMAL, "\n"); + } + + DesfireFillPICCInfo(&dctx, &PICCInfo, true); + DesfirePrintPICCInfo(&dctx, &PICCInfo); if (cardtype != DESFIRE_LIGHT) { // Free memory on card PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "--- " _CYAN_("Free memory")); - uint32_t free_mem = 0; - if (handler_desfire_freemem(&free_mem) == PM3_SUCCESS) { - desfire_print_freemem(free_mem); + if (PICCInfo.freemem != 0xffffffff) { + PrintAndLogEx(SUCCESS, " Available free memory on card : " _GREEN_("%d bytes"), PICCInfo.freemem); } else { PrintAndLogEx(SUCCESS, " Card doesn't support 'free mem' cmd"); } - PrintAndLogEx(INFO, "-------------------------------------------------------------"); } + PrintAndLogEx(NORMAL, ""); iso14a_card_select_t card; @@ -1931,239 +1419,7 @@ static int CmdHF14ADesInfo(const char *Cmd) { */ - DropFieldDesfire(); - return PM3_SUCCESS; -} - -static void DecodeFileType(uint8_t filetype) { - switch (filetype) { - case 0x00: - PrintAndLogEx(INFO, " File Type: 0x%02X -> Standard Data File", filetype); - break; - case 0x01: - PrintAndLogEx(INFO, " File Type: 0x%02X -> Backup Data File", filetype); - break; - case 0x02: - PrintAndLogEx(INFO, " File Type: 0x%02X -> Value Files with Backup", filetype); - break; - case 0x03: - PrintAndLogEx(INFO, " File Type: 0x%02X -> Linear Record Files with Backup", filetype); - break; - case 0x04: - PrintAndLogEx(INFO, " File Type: 0x%02X -> Cyclic Record Files with Backup", filetype); - break; - default: - PrintAndLogEx(INFO, " File Type: 0x%02X", filetype); - break; - } -} - -static void DecodeComSet(uint8_t comset) { - switch (comset) { - case 0x00: - PrintAndLogEx(INFO, " Com.Setting: 0x%02X -> Plain", comset); - break; - case 0x01: - PrintAndLogEx(INFO, " Com.Setting: 0x%02X -> Plain + MAC", comset); - break; - case 0x03: - PrintAndLogEx(INFO, " Com.Setting: 0x%02X -> Enciphered", comset); - break; - default: - PrintAndLogEx(INFO, " Com.Setting: 0x%02X", comset); - break; - } -} - -static char *DecodeAccessValue(uint8_t value) { - - char *car = (char *)calloc(255, sizeof(char)); - if (car == NULL) - return NULL; - - switch (value) { - case 0xE: - strcat(car, "(Free Access)"); - break; - case 0xF: - strcat(car, "(Denied Access)"); - break; - default: - snprintf(car, 255, "(Access Key: %d)", value); - break; - } - return car; -} - -static void DecodeAccessRights(uint16_t accrights) { - int change_access_rights = accrights & 0xF; - int read_write_access = (accrights >> 4) & 0xF; - int write_access = (accrights >> 8) & 0xF; - int read_access = (accrights >> 12) & 0xF; - char *car = DecodeAccessValue(change_access_rights); - if (car == NULL) return; - - char *rwa = DecodeAccessValue(read_write_access); - if (rwa == NULL) { - free(car); - return; - } - - char *wa = DecodeAccessValue(write_access); - if (wa == NULL) { - free(car); - free(rwa); - return; - } - - char *ra = DecodeAccessValue(read_access); - if (ra == NULL) { - free(car); - free(rwa); - free(wa); - return; - } - - PrintAndLogEx(INFO, " Access Rights: 0x%04X - Change %s - RW %s - W %s - R %s", accrights, car, rwa, wa, ra); - free(car); - free(rwa); - free(wa); - free(ra); -} - -static int DecodeFileSettings(uint8_t *src, int src_len, int maclen) { - uint8_t filetype = src[0]; - uint8_t comset = src[1]; - - uint16_t accrights = (src[3] << 8) + src[2]; - if (src_len == 1 + 1 + 2 + 3 + maclen) { - int filesize = (src[6] << 16) + (src[5] << 8) + src[4]; - DecodeFileType(filetype); - DecodeComSet(comset); - DecodeAccessRights(accrights); - PrintAndLogEx(INFO, " Filesize: %d (0x%X)", filesize, filesize); - return PM3_SUCCESS; - } else if (src_len == 1 + 1 + 2 + 4 + 4 + 4 + 1 + maclen) { - int lowerlimit = (src[7] << 24) + (src[6] << 16) + (src[5] << 8) + src[4]; - int upperlimit = (src[11] << 24) + (src[10] << 16) + (src[9] << 8) + src[8]; - int limitcredvalue = (src[15] << 24) + (src[14] << 16) + (src[13] << 8) + src[12]; - uint8_t limited_credit_enabled = src[17]; - DecodeFileType(filetype); - DecodeComSet(comset); - DecodeAccessRights(accrights); - PrintAndLogEx(INFO, " Lower limit: %d (0x%X) - Upper limit: %d (0x%X) - limited credit value: %d (0x%X) - limited credit enabled: %d", lowerlimit, lowerlimit, upperlimit, upperlimit, limitcredvalue, limitcredvalue, limited_credit_enabled); - return PM3_SUCCESS; - } else if (src_len == 1 + 1 + 2 + 3 + 3 + 3 + maclen) { - uint32_t recordsize = (src[6] << 16) + (src[5] << 8) + src[4]; - uint32_t maxrecords = (src[9] << 16) + (src[8] << 8) + src[7]; - uint32_t currentrecord = (src[12] << 16) + (src[11] << 8) + src[10]; - DecodeFileType(filetype); - DecodeComSet(comset); - DecodeAccessRights(accrights); - PrintAndLogEx(INFO, " Record size: %d (0x%X) - MaxNumberRecords: %d (0x%X) - Current Number Records: %d (0x%X)", recordsize, recordsize, maxrecords, maxrecords, currentrecord, currentrecord); - return PM3_SUCCESS; - } - return PM3_ESOFT; -} - -static int CmdHF14ADesEnumApplications(const char *Cmd) { - CLIParserContext *ctx; - CLIParserInit(&ctx, "hf mfdes enum", - "Enumerate all AID's on MIFARE DESfire tag", - "hf mfdes enum"); - - void *argtable[] = { - arg_param_begin, - arg_param_end - }; - CLIExecWithReturn(ctx, Cmd, argtable, true); - CLIParserFree(ctx); - - DropFieldDesfire(); - - uint8_t aid[3] = {0}; - uint8_t app_ids[78] = {0}; - uint32_t app_ids_len = 0; - - uint8_t file_ids[33] = {0}; - uint32_t file_ids_len = 0; - - dfname_t dfnames[255]; - uint8_t dfname_count = 0; - - if (handler_desfire_appids(app_ids, &app_ids_len) != PM3_SUCCESS) { - PrintAndLogEx(ERR, "Can't get list of applications on tag"); - DropFieldDesfire(); - return PM3_ESOFT; - } - - if (handler_desfire_dfnames(dfnames, &dfname_count) != PM3_SUCCESS) { - PrintAndLogEx(WARNING, _RED_("Can't get DF Names")); - } - - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "-- MIFARE DESFire Enumerate applications --------------------"); - PrintAndLogEx(INFO, "-------------------------------------------------------------"); - PrintAndLogEx(SUCCESS, " Tag report " _GREEN_("%d") " application%c", app_ids_len / 3, (app_ids_len == 3) ? ' ' : 's'); - - for (uint32_t i = 0; i < app_ids_len; i += 3) { - - aid[0] = app_ids[i]; - aid[1] = app_ids[i + 1]; - aid[2] = app_ids[i + 2]; - - PrintAndLogEx(NORMAL, ""); - - if (memcmp(aid, "\x00\x00\x00", 3) == 0) { - // CARD MASTER KEY - PrintAndLogEx(INFO, "--- " _CYAN_("CMK - PICC, Card Master Key settings")); - } else { - PrintAndLogEx(SUCCESS, "--- " _CYAN_("AMK - Application Master Key settings")); - } - - PrintAndLogEx(SUCCESS, " AID : " _GREEN_("%02X%02X%02X"), aid[2], aid[1], aid[0]); - if ((aid[2] >> 4) == 0xF) { - uint16_t short_aid = ((aid[2] & 0xF) << 12) | (aid[1] << 4) | (aid[0] >> 4); - PrintAndLogEx(SUCCESS, " AID mapped to MIFARE Classic AID (MAD): " _YELLOW_("%02X"), short_aid); - PrintAndLogEx(SUCCESS, " MAD AID Cluster 0x%02X : " _YELLOW_("%s"), short_aid >> 8, cluster_to_text(short_aid >> 8)); - MADDFDecodeAndPrint(short_aid); - } else { - AIDDFDecodeAndPrint(aid); - } - for (uint8_t m = 0; m < dfname_count; m++) { - if (dfnames[m].aid[0] == aid[0] && dfnames[m].aid[1] == aid[1] && dfnames[m].aid[2] == aid[2]) { - PrintAndLogEx(SUCCESS, " - DF " _YELLOW_("%02X%02X") " Name : " _YELLOW_("%s"), dfnames[m].fid[1], dfnames[m].fid[0], dfnames[m].name); - } - } - - int res = getKeySettings(aid); - if (res != PM3_SUCCESS) continue; - - res = handler_desfire_select_application(aid); - if (res != PM3_SUCCESS) continue; - - res = handler_desfire_fileids(file_ids, &file_ids_len); - if (res != PM3_SUCCESS) continue; - - PrintAndLogEx(SUCCESS, " Tag report " _GREEN_("%d") " file%c", file_ids_len, (file_ids_len == 1) ? ' ' : 's'); - for (int j = (int)file_ids_len - 1; j >= 0; j--) { - PrintAndLogEx(SUCCESS, " Fileid %d (0x%02x)", file_ids[j], file_ids[j]); - - uint8_t filesettings[20] = {0}; - uint32_t fileset_len = 0; - uint32_t maclen = 0; // To be implemented - - res = handler_desfire_filesettings(file_ids[j], filesettings, &fileset_len); - if (res != PM3_SUCCESS) continue; - - if (DecodeFileSettings(filesettings, fileset_len, maclen) != PM3_SUCCESS) { - PrintAndLogEx(INFO, " Settings [%u] %s", fileset_len, sprint_hex(filesettings, fileset_len)); - } - } - - } - PrintAndLogEx(INFO, "-------------------------------------------------------------"); - DropFieldDesfire(); + DropField(); return PM3_SUCCESS; } @@ -2849,29 +2105,6 @@ static int CmdHF14aDesMAD(const char *Cmd) { } */ -/*static int CmdTest(const char *Cmd) { - (void)Cmd; // Cmd is not used so far - uint8_t IV[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - uint8_t key[16] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - - uint8_t encRndB[8] = {0x1A, 0xBE, 0x10, 0x8D, 0x09, 0xE0, 0x18, 0x13}; - uint8_t RndB[8] = {0}; - uint8_t RndA[8] = {0x6E, 0x6A, 0xEB, 0x86, 0x6E, 0x6A, 0xEB, 0x86}; - tdes_nxp_receive(encRndB, RndB, 8, key, IV, 2); - uint8_t rotRndB[8] = {0}; - memcpy(rotRndB, RndB, 8); - rol(rotRndB, 8); - uint8_t tmp[16] = {0x00}; - uint8_t both[16] = {0x00}; - memcpy(tmp, RndA, 8); - memcpy(tmp + 8, rotRndB, 8); - PrintAndLogEx(INFO, "3keyenc: %s", sprint_hex(tmp, 16)); - PrintAndLogEx(SUCCESS, " Res : " _GREEN_("%s"), sprint_hex(IV, 8)); - tdes_nxp_send(tmp, both, 16, key, IV, 2); - PrintAndLogEx(SUCCESS, " Res : " _GREEN_("%s"), sprint_hex(both, 16)); - return PM3_SUCCESS; -} -*/ static uint8_t defaultKeyNum = 0; static enum DESFIRE_CRYPTOALGO defaultAlgoId = T_DES; static uint8_t defaultKey[DESFIRE_MAX_KEY_SIZE] = {0}; @@ -3038,7 +2271,10 @@ static int CmdHF14ADesSelectApp(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mfdes selectapp", "Select application on the card. It selects app if it is a valid one or returns an error.", - "hf mfdes selectapp --aid 123456 -> select application 123456"); + "hf mfdes selectapp --aid 123456 -> select application 123456\n" + "hf mfdes selectapp --mf -> select master file (PICC level)\n" + "hf mfdes selectapp --dfname aid123456 -> select application aid123456 by DF name\n" + "hf mfdes selectapp --isoid 1111 -> select application 1111 by ISO ID"); void *argtable[] = { arg_param_begin, @@ -3054,6 +2290,8 @@ static int CmdHF14ADesSelectApp(const char *Cmd) { arg_str0("s", "schann", "", "Secure channel: d40/ev1/ev2"), arg_str0(NULL, "aid", "", "Application ID of application for some parameters (3 hex bytes, big endian)"), arg_str0(NULL, "dfname", "", "Application DF Name (string, max 16 chars). Selects application via ISO SELECT command"), + arg_str0(NULL, "isoid", "", "Application ISO ID (ISO DF ID) (2 hex bytes, big endian)"), + arg_lit0(NULL, "mf", "Select MF (master file) via ISO channel"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -3074,12 +2312,50 @@ static int CmdHF14ADesSelectApp(const char *Cmd) { int dfnamelen = 16; CLIGetStrWithReturn(ctx, 12, dfname, &dfnamelen); + uint32_t isoid = 0x0000; + res = arg_get_u32_hexstr_def_nlen(ctx, 13, 0x0000, &isoid, 2, true); + bool idsoidpresent = (res == 1); + if (res == 2) { + PrintAndLogEx(ERR, "ISO ID for EF or DF must have 2 bytes length"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + + bool selectmf = arg_get_lit(ctx, 14); + SetAPDULogging(APDULogging); CLIParserFree(ctx); - if (dctx.cmdSet == DCCISO || dfnamelen > 0) { - uint8_t resp[250] = {0}; - size_t resplen = 0; + uint8_t resp[250] = {0}; + size_t resplen = 0; + + if (selectmf) { + res = DesfireISOSelect(&dctx, ISSMFDFEF, NULL, 0, resp, &resplen); + if (res != PM3_SUCCESS) { + DropField(); + PrintAndLogEx(FAILED, "ISO Select MF " _RED_("failed")); + return res; + } + + if (resplen > 0) + PrintAndLogEx(FAILED, "Application " _CYAN_("FCI template") " [%zu]%s", resplen, sprint_hex(resp, resplen)); + + PrintAndLogEx(SUCCESS, "PICC MF selected " _GREEN_("succesfully")); + } else if (idsoidpresent) { + uint8_t data[2] = {0}; + Uint2byteToMemLe(data, isoid); + res = DesfireISOSelect(&dctx, ISSMFDFEF, data, 2, resp, &resplen); + if (res != PM3_SUCCESS) { + DropField(); + PrintAndLogEx(FAILED, "ISO Select DF 0x%04x " _RED_("failed"), isoid); + return res; + } + + if (resplen > 0) + PrintAndLogEx(FAILED, "Application " _CYAN_("FCI template") " [%zu]%s", resplen, sprint_hex(resp, resplen)); + + PrintAndLogEx(SUCCESS, "PICC DF 0x%04x selected " _GREEN_("succesfully"), isoid); + } else if (dctx.cmdSet == DCCISO || dfnamelen > 0) { if (dfnamelen > 0) res = DesfireISOSelectDF(&dctx, (char *)dfname, resp, &resplen); else @@ -3281,8 +2557,20 @@ static int CmdHF14ADesSetConfiguration(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mfdes setconfig", "Set card configuration. WARNING! Danger zone! Needs to provide card's master key and works if not blocked by config.", - "hf mfdes setconfig --param 03 --data 0428 -> set parameter 03\n" - "hf mfdes setconfig --param 02 --data 0875778102637264 -> set parameter 02"); + "More about options MF2DLHX0.pdf. Options list:\n" + "00h PICC configuration.\n" + "02h ATS update.\n" + "03h SAK update\n" + "04h Secure Messaging Configuration.\n" + "05h Capability data. (here change for LRP in the Desfire Light)\n" + "06h DF Name renaming\n" + "08h File renaming\n" + "09h Value file configuration\n" + "0Ah Failed authentication counter setting\n" + "0Bh HW configuration\n" + "\n" + "hf mfdes setconfig --param 03 --data 0428 -> set SAK\n" + "hf mfdes setconfig --param 02 --data 0875778102637264 -> set ATS (first byte - length)"); void *argtable[] = { arg_param_begin, @@ -3353,14 +2641,12 @@ static int CmdHF14ADesSetConfiguration(const char *Cmd) { return res; } - DesfireSetCommMode(&dctx, DCMEncryptedPlain); res = DesfireSetConfiguration(&dctx, paramid, param, paramlen); if (res == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "Set configuration 0x%02x " _GREEN_("ok") " ", paramid); } else { PrintAndLogEx(FAILED, "Set configuration 0x%02x " _RED_("failed") " ", paramid); } - DesfireSetCommMode(&dctx, DCMEncrypted); DropField(); return res; @@ -3370,7 +2656,13 @@ static int CmdHF14ADesChangeKey(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mfdes changekey", "Change PICC/Application key. Needs to provide keynum/key for a valid authentication (may get from default parameters).", - "hf mfdes changekey --aid 123456 -> execute with default factory setup"); + "Change crypto algorithm for PICC key is possible, but for APP keys crypto algorithm is set by createapp command and can't be changed wo application delete\n" + "\n" + "hf mfdes changekey --aid 123456 -> execute with default factory setup. change des key 0 in the app 123456 from 00..00 to 00..00\n" + "hf mfdes changekey -t des --newalgo aes --newkey 11223344556677889900112233445566 --newver a5 -> change card master key to AES one\n" + "hf mfdes changekey --aid 123456 -t aes --key 00000000000000000000000000000000 --newkey 11223344556677889900112233445566 -> change app master key\n" + "hf mfdes changekey --aid 123456 -t des -n 0 --newkeyno 1 --oldkey 5555555555555555 --newkey 1122334455667788 -> change key 1 with auth from key 0\n" + "hf mfdes changekey --aid 123456 -t 3tdea --newkey 112233445566778899001122334455667788990011223344-> change 3tdea key 0 from default 00..00 to provided"); void *argtable[] = { arg_param_begin, @@ -3563,7 +2855,7 @@ static int CmdHF14ADesCreateApp(const char *Cmd) { DesfireContext dctx; int securechann = defaultSecureChannel; uint32_t appid = 0x000000; - int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 12, &securechann, DCMPlain, &appid); + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 12, &securechann, DCMMACed, &appid); if (res) { CLIParserFree(ctx); return res; @@ -3727,7 +3019,7 @@ static int CmdHF14ADesDeleteApp(const char *Cmd) { DesfireContext dctx; int securechann = defaultSecureChannel; uint32_t appid = 0x000000; - int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, &securechann, DCMPlain, &appid); + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, &securechann, DCMMACed, &appid); if (res) { CLIParserFree(ctx); return res; @@ -3812,7 +3104,24 @@ static int CmdHF14ADesGetUID(const char *Cmd) { return PM3_ESOFT; } - PrintAndLogEx(SUCCESS, "Desfire UID[%zu]: %s", buflen, sprint_hex(buf, buflen)); + if (verbose) + PrintAndLogEx(SUCCESS, "received data[%zu]: %s", buflen, sprint_hex(buf, buflen)); + + if (buflen > 0) { + if (buf[0] != 0) { + PrintAndLogEx(SUCCESS, "Desfire UID[%zu]: " _GREEN_("%s"), buflen, sprint_hex(buf, buflen)); + } else { + if (buf[1] == 0x04) { + PrintAndLogEx(SUCCESS, "Desfire UID4: " _GREEN_("%s"), sprint_hex(&buf[2], 4)); + } else if (buf[1] == 0x0a) { + PrintAndLogEx(SUCCESS, "Desfire UID10: " _GREEN_("%s"), sprint_hex(&buf[2], 10)); + } else { + PrintAndLogEx(WARNING, "Card returned wrong uid length: %d (0x%02x)", buf[1], buf[1]); + } + } + } else { + PrintAndLogEx(WARNING, "Card returned no data"); + } DropField(); return PM3_SUCCESS; @@ -4045,7 +3354,7 @@ static int CmdHF14ADesGetKeyVersions(const char *Cmd) { DesfireContext dctx; int securechann = defaultSecureChannel; uint32_t appid = 0x000000; - int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, &securechann, DCMPlain, &appid); // DCMMACed + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, &securechann, DCMMACed, &appid); if (res) { CLIParserFree(ctx); return res; @@ -4507,7 +3816,7 @@ static int CmdHF14ADesGetFileSettings(const char *Cmd) { DesfireContext dctx; int securechann = defaultSecureChannel; uint32_t appid = 0x000000; - int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, &securechann, DCMPlain, &appid); + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, &securechann, DCMMACed, &appid); if (res) { CLIParserFree(ctx); return res; @@ -4840,7 +4149,7 @@ static int CmdHF14ADesCreateFile(const char *Cmd) { DesfireContext dctx; int securechann = defaultSecureChannel; uint32_t appid = 0x000000; - int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, &securechann, DCMPlain, &appid); + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, &securechann, DCMMACed, &appid); if (res) { CLIParserFree(ctx); return res; @@ -4981,7 +4290,7 @@ static int CmdHF14ADesCreateValueFile(const char *Cmd) { DesfireContext dctx; int securechann = defaultSecureChannel; uint32_t appid = 0x000000; - int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, &securechann, DCMPlain, &appid); + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, &securechann, DCMMACed, &appid); if (res) { CLIParserFree(ctx); return res; @@ -5114,7 +4423,7 @@ static int CmdHF14ADesCreateRecordFile(const char *Cmd) { DesfireContext dctx; int securechann = defaultSecureChannel; uint32_t appid = 0x000000; - int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, &securechann, DCMPlain, &appid); + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, &securechann, DCMMACed, &appid); if (res) { CLIParserFree(ctx); return res; @@ -5335,7 +4644,7 @@ static int CmdHF14ADesDeleteFile(const char *Cmd) { DesfireContext dctx; int securechann = defaultSecureChannel; uint32_t appid = 0x000000; - int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, &securechann, DCMPlain, &appid); + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, &securechann, DCMMACed, &appid); if (res) { CLIParserFree(ctx); return res; @@ -5412,7 +4721,7 @@ static int CmdHF14ADesValueOperations(const char *Cmd) { DesfireContext dctx; int securechann = defaultSecureChannel; uint32_t appid = 0x000000; - int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, &securechann, DCMPlain, &appid); + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, &securechann, DCMMACed, &appid); if (res) { CLIParserFree(ctx); return res; @@ -5572,7 +4881,7 @@ static int CmdHF14ADesClearRecordFile(const char *Cmd) { DesfireContext dctx; int securechann = defaultSecureChannel; uint32_t appid = 0x000000; - int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, &securechann, DCMPlain, &appid); + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, &securechann, DCMMACed, &appid); if (res) { CLIParserFree(ctx); return res; @@ -5613,6 +4922,99 @@ static int CmdHF14ADesClearRecordFile(const char *Cmd) { return PM3_SUCCESS; } +static int DesfileReadISOFileAndPrint(DesfireContext *dctx, bool select_current_file, uint8_t fnum, uint16_t fisoid, int filetype, uint32_t offset, uint32_t length, bool noauth, bool verbose) { + + if (filetype == RFTAuto) { + PrintAndLogEx(ERR, "ISO mode needs to specify file type"); + return PM3_EINVARG; + } + + if (filetype == RFTValue) { + PrintAndLogEx(ERR, "ISO mode can't read Value file type"); + return PM3_EINVARG; + } + + if (filetype == RFTMAC) { + PrintAndLogEx(ERR, "ISO mode can't read Transaction MAC file type"); + return PM3_EINVARG; + } + + if (select_current_file) + PrintAndLogEx(INFO, "------------------------------- " _CYAN_("File ISO %04x data") " -------------------------------", fisoid); + else + PrintAndLogEx(INFO, "---------------------------- " _CYAN_("File ISO short %02x data") " ----------------------------", fnum); + + uint8_t resp[2048] = {0}; + size_t resplen = 0; + int res = 0; + + if (filetype == RFTData) { + res = DesfireISOReadBinary(dctx, !select_current_file, (select_current_file) ? 0x00 : fnum, offset, length, resp, &resplen); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Desfire ISOReadBinary command " _RED_("error") ". Result: %d", res); + DropField(); + return PM3_ESOFT; + } + + if (resplen > 0) { + if (select_current_file) + PrintAndLogEx(SUCCESS, "Read %zu bytes from file 0x%04x offset %u", resplen, fisoid, offset); + else + PrintAndLogEx(SUCCESS, "Read %zu bytes from file 0x%02x offset %u", resplen, fnum, offset); + print_buffer_with_offset(resp, resplen, offset, true); + } else { + if (select_current_file) + PrintAndLogEx(SUCCESS, "Read operation returned no data from file %04x", fisoid); + else + PrintAndLogEx(SUCCESS, "Read operation returned no data from file %02x", fnum); + } + } + + if (filetype == RFTRecord) { + size_t reclen = 0; + res = DesfireISOReadRecords(dctx, offset, false, (select_current_file) ? 0x00 : fnum, 0, resp, &resplen); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Desfire ISOReadRecords (one record) command " _RED_("error") ". Result: %d", res); + DropField(); + return PM3_ESOFT; + } + reclen = resplen; + + if (verbose) + PrintAndLogEx(INFO, "Record length %zu", reclen); + + if (length != 1) { + res = DesfireISOReadRecords(dctx, offset, true, (select_current_file) ? 0x00 : fnum, 0, resp, &resplen); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Desfire ISOReadRecords (one record) command " _RED_("error") ". Result: %d", res); + DropField(); + return PM3_ESOFT; + } + } + + if (resplen > 0) { + size_t reccount = resplen / reclen; + if (select_current_file) + PrintAndLogEx(SUCCESS, "Read %zu bytes from file 0x%04x from record %d record count %zu record length %zu", resplen, fisoid, offset, reccount, reclen); + else + PrintAndLogEx(SUCCESS, "Read %zu bytes from file 0x%02x from record %d record count %zu record length %zu", resplen, fnum, offset, reccount, reclen); + if (reccount > 1) + PrintAndLogEx(SUCCESS, "Lastest record at the bottom."); + for (int i = 0; i < reccount; i++) { + if (i != 0) + PrintAndLogEx(SUCCESS, "Record %zu", reccount - (i + offset + 1)); + print_buffer_with_offset(&resp[i * reclen], reclen, offset, (i == 0)); + } + } else { + if (select_current_file) + PrintAndLogEx(SUCCESS, "Read operation returned no data from file %04x", fisoid); + else + PrintAndLogEx(SUCCESS, "Read operation returned no data from file %02x", fnum); + } + } + + return PM3_SUCCESS; +} static int DesfileReadFileAndPrint(DesfireContext *dctx, uint8_t fnum, int filetype, uint32_t offset, uint32_t length, bool noauth, bool verbose) { int res = 0; @@ -5624,7 +5026,7 @@ static int DesfileReadFileAndPrint(DesfireContext *dctx, uint8_t fnum, int filet FileSettingsS fsettings; DesfireCommunicationMode commMode = dctx->commMode; - DesfireSetCommMode(dctx, DCMPlain); + DesfireSetCommMode(dctx, DCMMACed); res = DesfireGetFileSettingsStruct(dctx, fnum, &fsettings); DesfireSetCommMode(dctx, commMode); @@ -5776,8 +5178,17 @@ static int CmdHF14ADesReadData(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mfdes read", "Read data from file. Key needs to be provided or flag --no-auth set (depend on file settings).", + "It reads file via all command sets. \n" + "For ISO command set it can be read by specifying full 2-byte iso id or 1-byte short iso id (first byte of the full iso id). ISO id lays in the data in BIG ENDIAN format.\n" + "ISO record commands: offset - record number (0-current, 1..ff-number, 1-lastest), length - if 0 - all records, if 1 - one\n" + "\n" "hf mfdes read --aid 123456 --fid 01 -> read file: app=123456, file=01, offset=0, all the data. use default channel settings from `default` command\n" - "hf mfdes read --aid 123456 --fid 01 --type record --offset 000000 --length 000001 -> read one last record from record file. use default channel settings from `default` command"); + "hf mfdes read --aid 123456 --fid 01 --type record --offset 000000 --length 000001 -> read one last record from record file. use default channel settings from `default` command\n" + "hf mfdes read --aid 123456 --fid 10 --type data -c iso -> read file via ISO channel: app=123456, short iso id=10, offset=0.\n" + "hf mfdes read --aid 123456 --fileisoid 1000 --type data -c iso -> read file via ISO channel: app=123456, iso id=1000, offset=0. Select via native ISO wrapper\n" + "hf mfdes read --appisoid 0102 --fileisoid 1000 --type data -c iso -> read file via ISO channel: app iso id=0102, iso id=1000, offset=0. Select via ISO commands\n" + "hf mfdes read --appisoid 0102 --fileisoid 1100 --type record -c iso --offset 000005 --length 000001 -> get one record (number 5) from file 1100 via iso commands\n" + "hf mfdes read --appisoid 0102 --fileisoid 1100 --type record -c iso --offset 000005 --length 000000 -> get all record (from 5 to 1) from file 1100 via iso commands"); void *argtable[] = { arg_param_begin, @@ -5797,6 +5208,8 @@ static int CmdHF14ADesReadData(const char *Cmd) { arg_str0(NULL, "type", "", "File Type auto/data(Standard/Backup)/value/record(linear/cyclic)/mac). Auto - check file settings and then read. Default: auto"), arg_str0("o", "offset", "", "File Offset (3 hex bytes, big endian). For records - record number (0 - lastest record). Default 0"), arg_str0("l", "length", "", "Length to read (3 hex bytes, big endian -> 000000 = Read all data). For records - records count (0 - all). Default 0."), + arg_str0(NULL, "appisoid", "", "Application ISO ID (ISO DF ID) (2 hex bytes, big endian). Works only for ISO read commands."), + arg_str0(NULL, "fileisoid", "", "File ISO ID (ISO DF ID) (2 hex bytes, big endian). Works only for ISO read commands."), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -5808,7 +5221,7 @@ static int CmdHF14ADesReadData(const char *Cmd) { DesfireContext dctx; int securechann = defaultSecureChannel; uint32_t appid = 0x000000; - int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, &securechann, DCMPlain, &appid); + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, &securechann, DCMMACed, &appid); if (res) { CLIParserFree(ctx); return res; @@ -5844,6 +5257,24 @@ static int CmdHF14ADesReadData(const char *Cmd) { return PM3_EINVARG; } + uint32_t appisoid = 0x0000; + res = arg_get_u32_hexstr_def_nlen(ctx, 17, 0x0000, &appisoid, 2, true); + bool isoidpresent = (res == 1); + if (res == 2) { + PrintAndLogEx(ERR, "Application ISO ID (for EF) must have 2 bytes length"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + + uint32_t fileisoid = 0x0000; + res = arg_get_u32_hexstr_def_nlen(ctx, 18, 0x0000, &fileisoid, 2, true); + bool fileisoidpresent = (res == 1); + if (res == 2) { + PrintAndLogEx(ERR, "File ISO ID (for DF) must have 2 bytes length"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + SetAPDULogging(APDULogging); CLIParserFree(ctx); @@ -5852,18 +5283,80 @@ static int CmdHF14ADesReadData(const char *Cmd) { return PM3_EINVARG; } - res = DesfireSelectAndAuthenticateEx(&dctx, securechann, appid, noauth, verbose); - if (res != PM3_SUCCESS) { - DropField(); - return res; + if (!isoidpresent) { + res = DesfireSelectAndAuthenticateEx(&dctx, securechann, appid, noauth, verbose); + if (res != PM3_SUCCESS) { + DropField(); + return res; + } + } else { + res = DesfireSelectAndAuthenticateISO(&dctx, securechann, (appid != 0), appid, appisoid, fileisoid, noauth, verbose); + if (res != PM3_SUCCESS) { + DropField(); + return res; + } } - res = DesfileReadFileAndPrint(&dctx, fnum, op, offset, length, noauth, verbose); + if (dctx.cmdSet != DCCISO) + res = DesfileReadFileAndPrint(&dctx, fnum, op, offset, length, noauth, verbose); + else + res = DesfileReadISOFileAndPrint(&dctx, fileisoidpresent, fnum, fileisoid, op, offset, length, noauth, verbose); DropField(); return res; } +static int DesfileWriteISOFile(DesfireContext *dctx, bool select_current_file, uint8_t fnum, uint16_t fisoid, int filetype, uint32_t offset, uint8_t *data, uint32_t datalen, bool verbose) { + + if (filetype == RFTAuto) { + PrintAndLogEx(ERR, "ISO mode needs to specify file type"); + return PM3_EINVARG; + } + + if (filetype == RFTValue) { + PrintAndLogEx(ERR, "ISO mode can't write Value file type"); + return PM3_EINVARG; + } + + if (filetype == RFTMAC) { + PrintAndLogEx(ERR, "ISO mode can't write Transaction MAC file type"); + return PM3_EINVARG; + } + + if (dctx->commMode != DCMPlain) { + PrintAndLogEx(ERR, "ISO mode can write only in plain mode"); + return PM3_EINVARG; + } + + int res = 0; + if (filetype == RFTData) { + res = DesfireISOUpdateBinary(dctx, !select_current_file, (select_current_file) ? 0x00 : fnum, offset, data, datalen); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Desfire ISOUpdateBinary command " _RED_("error") ". Result: %d", res); + return PM3_ESOFT; + } + + if (select_current_file) + PrintAndLogEx(INFO, "Write data file %04x " _GREEN_("success"), fisoid); + else + PrintAndLogEx(INFO, "Write data file %02x " _GREEN_("success"), fnum); + } + + if (filetype == RFTRecord) { + res = DesfireISOAppendRecord(dctx, (select_current_file) ? 0x00 : fnum, data, datalen); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Desfire WriteRecord command " _RED_("error") ". Result: %d", res); + return PM3_ESOFT; + } + if (select_current_file) + PrintAndLogEx(INFO, "Write record to file %04x " _GREEN_("success"), fisoid); + else + PrintAndLogEx(INFO, "Write record to file %02x " _GREEN_("success"), fnum); + } + + return PM3_SUCCESS; +} + static int CmdHF14ADesWriteData(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mfdes write", @@ -5876,7 +5369,9 @@ static int CmdHF14ADesWriteData(const char *Cmd) { "hf mfdes write --aid 123456 --fid 01 -d 01020304 -> write data to record file with `auto` type\n" "hf mfdes write --aid 123456 --fid 01 --type record -d 01020304 -> write data to record file\n" "hf mfdes write --aid 123456 --fid 01 --type record -d 01020304 --updaterec 0 -> update record in the record file. record 0 - lastest record.\n" - "hf mfdes write --aid 123456 --fid 01 --type record --offset 000000 -d 11223344 -> write record to record file. use default channel settings from `default` command"); + "hf mfdes write --aid 123456 --fid 01 --type record --offset 000000 -d 11223344 -> write record to record file. use default channel settings from `default` command\n" + "hf mfdes write --appisoid 1234 --fileisoid 1000 --type data -c iso -d 01020304 -> write data to std/backup file via iso commandset\n" + "hf mfdes write --appisoid 1234 --fileisoid 2000 --type record -c iso -d 01020304 -> aend record to record file via iso commandset"); void *argtable[] = { arg_param_begin, @@ -5899,6 +5394,8 @@ static int CmdHF14ADesWriteData(const char *Cmd) { arg_lit0(NULL, "debit", "use for value file debit operation instead of credit"), arg_lit0(NULL, "commit", "commit needs for backup file only. For the other file types and in the `auto` mode - command set it automatically."), arg_int0(NULL, "updaterec", "", "Record number for update record command. Updates record instead of write. Lastest record - 0"), + arg_str0(NULL, "appisoid", "", "Application ISO ID (ISO DF ID) (2 hex bytes, big endian). Works only for ISO read commands."), + arg_str0(NULL, "fileisoid", "", "File ISO ID (ISO DF ID) (2 hex bytes, big endian). Works only for ISO read commands."), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -5952,6 +5449,24 @@ static int CmdHF14ADesWriteData(const char *Cmd) { int updaterecno = arg_get_int_def(ctx, 19, -1); + uint32_t appisoid = 0x0000; + res = arg_get_u32_hexstr_def_nlen(ctx, 20, 0x0000, &appisoid, 2, true); + bool isoidpresent = (res == 1); + if (res == 2) { + PrintAndLogEx(ERR, "Application ISO ID (for EF) must have 2 bytes length"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + + uint32_t fileisoid = 0x0000; + res = arg_get_u32_hexstr_def_nlen(ctx, 21, 0x0000, &fileisoid, 2, true); + bool fileisoidpresent = (res == 1); + if (res == 2) { + PrintAndLogEx(ERR, "File ISO ID (for DF) must have 2 bytes length"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + SetAPDULogging(APDULogging); CLIParserFree(ctx); @@ -5960,8 +5475,29 @@ static int CmdHF14ADesWriteData(const char *Cmd) { return PM3_EINVARG; } - res = DesfireSelectAndAuthenticateEx(&dctx, securechann, appid, noauth, verbose); - if (res != PM3_SUCCESS) { + if (!isoidpresent) { + res = DesfireSelectAndAuthenticateEx(&dctx, securechann, appid, noauth, verbose); + if (res != PM3_SUCCESS) { + DropField(); + return res; + } + } else { + res = DesfireSelectAndAuthenticateISO(&dctx, securechann, (appid != 0), appid, appisoid, fileisoid, noauth, verbose); + if (res != PM3_SUCCESS) { + DropField(); + return res; + } + } + + // ISO command set + if (dctx.cmdSet == DCCISO) { + if (op == RFTRecord && updaterecno >= 0) { + PrintAndLogEx(ERR, "ISO mode can't update record. Only append."); + DropField(); + return PM3_EINVARG; + } + + res = DesfileWriteISOFile(&dctx, fileisoidpresent, fnum, fileisoid, op, offset, data, datalen, verbose); DropField(); return res; } @@ -5971,7 +5507,7 @@ static int CmdHF14ADesWriteData(const char *Cmd) { FileSettingsS fsettings; DesfireCommunicationMode commMode = dctx.commMode; - DesfireSetCommMode(&dctx, DCMPlain); + DesfireSetCommMode(&dctx, DCMMACed); res = DesfireGetFileSettingsStruct(&dctx, fnum, &fsettings); DesfireSetCommMode(&dctx, commMode); @@ -6134,7 +5670,7 @@ static int CmdHF14ADesLsFiles(const char *Cmd) { DesfireContext dctx; int securechann = defaultSecureChannel; uint32_t appid = 0x000000; - int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, &securechann, DCMPlain, &appid); + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, &securechann, DCMMACed, &appid); if (res) { CLIParserFree(ctx); return res; @@ -6164,19 +5700,76 @@ static int CmdHF14ADesLsFiles(const char *Cmd) { return res; } - PrintAndLogEx(INFO, "---------------------------- " _CYAN_("File list") " -----------------------(r w rw ch)-----"); - for (int i = 0; i < filescount; i++) { - PrintAndLogEx(SUCCESS, "ID: " _GREEN_("%02x ") NOLF, FileList[i].fileNum); - if (isopresent) { - if (FileList[i].fileISONum != 0) - PrintAndLogEx(NORMAL, "ISO ID: " _CYAN_("%04x ") NOLF, FileList[i].fileISONum); - else - PrintAndLogEx(NORMAL, "ISO ID: " _YELLOW_("n/a ") NOLF); - } + PrintAndLogEx(INFO, "------------------------------------------ " _CYAN_("File list") " -----------------------------------------------------"); + for (int i = 0; i < filescount; i++) + DesfirePrintFileSettingsTable((i == 0), FileList[i].fileNum, isopresent, FileList[i].fileISONum, &FileList[i].fileSettings); - DesfirePrintFileSettingsOneLine(&FileList[i].fileSettings); + DropField(); + return PM3_SUCCESS; +} + +static int CmdHF14ADesLsApp(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mfdes lsapp", + "Show application list. Master key needs to be provided or flag --no-auth set (depend on cards settings).", + "hf mfdes lsapp -> show application list with defaults from `default` command\n" + "hf mfdes lsapp --files -> show application list and show each file type/settings/etc for each application"); + + void *argtable[] = { + arg_param_begin, + arg_lit0("a", "apdu", "show APDU requests and responses"), + arg_lit0("v", "verbose", "show technical data"), + arg_int0("n", "keyno", "", "Key number"), + arg_str0("t", "algo", "", "Crypt algo: DES, 2TDEA, 3TDEA, AES"), + arg_str0("k", "key", "", "Key for authenticate (HEX 8(DES), 16(2TDEA or AES) or 24(3TDEA) bytes)"), + arg_str0("f", "kdf", "", "Key Derivation Function (KDF): None, AN10922, Gallagher"), + arg_str0("i", "kdfi", "", "KDF input (HEX 1-31 bytes)"), + arg_str0("m", "cmode", "", "Communicaton mode: plain/mac/encrypt"), + arg_str0("c", "ccset", "", "Communicaton command set: native/niso/iso"), + arg_str0("s", "schann", "", "Secure channel: d40/ev1/ev2"), + arg_lit0(NULL, "no-auth", "execute without authentication"), + arg_lit0(NULL, "no-deep", "not to check authentication commands that avail for any application"), + arg_lit0(NULL, "files", "scan files and print file settings for each application"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + bool APDULogging = arg_get_lit(ctx, 1); + bool verbose = arg_get_lit(ctx, 2); + bool noauth = arg_get_lit(ctx, 11); + bool nodeep = arg_get_lit(ctx, 12); + bool scanfiles = arg_get_lit(ctx, 13); + + DesfireContext dctx; + int securechann = defaultSecureChannel; + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 0, &securechann, (noauth) ? DCMPlain : DCMMACed, NULL); + if (res) { + CLIParserFree(ctx); + return res; } + SetAPDULogging(APDULogging); + CLIParserFree(ctx); + + PrintAndLogEx(INPLACE, _YELLOW_("It may take up to 15 seconds. Processing....")); + + res = DesfireSelectAndAuthenticateEx(&dctx, securechann, 0x000000, noauth, verbose); + if (res != PM3_SUCCESS) { + DropField(); + return res; + } + + PICCInfoS PICCInfo = {0}; + AppListS AppList = {0}; + DesfireFillAppList(&dctx, &PICCInfo, AppList, !nodeep, scanfiles); + + printf("\33[2K\r"); // clear current line before printing + PrintAndLogEx(NORMAL, ""); + + // print zone + DesfirePrintPICCInfo(&dctx, &PICCInfo); + DesfirePrintAppList(&dctx, &PICCInfo, AppList); + DropField(); return PM3_SUCCESS; } @@ -6212,7 +5805,7 @@ static int CmdHF14ADesDump(const char *Cmd) { DesfireContext dctx; int securechann = defaultSecureChannel; uint32_t appid = 0x000000; - int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, &securechann, DCMPlain, &appid); + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, &securechann, (noauth) ? DCMPlain : DCMMACed, &appid); if (res) { CLIParserFree(ctx); return res; @@ -6239,16 +5832,7 @@ static int CmdHF14ADesDump(const char *Cmd) { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(SUCCESS, "Application " _CYAN_("%06x") " have " _GREEN_("%zu") " files", appid, filescount); - uint8_t aid[3] = {0}; - DesfireAIDUintToByte(appid, aid); - if ((aid[2] >> 4) == 0xF) { - uint16_t short_aid = ((aid[2] & 0xF) << 12) | (aid[1] << 4) | (aid[0] >> 4); - PrintAndLogEx(SUCCESS, " AID mapped to MIFARE Classic AID (MAD): " _YELLOW_("%02X"), short_aid); - PrintAndLogEx(SUCCESS, " MAD AID Cluster 0x%02X : " _YELLOW_("%s"), short_aid >> 8, cluster_to_text(short_aid >> 8)); - MADDFDecodeAndPrint(short_aid); - } else { - AIDDFDecodeAndPrint(aid); - } + DesfirePrintAIDFunctions(appid); if (filescount == 0) { PrintAndLogEx(INFO, "There is no files in the application %06x", appid); @@ -6293,30 +5877,30 @@ static int CmdHF14ADesTest(const char *Cmd) { static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, {"-----------", CmdHelp, IfPm3Iso14443a, "---------------------- " _CYAN_("general") " ----------------------"}, + {"info", CmdHF14ADesInfo, IfPm3Iso14443a, "Tag information"}, + {"getuid", CmdHF14ADesGetUID, IfPm3Iso14443a, "Get uid from card"}, {"default", CmdHF14ADesDefault, IfPm3Iso14443a, "Set defaults for all the commands"}, {"auth", CmdHF14ADesAuth, IfPm3Iso14443a, "MIFARE DesFire Authentication"}, {"chk", CmdHF14aDesChk, IfPm3Iso14443a, "[old]Check keys"}, - {"enum", CmdHF14ADesEnumApplications, IfPm3Iso14443a, "[old]Tries enumerate all applications"}, - {"formatpicc", CmdHF14ADesFormatPICC, IfPm3Iso14443a, "Format PICC"}, {"freemem", CmdHF14ADesGetFreeMem, IfPm3Iso14443a, "Get free memory size"}, - {"getuid", CmdHF14ADesGetUID, IfPm3Iso14443a, "Get uid from card"}, {"setconfig", CmdHF14ADesSetConfiguration, IfPm3Iso14443a, "Set card configuration"}, - {"info", CmdHF14ADesInfo, IfPm3Iso14443a, "[old]Tag information"}, + {"formatpicc", CmdHF14ADesFormatPICC, IfPm3Iso14443a, "Format PICC"}, {"list", CmdHF14ADesList, AlwaysAvailable, "List DESFire (ISO 14443A) history"}, // {"ndefread", CmdHF14aDesNDEFRead, IfPm3Iso14443a, "Prints NDEF records from card"}, // {"mad", CmdHF14aDesMAD, IfPm3Iso14443a, "Prints MAD records from card"}, + {"-----------", CmdHelp, IfPm3Iso14443a, "-------------------- " _CYAN_("Applications") " -------------------"}, + {"lsapp", CmdHF14ADesLsApp, IfPm3Iso14443a, "Show all applications with files list"}, + {"getaids", CmdHF14ADesGetAIDs, IfPm3Iso14443a, "Get Application IDs list"}, + {"getappnames", CmdHF14ADesGetAppNames, IfPm3Iso14443a, "Get Applications list"}, + {"bruteaid", CmdHF14ADesBruteApps, IfPm3Iso14443a, "Recover AIDs by bruteforce"}, + {"createapp", CmdHF14ADesCreateApp, IfPm3Iso14443a, "Create Application"}, + {"deleteapp", CmdHF14ADesDeleteApp, IfPm3Iso14443a, "Delete Application"}, + {"selectapp", CmdHF14ADesSelectApp, IfPm3Iso14443a, "Select Application ID"}, {"-----------", CmdHelp, IfPm3Iso14443a, "------------------------ " _CYAN_("Keys") " -----------------------"}, {"changekey", CmdHF14ADesChangeKey, IfPm3Iso14443a, "Change Key"}, {"chkeysettings", CmdHF14ADesChKeySettings, IfPm3Iso14443a, "Change Key Settings"}, {"getkeysettings", CmdHF14ADesGetKeySettings, IfPm3Iso14443a, "Get Key Settings"}, {"getkeyversions", CmdHF14ADesGetKeyVersions, IfPm3Iso14443a, "Get Key Versions"}, - {"-----------", CmdHelp, IfPm3Iso14443a, "-------------------- " _CYAN_("Applications") " -------------------"}, - {"bruteaid", CmdHF14ADesBruteApps, IfPm3Iso14443a, "Recover AIDs by bruteforce"}, - {"createapp", CmdHF14ADesCreateApp, IfPm3Iso14443a, "Create Application"}, - {"deleteapp", CmdHF14ADesDeleteApp, IfPm3Iso14443a, "Delete Application"}, - {"selectapp", CmdHF14ADesSelectApp, IfPm3Iso14443a, "Select Application ID"}, - {"getaids", CmdHF14ADesGetAIDs, IfPm3Iso14443a, "Get Application IDs list"}, - {"getappnames", CmdHF14ADesGetAppNames, IfPm3Iso14443a, "Get Applications list"}, {"-----------", CmdHelp, IfPm3Iso14443a, "----------------------- " _CYAN_("Files") " -----------------------"}, {"getfileids", CmdHF14ADesGetFileIDs, IfPm3Iso14443a, "Get File IDs list"}, {"getfileisoids", CmdHF14ADesGetFileISOIDs, IfPm3Iso14443a, "Get File ISO IDs list"}, @@ -6348,15 +5932,3 @@ int CmdHFMFDes(const char *Cmd) { clearCommandBuffer(); return CmdsParse(CommandTable, Cmd); } - -/* - ToDo: - - ISO/IEC 7816 Cmds - ----------------- - 'B0' Read Binary - 'D6' Update Binary - 'B2' Read Records - 'E2' Append Records - -*/ diff --git a/client/src/cmdhfmfu.c b/client/src/cmdhfmfu.c index 146fd0121..2ed3ce86b 100644 --- a/client/src/cmdhfmfu.c +++ b/client/src/cmdhfmfu.c @@ -1269,6 +1269,7 @@ uint32_t GetHF14AMfU_Type(void) { NT2H1001G0DUx 0004040202000B03 NT2H1311TTDUx 0004040203000F03 Micron UL 0034210101000E03 + Feiju NTAG 0053040201000F03 */ if (memcmp(version, "\x00\x04\x03\x01\x01\x00\x0B", 7) == 0) { tagtype = UL_EV1_48; break; } @@ -1281,6 +1282,7 @@ uint32_t GetHF14AMfU_Type(void) { else if (memcmp(version, "\x00\x04\x04\x01\x02\x00\x0B", 7) == 0) { tagtype = NTAG_210u; break; } else if (memcmp(version, "\x00\x04\x04\x01\x01\x00\x0E", 7) == 0) { tagtype = NTAG_212; break; } else if (memcmp(version, "\x00\x04\x04\x02\x01\x00\x0F", 7) == 0) { tagtype = NTAG_213; break; } + else if (memcmp(version, "\x00\x53\x04\x02\x01\x00\x0F", 7) == 0) { tagtype = NTAG_213; break; } //Shanghai Feiju Microelectronics Co. Ltd. China (Xiaomi Air Purifier filter) else if (memcmp(version, "\x00\x04\x04\x02\x01\x01\x0F", 7) == 0) { tagtype = NTAG_213_C; break; } else if (memcmp(version, "\x00\x04\x04\x02\x01\x00\x11", 7) == 0) { tagtype = NTAG_215; break; } else if (memcmp(version, "\x00\x04\x04\x02\x01\x00\x13", 7) == 0) { tagtype = NTAG_216; break; } diff --git a/client/src/cmdlfem410x.c b/client/src/cmdlfem410x.c index 5619b9a15..0b45f9d0b 100644 --- a/client/src/cmdlfem410x.c +++ b/client/src/cmdlfem410x.c @@ -100,35 +100,55 @@ static void em410x_construct_emul_graph(uint8_t *uid, uint8_t clock, uint8_t gap } //print 64 bit EM410x ID in multiple formats -void printEM410x(uint32_t hi, uint64_t id, bool verbose) { +void printEM410x(uint32_t hi, uint64_t id, bool verbose, int type) { if (!id && !hi) return; - uint64_t n = 1; - uint64_t id2lo = 0; - uint8_t m, i; - for (m = 5; m > 0; m--) { - for (i = 0; i < 8; i++) { - id2lo = (id2lo << 1LL) | ((id & (n << (i + ((m - 1) * 8)))) >> (i + ((m - 1) * 8))); - } - } - if (verbose == false) { - - if (hi) { - PrintAndLogEx(SUCCESS, "EM 410x ID "_GREEN_("%06X%016" PRIX64), hi, id); - } else { + if (type & 0x1) { // Short ID PrintAndLogEx(SUCCESS, "EM 410x ID "_GREEN_("%010" PRIX64), id); } + if (type & 0x2) { // Long ID + PrintAndLogEx(SUCCESS, "EM 410x XL ID "_GREEN_("%06X%016" PRIX64), hi, id); + } + if (type & 0x4) { // Short Extended ID + uint64_t data = (id << 20) >> 20; + // Convert back to Short ID + id = ((uint64_t)hi << 16) | (id >> 48); + if ((data & 0xFFFFFFFF) == 0) { + PrintAndLogEx(SUCCESS, "EM 410x ID "_GREEN_("%010" PRIX64)" Electra "_GREEN_("%03" PRIu64), id, data >> 32); + } else { + PrintAndLogEx(SUCCESS, "EM 410x ID "_GREEN_("%010" PRIX64)" on 128b frame with data "_GREEN_("%011" PRIX64), id, data); + } + } return; } - if (hi) { + if (type & 0x2) { // Long ID //output 88 bit em id - PrintAndLogEx(SUCCESS, "EM 410x ID "_GREEN_("%06X%016" PRIX64), hi, id); - PrintAndLogEx(SUCCESS, "EM410x XL ( RF/%d )", g_DemodClock); - } else { + PrintAndLogEx(SUCCESS, "EM 410x XL ID "_GREEN_("%06X%016" PRIX64)" ( RF/%d )", hi, id, g_DemodClock); + } + if (type & 0x4) { // Short Extended ID + PrintAndLogEx(SUCCESS, "EM 410x Short ID found on a 128b frame"); + uint64_t data = (id << 20) >> 20; + PrintAndLogEx(SUCCESS, " Data after ID: "_GREEN_("%011" PRIX64), data); + if ((data & 0xFFFFFFFF) == 0) { + PrintAndLogEx(SUCCESS, " Possibly an Electra (RO), 0x"_GREEN_("%03" PRIX64)" = "_GREEN_("%03" PRIu64), data >> 32, data >> 32); + } + PrintAndLogEx(SUCCESS, " Short ID details:"); + // Convert back to Short ID + id = ((uint64_t)hi << 16) | (id >> 48); + } + if (type & (0x4 | 0x1)) { // Short Extended or Short ID //output 40 bit em id + uint64_t n = 1; + uint64_t id2lo = 0; + uint8_t m, i; + for (m = 5; m > 0; m--) { + for (i = 0; i < 8; i++) { + id2lo = (id2lo << 1LL) | ((id & (n << (i + ((m - 1) * 8)))) >> (i + ((m - 1) * 8))); + } + } PrintAndLogEx(SUCCESS, "EM 410x ID "_GREEN_("%010" PRIX64), id); PrintAndLogEx(SUCCESS, "EM410x ( RF/%d )", g_DemodClock); PrintAndLogEx(INFO, "-------- " _CYAN_("Possible de-scramble patterns") " ---------"); @@ -252,7 +272,7 @@ int AskEm410xDecode(bool verbose, uint32_t *hi, uint64_t *lo) { printDemodBuff(0, false, false, true); } - printEM410x(*hi, *lo, verbose); + printEM410x(*hi, *lo, verbose, ans); g_em410xid = *lo; return PM3_SUCCESS; } diff --git a/client/src/cmdlfem410x.h b/client/src/cmdlfem410x.h index d31812b56..92f2f3fcf 100644 --- a/client/src/cmdlfem410x.h +++ b/client/src/cmdlfem410x.h @@ -16,7 +16,7 @@ int CmdLFEM410X(const char *Cmd); int demodEM410x(bool verbose); -void printEM410x(uint32_t hi, uint64_t id, bool verbose); +void printEM410x(uint32_t hi, uint64_t id, bool verbose, int type); int AskEm410xDecode(bool verbose, uint32_t *hi, uint64_t *lo); int AskEm410xDemod(int clk, int invert, int maxErr, size_t maxLen, bool amplify, uint32_t *hi, uint64_t *lo, bool verbose); diff --git a/client/src/cmdlfindala.c b/client/src/cmdlfindala.c index 98e90b3ef..38b7c264c 100644 --- a/client/src/cmdlfindala.c +++ b/client/src/cmdlfindala.c @@ -198,12 +198,17 @@ int demodIndalaEx(int clk, int invert, int maxErr, bool verbose) { parity |= DemodBuffer[34] << 1; // b2 parity |= DemodBuffer[38] << 0; // b1 + uint8_t checksum = 0; + checksum |= DemodBuffer[62] << 1; // b2 + checksum |= DemodBuffer[63] << 0; // b1 + PrintAndLogEx(SUCCESS, "Fmt " _GREEN_("26") " FC: " _GREEN_("%u") " Card: " _GREEN_("%u") " Parity: " _GREEN_("%1d%1d") , fc , csn , parity >> 1 & 0x01 , parity & 0x01 ); + PrintAndLogEx(DEBUG, "two bit checksum... " _GREEN_("%1d%1d"), checksum >> 1 & 0x01, checksum & 0x01); PrintAndLogEx(SUCCESS, "Possible de-scramble patterns"); // This doesn't seem to line up with the hot-stamp numbers on any HID cards I have seen, but, leaving it alone since I do not know how those work. -MS diff --git a/client/src/crypto/asn1dump.c b/client/src/crypto/asn1dump.c index f09c4284f..fa5216825 100644 --- a/client/src/crypto/asn1dump.c +++ b/client/src/crypto/asn1dump.c @@ -327,7 +327,7 @@ bool asn1_tag_dump(const struct tlv *tlv, int level, bool *candump) { */ PrintAndLogEx(INFO, - "%*s-- %02X [%02ZX] '"_YELLOW_("%s") "'" NOLF + "%*s-- %02X [%02zX] '"_YELLOW_("%s") "'" NOLF , (level * 4) , " " , tlv->tag diff --git a/client/src/mifare/aiddesfire.c b/client/src/mifare/aiddesfire.c new file mode 100644 index 000000000..0957fe6b3 --- /dev/null +++ b/client/src/mifare/aiddesfire.c @@ -0,0 +1,329 @@ +//----------------------------------------------------------------------------- +// 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. +//----------------------------------------------------------------------------- +// AID DESFire functions +//----------------------------------------------------------------------------- + +#include "aiddesfire.h" +#include "pm3_cmd.h" +#include "fileutils.h" +#include "jansson.h" + +// NXP Appnote AN10787 - Application Directory (MAD) +typedef enum { + CL_ADMIN = 0, + CL_MISC1, + CL_MISC2, + CL_MISC3, + CL_MISC4, + CL_MISC5, + CL_MISC6, + CL_MISC7, + CL_AIRLINES = 8, + CL_FERRY, + CL_RAIL, + CL_MISC, + CL_TRANSPORT, + CL_SECURITY = 0x14, + CL_CITYTRAFFIC = 0x18, + CL_CZECH_RAIL, + CL_BUS, + CL_MMT, + CL_TAXI = 0x28, + CL_TOLL = 0x30, + CL_GENERIC_TRANS, + CL_COMPANY_SERVICES = 0x38, + CL_CITYCARD = 0x40, + CL_ACCESS_CONTROL_1 = 0x47, + CL_ACCESS_CONTROL_2, + CL_VIGIK = 0x49, + CL_NED_DEFENCE = 0x4A, + CL_BOSCH_TELECOM = 0x4B, + CL_EU = 0x4C, + CL_SKI_TICKET = 0x50, + CL_SOAA = 0x55, + CL_ACCESS2 = 0x56, + CL_FOOD = 0x60, + CL_NONFOOD = 0x68, + CL_HOTEL = 0x70, + CL_LOYALTY = 0x71, + CL_AIRPORT = 0x75, + CL_CAR_RENTAL = 0x78, + CL_NED_GOV = 0x79, + CL_ADMIN2 = 0x80, + CL_PURSE = 0x88, + CL_TV = 0x90, + CL_CRUISESHIP = 0x91, + CL_IOPTA = 0x95, + CL_METERING = 0x97, + CL_TELEPHONE = 0x98, + CL_HEALTH = 0xA0, + CL_WAREHOUSE = 0xA8, + CL_BANKING = 0xB8, + CL_ENTERTAIN = 0xC0, + CL_PARKING = 0xC8, + CL_FLEET = 0xC9, + CL_FUEL = 0xD0, + CL_INFO = 0xD8, + CL_PRESS = 0xE0, + CL_NFC = 0xE1, + CL_COMPUTER = 0xE8, + CL_MAIL = 0xF0, + CL_AMISC = 0xF8, + CL_AMISC1 = 0xF9, + CL_AMISC2 = 0xFA, + CL_AMISC3 = 0xFB, + CL_AMISC4 = 0xFC, + CL_AMISC5 = 0xFD, + CL_AMISC6 = 0xFE, + CL_AMISC7 = 0xFF, +} aidcluster_h; + +const char *nxp_cluster_to_text(uint8_t cluster) { + switch (cluster) { + case CL_ADMIN: + return "card administration"; + case CL_MISC1: + case CL_MISC2: + case CL_MISC3: + case CL_MISC4: + case CL_MISC5: + case CL_MISC6: + case CL_MISC7: + return "miscellaneous applications"; + case CL_AIRLINES: + return "airlines"; + case CL_FERRY: + return "ferry traffic"; + case CL_RAIL: + return "railway services"; + case CL_MISC: + return "miscellaneous applications"; + case CL_TRANSPORT: + return "transport"; + case CL_SECURITY: + return "security solutions"; + case CL_CITYTRAFFIC: + return "city traffic"; + case CL_CZECH_RAIL: + return "Czech Railways"; + case CL_BUS: + return "bus services"; + case CL_MMT: + return "multi modal transit"; + case CL_TAXI: + return "taxi"; + case CL_TOLL: + return "road toll"; + case CL_GENERIC_TRANS: + return "generic transport"; + case CL_COMPANY_SERVICES: + return "company services"; + case CL_CITYCARD: + return "city card services"; + case CL_ACCESS_CONTROL_1: + case CL_ACCESS_CONTROL_2: + return "access control & security"; + case CL_VIGIK: + return "VIGIK"; + case CL_NED_DEFENCE: + return "Ministry of Defence, Netherlands"; + case CL_BOSCH_TELECOM: + return "Bosch Telecom, Germany"; + case CL_EU: + return "European Union Institutions"; + case CL_SKI_TICKET: + return "ski ticketing"; + case CL_SOAA: + return "SOAA standard for offline access standard"; + case CL_ACCESS2: + return "access control & security"; + case CL_FOOD: + return "food"; + case CL_NONFOOD: + return "non-food trade"; + case CL_HOTEL: + return "hotel"; + case CL_LOYALTY: + return "loyalty"; + case CL_AIRPORT: + return "airport services"; + case CL_CAR_RENTAL: + return "car rental"; + case CL_NED_GOV: + return "Dutch government"; + case CL_ADMIN2: + return "administration services"; + case CL_PURSE: + return "electronic purse"; + case CL_TV: + return "television"; + case CL_CRUISESHIP: + return "cruise ship"; + case CL_IOPTA: + return "IOPTA"; + case CL_METERING: + return "metering"; + case CL_TELEPHONE: + return "telephone"; + case CL_HEALTH: + return "health services"; + case CL_WAREHOUSE: + return "warehouse"; + case CL_BANKING: + return "banking"; + case CL_ENTERTAIN: + return "entertainment & sports"; + case CL_PARKING: + return "car parking"; + case CL_FLEET: + return "fleet management"; + case CL_FUEL: + return "fuel, gasoline"; + case CL_INFO: + return "info services"; + case CL_PRESS: + return "press"; + case CL_NFC: + return "NFC Forum"; + case CL_COMPUTER: + return "computer"; + case CL_MAIL: + return "mail"; + case CL_AMISC: + case CL_AMISC1: + case CL_AMISC2: + case CL_AMISC3: + case CL_AMISC4: + case CL_AMISC5: + case CL_AMISC6: + case CL_AMISC7: + return "miscellaneous applications"; + default: + break; + } + return "reserved"; +} + +static json_t *df_known_aids = NULL; + +static int open_aiddf_file(json_t **root, bool verbose) { + + char *path; + int res = searchFile(&path, RESOURCES_SUBDIR, "aid_desfire", ".json", true); + if (res != PM3_SUCCESS) { + return PM3_EFILE; + } + + int retval = PM3_SUCCESS; + json_error_t error; + + *root = json_load_file(path, 0, &error); + if (!*root) { + PrintAndLogEx(ERR, "json (%s) error on line %d: %s", path, error.line, error.text); + retval = PM3_ESOFT; + goto out; + } + + if (!json_is_array(*root)) { + PrintAndLogEx(ERR, "Invalid json (%s) format. root must be an array.", path); + retval = PM3_ESOFT; + goto out; + } + + if (verbose) + PrintAndLogEx(SUCCESS, "Loaded file " _YELLOW_("`%s`") " (%s) %zu records.", path, _GREEN_("ok"), json_array_size(*root)); +out: + free(path); + return retval; +} + +static int close_aiddf_file(json_t *root) { + json_decref(root); + return PM3_SUCCESS; +} + +static const char *aiddf_json_get_str(json_t *data, const char *name) { + + json_t *jstr = json_object_get(data, name); + if (jstr == NULL) + return NULL; + + if (!json_is_string(jstr)) { + PrintAndLogEx(WARNING, _YELLOW_("`%s`") " is not a string", name); + return NULL; + } + + const char *cstr = json_string_value(jstr); + if (strlen(cstr) == 0) + return NULL; + + return cstr; +} + +static int print_aiddf_description(json_t *root, uint8_t aid[3], char *fmt, bool verbose) { + char laid[7] = {0}; + sprintf(laid, "%02x%02x%02x", aid[2], aid[1], aid[0]); // must be lowercase + + json_t *elm = NULL; + + for (uint32_t idx = 0; idx < json_array_size(root); idx++) { + json_t *data = json_array_get(root, idx); + if (!json_is_object(data)) { + PrintAndLogEx(ERR, "data [%d] is not an object\n", idx); + continue; + } + const char *faid = aiddf_json_get_str(data, "AID"); + char lfaid[strlen(faid) + 1]; + strcpy(lfaid, faid); + str_lower(lfaid); + if (strcmp(laid, lfaid) == 0) { + elm = data; + break; + } + } + + if (elm == NULL) { + PrintAndLogEx(INFO, fmt, " (unknown)"); + return PM3_ENODATA; + } + const char *vaid = aiddf_json_get_str(elm, "AID"); + const char *vendor = aiddf_json_get_str(elm, "Vendor"); + const char *country = aiddf_json_get_str(elm, "Country"); + const char *name = aiddf_json_get_str(elm, "Name"); + const char *description = aiddf_json_get_str(elm, "Description"); + const char *type = aiddf_json_get_str(elm, "Type"); + + if (name && vendor) { + char result[5 + strlen(name) + strlen(vendor)]; + sprintf(result, " %s [%s]", name, vendor); + PrintAndLogEx(INFO, fmt, result); + } + + if (verbose) { + PrintAndLogEx(SUCCESS, " AID: %s", vaid); + if (name) + PrintAndLogEx(SUCCESS, " Name: %s", name); + if (description) + PrintAndLogEx(SUCCESS, " Description: %s", description); + if (type) + PrintAndLogEx(SUCCESS, " Type: %s", type); + if (vendor) + PrintAndLogEx(SUCCESS, " Vendor: %s", vendor); + if (country) + PrintAndLogEx(SUCCESS, " Country: %s", country); + } + return PM3_SUCCESS; +} + +int AIDDFDecodeAndPrint(uint8_t aid[3]) { + open_aiddf_file(&df_known_aids, false); + + char fmt[80]; + sprintf(fmt, " DF AID Function %02X%02X%02X :" _YELLOW_("%s"), aid[2], aid[1], aid[0], "%s"); + print_aiddf_description(df_known_aids, aid, fmt, false); + close_aiddf_file(df_known_aids); + return PM3_SUCCESS; +} diff --git a/client/src/aiddesfire.h b/client/src/mifare/aiddesfire.h similarity index 91% rename from client/src/aiddesfire.h rename to client/src/mifare/aiddesfire.h index bb67dab83..c09f6e971 100644 --- a/client/src/aiddesfire.h +++ b/client/src/mifare/aiddesfire.h @@ -11,6 +11,7 @@ #include "common.h" +const char *nxp_cluster_to_text(uint8_t cluster); int AIDDFDecodeAndPrint(uint8_t aid[3]); #endif // _AIDDESFIRE_H_ diff --git a/client/src/mifare/desfirecore.c b/client/src/mifare/desfirecore.c index e60093317..0a2edd8e5 100644 --- a/client/src/mifare/desfirecore.c +++ b/client/src/mifare/desfirecore.c @@ -32,6 +32,8 @@ #include "util_posix.h" // msleep #include "mifare/desfire_crypto.h" #include "desfiresecurechan.h" +#include "mifare/mad.h" +#include "mifare/aiddesfire.h" const CLIParserOption DesfireAlgoOpts[] = { {T_DES, "des"}, @@ -261,6 +263,10 @@ const char *DesfireAuthErrorToStr(int error) { return "Can't select application."; case 201: return "Authentication retured no error but channel not authenticated."; + case 202: + return "Can't select application by ISO ID."; + case 203: + return "Can't select file by ISO ID."; case 301: return "ISO Get challenge error."; case 302: @@ -347,7 +353,7 @@ void DesfirePrintContext(DesfireContext *ctx) { desfire_get_key_block_length(ctx->keyType), sprint_hex(ctx->IV, desfire_get_key_block_length(ctx->keyType))); if (ctx->secureChannel == DACEV2) { - PrintAndLogEx(INFO, " TI: %s cmdCntr: 0x%08x", + PrintAndLogEx(INFO, " TI: %s cmdCntr: 0x%04x", sprint_hex(ctx->TI, 4), ctx->cmdCntr); } @@ -671,11 +677,12 @@ static int DesfireExchangeISONative(bool activate_field, DesfireContext *ctx, ui } static int DesfireExchangeISO(bool activate_field, DesfireContext *ctx, sAPDU apdu, uint16_t le, uint8_t *resp, size_t *resplen, uint16_t *sw) { - uint32_t rlen = 0; - int res = DESFIRESendApduEx(activate_field, apdu, le, resp, 255, &rlen, sw); + uint8_t data[1050] = {0}; + uint32_t datalen = 0; + int res = DESFIRESendApduEx(activate_field, apdu, le, data, sizeof(data), &datalen, sw); if (res == PM3_SUCCESS) - *resplen = rlen; + DesfireSecureChannelDecode(ctx, data, datalen, 0, resp, resplen); return res; } @@ -694,9 +701,15 @@ static void DesfireJoinBlockToBytes(uint8_t *blockdata, size_t blockdatacount, s static void DesfireSplitBytesToBlock(uint8_t *blockdata, size_t *blockdatacount, size_t blockdatasize, uint8_t *dstdata, size_t dstdatalen) { size_t len = 0; for (int i = 0; i < *blockdatacount; i++) { + memset(&blockdata[i * blockdatasize + 1], 0, blockdatasize - 1); size_t tlen = len + blockdata[i * blockdatasize]; - if (tlen > dstdatalen) + if (tlen > dstdatalen) { tlen = dstdatalen; + if (tlen >= len) + blockdata[i * blockdatasize] = tlen - len; + else + blockdata[i * blockdatasize] = 0; + } if (len == tlen) { *blockdatacount = i; break; @@ -764,6 +777,7 @@ int DesfireSelectAID(DesfireContext *ctx, uint8_t *aid1, uint8_t *aid2) { size_t resplen = 0; uint8_t respcode = 0; + ctx->secureChannel = DACNone; int res = DesfireExchangeEx(true, ctx, MFDES_SELECT_APPLICATION, data, (aid2 == NULL) ? 3 : 6, &respcode, resp, &resplen, true, 0); if (res == PM3_SUCCESS) { if (resplen != 0) @@ -800,6 +814,7 @@ int DesfireSelectAIDHexNoFieldOn(DesfireContext *ctx, uint32_t aid) { size_t resplen = 0; uint8_t respcode = 0; + ctx->secureChannel = DACNone; int res = DesfireExchangeEx(false, ctx, MFDES_SELECT_APPLICATION, data, 3, &respcode, resp, &resplen, true, 0); if (res == PM3_SUCCESS) { if (resplen != 0) @@ -809,11 +824,28 @@ int DesfireSelectAIDHexNoFieldOn(DesfireContext *ctx, uint32_t aid) { if (respcode != MFDES_S_OPERATION_OK) return PM3_EAPDU_FAIL; + DesfireClearSession(ctx); + ctx->appSelected = (aid != 0x000000); + return PM3_SUCCESS; } return res; } +void DesfirePrintAIDFunctions(uint32_t appid) { + uint8_t aid[3] = {0}; + DesfireAIDUintToByte(appid, aid); + if ((aid[2] >> 4) == 0xF) { + uint16_t short_aid = ((aid[2] & 0xF) << 12) | (aid[1] << 4) | (aid[0] >> 4); + PrintAndLogEx(SUCCESS, " AID mapped to MIFARE Classic AID (MAD): " _YELLOW_("%02X"), short_aid); + PrintAndLogEx(SUCCESS, " MAD AID Cluster 0x%02X : " _YELLOW_("%s"), short_aid >> 8, nxp_cluster_to_text(short_aid >> 8)); + MADDFDecodeAndPrint(short_aid); + } else { + AIDDFDecodeAndPrint(aid); + } +} + + int DesfireSelectAndAuthenticateEx(DesfireContext *dctx, DesfireSecureChannel secureChannel, uint32_t aid, bool noauth, bool verbose) { if (verbose) DesfirePrintContext(dctx); @@ -859,6 +891,62 @@ int DesfireSelectAndAuthenticate(DesfireContext *dctx, DesfireSecureChannel secu return DesfireSelectAndAuthenticateEx(dctx, secureChannel, aid, false, verbose); } +int DesfireSelectAndAuthenticateISO(DesfireContext *dctx, DesfireSecureChannel secureChannel, bool useaid, uint32_t aid, uint16_t isoappid, uint16_t isofileid, bool noauth, bool verbose) { + if (verbose) + DesfirePrintContext(dctx); + + int res = 0; + if (useaid) { + dctx->cmdSet = DCCNativeISO; + if (verbose) + PrintAndLogEx(INFO, "Select via " _CYAN_("native iso wrapping") " interface"); + + res = DesfireSelectAIDHex(dctx, aid, false, 0); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Desfire select " _RED_("error") "."); + return 200; + } + if (verbose) + PrintAndLogEx(INFO, "App %06x via native iso channel is " _GREEN_("selected"), aid); + + dctx->cmdSet = DCCISO; + } else { + res = DesfireSelectEx(dctx, true, ISWIsoID, isoappid, NULL); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Desfire iso application select " _RED_("error") "."); + return 202; + } + if (verbose) + PrintAndLogEx(INFO, "Application iso id %04x is " _GREEN_("selected"), isoappid); + + res = DesfireSelectEx(dctx, false, ISWIsoID, isofileid, NULL); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Desfire iso file select " _RED_("error") "."); + return 203; + } + + if (verbose) + PrintAndLogEx(INFO, "Application iso id %04x file iso id %04x is " _GREEN_("selected"), isoappid, isofileid); + } + + if (!noauth) { + res = DesfireAuthenticate(dctx, secureChannel, verbose); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Desfire authenticate " _RED_("error") ". Result: [%d] %s", res, DesfireAuthErrorToStr(res)); + return res; + } + + if (DesfireIsAuthenticated(dctx)) { + if (verbose) + PrintAndLogEx(INFO, "Desfire " _GREEN_("authenticated")); + } else { + return 201; + } + } + + return PM3_SUCCESS; +} + static int DesfireAuthenticateEV1(DesfireContext *dctx, DesfireSecureChannel secureChannel, bool verbose) { // 3 different way to authenticate AUTH (CRC16) , AUTH_ISO (CRC32) , AUTH_AES (CRC32) // 4 different crypto arg1 DES, 3DES, 3K3DES, AES @@ -1153,6 +1241,7 @@ static int DesfireAuthenticateEV2(DesfireContext *dctx, DesfireSecureChannel sec uint8_t RndA[CRYPTO_AES_BLOCK_SIZE] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16}; uint8_t RndB[CRYPTO_AES_BLOCK_SIZE] = {0}; uint8_t encRndB[CRYPTO_AES_BLOCK_SIZE] = {0}; + uint8_t rotRndA[CRYPTO_AES_BLOCK_SIZE] = {0}; //RndA' uint8_t rotRndB[CRYPTO_AES_BLOCK_SIZE] = {0}; //RndB' uint8_t both[CRYPTO_AES_BLOCK_SIZE * 2 + 1] = {0}; // ek/dk_keyNo(RndA+RndB') @@ -1201,8 +1290,6 @@ static int DesfireAuthenticateEV2(DesfireContext *dctx, DesfireSecureChannel sec memcpy(rotRndB, RndB, CRYPTO_AES_BLOCK_SIZE); rol(rotRndB, CRYPTO_AES_BLOCK_SIZE); - uint8_t encRndA[16] = {0x00}; - // - Encrypt our response uint8_t tmp[32] = {0x00}; memcpy(tmp, RndA, CRYPTO_AES_BLOCK_SIZE); @@ -1232,20 +1319,21 @@ static int DesfireAuthenticateEV2(DesfireContext *dctx, DesfireSecureChannel sec } // Part 4 - memcpy(encRndA, recv_data, CRYPTO_AES_BLOCK_SIZE); - uint8_t data[32] = {0}; if (aes_decode(IV, key, recv_data, data, recv_len)) return 10; - rol(RndA, CRYPTO_AES_BLOCK_SIZE); + // rotate rndA to check + memcpy(rotRndA, RndA, CRYPTO_AES_BLOCK_SIZE); + rol(rotRndA, CRYPTO_AES_BLOCK_SIZE); + uint8_t *recRndA = (firstauth) ? &data[4] : data; - if (memcmp(RndA, recRndA, CRYPTO_AES_BLOCK_SIZE) != 0) { + if (memcmp(rotRndA, recRndA, CRYPTO_AES_BLOCK_SIZE) != 0) { if (g_debugMode > 1) { - PrintAndLogEx(DEBUG, "Expected_RndA : %s", sprint_hex(RndA, CRYPTO_AES_BLOCK_SIZE)); - PrintAndLogEx(DEBUG, "Generated_RndA : %s", sprint_hex(recRndA, CRYPTO_AES_BLOCK_SIZE)); + PrintAndLogEx(DEBUG, "Expected_RndA' : %s", sprint_hex(rotRndA, CRYPTO_AES_BLOCK_SIZE)); + PrintAndLogEx(DEBUG, "Generated_RndA' : %s", sprint_hex(recRndA, CRYPTO_AES_BLOCK_SIZE)); } return 11; } @@ -1295,7 +1383,7 @@ static int DesfireAuthenticateISO(DesfireContext *dctx, DesfireSecureChannel sec // encode DesfireClearIV(dctx); - DesfireCryptoEncDec(dctx, false, both, rndlen * 2, both, true); // error 303 + DesfireCryptoEncDec(dctx, DCOMainKey, both, rndlen * 2, both, true); // error 303 // external authenticate res = DesfireISOExternalAuth(dctx, dctx->appSelected, dctx->keyNum, dctx->keyType, both); @@ -1314,7 +1402,7 @@ static int DesfireAuthenticateISO(DesfireContext *dctx, DesfireSecureChannel sec // decode rnddata uint8_t piccrnd2[64] = {0}; - DesfireCryptoEncDec(dctx, false, rnddata, rndlen * 2, piccrnd2, false); // error 307 + DesfireCryptoEncDec(dctx, DCOMainKey, rnddata, rndlen * 2, piccrnd2, false); // error 307 // check if (memcmp(hostrnd2, &piccrnd2[rndlen], rndlen) != 0) @@ -1344,6 +1432,279 @@ int DesfireAuthenticate(DesfireContext *dctx, DesfireSecureChannel secureChannel return 100; } +static bool DesfireCheckAuthCmd(uint32_t appAID, uint8_t keyNum, uint8_t authcmd) { + size_t recv_len = 0; + uint8_t respcode = 0; + uint8_t recv_data[256] = {0}; + + DesfireContext dctx = {0}; + dctx.keyNum = keyNum; + dctx.commMode = DCMPlain; + dctx.cmdSet = DCCNative; + + // if cant select - return false + int res = DesfireSelectAIDHex(&dctx, appAID, false, 0); + if (res != PM3_SUCCESS) + return false; + + uint8_t data[] = {keyNum, 0x00}; + res = DesfireExchangeEx(false, &dctx, authcmd, data, (authcmd == MFDES_AUTHENTICATE_EV2F) ? 2 : 1, &respcode, recv_data, &recv_len, false, 0); + DropField(); + return (res == PM3_SUCCESS && respcode == 0xaf); +} + +static bool DesfireCheckISOAuthCmd(uint32_t appAID, char *dfname, uint8_t keyNum, DesfireCryptoAlgorythm keytype) { + + DesfireContext dctx = {0}; + dctx.keyNum = keyNum; + dctx.commMode = DCMPlain; + dctx.cmdSet = DCCISO; + + bool app_level = (appAID != 0x000000); + int res = 0; + if (dfname == NULL || strnlen(dfname, 16) == 0) { + if (appAID == 0x000000) { + res = DesfireISOSelect(&dctx, ISSMFDFEF, NULL, 0, NULL, NULL); + if (res != PM3_SUCCESS) + return false; + } else { + res = DesfireSelectAIDHex(&dctx, appAID, false, 0); + if (res != PM3_SUCCESS) + return false; + } + } else { + res = DesfireISOSelectDF(&dctx, dfname, NULL, NULL); + if (res != PM3_SUCCESS) + return false; + app_level = true; + } + + uint8_t rndlen = DesfireGetRndLenForKey(keytype); + + uint8_t piccrnd[64] = {0}; + size_t xlen = 0; + res = DesfireISOGetChallenge(&dctx, keytype, piccrnd, &xlen); + if (res != PM3_SUCCESS || xlen != rndlen) + return false; + + uint8_t resp[250] = {0}; + size_t resplen = 0; + + uint16_t sw = 0; + uint8_t p1 = DesfireKeyToISOKey(keytype); + uint8_t p2 = ((app_level) ? 0x80 : 0x00) | keyNum; + res = DesfireExchangeISO(false, &dctx, (sAPDU) {0x00, ISO7816_EXTERNAL_AUTHENTICATION, p1, p2, rndlen * 2, piccrnd}, 0, resp, &resplen, &sw); + DropField(); + return (sw == 0x9000 || sw == 0x6982); +} + +void DesfireCheckAuthCommands(uint32_t appAID, char *dfname, uint8_t keyNum, AuthCommandsChk *authCmdCheck) { + memset(authCmdCheck, 0, sizeof(AuthCommandsChk)); + + authCmdCheck->auth = DesfireCheckAuthCmd(appAID, keyNum, MFDES_AUTHENTICATE); + authCmdCheck->authISO = DesfireCheckAuthCmd(appAID, keyNum, MFDES_AUTHENTICATE_ISO); + authCmdCheck->authAES = DesfireCheckAuthCmd(appAID, keyNum, MFDES_AUTHENTICATE_AES); + authCmdCheck->authEV2 = DesfireCheckAuthCmd(appAID, keyNum, MFDES_AUTHENTICATE_EV2F); + authCmdCheck->authISONative = DesfireCheckISOAuthCmd(appAID, dfname, keyNum, T_DES); + authCmdCheck->checked = true; +} + +void DesfireCheckAuthCommandsPrint(AuthCommandsChk *authCmdCheck) { + PrintAndLogEx(NORMAL, "auth: %s auth iso: %s auth aes: %s auth ev2: %s auth iso native: %s", + authCmdCheck->auth ? _GREEN_("YES") : _RED_("NO"), + authCmdCheck->authISO ? _GREEN_("YES") : _RED_("NO"), + authCmdCheck->authAES ? _GREEN_("YES") : _RED_("NO"), + authCmdCheck->authEV2 ? _GREEN_("YES") : _RED_("NO"), + authCmdCheck->authISONative ? _GREEN_("YES") : _RED_("NO") + ); +} + +int DesfireFillPICCInfo(DesfireContext *dctx, PICCInfoS *PICCInfo, bool deepmode) { + uint8_t buf[250] = {0}; + size_t buflen = 0; + + uint32_t freemem = 0; + int res = DesfireGetFreeMem(dctx, &freemem); + if (res == PM3_SUCCESS) + PICCInfo->freemem = freemem; + else + PICCInfo->freemem = 0xffffffff; + + PICCInfo->keySettings = 0; + PICCInfo->numKeysRaw = 0; + PICCInfo->keyVersion0 = 0; + res = DesfireGetKeySettings(dctx, buf, &buflen); + if (res == PM3_SUCCESS && buflen >= 2) { + PICCInfo->keySettings = buf[0]; + PICCInfo->numKeysRaw = buf[1]; + PICCInfo->numberOfKeys = PICCInfo->numKeysRaw & 0x1f; + if (PICCInfo->numKeysRaw > 0) { + uint8_t keyNum0 = 0; + res = DesfireGetKeyVersion(dctx, &keyNum0, 1, buf, &buflen); + if (res == PM3_SUCCESS && buflen > 0) { + PICCInfo->keyVersion0 = buf[0]; + } + } + } + + // field on-off zone + if (deepmode) + DesfireCheckAuthCommands(0x000000, NULL, 0, &PICCInfo->authCmdCheck); + + return PM3_SUCCESS; +} + +static int AppListSearchAID(uint32_t appNum, AppListS AppList, size_t appcount) { + for (int i = 0; i < appcount; i++) + if (AppList[i].appNum == appNum) + return i; + + return -1; +} + +int DesfireFillAppList(DesfireContext *dctx, PICCInfoS *PICCInfo, AppListS appList, bool deepmode, bool readFiles) { + uint8_t buf[250] = {0}; + size_t buflen = 0; + + int res = DesfireGetAIDList(dctx, buf, &buflen); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Desfire GetAIDList command " _RED_("error") ". Result: %d", res); + DropField(); + return PM3_ESOFT; + } + + PICCInfo->appCount = buflen / 3; + for (int i = 0; i < buflen; i += 3) + appList[i / 3].appNum = DesfireAIDByteToUint(&buf[i]); + + // result bytes: 3, 2, 1-16. total record size = 24 + res = DesfireGetDFList(dctx, buf, &buflen); + if (res != PM3_SUCCESS) { + PrintAndLogEx(WARNING, "Desfire GetDFList command " _RED_("error") ". Result: %d", res); + } else if (buflen > 0) { + for (int i = 0; i < buflen; i++) { + int indx = AppListSearchAID(DesfireAIDByteToUint(&buf[i * 24 + 1]), appList, PICCInfo->appCount); + if (indx >= 0) { + appList[indx].appISONum = MemBeToUint2byte(&buf[i * 24 + 1 + 3]); + memcpy(appList[indx].appDFName, &buf[i * 24 + 1 + 5], strnlen((char *)&buf[i * 24 + 1 + 5], 16)); + } + } + } + + // field on-off zone + DesfireFillPICCInfo(dctx, PICCInfo, deepmode); + + if (PICCInfo->appCount > 0) { + for (int i = 0; i < PICCInfo->appCount; i++) { + if (i == 0) + res = DesfireSelectAIDHex(dctx, appList[i].appNum, false, 0); + else + res = DesfireSelectAIDHexNoFieldOn(dctx, appList[i].appNum); + if (res != PM3_SUCCESS) + continue; + + DesfireGetKeySettings(dctx, buf, &buflen); + if (res == PM3_SUCCESS && buflen >= 2) { + appList[i].keySettings = buf[0]; + appList[i].numKeysRaw = buf[1]; + appList[i].numberOfKeys = appList[i].numKeysRaw & 0x1f; + appList[i].isoFileIDEnabled = ((appList[i].numKeysRaw & 0x20) != 0); + appList[i].keyType = DesfireKeyTypeToAlgo(appList[i].numKeysRaw >> 6); + + if (appList[i].numberOfKeys > 0) + for (uint8_t keyn = 0; keyn < appList[i].numberOfKeys; keyn++) { + res = DesfireGetKeyVersion(dctx, &keyn, 1, buf, &buflen); + if (res == PM3_SUCCESS && buflen > 0) { + appList[i].keyVersions[keyn] = buf[0]; + } + } + + appList[i].filesReaded = false; + if (readFiles) { + res = DesfireFillFileList(dctx, appList[i].fileList, &appList[i].filesCount, &appList[i].isoPresent); + appList[i].filesReaded = (res == PM3_SUCCESS); + } + } + } + } + + // field on-off zone + if (PICCInfo->appCount > 0 && deepmode) { + for (int i = 0; i < PICCInfo->appCount; i++) { + DesfireCheckAuthCommands(appList[i].appNum, appList[i].appDFName, 0, &appList[i].authCmdCheck); + } + } + + return PM3_SUCCESS; +} + +void DesfirePrintPICCInfo(DesfireContext *dctx, PICCInfoS *PICCInfo) { + PrintAndLogEx(SUCCESS, "------------------------------------ " _CYAN_("PICC level") " -------------------------------------"); + if (PICCInfo->freemem == 0xffffffff) + PrintAndLogEx(SUCCESS, "Applications count: " _GREEN_("%zu") " free memory " _YELLOW_("n/a"), PICCInfo->appCount); + else + PrintAndLogEx(SUCCESS, "Applications count: " _GREEN_("%zu") " free memory " _GREEN_("%d") " bytes", PICCInfo->appCount, PICCInfo->freemem); + PrintAndLogEx(SUCCESS, "PICC level auth commands: " NOLF); + if (PICCInfo->authCmdCheck.checked) + DesfireCheckAuthCommandsPrint(&PICCInfo->authCmdCheck); + if (PICCInfo->numberOfKeys > 0) { + PrintKeySettings(PICCInfo->keySettings, PICCInfo->numKeysRaw, false, true); + PrintAndLogEx(SUCCESS, "PICC key 0 version: %d (0x%02x)", PICCInfo->keyVersion0, PICCInfo->keyVersion0); + } +} + +void DesfirePrintAppList(DesfireContext *dctx, PICCInfoS *PICCInfo, AppListS appList) { + if (PICCInfo->appCount == 0) + return; + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(SUCCESS, "--------------------------------- " _CYAN_("Applications list") " ---------------------------------"); + + for (int i = 0; i < PICCInfo->appCount; i++) { + PrintAndLogEx(SUCCESS, _CYAN_("Application number: 0x%02x") " iso id: " _GREEN_("0x%04x") " name: " _GREEN_("%s"), appList[i].appNum, appList[i].appISONum, appList[i].appDFName); + + DesfirePrintAIDFunctions(appList[i].appNum); + + if (PICCInfo->authCmdCheck.checked) { + PrintAndLogEx(SUCCESS, "Auth commands: " NOLF); + DesfireCheckAuthCommandsPrint(&appList[i].authCmdCheck); + PrintAndLogEx(SUCCESS, ""); + } + + if (appList[i].numberOfKeys > 0) { + PrintKeySettings(appList[i].keySettings, appList[i].numKeysRaw, true, true); + + if (appList[i].numberOfKeys > 0) { + PrintAndLogEx(SUCCESS, "Key versions [0..%d]: " NOLF, appList[i].numberOfKeys - 1); + for (uint8_t keyn = 0; keyn < appList[i].numberOfKeys; keyn++) { + PrintAndLogEx(NORMAL, "%s %02x" NOLF, (keyn == 0) ? "" : ",", appList[i].keyVersions[keyn]); + } + PrintAndLogEx(NORMAL, "\n"); + } + + if (appList[i].filesReaded) { + PrintAndLogEx(SUCCESS, "Application have " _GREEN_("%zu") " files", appList[i].filesCount); + + if (appList[i].filesCount > 0) { + for (int fnum = 0; fnum < appList[i].filesCount; fnum++) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(SUCCESS, "--------------------------------- " _CYAN_("File %02x") " ----------------------------------", appList[i].fileList[fnum].fileNum); + PrintAndLogEx(SUCCESS, "File ID : " _GREEN_("%02x"), appList[i].fileList[fnum].fileNum); + if (appList[i].isoPresent) { + if (appList[i].fileList[fnum].fileISONum != 0) + PrintAndLogEx(SUCCESS, "File ISO ID : %04x", appList[i].fileList[fnum].fileISONum); + else + PrintAndLogEx(SUCCESS, "File ISO ID : " _YELLOW_("n/a")); + } + DesfirePrintFileSettingsExtended(&appList[i].fileList[fnum].fileSettings); + } + } + PrintAndLogEx(NORMAL, ""); + } + } + } +} + static int DesfireCommandEx(DesfireContext *dctx, uint8_t cmd, uint8_t *data, size_t datalen, uint8_t *resp, size_t *resplen, int checklength, size_t splitbysize) { if (resplen) *resplen = 0; @@ -1397,6 +1758,26 @@ int DesfireGetFreeMem(DesfireContext *dctx, uint32_t *freemem) { return res; } +int DesfireReadSignature(DesfireContext *dctx, uint8_t sid, uint8_t *resp, size_t *resplen) { + *resplen = 0; + + uint8_t xresp[257] = {0}; + size_t xresplen = 0; + uint8_t respcode = 0xff; + + int res = DesfireExchange(dctx, MFDES_READSIG, &sid, 1, &respcode, xresp, &xresplen); + if (res != PM3_SUCCESS) + return res; + + if (respcode != 0x90) + return PM3_EAPDU_FAIL; + + memcpy(resp, xresp, xresplen); + *resplen = xresplen; + + return PM3_SUCCESS; +} + int DesfireGetUID(DesfireContext *dctx, uint8_t *resp, size_t *resplen) { return DesfireCommandRxData(dctx, MFDES_GET_UID, resp, resplen, -1); } @@ -1502,8 +1883,7 @@ int DesfireFillFileList(DesfireContext *dctx, FileListS FileList, size_t *filesc isoindx++; } } - if (isoindx > 0) - isoindx--; + if (isoindx * 2 != buflen) PrintAndLogEx(WARNING, "Wrong ISO ID list length. must be %zu but %zu", buflen, isoindx * 2); } else { @@ -1610,46 +1990,18 @@ int DesfireUpdateRecord(DesfireContext *dctx, uint8_t fnum, uint32_t recnum, uin return DesfireCommandTxData(dctx, MFDES_UPDATE_RECORD, xdata, 10 + len); } -uint8_t DesfireKeyAlgoToType(DesfireCryptoAlgorythm keyType) { - switch (keyType) { - case T_DES: - return 0x00; - case T_3DES: - return 0x00; - case T_3K3DES: - return 0x01; - case T_AES: - return 0x02; - } - return 0; -} -static void PrintKeyType(uint8_t keytype) { - switch (keytype) { - case 00: - PrintAndLogEx(SUCCESS, "Key: 2TDEA"); - break; - case 01: - PrintAndLogEx(SUCCESS, "Key: 3TDEA"); - break; - case 02: - PrintAndLogEx(SUCCESS, "Key: AES"); - break; - default: - PrintAndLogEx(SUCCESS, "Key: unknown: 0x%02x", keytype); - break; - } -} - static void PrintKeySettingsPICC(uint8_t keysettings, uint8_t numkeys, bool print2ndbyte) { PrintAndLogEx(SUCCESS, "PICC level rights:"); - PrintAndLogEx(SUCCESS, "[%c...] CMK Configuration changeable : %s", (keysettings & (1 << 3)) ? '1' : '0', (keysettings & (1 << 3)) ? _GREEN_("YES") : "NO (frozen)"); + PrintAndLogEx(SUCCESS, "[%c...] CMK Configuration changeable : %s", (keysettings & (1 << 3)) ? '1' : '0', (keysettings & (1 << 3)) ? _GREEN_("YES") : _RED_("NO (frozen)")); PrintAndLogEx(SUCCESS, "[.%c..] CMK required for create/delete : %s", (keysettings & (1 << 2)) ? '1' : '0', (keysettings & (1 << 2)) ? _GREEN_("NO") : "YES"); PrintAndLogEx(SUCCESS, "[..%c.] Directory list access with CMK : %s", (keysettings & (1 << 1)) ? '1' : '0', (keysettings & (1 << 1)) ? _GREEN_("NO") : "YES"); - PrintAndLogEx(SUCCESS, "[...%c] CMK is changeable : %s", (keysettings & (1 << 0)) ? '1' : '0', (keysettings & (1 << 0)) ? _GREEN_("YES") : "NO (frozen)"); + PrintAndLogEx(SUCCESS, "[...%c] CMK is changeable : %s", (keysettings & (1 << 0)) ? '1' : '0', (keysettings & (1 << 0)) ? _GREEN_("YES") : _RED_("NO (frozen)")); PrintAndLogEx(SUCCESS, ""); - if (print2ndbyte) + if (print2ndbyte) { + DesfirePrintCardKeyType(numkeys >> 6); PrintAndLogEx(SUCCESS, "key count: %d", numkeys & 0x0f); + } } static void PrintKeySettingsApp(uint8_t keysettings, uint8_t numkeys, bool print2ndbyte) { @@ -1676,14 +2028,14 @@ static void PrintKeySettingsApp(uint8_t keysettings, uint8_t numkeys, bool print break; } - PrintAndLogEx(SUCCESS, "[%c...] AMK Configuration changeable : %s", (keysettings & (1 << 3)) ? '1' : '0', (keysettings & (1 << 3)) ? _GREEN_("YES") : "NO (frozen)"); - PrintAndLogEx(SUCCESS, "[.%c..] AMK required for create/delete : %s", (keysettings & (1 << 2)) ? '1' : '0', (keysettings & (1 << 2)) ? "NO" : "YES"); - PrintAndLogEx(SUCCESS, "[..%c.] Directory list access with AMK : %s", (keysettings & (1 << 1)) ? '1' : '0', (keysettings & (1 << 1)) ? "NO" : "YES"); - PrintAndLogEx(SUCCESS, "[...%c] AMK is changeable : %s", (keysettings & (1 << 0)) ? '1' : '0', (keysettings & (1 << 0)) ? _GREEN_("YES") : "NO (frozen)"); + PrintAndLogEx(SUCCESS, "[%c...] AMK Configuration changeable : %s", (keysettings & (1 << 3)) ? '1' : '0', (keysettings & (1 << 3)) ? _GREEN_("YES") : _RED_("NO (frozen)")); + PrintAndLogEx(SUCCESS, "[.%c..] AMK required for create/delete : %s", (keysettings & (1 << 2)) ? '1' : '0', (keysettings & (1 << 2)) ? _GREEN_("NO") : "YES"); + PrintAndLogEx(SUCCESS, "[..%c.] Directory list access with AMK : %s", (keysettings & (1 << 1)) ? '1' : '0', (keysettings & (1 << 1)) ? _GREEN_("NO") : "YES"); + PrintAndLogEx(SUCCESS, "[...%c] AMK is changeable : %s", (keysettings & (1 << 0)) ? '1' : '0', (keysettings & (1 << 0)) ? _GREEN_("YES") : _RED_("NO (frozen)")); PrintAndLogEx(SUCCESS, ""); if (print2ndbyte) { - PrintKeyType(numkeys >> 6); + DesfirePrintCardKeyType(numkeys >> 6); PrintAndLogEx(SUCCESS, "key count: %d", numkeys & 0x0f); if (numkeys & 0x20) PrintAndLogEx(SUCCESS, "iso file id: enabled"); @@ -1701,6 +2053,7 @@ void PrintKeySettings(uint8_t keysettings, uint8_t numkeys, bool applevel, bool static const char *DesfireUnknownStr = "unknown"; static const char *DesfireDisabledStr = "disabled"; static const char *DesfireFreeStr = "free"; +static const char *DesfireNAStr = "n/a"; static const DesfireCreateFileCommandsS DesfireFileCommands[] = { {0x00, "Standard data", MFDES_CREATE_STD_DATA_FILE, 6, 6, true}, {0x01, "Backup data", MFDES_CREATE_BACKUP_DATA_FILE, 6, 6, true}, @@ -1771,6 +2124,32 @@ const char *GetDesfireAccessRightStr(uint8_t right) { return DesfireUnknownStr; } +const char *AccessRightShortStr[] = { + "key0", + "key1", + "key2", + "key3", + "key4", + "key5", + "key6", + "key7", + "key8", + "key9", + "keyA", + "keyB", + "keyC", + "keyD", + "free", + "deny" +}; + +const char *GetDesfireAccessRightShortStr(uint8_t right) { + if (right > 0x0f) + return DesfireNAStr; + + return AccessRightShortStr[right]; +} + void DesfireEncodeFileAcessMode(uint8_t *mode, uint8_t r, uint8_t w, uint8_t rw, uint8_t ch) { mode[0] = (ch & 0x0f) | ((rw << 4) & 0xf0); mode[1] = (w & 0x0f) | ((r << 4) & 0xf0); @@ -1863,10 +2242,7 @@ void DesfireFillFileSettings(uint8_t *data, size_t datalen, FileSettingsS *fsett } } -void DesfirePrintFileSettingsOneLine(FileSettingsS *fsettings) { - PrintAndLogEx(NORMAL, "(%-5s) " NOLF, GetDesfireCommunicationMode(fsettings->fileCommMode)); - PrintAndLogEx(NORMAL, "[0x%02x] " _CYAN_("%-13s ") NOLF, fsettings->fileType, GetDesfireFileType(fsettings->fileType)); - +static void DesfirePrintShortFileTypeSettings(FileSettingsS *fsettings) { switch (fsettings->fileType) { case 0x00: case 0x01: { @@ -1874,13 +2250,13 @@ void DesfirePrintFileSettingsOneLine(FileSettingsS *fsettings) { break; } case 0x02: { - PrintAndLogEx(NORMAL, "[%d .. %d] lim cred: 0x%02x (%d [0x%x]) " NOLF, + PrintAndLogEx(NORMAL, "value [%d .. %d] lim cred: 0x%02x (%d [0x%x]) " NOLF, fsettings->lowerLimit, fsettings->upperLimit, fsettings->limitedCredit, fsettings->value, fsettings->value); break; } case 0x03: case 0x04: { - PrintAndLogEx(NORMAL, "%d/%d record size: %d [0x%x]b " NOLF, + PrintAndLogEx(NORMAL, "record count %d/%d size: %d [0x%x]b " NOLF, fsettings->curRecordCount, fsettings->maxRecordCount, fsettings->recordSize, fsettings->recordSize); break; } @@ -1892,12 +2268,49 @@ void DesfirePrintFileSettingsOneLine(FileSettingsS *fsettings) { break; } } +} + +void DesfirePrintFileSettingsOneLine(FileSettingsS *fsettings) { + PrintAndLogEx(NORMAL, "(%-5s) " NOLF, GetDesfireCommunicationMode(fsettings->fileCommMode)); + PrintAndLogEx(NORMAL, "[0x%02x] " _CYAN_("%-13s ") NOLF, fsettings->fileType, GetDesfireFileType(fsettings->fileType)); + + DesfirePrintShortFileTypeSettings(fsettings); PrintAndLogEx(NORMAL, "(%s %s %s %s)", - GetDesfireAccessRightStr(fsettings->rAccess), - GetDesfireAccessRightStr(fsettings->wAccess), - GetDesfireAccessRightStr(fsettings->rwAccess), - GetDesfireAccessRightStr(fsettings->chAccess)); + GetDesfireAccessRightShortStr(fsettings->rAccess), + GetDesfireAccessRightShortStr(fsettings->wAccess), + GetDesfireAccessRightShortStr(fsettings->rwAccess), + GetDesfireAccessRightShortStr(fsettings->chAccess)); +} + +void DesfirePrintFileSettingsTable(bool printheader, uint8_t id, bool isoidavail, uint16_t isoid, FileSettingsS *fsettings) { + if (printheader) { + PrintAndLogEx(SUCCESS, " ID |ISO ID| File type | Mode | Rights: raw, r w rw ch | File settings "); + PrintAndLogEx(SUCCESS, "----------------------------------------------------------------------------------------------------------"); + } + PrintAndLogEx(SUCCESS, " " _GREEN_("%02x") " |" NOLF, id); + if (isoidavail) { + if (isoid != 0) + PrintAndLogEx(NORMAL, " " _CYAN_("%04x") " |" NOLF, isoid); + else + PrintAndLogEx(NORMAL, " " _YELLOW_("n/a ") " |" NOLF); + } else { + PrintAndLogEx(NORMAL, " |" NOLF); + } + + PrintAndLogEx(NORMAL, "0x%02x " _CYAN_("%-13s") " |" NOLF, fsettings->fileType, GetDesfireFileType(fsettings->fileType)); + PrintAndLogEx(NORMAL, " %-5s |" NOLF, GetDesfireCommunicationMode(fsettings->fileCommMode)); + + PrintAndLogEx(NORMAL, "%04x, %-4s %-4s %-4s %-4s |" NOLF, + fsettings->rawAccessRights, + GetDesfireAccessRightShortStr(fsettings->rAccess), + GetDesfireAccessRightShortStr(fsettings->wAccess), + GetDesfireAccessRightShortStr(fsettings->rwAccess), + GetDesfireAccessRightShortStr(fsettings->chAccess)); + + PrintAndLogEx(NORMAL, " " NOLF); + DesfirePrintShortFileTypeSettings(fsettings); + PrintAndLogEx(NORMAL, ""); } void DesfirePrintFileSettingsExtended(FileSettingsS *fsettings) { @@ -2158,7 +2571,7 @@ int DesfireChangeKey(DesfireContext *dctx, bool change_master_key, uint8_t newke iso14443a_crc(nkeybuf, nkeylen, &cdata[cdatalen]); cdatalen += 2; } - } else { + } else if (dctx->secureChannel == DACEV1) { // EV1 Checksum must cover : [] desfire_crc32_append(pckcdata, cdatalen + 2); cdatalen += 4; @@ -2166,12 +2579,18 @@ int DesfireChangeKey(DesfireContext *dctx, bool change_master_key, uint8_t newke desfire_crc32(nkeybuf, nkeylen, &cdata[cdatalen]); cdatalen += 4; } + } else if (dctx->secureChannel == DACEV2) { + // EV2 : [] + if (newkeynum != dctx->keyNum) { + desfire_crc32(nkeybuf, nkeylen, &cdata[cdatalen]); + cdatalen += 4; + } } // send command uint8_t resp[257] = {0}; size_t resplen = 0; - int res = DesfireChangeKeyCmd(dctx, &pckcdata[1], cdatalen, resp, &resplen); + int res = DesfireChangeKeyCmd(dctx, &pckcdata[1], cdatalen + 1, resp, &resplen); // check response if (res == 0 && resplen > 0) @@ -2192,21 +2611,9 @@ int DesfireSetConfiguration(DesfireContext *dctx, uint8_t paramid, uint8_t *para memcpy(&data[1], param, paramlen); size_t datalen = 1 + paramlen; - - // add crc - if (dctx->secureChannel == DACd40) { - iso14443a_crc_append(&data[1], datalen - 1); - datalen += 2; - } else { - desfire_crc32_append(cdata, datalen + 1); - datalen += 4; - } - // dynamic length - if (paramid == 0x02) { - data[datalen] = 0x80; - datalen++; - } + if (paramid == 0x02 && dctx->commMode == DCMEncrypted) + dctx->commMode = DCMEncryptedWithPadding; // send command uint8_t resp[257] = {0}; @@ -2220,11 +2627,11 @@ int DesfireSetConfiguration(DesfireContext *dctx, uint8_t paramid, uint8_t *para return res; } -int DesfireISOSelect(DesfireContext *dctx, DesfireISOSelectControl cntr, uint8_t *data, uint8_t datalen, uint8_t *resp, size_t *resplen) { +int DesfireISOSelectEx(DesfireContext *dctx, bool fieldon, DesfireISOSelectControl cntr, uint8_t *data, uint8_t datalen, uint8_t *resp, size_t *resplen) { uint8_t xresp[250] = {0}; size_t xresplen = 0; uint16_t sw = 0; - int res = DesfireExchangeISO(true, dctx, (sAPDU) {0x00, ISO7816_SELECT_FILE, cntr, ((resp == NULL) ? 0x0C : 0x00), datalen, data}, APDU_INCLUDE_LE_00, xresp, &xresplen, &sw); + int res = DesfireExchangeISO(fieldon, dctx, (sAPDU) {0x00, ISO7816_SELECT_FILE, cntr, ((resp == NULL) ? 0x0C : 0x00), datalen, data}, APDU_INCLUDE_LE_00, xresp, &xresplen, &sw); if (res == PM3_SUCCESS && sw != 0x9000) return PM3_ESOFT; @@ -2239,6 +2646,10 @@ int DesfireISOSelect(DesfireContext *dctx, DesfireISOSelectControl cntr, uint8_t return res; } +int DesfireISOSelect(DesfireContext *dctx, DesfireISOSelectControl cntr, uint8_t *data, uint8_t datalen, uint8_t *resp, size_t *resplen) { + return DesfireISOSelectEx(dctx, true, cntr, data, datalen, resp, resplen); +} + int DesfireISOSelectDF(DesfireContext *dctx, char *dfname, uint8_t *resp, size_t *resplen) { return DesfireISOSelect(dctx, ISSDFName, (uint8_t *)dfname, strnlen(dfname, 16), resp, resplen); } @@ -2280,3 +2691,87 @@ int DesfireISOInternalAuth(DesfireContext *dctx, bool app_level, uint8_t keynum, return res; } +int DesfireISOReadBinary(DesfireContext *dctx, bool use_file_id, uint8_t fileid, uint16_t offset, uint8_t length, uint8_t *resp, size_t *resplen) { + uint8_t p1 = 0; + if (use_file_id) + p1 = 0x80 | (fileid & 0x1f); + else + p1 = (offset >> 8) & 0x7f; + uint8_t p2 = offset & 0xff; + + uint16_t sw = 0; + int res = DesfireExchangeISO(false, dctx, (sAPDU) {0x00, ISO7816_READ_BINARY, p1, p2, 0, NULL}, (length == 0) ? APDU_INCLUDE_LE_00 : length, resp, resplen, &sw); + if (res == PM3_SUCCESS && sw != 0x9000) + return PM3_ESOFT; + + return res; +} + +int DesfireISOUpdateBinary(DesfireContext *dctx, bool use_file_id, uint8_t fileid, uint16_t offset, uint8_t *data, size_t datalen) { + uint8_t p1 = 0; + if (use_file_id) + p1 = 0x80 | (fileid & 0x1f); + else + p1 = (offset >> 8) & 0x7f; + uint8_t p2 = offset & 0xff; + + uint8_t resp[250] = {0}; + size_t resplen = 0; + + uint16_t sw = 0; + int res = DesfireExchangeISO(false, dctx, (sAPDU) {0x00, ISO7816_UPDATE_BINARY, p1, p2, datalen, data}, 0, resp, &resplen, &sw); + if (res == PM3_SUCCESS && sw != 0x9000) + return PM3_ESOFT; + + return res; +} + +int DesfireISOReadRecords(DesfireContext *dctx, uint8_t recordnum, bool read_all_records, uint8_t fileid, uint8_t length, uint8_t *resp, size_t *resplen) { + uint8_t p2 = ((fileid & 0x1f) << 3) | ((read_all_records) ? 0x05 : 0x04); + + uint16_t sw = 0; + int res = DesfireExchangeISO(false, dctx, (sAPDU) {0x00, ISO7816_READ_RECORDS, recordnum, p2, 0, NULL}, (length == 0) ? APDU_INCLUDE_LE_00 : length, resp, resplen, &sw); + if (res == PM3_SUCCESS && sw != 0x9000) + return PM3_ESOFT; + + return res; +} + +int DesfireISOAppendRecord(DesfireContext *dctx, uint8_t fileid, uint8_t *data, size_t datalen) { + uint8_t p2 = ((fileid & 0x1f) << 3); + + uint8_t resp[250] = {0}; + size_t resplen = 0; + + uint16_t sw = 0; + int res = DesfireExchangeISO(false, dctx, (sAPDU) {0x00, ISO7816_APPEND_RECORD, 0x00, p2, datalen, data}, 0, resp, &resplen, &sw); + if (res == PM3_SUCCESS && sw != 0x9000) + return PM3_ESOFT; + + return res; +} + +int DesfireSelectEx(DesfireContext *ctx, bool fieldon, DesfireISOSelectWay way, uint32_t id, char *dfname) { + uint8_t resp[250] = {0}; + size_t resplen = 0; + + if (way == ISWMF || (way == ISWDFName && dfname == NULL)) { + return DesfireISOSelect(ctx, ISSMFDFEF, NULL, 0, resp, &resplen); + } else if (way == ISW6bAID) { + if (fieldon) + return DesfireSelectAIDHex(ctx, id, false, 0); + else + return DesfireSelectAIDHexNoFieldOn(ctx, id); + } else if (way == ISWIsoID) { + uint8_t data[2] = {0}; + Uint2byteToMemBe(data, id); + return DesfireISOSelectEx(ctx, fieldon, ISSMFDFEF, data, 2, resp, &resplen); + } else if (way == ISWDFName) { + return DesfireISOSelect(ctx, ISSMFDFEF, NULL, 0, resp, &resplen); + } + return PM3_ESOFT; +} + +int DesfireSelect(DesfireContext *ctx, DesfireISOSelectWay way, uint32_t id, char *dfname) { + return DesfireSelectEx(ctx, true, way, id, dfname); +} diff --git a/client/src/mifare/desfirecore.h b/client/src/mifare/desfirecore.h index a149ba36c..a03e489eb 100644 --- a/client/src/mifare/desfirecore.h +++ b/client/src/mifare/desfirecore.h @@ -30,6 +30,14 @@ enum DesfireISOSelectControlEnum { }; typedef enum DesfireISOSelectControlEnum DesfireISOSelectControl; +enum DesfireISOSelectWayEnum { + ISW6bAID, + ISWMF, + ISWIsoID, + ISWDFName +}; +typedef enum DesfireISOSelectWayEnum DesfireISOSelectWay; + typedef struct { const uint8_t id; const char *text; @@ -85,6 +93,48 @@ typedef struct { typedef FileListElmS FileListS[32]; +typedef struct { + bool checked; + bool auth; + bool authISO; + bool authAES; + bool authEV2; + bool authISONative; +} AuthCommandsChk; + +typedef struct { + uint32_t appNum; + uint16_t appISONum; + char appDFName[16]; + AuthCommandsChk authCmdCheck; + + uint8_t keySettings; + uint8_t numKeysRaw; + bool isoFileIDEnabled; // from numKeysRaw + uint8_t numberOfKeys; // from numKeysRaw + DesfireCryptoAlgorythm keyType; // from numKeysRaw + + uint8_t keyVersions[16]; + + bool filesReaded; + size_t filesCount; + bool isoPresent; + FileListS fileList; +} AppListElmS; +typedef AppListElmS AppListS[64]; + +typedef struct { + size_t appCount; + uint32_t freemem; + AuthCommandsChk authCmdCheck; + + uint8_t keySettings; + uint8_t numKeysRaw; + uint8_t numberOfKeys; // from numKeysRaw + + uint8_t keyVersion0; +} PICCInfoS; + typedef enum { RFTAuto, RFTData, @@ -111,20 +161,33 @@ void DesfirePrintContext(DesfireContext *ctx); int DesfireExchange(DesfireContext *ctx, uint8_t cmd, uint8_t *data, size_t datalen, uint8_t *respcode, uint8_t *resp, size_t *resplen); int DesfireExchangeEx(bool activate_field, DesfireContext *ctx, uint8_t cmd, uint8_t *data, size_t datalen, uint8_t *respcode, uint8_t *resp, size_t *resplen, bool enable_chaining, size_t splitbysize); +int DesfireReadSignature(DesfireContext *dctx, uint8_t sid, uint8_t *resp, size_t *resplen); + int DesfireSelectAID(DesfireContext *ctx, uint8_t *aid1, uint8_t *aid2); int DesfireSelectAIDHex(DesfireContext *ctx, uint32_t aid1, bool select_two, uint32_t aid2); int DesfireSelectAIDHexNoFieldOn(DesfireContext *ctx, uint32_t aid); +void DesfirePrintAIDFunctions(uint32_t appid); + +int DesfireSelectEx(DesfireContext *ctx, bool fieldon, DesfireISOSelectWay way, uint32_t id, char *dfname); +int DesfireSelect(DesfireContext *ctx, DesfireISOSelectWay way, uint32_t id, char *dfname); const char *DesfireAuthErrorToStr(int error); int DesfireSelectAndAuthenticate(DesfireContext *dctx, DesfireSecureChannel secureChannel, uint32_t aid, bool verbose); int DesfireSelectAndAuthenticateEx(DesfireContext *dctx, DesfireSecureChannel secureChannel, uint32_t aid, bool noauth, bool verbose); +int DesfireSelectAndAuthenticateISO(DesfireContext *dctx, DesfireSecureChannel secureChannel, bool useaid, uint32_t aid, uint16_t isoappid, uint16_t isofileid, bool noauth, bool verbose); int DesfireAuthenticate(DesfireContext *dctx, DesfireSecureChannel secureChannel, bool verbose); +void DesfireCheckAuthCommands(uint32_t appAID, char *dfname, uint8_t keyNum, AuthCommandsChk *authCmdCheck); +void DesfireCheckAuthCommandsPrint(AuthCommandsChk *authCmdCheck); int DesfireFormatPICC(DesfireContext *dctx); int DesfireGetFreeMem(DesfireContext *dctx, uint32_t *freemem); int DesfireGetUID(DesfireContext *dctx, uint8_t *resp, size_t *resplen); int DesfireGetAIDList(DesfireContext *dctx, uint8_t *resp, size_t *resplen); int DesfireGetDFList(DesfireContext *dctx, uint8_t *resp, size_t *resplen); +int DesfireFillPICCInfo(DesfireContext *dctx, PICCInfoS *PICCInfo, bool deepmode); +int DesfireFillAppList(DesfireContext *dctx, PICCInfoS *PICCInfo, AppListS appList, bool deepmode, bool readFiles); +void DesfirePrintPICCInfo(DesfireContext *dctx, PICCInfoS *PICCInfo); +void DesfirePrintAppList(DesfireContext *dctx, PICCInfoS *PICCInfo, AppListS appList); int DesfireCreateApplication(DesfireContext *dctx, uint8_t *appdata, size_t appdatalen); int DesfireDeleteApplication(DesfireContext *dctx, uint32_t aid); @@ -133,7 +196,6 @@ int DesfireGetKeyVersion(DesfireContext *dctx, uint8_t *data, size_t len, uint8_ int DesfireGetKeySettings(DesfireContext *dctx, uint8_t *resp, size_t *resplen); int DesfireChangeKeySettings(DesfireContext *dctx, uint8_t *data, size_t len); void PrintKeySettings(uint8_t keysettings, uint8_t numkeys, bool applevel, bool print2ndbyte); -uint8_t DesfireKeyAlgoToType(DesfireCryptoAlgorythm keyType); int DesfireChangeKeyCmd(DesfireContext *dctx, uint8_t *data, size_t datalen, uint8_t *resp, size_t *resplen); int DesfireChangeKey(DesfireContext *dctx, bool change_master_key, uint8_t newkeynum, DesfireCryptoAlgorythm newkeytype, uint32_t newkeyver, uint8_t *newkey, DesfireCryptoAlgorythm oldkeytype, uint8_t *oldkey, bool verbose); @@ -147,6 +209,7 @@ int DesfireGetFileISOIDList(DesfireContext *dctx, uint8_t *resp, size_t *resplen void DesfireFillFileSettings(uint8_t *data, size_t datalen, FileSettingsS *fsettings); void DesfirePrintFileSettingsOneLine(FileSettingsS *fsettings); +void DesfirePrintFileSettingsTable(bool printheader, uint8_t id, bool isoidavail, uint16_t isoid, FileSettingsS *fsettings); void DesfirePrintFileSettingsExtended(FileSettingsS *fsettings); int DesfireGetFileSettings(DesfireContext *dctx, uint8_t fileid, uint8_t *resp, size_t *resplen); int DesfireGetFileSettingsStruct(DesfireContext *dctx, uint8_t fileid, FileSettingsS *fsettings); @@ -154,6 +217,7 @@ int DesfireChangeFileSettings(DesfireContext *dctx, uint8_t *data, size_t datale const DesfireCreateFileCommandsS *GetDesfireFileCmdRec(uint8_t type); const char *GetDesfireAccessRightStr(uint8_t right); +const char *GetDesfireAccessRightShortStr(uint8_t right); void DesfireEncodeFileAcessMode(uint8_t *mode, uint8_t r, uint8_t w, uint8_t rw, uint8_t ch); void DesfireDecodeFileAcessMode(uint8_t *mode, uint8_t *r, uint8_t *w, uint8_t *rw, uint8_t *ch); void DesfirePrintAccessRight(uint8_t *data); @@ -178,8 +242,15 @@ int DesfireUpdateRecord(DesfireContext *dctx, uint8_t fnum, uint32_t recnum, uin int DesfireISOSelectDF(DesfireContext *dctx, char *dfname, uint8_t *resp, size_t *resplen); int DesfireISOSelect(DesfireContext *dctx, DesfireISOSelectControl cntr, uint8_t *data, uint8_t datalen, uint8_t *resp, size_t *resplen); +int DesfireISOSelectFile(DesfireContext *dctx, char *appdfname, uint16_t appid, uint16_t fileid); +int DesfireISOSelectEx(DesfireContext *dctx, bool fieldon, DesfireISOSelectControl cntr, uint8_t *data, uint8_t datalen, uint8_t *resp, size_t *resplen); int DesfireISOGetChallenge(DesfireContext *dctx, DesfireCryptoAlgorythm keytype, uint8_t *resp, size_t *resplen); int DesfireISOExternalAuth(DesfireContext *dctx, bool app_level, uint8_t keynum, DesfireCryptoAlgorythm keytype, uint8_t *data); int DesfireISOInternalAuth(DesfireContext *dctx, bool app_level, uint8_t keynum, DesfireCryptoAlgorythm keytype, uint8_t *data, uint8_t *resp, size_t *resplen); +int DesfireISOReadBinary(DesfireContext *dctx, bool use_file_id, uint8_t fileid, uint16_t offset, uint8_t length, uint8_t *resp, size_t *resplen); +int DesfireISOUpdateBinary(DesfireContext *dctx, bool use_file_id, uint8_t fileid, uint16_t offset, uint8_t *data, size_t datalen); +int DesfireISOReadRecords(DesfireContext *dctx, uint8_t recordnum, bool read_all_records, uint8_t fileid, uint8_t length, uint8_t *resp, size_t *resplen); +int DesfireISOAppendRecord(DesfireContext *dctx, uint8_t fileid, uint8_t *data, size_t datalen); + #endif // __DESFIRECORE_H diff --git a/client/src/mifare/desfirecrypto.c b/client/src/mifare/desfirecrypto.c index ba82cdae0..366995a06 100644 --- a/client/src/mifare/desfirecrypto.c +++ b/client/src/mifare/desfirecrypto.c @@ -217,7 +217,7 @@ static void DesfireCryptoEncDecSingleBlock(uint8_t *key, DesfireCryptoAlgorythm memcpy(dstdata, edata, block_size); } -void DesfireCryptoEncDecEx(DesfireContext *ctx, bool use_session_key, uint8_t *srcdata, size_t srcdatalen, uint8_t *dstdata, bool dir_to_send, bool encode, uint8_t *iv) { +void DesfireCryptoEncDecEx(DesfireContext *ctx, DesfireCryptoOpKeyType key_type, uint8_t *srcdata, size_t srcdatalen, uint8_t *dstdata, bool dir_to_send, bool encode, uint8_t *iv) { uint8_t data[1024] = {0}; uint8_t xiv[DESFIRE_MAX_CRYPTO_BLOCK_SIZE] = {0}; @@ -234,10 +234,13 @@ void DesfireCryptoEncDecEx(DesfireContext *ctx, bool use_session_key, uint8_t *s size_t offset = 0; while (offset < srcdatalen) { - if (use_session_key) + if (key_type == DCOSessionKeyMac) { DesfireCryptoEncDecSingleBlock(ctx->sessionKeyMAC, ctx->keyType, srcdata + offset, data + offset, xiv, dir_to_send, encode); - else + } else if (key_type == DCOSessionKeyEnc) { + DesfireCryptoEncDecSingleBlock(ctx->sessionKeyEnc, ctx->keyType, srcdata + offset, data + offset, xiv, dir_to_send, encode); + } else { DesfireCryptoEncDecSingleBlock(ctx->key, ctx->keyType, srcdata + offset, data + offset, xiv, dir_to_send, encode); + } offset += block_size; } @@ -250,13 +253,13 @@ void DesfireCryptoEncDecEx(DesfireContext *ctx, bool use_session_key, uint8_t *s memcpy(dstdata, data, srcdatalen); } -void DesfireCryptoEncDec(DesfireContext *ctx, bool use_session_key, uint8_t *srcdata, size_t srcdatalen, uint8_t *dstdata, bool encode) { +void DesfireCryptoEncDec(DesfireContext *ctx, DesfireCryptoOpKeyType key_type, uint8_t *srcdata, size_t srcdatalen, uint8_t *dstdata, bool encode) { bool dir_to_send = encode; bool xencode = encode; if (ctx->secureChannel == DACd40) xencode = false; - DesfireCryptoEncDecEx(ctx, use_session_key, srcdata, srcdatalen, dstdata, dir_to_send, xencode, NULL); + DesfireCryptoEncDecEx(ctx, key_type, srcdata, srcdatalen, dstdata, dir_to_send, xencode, NULL); } static void DesfireCMACGenerateSubkeys(DesfireContext *ctx, uint8_t *sk1, uint8_t *sk2) { @@ -269,7 +272,7 @@ static void DesfireCMACGenerateSubkeys(DesfireContext *ctx, uint8_t *sk1, uint8_ uint8_t ivect[kbs]; memset(ivect, 0, kbs); - DesfireCryptoEncDecEx(ctx, true, l, kbs, l, true, true, ivect); + DesfireCryptoEncDecEx(ctx, DCOSessionKeyMac, l, kbs, l, true, true, ivect); bool txor = false; @@ -314,7 +317,7 @@ void DesfireCryptoCMAC(DesfireContext *ctx, uint8_t *data, size_t len, uint8_t * bin_xor(buffer + len - kbs, sk1, kbs); } - DesfireCryptoEncDec(ctx, true, buffer, len, NULL, true); + DesfireCryptoEncDec(ctx, DCOSessionKeyMac, buffer, len, NULL, true); if (cmac != NULL) memcpy(cmac, ctx->IV, kbs); @@ -353,6 +356,50 @@ uint8_t DesfireDESKeyGetVersion(uint8_t *key) { return version; } +DesfireCryptoAlgorythm DesfireKeyTypeToAlgo(uint8_t keyType) { + switch (keyType) { + case 00: + return T_3DES; + case 01: + return T_3K3DES; + case 02: + return T_AES; + default: + return T_3DES; // unknown.... + } +} + +uint8_t DesfireKeyAlgoToType(DesfireCryptoAlgorythm keyType) { + switch (keyType) { + case T_DES: + return 0x00; + case T_3DES: + return 0x00; + case T_3K3DES: + return 0x01; + case T_AES: + return 0x02; + } + return 0; +} + +void DesfirePrintCardKeyType(uint8_t keyType) { + switch (keyType) { + case 00: + PrintAndLogEx(SUCCESS, "Key: 2TDEA"); + break; + case 01: + PrintAndLogEx(SUCCESS, "Key: 3TDEA"); + break; + case 02: + PrintAndLogEx(SUCCESS, "Key: AES"); + break; + default: + PrintAndLogEx(SUCCESS, "Key: unknown: 0x%02x", keyType); + break; + } +} + DesfireCommunicationMode DesfireFileCommModeToCommMode(uint8_t file_comm_mode) { DesfireCommunicationMode mode = DCMNone; switch (file_comm_mode & 0x03) { @@ -382,6 +429,7 @@ uint8_t DesfireCommModeToFileCommMode(DesfireCommunicationMode comm_mode) { fmode = 0x01; break; case DCMEncrypted: + case DCMEncryptedWithPadding: case DCMEncryptedPlain: fmode = 0x11; break; @@ -474,6 +522,19 @@ void DesfireEV2FillIV(DesfireContext *ctx, bool ivforcommand, uint8_t *iv) { memcpy(iv, xiv, CRYPTO_AES_BLOCK_SIZE); } +int DesfireEV2CalcCMAC(DesfireContext *ctx, uint8_t cmd, uint8_t *data, size_t datalen, uint8_t *mac) { + uint8_t mdata[1050] = {0}; + size_t mdatalen = 0; + + mdata[0] = cmd; + Uint2byteToMemLe(&mdata[1], ctx->cmdCntr); + memcpy(&mdata[3], ctx->TI, 4); + if (data != NULL && datalen > 0) + memcpy(&mdata[7], data, datalen); + mdatalen = 1 + 2 + 4 + datalen; + + return aes_cmac8(NULL, ctx->sessionKeyMAC, mdata, mac, mdatalen); +} void desfire_crc32(const uint8_t *data, const size_t len, uint8_t *crc) { crc32_ex(data, len, crc); diff --git a/client/src/mifare/desfirecrypto.h b/client/src/mifare/desfirecrypto.h index a336f9e3f..9fa379049 100644 --- a/client/src/mifare/desfirecrypto.h +++ b/client/src/mifare/desfirecrypto.h @@ -58,9 +58,15 @@ typedef enum { DCMPlain, DCMMACed, DCMEncrypted, + DCMEncryptedWithPadding, DCMEncryptedPlain } DesfireCommunicationMode; +typedef enum { + DCOMainKey, + DCOSessionKeyMac, + DCOSessionKeyEnc +} DesfireCryptoOpKeyType; typedef struct DesfireContextS { uint8_t keyNum; @@ -100,19 +106,24 @@ size_t DesfireGetMACLength(DesfireContext *ctx); size_t DesfireSearchCRCPos(uint8_t *data, size_t datalen, uint8_t respcode, uint8_t crclen); -void DesfireCryptoEncDec(DesfireContext *ctx, bool use_session_key, uint8_t *srcdata, size_t srcdatalen, uint8_t *dstdata, bool encode); -void DesfireCryptoEncDecEx(DesfireContext *ctx, bool use_session_key, uint8_t *srcdata, size_t srcdatalen, uint8_t *dstdata, bool dir_to_send, bool encode, uint8_t *iv); +void DesfireCryptoEncDec(DesfireContext *ctx, DesfireCryptoOpKeyType key_type, uint8_t *srcdata, size_t srcdatalen, uint8_t *dstdata, bool encode); +void DesfireCryptoEncDecEx(DesfireContext *ctx, DesfireCryptoOpKeyType key_type, uint8_t *srcdata, size_t srcdatalen, uint8_t *dstdata, bool dir_to_send, bool encode, uint8_t *iv); void DesfireCryptoCMAC(DesfireContext *ctx, uint8_t *srcdata, size_t srcdatalen, uint8_t *cmac); void DesfireDESKeySetVersion(uint8_t *key, DesfireCryptoAlgorythm keytype, uint8_t version); uint8_t DesfireDESKeyGetVersion(uint8_t *key); +DesfireCryptoAlgorythm DesfireKeyTypeToAlgo(uint8_t keyType); +uint8_t DesfireKeyAlgoToType(DesfireCryptoAlgorythm keyType); +void DesfirePrintCardKeyType(uint8_t keyType); + DesfireCommunicationMode DesfireFileCommModeToCommMode(uint8_t file_comm_mode); uint8_t DesfireCommModeToFileCommMode(DesfireCommunicationMode comm_mode); void DesfireGenSessionKeyEV1(const uint8_t rnda[], const uint8_t rndb[], DesfireCryptoAlgorythm keytype, uint8_t *key); void DesfireGenSessionKeyEV2(uint8_t *key, uint8_t *rndA, uint8_t *rndB, bool enckey, uint8_t *sessionkey); void DesfireEV2FillIV(DesfireContext *ctx, bool ivforcommand, uint8_t *iv); +int DesfireEV2CalcCMAC(DesfireContext *ctx, uint8_t cmd, uint8_t *data, size_t datalen, uint8_t *mac); void desfire_crc32(const uint8_t *data, const size_t len, uint8_t *crc); void desfire_crc32_append(uint8_t *data, const size_t len); diff --git a/client/src/mifare/desfiresecurechan.c b/client/src/mifare/desfiresecurechan.c index 73aba69a6..ced1fd073 100644 --- a/client/src/mifare/desfiresecurechan.c +++ b/client/src/mifare/desfiresecurechan.c @@ -41,27 +41,24 @@ static bool CommandCanUseAnyChannel(uint8_t cmd) { } static const AllowedChannelModesS AllowedChannelModes[] = { + // D40 channel {MFDES_SELECT_APPLICATION, DACd40, DCCNative, DCMPlain}, - {MFDES_CREATE_APPLICATION, DACd40, DCCNative, DCMPlain}, - {MFDES_DELETE_APPLICATION, DACd40, DCCNative, DCMPlain}, - {MFDES_GET_APPLICATION_IDS, DACd40, DCCNative, DCMPlain}, - {MFDES_GET_DF_NAMES, DACd40, DCCNative, DCMPlain}, - {MFDES_GET_KEY_SETTINGS, DACd40, DCCNative, DCMPlain}, - {MFDES_GET_KEY_VERSION, DACd40, DCCNative, DCMPlain}, - {MFDES_GET_FREE_MEMORY, DACd40, DCCNative, DCMPlain}, - {MFDES_CREATE_STD_DATA_FILE, DACd40, DCCNative, DCMPlain}, - {MFDES_CREATE_BACKUP_DATA_FILE, DACd40, DCCNative, DCMPlain}, - {MFDES_CREATE_VALUE_FILE, DACd40, DCCNative, DCMPlain}, - {MFDES_CREATE_LINEAR_RECORD_FILE, DACd40, DCCNative, DCMPlain}, - {MFDES_CREATE_CYCLIC_RECORD_FILE, DACd40, DCCNative, DCMPlain}, - {MFDES_GET_VALUE, DACd40, DCCNative, DCMPlain}, - {MFDES_CREDIT, DACd40, DCCNative, DCMPlain}, - {MFDES_LIMITED_CREDIT, DACd40, DCCNative, DCMPlain}, - {MFDES_DEBIT, DACd40, DCCNative, DCMPlain}, - {MFDES_COMMIT_TRANSACTION, DACd40, DCCNative, DCMPlain}, - {MFDES_CLEAR_RECORD_FILE, DACd40, DCCNative, DCMPlain}, - {MFDES_GET_FILE_SETTINGS, DACd40, DCCNative, DCMPlain}, + {MFDES_CREATE_APPLICATION, DACd40, DCCNative, DCMMACed}, + {MFDES_DELETE_APPLICATION, DACd40, DCCNative, DCMMACed}, + {MFDES_GET_APPLICATION_IDS, DACd40, DCCNative, DCMMACed}, + {MFDES_GET_DF_NAMES, DACd40, DCCNative, DCMMACed}, + {MFDES_GET_KEY_SETTINGS, DACd40, DCCNative, DCMMACed}, + {MFDES_GET_KEY_VERSION, DACd40, DCCNative, DCMMACed}, + {MFDES_GET_FREE_MEMORY, DACd40, DCCNative, DCMMACed}, + {MFDES_CREATE_STD_DATA_FILE, DACd40, DCCNative, DCMMACed}, + {MFDES_CREATE_BACKUP_DATA_FILE, DACd40, DCCNative, DCMMACed}, + {MFDES_CREATE_VALUE_FILE, DACd40, DCCNative, DCMMACed}, + {MFDES_CREATE_LINEAR_RECORD_FILE, DACd40, DCCNative, DCMMACed}, + {MFDES_CREATE_CYCLIC_RECORD_FILE, DACd40, DCCNative, DCMMACed}, + {MFDES_COMMIT_TRANSACTION, DACd40, DCCNative, DCMMACed}, + {MFDES_CLEAR_RECORD_FILE, DACd40, DCCNative, DCMMACed}, + {MFDES_GET_FILE_SETTINGS, DACd40, DCCNative, DCMMACed}, {MFDES_GET_VALUE, DACd40, DCCNative, DCMMACed}, {MFDES_CREDIT, DACd40, DCCNative, DCMMACed}, {MFDES_DEBIT, DACd40, DCCNative, DCMMACed}, @@ -85,10 +82,11 @@ static const AllowedChannelModesS AllowedChannelModes[] = { {MFDES_CHANGE_KEY, DACd40, DCCNative, DCMEncryptedPlain}, {MFDES_CHANGE_KEY_EV2, DACd40, DCCNative, DCMEncryptedPlain}, - {MFDES_GET_KEY_VERSION, DACEV1, DCCNative, DCMPlain}, - {MFDES_GET_FREE_MEMORY, DACEV1, DCCNative, DCMPlain}, + // EV1 and EV2 channel {MFDES_SELECT_APPLICATION, DACEV1, DCCNative, DCMPlain}, + {MFDES_GET_KEY_VERSION, DACEV1, DCCNative, DCMMACed}, + {MFDES_GET_FREE_MEMORY, DACEV1, DCCNative, DCMMACed}, {MFDES_CREATE_APPLICATION, DACEV1, DCCNative, DCMMACed}, {MFDES_DELETE_APPLICATION, DACEV1, DCCNative, DCMMACed}, {MFDES_GET_APPLICATION_IDS, DACEV1, DCCNative, DCMMACed}, @@ -118,8 +116,26 @@ static const AllowedChannelModesS AllowedChannelModes[] = { {MFDES_CHANGE_KEY, DACEV1, DCCNative, DCMEncryptedPlain}, {MFDES_CHANGE_KEY_EV2, DACEV1, DCCNative, DCMEncryptedPlain}, + // EV2 channel separately {MFDES_AUTHENTICATE_EV2F, DACEV2, DCCNative, DCMPlain}, {MFDES_AUTHENTICATE_EV2NF, DACEV2, DCCNative, DCMPlain}, + + // ISO channel + {ISO7816_READ_BINARY, DACd40, DCCISO, DCMPlain}, + {ISO7816_UPDATE_BINARY, DACd40, DCCISO, DCMPlain}, + {ISO7816_READ_RECORDS, DACd40, DCCISO, DCMPlain}, + {ISO7816_APPEND_RECORD, DACd40, DCCISO, DCMPlain}, + + {ISO7816_READ_BINARY, DACd40, DCCISO, DCMMACed}, + {ISO7816_READ_RECORDS, DACd40, DCCISO, DCMMACed}, + + {ISO7816_READ_BINARY, DACEV1, DCCISO, DCMPlain}, + {ISO7816_UPDATE_BINARY, DACEV1, DCCISO, DCMPlain}, + {ISO7816_READ_RECORDS, DACEV1, DCCISO, DCMPlain}, + {ISO7816_APPEND_RECORD, DACEV1, DCCISO, DCMPlain}, + + {ISO7816_READ_BINARY, DACEV1, DCCISO, DCMMACed}, + {ISO7816_READ_RECORDS, DACEV1, DCCISO, DCMMACed}, }; #define CMD_HEADER_LEN_ALL 0xffff @@ -147,6 +163,68 @@ static uint8_t DesfireGetCmdHeaderLen(uint8_t cmd) { return 0; } +static const uint8_t EV1D40TransmitMAC[] = { + MFDES_WRITE_DATA, + MFDES_CREDIT, + MFDES_LIMITED_CREDIT, + MFDES_DEBIT, + MFDES_WRITE_RECORD, + MFDES_UPDATE_RECORD, + MFDES_COMMIT_READER_ID, + MFDES_INIT_KEY_SETTINGS, + MFDES_ROLL_KEY_SETTINGS, + MFDES_FINALIZE_KEY_SETTINGS, +}; + +static bool DesfireEV1D40TransmitMAC(DesfireContext *ctx, uint8_t cmd) { + if (ctx->secureChannel != DACd40 && ctx->secureChannel != DACEV1) + return true; + + for (int i = 0; i < ARRAY_LENGTH(EV1D40TransmitMAC); i++) + if (EV1D40TransmitMAC[i] == cmd) + return true; + + return false; +} + +static const uint8_t D40ReceiveMAC[] = { + MFDES_READ_DATA, + MFDES_READ_DATA2, + MFDES_READ_RECORDS, + MFDES_READ_RECORDS2, + MFDES_GET_VALUE, +}; + +static bool DesfireEV1D40ReceiveMAC(DesfireContext *ctx, uint8_t cmd) { + if (ctx->secureChannel != DACd40) + return true; + + for (int i = 0; i < ARRAY_LENGTH(D40ReceiveMAC); i++) + if (D40ReceiveMAC[i] == cmd) + return true; + + return false; +} + +static const uint8_t ISOChannelValidCmd[] = { + ISO7816_SELECT_FILE, + ISO7816_READ_BINARY, + ISO7816_UPDATE_BINARY, + ISO7816_READ_RECORDS, + ISO7816_APPEND_RECORD, + ISO7816_GET_CHALLENGE, + ISO7816_EXTERNAL_AUTHENTICATION, + ISO7816_INTERNAL_AUTHENTICATION +}; + +static bool DesfireISOChannelValidCmd(uint8_t cmd) { + for (int i = 0; i < ARRAY_LENGTH(ISOChannelValidCmd); i++) + if (ISOChannelValidCmd[i] == cmd) + return true; + + return false; +} + static void DesfireSecureChannelEncodeD40(DesfireContext *ctx, uint8_t cmd, uint8_t *srcdata, size_t srcdatalen, uint8_t *dstdata, size_t *dstdatalen) { uint8_t data[1024] = {0}; size_t rlen = 0; @@ -166,22 +244,28 @@ static void DesfireSecureChannelEncodeD40(DesfireContext *ctx, uint8_t cmd, uint size_t srcmaclen = padded_data_length(srcdatalen - hdrlen, desfire_get_key_block_length(ctx->keyType)); uint8_t mac[32] = {0}; - DesfireCryptoEncDecEx(ctx, true, data, srcmaclen, NULL, true, true, mac); + DesfireCryptoEncDecEx(ctx, DCOSessionKeyMac, data, srcmaclen, NULL, true, true, mac); - memcpy(dstdata, srcdata, srcdatalen); - memcpy(&dstdata[srcdatalen], mac, DesfireGetMACLength(ctx)); - *dstdatalen = rlen; - } else if (ctx->commMode == DCMEncrypted) { + if (DesfireEV1D40TransmitMAC(ctx, cmd)) { + memcpy(&dstdata[srcdatalen], mac, DesfireGetMACLength(ctx)); + *dstdatalen = rlen; + } + } else if (ctx->commMode == DCMEncrypted || ctx->commMode == DCMEncryptedWithPadding) { if (srcdatalen <= hdrlen) return; - rlen = padded_data_length(srcdatalen + 2 - hdrlen, desfire_get_key_block_length(ctx->keyType)) + hdrlen; // 2 - crc16 + uint8_t paddinglen = (ctx->commMode == DCMEncryptedWithPadding) ? 1 : 0; + rlen = padded_data_length(srcdatalen + 2 + paddinglen - hdrlen, desfire_get_key_block_length(ctx->keyType)) + hdrlen; // 2 - crc16 memcpy(data, &srcdata[hdrlen], srcdatalen - hdrlen); iso14443a_crc_append(data, srcdatalen - hdrlen); + // add padding + if (paddinglen > 0) + data[srcdatalen - hdrlen + 2] = 0x80; + memcpy(dstdata, srcdata, hdrlen); //PrintAndLogEx(INFO, "src[%d]: %s", srcdatalen - hdrlen + 2, sprint_hex(data, srcdatalen - hdrlen + 2)); - DesfireCryptoEncDec(ctx, true, data, rlen - hdrlen, &dstdata[hdrlen], true); + DesfireCryptoEncDec(ctx, DCOSessionKeyEnc, data, rlen - hdrlen, &dstdata[hdrlen], true); *dstdatalen = rlen; } else if (ctx->commMode == DCMEncryptedPlain) { @@ -191,7 +275,7 @@ static void DesfireSecureChannelEncodeD40(DesfireContext *ctx, uint8_t cmd, uint rlen = padded_data_length(srcdatalen - hdrlen, desfire_get_key_block_length(ctx->keyType)) + hdrlen; memcpy(data, srcdata, srcdatalen); memcpy(dstdata, srcdata, hdrlen); - DesfireCryptoEncDec(ctx, true, &data[hdrlen], rlen - hdrlen, &dstdata[hdrlen], true); + DesfireCryptoEncDec(ctx, DCOSessionKeyEnc, &data[hdrlen], rlen - hdrlen, &dstdata[hdrlen], true); *dstdatalen = rlen; ctx->commMode = DCMEncrypted; } @@ -216,20 +300,28 @@ static void DesfireSecureChannelEncodeEV1(DesfireContext *ctx, uint8_t cmd, uint memcpy(dstdata, srcdata, srcdatalen); *dstdatalen = srcdatalen; - if (srcdatalen > hdrlen && ctx->commMode == DCMMACed) { + if (ctx->commMode == DCMMACed && DesfireEV1D40TransmitMAC(ctx, cmd)) { memcpy(&dstdata[srcdatalen], cmac, DesfireGetMACLength(ctx)); *dstdatalen = srcdatalen + DesfireGetMACLength(ctx); } - } else if (ctx->commMode == DCMEncrypted) { - rlen = padded_data_length(srcdatalen + 4 - hdrlen, desfire_get_key_block_length(ctx->keyType)); + } else if (ctx->commMode == DCMEncrypted || ctx->commMode == DCMEncryptedWithPadding) { + uint8_t paddinglen = (ctx->commMode == DCMEncryptedWithPadding) ? 1 : 0; + rlen = padded_data_length(srcdatalen + 4 + paddinglen - hdrlen, desfire_get_key_block_length(ctx->keyType)); data[0] = cmd; + + // crc memcpy(&data[1], srcdata, srcdatalen); desfire_crc32_append(data, srcdatalen + 1); + // add padding + if (paddinglen > 0) + data[srcdatalen + 1 + 4] = 0x80; + memcpy(dstdata, srcdata, hdrlen); - DesfireCryptoEncDec(ctx, true, &data[1 + hdrlen], rlen, &dstdata[hdrlen], true); + DesfireCryptoEncDec(ctx, DCOSessionKeyEnc, &data[1 + hdrlen], rlen, &dstdata[hdrlen], true); *dstdatalen = hdrlen + rlen; + ctx->commMode = DCMEncrypted; } else if (ctx->commMode == DCMEncryptedPlain) { if (srcdatalen <= hdrlen) return; @@ -237,16 +329,47 @@ static void DesfireSecureChannelEncodeEV1(DesfireContext *ctx, uint8_t cmd, uint memcpy(dstdata, srcdata, hdrlen); memcpy(data, &srcdata[hdrlen], srcdatalen); rlen = padded_data_length(srcdatalen - hdrlen, desfire_get_key_block_length(ctx->keyType)); - DesfireCryptoEncDec(ctx, true, data, rlen, &dstdata[hdrlen], true); + DesfireCryptoEncDec(ctx, DCOSessionKeyEnc, data, rlen, &dstdata[hdrlen], true); *dstdatalen = hdrlen + rlen; ctx->commMode = DCMEncrypted; } } static void DesfireSecureChannelEncodeEV2(DesfireContext *ctx, uint8_t cmd, uint8_t *srcdata, size_t srcdatalen, uint8_t *dstdata, size_t *dstdatalen) { + uint8_t data[1050] = {0}; + size_t rlen = 0; + memcpy(dstdata, srcdata, srcdatalen); *dstdatalen = srcdatalen; + uint8_t hdrlen = DesfireGetCmdHeaderLen(cmd); + + if (ctx->commMode == DCMMACed) { + uint8_t cmac[DESFIRE_MAX_CRYPTO_BLOCK_SIZE] = {0}; + DesfireEV2CalcCMAC(ctx, cmd, srcdata, srcdatalen, cmac); + + memcpy(&dstdata[srcdatalen], cmac, DesfireGetMACLength(ctx)); + *dstdatalen = srcdatalen + DesfireGetMACLength(ctx); + } else if (ctx->commMode == DCMEncrypted || ctx->commMode == DCMEncryptedWithPadding || ctx->commMode == DCMEncryptedPlain) { + memcpy(dstdata, srcdata, hdrlen); + + if (srcdatalen > hdrlen) { + rlen = padded_data_length(srcdatalen + 1 - hdrlen, desfire_get_key_block_length(ctx->keyType)); + memcpy(data, &srcdata[hdrlen], srcdatalen - hdrlen); + data[srcdatalen - hdrlen] = 0x80; // padding + + DesfireEV2FillIV(ctx, true, NULL); // fill IV to ctx + DesfireCryptoEncDec(ctx, DCOSessionKeyEnc, data, rlen, &dstdata[hdrlen], true); + } + + uint8_t cmac[DESFIRE_MAX_CRYPTO_BLOCK_SIZE] = {0}; + DesfireEV2CalcCMAC(ctx, cmd, dstdata, hdrlen + rlen, cmac); + + memcpy(&dstdata[hdrlen + rlen], cmac, DesfireGetMACLength(ctx)); + + *dstdatalen = hdrlen + rlen + DesfireGetMACLength(ctx); + ctx->commMode = DCMEncrypted; + } } void DesfireSecureChannelEncode(DesfireContext *ctx, uint8_t cmd, uint8_t *srcdata, size_t srcdatalen, uint8_t *dstdata, size_t *dstdatalen) { @@ -280,30 +403,33 @@ static void DesfireSecureChannelDecodeD40(DesfireContext *ctx, uint8_t *srcdata, switch (ctx->commMode) { case DCMMACed: { size_t maclen = DesfireGetMACLength(ctx); - if (srcdatalen > maclen) { + if (srcdatalen > maclen && DesfireEV1D40ReceiveMAC(ctx, ctx->lastCommand)) { uint8_t mac[16] = {0}; rlen = padded_data_length(srcdatalen - maclen, desfire_get_key_block_length(ctx->keyType)); memcpy(data, srcdata, srcdatalen - maclen); - DesfireCryptoEncDecEx(ctx, true, data, rlen, NULL, true, true, mac); + DesfireCryptoEncDecEx(ctx, DCOSessionKeyMac, data, rlen, NULL, true, true, mac); if (memcmp(mac, &srcdata[srcdatalen - maclen], maclen) == 0) { *dstdatalen = srcdatalen - maclen; + if (GetAPDULogging()) + PrintAndLogEx(INFO, "Received MAC OK"); } else { PrintAndLogEx(WARNING, "Received MAC is not match with calculated"); - //PrintAndLogEx(INFO, " received MAC: %s", sprint_hex(&srcdata[srcdatalen - maclen], maclen)); - //PrintAndLogEx(INFO, " calculated MAC: %s", sprint_hex(mac, maclen)); + PrintAndLogEx(INFO, " received MAC: %s", sprint_hex(&srcdata[srcdatalen - maclen], maclen)); + PrintAndLogEx(INFO, " calculated MAC: %s", sprint_hex(mac, maclen)); } } break; } case DCMEncrypted: + case DCMEncryptedWithPadding: if (srcdatalen < desfire_get_key_block_length(ctx->keyType)) { memcpy(dstdata, srcdata, srcdatalen); *dstdatalen = srcdatalen; return; } - DesfireCryptoEncDec(ctx, true, srcdata, srcdatalen, dstdata, false); + DesfireCryptoEncDec(ctx, DCOSessionKeyEnc, srcdata, srcdatalen, dstdata, false); //PrintAndLogEx(INFO, "decoded[%d]: %s", srcdatalen, sprint_hex(dstdata, srcdatalen)); size_t puredatalen = DesfireSearchCRCPos(dstdata, srcdatalen, respcode, 2); @@ -345,17 +471,20 @@ static void DesfireSecureChannelDecodeEV1(DesfireContext *ctx, uint8_t *srcdata, DesfireCryptoCMAC(ctx, data, *dstdatalen + 1, cmac); if (memcmp(&srcdata[*dstdatalen], cmac, DesfireGetMACLength(ctx)) != 0) { PrintAndLogEx(WARNING, "Received MAC is not match with calculated"); - PrintAndLogEx(INFO, " received MAC: %s", sprint_hex(&srcdata[*dstdatalen], desfire_get_key_block_length(ctx->keyType))); - PrintAndLogEx(INFO, " calculated MAC: %s", sprint_hex(cmac, desfire_get_key_block_length(ctx->keyType))); + PrintAndLogEx(INFO, " received MAC: %s", sprint_hex(&srcdata[*dstdatalen], DesfireGetMACLength(ctx))); + PrintAndLogEx(INFO, " calculated MAC: %s", sprint_hex(cmac, DesfireGetMACLength(ctx))); + } else { + if (GetAPDULogging()) + PrintAndLogEx(INFO, "Received MAC OK"); } - } else if (ctx->commMode == DCMEncrypted) { + } else if (ctx->commMode == DCMEncrypted || ctx->commMode == DCMEncryptedWithPadding) { if (srcdatalen < desfire_get_key_block_length(ctx->keyType)) { memcpy(dstdata, srcdata, srcdatalen); *dstdatalen = srcdatalen; return; } - DesfireCryptoEncDec(ctx, true, srcdata, srcdatalen, dstdata, false); + DesfireCryptoEncDec(ctx, DCOSessionKeyEnc, srcdata, srcdatalen, dstdata, false); //PrintAndLogEx(INFO, "decoded[%d]: %s", srcdatalen, sprint_hex(dstdata, srcdatalen)); size_t puredatalen = DesfireSearchCRCPos(dstdata, srcdatalen, respcode, 4); @@ -377,9 +506,92 @@ static void DesfireSecureChannelDecodeEV2(DesfireContext *ctx, uint8_t *srcdata, memcpy(dstdata, srcdata, srcdatalen); *dstdatalen = srcdatalen; + uint8_t cmac[DESFIRE_MAX_CRYPTO_BLOCK_SIZE] = {0}; + + if (ctx->commMode == DCMMACed) { + if (srcdatalen < DesfireGetMACLength(ctx)) { + memcpy(dstdata, srcdata, srcdatalen); + *dstdatalen = srcdatalen; + return; + } + + memcpy(dstdata, srcdata, srcdatalen - DesfireGetMACLength(ctx)); + *dstdatalen = srcdatalen - DesfireGetMACLength(ctx); + + DesfireEV2CalcCMAC(ctx, 0x00, srcdata, *dstdatalen, cmac); + if (memcmp(&srcdata[*dstdatalen], cmac, DesfireGetMACLength(ctx)) != 0) { + PrintAndLogEx(WARNING, "Received MAC is not match with calculated"); + PrintAndLogEx(INFO, " received MAC: %s", sprint_hex(&srcdata[*dstdatalen], DesfireGetMACLength(ctx))); + PrintAndLogEx(INFO, " calculated MAC: %s", sprint_hex(cmac, DesfireGetMACLength(ctx))); + } else { + if (GetAPDULogging()) + PrintAndLogEx(INFO, "Received MAC OK"); + } + } else if (ctx->commMode == DCMEncrypted || ctx->commMode == DCMEncryptedWithPadding) { + if (srcdatalen < DesfireGetMACLength(ctx)) { + memcpy(dstdata, srcdata, srcdatalen); + *dstdatalen = srcdatalen; + return; + } + + *dstdatalen = srcdatalen - DesfireGetMACLength(ctx); + DesfireEV2CalcCMAC(ctx, 0x00, srcdata, *dstdatalen, cmac); + if (memcmp(&srcdata[*dstdatalen], cmac, DesfireGetMACLength(ctx)) != 0) { + PrintAndLogEx(WARNING, "Received MAC is not match with calculated"); + PrintAndLogEx(INFO, " received MAC: %s", sprint_hex(&srcdata[*dstdatalen], DesfireGetMACLength(ctx))); + PrintAndLogEx(INFO, " calculated MAC: %s", sprint_hex(cmac, DesfireGetMACLength(ctx))); + } else { + if (GetAPDULogging()) + PrintAndLogEx(INFO, "Received MAC OK"); + } + + if (*dstdatalen >= desfire_get_key_block_length(ctx->keyType)) { + DesfireEV2FillIV(ctx, false, NULL); // fill response IV to ctx + DesfireCryptoEncDec(ctx, DCOSessionKeyEnc, srcdata, *dstdatalen, dstdata, false); + + size_t puredatalen = FindISO9797M2PaddingDataLen(dstdata, *dstdatalen); + if (puredatalen != 0) { + *dstdatalen = puredatalen; + } else { + PrintAndLogEx(WARNING, "Padding search error."); + } + } + } +} + +static void DesfireISODecode(DesfireContext *ctx, uint8_t *srcdata, size_t srcdatalen, uint8_t *dstdata, size_t *dstdatalen) { + memcpy(dstdata, srcdata, srcdatalen); + *dstdatalen = srcdatalen; + uint8_t data[1050] = {0}; + + if (srcdatalen < DesfireGetMACLength(ctx)) + return; + + uint8_t maclen = DesfireGetMACLength(ctx); + if (DesfireIsAuthenticated(ctx)) { + memcpy(data, srcdata, srcdatalen - maclen); + data[*dstdatalen] = 0x00; // respcode + + uint8_t cmac[DESFIRE_MAX_CRYPTO_BLOCK_SIZE] = {0}; + DesfireCryptoCMAC(ctx, data, srcdatalen - maclen + 1, cmac); + if (memcmp(&srcdata[srcdatalen - maclen], cmac, maclen) != 0) { + PrintAndLogEx(WARNING, "Received MAC is not match with calculated"); + PrintAndLogEx(INFO, " received MAC: %s", sprint_hex(&srcdata[srcdatalen - maclen], maclen)); + PrintAndLogEx(INFO, " calculated MAC: %s", sprint_hex(cmac, maclen)); + } else { + *dstdatalen = srcdatalen - maclen; + if (GetAPDULogging()) + PrintAndLogEx(INFO, "Received MAC OK"); + } + } } void DesfireSecureChannelDecode(DesfireContext *ctx, uint8_t *srcdata, size_t srcdatalen, uint8_t respcode, uint8_t *dstdata, size_t *dstdatalen) { + if (ctx->cmdSet == DCCISO) { + DesfireISODecode(ctx, srcdata, srcdatalen, dstdata, dstdatalen); + return; + } + switch (ctx->secureChannel) { case DACd40: DesfireSecureChannelDecodeD40(ctx, srcdata, srcdatalen, respcode, dstdata, dstdatalen); @@ -406,9 +618,17 @@ bool PrintChannelModeWarning(uint8_t cmd, DesfireSecureChannel secureChannel, De // no security set if (secureChannel == DACNone) return true; + if (CommandCanUseAnyChannel(cmd)) return true; + // ISO commands + if (cmdSet == DCCISO) { + bool res = DesfireISOChannelValidCmd(cmd); + if (!res) + return false; + } + bool found = false; for (int i = 0; i < ARRAY_LENGTH(AllowedChannelModes); i++) if (AllowedChannelModes[i].cmd == cmd) { @@ -431,6 +651,15 @@ bool PrintChannelModeWarning(uint8_t cmd, DesfireSecureChannel secureChannel, De break; } + // ev2 like ev1 + if (secureChannel == DACEV2 && + AllowedChannelModes[i].secureChannel == DACEV1 && + (AllowedChannelModes[i].cmdSet == cmdSet || (AllowedChannelModes[i].cmdSet == DCCNative && cmdSet == DCCNativeISO)) && + AllowedChannelModes[i].commMode == commMode) { + + found = true; + break; + } } if (!found) diff --git a/client/src/mifare/desfiretest.c b/client/src/mifare/desfiretest.c index c08d7600d..9972a6793 100644 --- a/client/src/mifare/desfiretest.c +++ b/client/src/mifare/desfiretest.c @@ -269,6 +269,66 @@ static bool TestEV2IVEncode(void) { return res; } +// https://www.nxp.com/docs/en/application-note/AN12343.pdf +// page 54 +static bool TestEV2MAC(void) { + bool res = true; + + uint8_t key[] = {0x93, 0x66, 0xFA, 0x19, 0x5E, 0xB5, 0x66, 0xF5, 0xBD, 0x2B, 0xAD, 0x40, 0x20, 0xB8, 0x30, 0x02}; + uint8_t ti[] = {0xE2, 0xD3, 0xAF, 0x69}; + uint8_t cmd = 0x8D; + uint8_t cmddata[] = {0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22 + }; + uint8_t macres[] = {0x68, 0xF2, 0xC2, 0x8C, 0x57, 0x5A, 0x16, 0x28}; + + // init + DesfireContext ctx = {0}; + ctx.keyType = T_AES; + memcpy(ctx.sessionKeyMAC, key, 16); + memcpy(ctx.TI, ti, 4); + ctx.cmdCntr = 0; + + // tx 1 + uint8_t mac[16] = {0}; + DesfireEV2CalcCMAC(&ctx, cmd, cmddata, sizeof(cmddata), mac); + res = res && (memcmp(mac, macres, sizeof(macres)) == 0); + + // rx 1 + memset(mac, 0, sizeof(mac)); + uint8_t macres2[] = {0x08, 0x20, 0xF6, 0x88, 0x98, 0xC2, 0xA7, 0xF1}; + uint8_t rc = 0; + ctx.cmdCntr++; + DesfireEV2CalcCMAC(&ctx, rc, NULL, 0, mac); + res = res && (memcmp(mac, macres2, sizeof(macres2)) == 0); + + // tx 2 + memset(mac, 0, sizeof(mac)); + cmd = 0xAD; + uint8_t cmddata3[] = {0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00}; + uint8_t macres3[] = {0x0D, 0x9B, 0xE1, 0x91, 0xD5, 0x96, 0x08, 0x34}; + DesfireEV2CalcCMAC(&ctx, cmd, cmddata3, sizeof(cmddata3), mac); + res = res && (memcmp(mac, macres3, sizeof(macres3)) == 0); + + // rx 2 + rc = 0; + ctx.cmdCntr++; + uint8_t cmddata4[] = {0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + uint8_t macres4[] = {0xA4, 0x9A, 0x44, 0x22, 0x2D, 0x92, 0x66, 0x66}; + DesfireEV2CalcCMAC(&ctx, rc, cmddata4, sizeof(cmddata4), mac); + res = res && (memcmp(mac, macres4, sizeof(macres4)) == 0); + + if (res) + PrintAndLogEx(INFO, "EV2 MAC calc...... " _GREEN_("passed")); + else + PrintAndLogEx(ERR, "EV2 MAC calc...... " _RED_("fail")); + + return res; +} + bool DesfireTest(bool verbose) { bool res = true; @@ -281,6 +341,7 @@ bool DesfireTest(bool verbose) { res = res && TestCMACDES(); res = res && TestEV2SessionKeys(); res = res && TestEV2IVEncode(); + res = res && TestEV2MAC(); PrintAndLogEx(INFO, "---------------------------"); if (res) diff --git a/client/src/proxmark3.c b/client/src/proxmark3.c index d1507923a..d1f74bcba 100644 --- a/client/src/proxmark3.c +++ b/client/src/proxmark3.c @@ -135,7 +135,7 @@ static int check_comm(void) { char prompt_filtered[PROXPROMPT_MAX_SIZE] = {0}; memcpy_filter_ansi(prompt_filtered, prompt, sizeof(prompt_filtered), !session.supports_colors); rl_set_prompt(prompt_filtered); - rl_forced_update_display(); + rl_redisplay(); #endif CloseProxmark(session.current_device); } diff --git a/common/lfdemod.c b/common/lfdemod.c index 3c32834f6..d202d9aa0 100644 --- a/common/lfdemod.c +++ b/common/lfdemod.c @@ -253,6 +253,55 @@ size_t removeParity(uint8_t *bits, size_t startIdx, uint8_t pLen, uint8_t pType, return bitCnt; } +static size_t removeEm410xParity(uint8_t *bits, size_t startIdx, bool isLong, bool *validShort, bool *validShortExtended, bool *validLong) { + uint32_t parityWd = 0; + size_t bitCnt = 0; + bool validColParity = false; + bool validRowParity = true; + bool validRowParitySkipColP = true; + *validShort = false; + *validShortExtended = false; + *validLong = false; + uint8_t bLen = isLong ? 110 : 55; + uint16_t parityCol[4] = { 0, 0, 0, 0 }; + for (int word = 0; word < bLen; word += 5) { + for (int bit = 0; bit < 5; bit++) { + if (word + bit >= bLen) break; + parityWd = (parityWd << 1) | bits[startIdx + word + bit]; + if ((word <= 50) && (bit < 4)) + parityCol[bit] = (parityCol[bit] << 1) | bits[startIdx + word + bit]; + bits[bitCnt++] = (bits[startIdx + word + bit]); + } + if (word + 5 > bLen) break; + + bitCnt--; // overwrite parity with next data + validRowParity &= parityTest(parityWd, 5, 0) != 0; + if (word == 50) { // column parity nibble on short EM and on Electra + validColParity = parityTest(parityCol[0], 11, 0) != 0; + validColParity &= parityTest(parityCol[1], 11, 0) != 0; + validColParity &= parityTest(parityCol[2], 11, 0) != 0; + validColParity &= parityTest(parityCol[3], 11, 0) != 0; + } else { + validRowParitySkipColP &= parityTest(parityWd, 5, 0) != 0; + } + parityWd = 0; + } + if (!isLong && validRowParitySkipColP && validColParity) { + *validShort = true; + } + if (isLong && validRowParity) { + *validLong = true; + } + if (isLong && validRowParitySkipColP && validColParity) { + *validShortExtended = true; + } + if (*validShort || *validShortExtended || *validLong) { + return bitCnt; + } else { + return 0; + } +} + // by marshmellow // takes a array of binary values, length of bits per parity (includes parity bit), // Parity Type (1 for odd; 0 for even; 2 Always 1's; 3 Always 0's), and binary Length (length to run) @@ -2122,7 +2171,6 @@ int Em410xDecode(uint8_t *bits, size_t *size, size_t *start_idx, uint32_t *hi, u if (bits[1] > 1) return -1; if (*size < 64) return -2; - uint8_t fmtlen; *start_idx = 0; // preamble 0111111111 @@ -2131,32 +2179,29 @@ int Em410xDecode(uint8_t *bits, size_t *size, size_t *start_idx, uint32_t *hi, u if (!preambleSearch(bits, preamble, sizeof(preamble), size, start_idx)) return -4; - // (iceman) if the preamble doesn't find two occuriences, this identification fails. - fmtlen = (*size == 128) ? 22 : 10; + bool validShort = false; + bool validShortExtended = false; + bool validLong = false; + *size = removeEm410xParity(bits, *start_idx + sizeof(preamble), *size == 128, &validShort, &validShortExtended, &validLong); - //skip last 4bit parity row for simplicity - *size = removeParity(bits, *start_idx + sizeof(preamble), 5, 0, fmtlen * 5); - - switch (*size) { - case 40: { - // std em410x format - *hi = 0; - *lo = ((uint64_t)(bytebits_to_byte(bits, 8)) << 32) | (bytebits_to_byte(bits + 8, 32)); - break; - } - case 88: { - // long em format - *hi = (bytebits_to_byte(bits, 24)); - *lo = ((uint64_t)(bytebits_to_byte(bits + 24, 32)) << 32) | (bytebits_to_byte(bits + 24 + 32, 32)); - break; - } - default: - return -6; + if (validShort) { + // std em410x format + *hi = 0; + *lo = ((uint64_t)(bytebits_to_byte(bits, 8)) << 32) | (bytebits_to_byte(bits + 8, 32)); + // 1 = Short + return 1; } - return 1; + if (validShortExtended || validLong) { + // store in long em format + *hi = (bytebits_to_byte(bits, 24)); + *lo = ((uint64_t)(bytebits_to_byte(bits + 24, 32)) << 32) | (bytebits_to_byte(bits + 24 + 32, 32)); + // 2 = Long + // 4 = ShortExtended + return ((int)validShortExtended << 2) + ((int)validLong << 1); + } + return -6; } - // loop to get raw HID waveform then FSK demodulate the TAG ID from it int HIDdemodFSK(uint8_t *dest, size_t *size, uint32_t *hi2, uint32_t *hi, uint32_t *lo, int *waveStartIdx) { //make sure buffer has data diff --git a/doc/commands.json b/doc/commands.json index 103ead0b0..4c1d8f632 100644 --- a/doc/commands.json +++ b/doc/commands.json @@ -973,7 +973,7 @@ }, "help": { "command": "help", - "description": "help use ` help` for details of a command prefs { edit client/device preferences... } -------- ----------------------- technology ----------------------- analyse { analyse utils... } data { plot window / data buffer manipulation... } emv { emv iso-14443 / iso-7816... } hf { high frequency commands... } hw { hardware commands... } lf { low frequency commands... } nfc { nfc commands... } reveng { crc calculations from reveng software... } smart { smart card iso-7816 commands... } script { scripting commands... } trace { trace manipulation... } wiegand { wiegand format manipulation... } -------- ----------------------- general ----------------------- clear clear screen hints turn hints on / off msleep add a pause in milliseconds rem add a text line in log file quit exit exit program [=] session log e:\\proxspace\\pm3/.proxmark3/logs/log_20210731.txt --------------------------------------------------------------------------------------- auto available offline: no run lf search / hf search / data plot / data save", + "description": "help use ` help` for details of a command prefs { edit client/device preferences... } -------- ----------------------- technology ----------------------- analyse { analyse utils... } data { plot window / data buffer manipulation... } emv { emv iso-14443 / iso-7816... } hf { high frequency commands... } hw { hardware commands... } lf { low frequency commands... } nfc { nfc commands... } reveng { crc calculations from reveng software... } smart { smart card iso-7816 commands... } script { scripting commands... } trace { trace manipulation... } wiegand { wiegand format manipulation... } -------- ----------------------- general ----------------------- clear clear screen hints turn hints on / off msleep add a pause in milliseconds rem add a text line in log file quit exit exit program [=] session log e:\\proxspace\\pm3/.proxmark3/logs/log_20210806.txt --------------------------------------------------------------------------------------- auto available offline: no run lf search / hf search / data plot / data save", "notes": [ "auto" ], @@ -3990,7 +3990,13 @@ "command": "hf mfdes changekey", "description": "change picc/application key. needs to provide keynum/key for a valid authentication (may get from default parameters).", "notes": [ - "hf mfdes changekey --aid 123456 -> execute with default factory setup" + "change crypto algorithm for picc key is possible, but for app keys crypto algorithm is set by createapp command and can't be changed wo application delete", + "", + "hf mfdes changekey --aid 123456 -> execute with default factory setup. change des key 0 in the app 123456 from 00..00 to 00..00", + "hf mfdes changekey -t des --newalgo aes --newkey 11223344556677889900112233445566 --newver a5 -> change card master key to aes one", + "hf mfdes changekey --aid 123456 -t aes --key 00000000000000000000000000000000 --newkey 11223344556677889900112233445566 -> change app master key", + "hf mfdes changekey --aid 123456 -t des -n 0 --newkeyno 1 --oldkey 5555555555555555 --newkey 1122334455667788 -> change key 1 with auth from key 0", + "hf mfdes changekey --aid 123456 -t 3tdea --newkey 11223344556677889900112233445566778899001122334 -> change 3tdea key 0 from default 00..00 to provided" ], "offline": false, "options": [ @@ -4342,6 +4348,26 @@ ], "usage": "hf mfdes createvaluefile [-hav] [-n ] [-t ] [-k ] [-f ] [-i ] [-m ] [-c ] [-s ] [--aid ] [--fid ] [--amode ] [--rawrights ] [--rrights ] [--wrights ] [--rwrights ] [--chrights ] [--no-auth] [--lower ] [--upper ] [--value ] [--lcredit ]" }, + "hf mfdes default": { + "command": "hf mfdes default", + "description": "set default parameters for access to desfire card.", + "notes": [ + "hf mfdes default -n 0 -t des -k 0000000000000000 -f none -> save to the default parameters" + ], + "offline": false, + "options": [ + "-h, --help this help", + "-n, --keyno key number", + "-t, --algo crypt algo: des, 2tdea, 3tdea, aes", + "-k, --key key for authenticate (hex 8(des), 16(2tdea or aes) or 24(3tdea) bytes)", + "-f, --kdf key derivation function (kdf): none, an10922, gallagher", + "-i, --kdfi kdf input (hex 1-31 bytes)", + "-m, --cmode communicaton mode: plain/mac/encrypt", + "-c, --ccset communicaton command set: native/niso/iso", + "-s, --schann secure channel: d40/ev1/ev2" + ], + "usage": "hf mfdes default [-h] [-n ] [-t ] [-k ] [-f ] [-i ] [-m ] [-c ] [-s ]" + }, "hf mfdes deleteapp": { "command": "hf mfdes deleteapp", "description": "delete application by its 3-byte aid. master key needs to be provided.", @@ -4414,18 +4440,6 @@ ], "usage": "hf mfdes dump [-hav] [-n ] [-t ] [-k ] [-f ] [-i ] [-m ] [-c ] [-s ] [--aid ] [--no-auth]" }, - "hf mfdes enum": { - "command": "hf mfdes enum", - "description": "enumerate all aid's on mifare desfire tag", - "notes": [ - "hf mfdes enum" - ], - "offline": false, - "options": [ - "-h, --help this help" - ], - "usage": "hf mfdes enum [-h]" - }, "hf mfdes formatpicc": { "command": "hf mfdes formatpicc", "description": "format card. can be done only if enabled in the configuration. master key needs to be provided.", @@ -4669,31 +4683,11 @@ }, "hf mfdes help": { "command": "hf mfdes help", - "description": "help this help list list desfire (iso 14443a) history test test crypto --------------------------------------------------------------------------------------- hf mfdes default available offline: no set default parameters for access to desfire card.", - "notes": [ - "hf mfdes default -n 0 -t des -k 0000000000000000 -f none -> save to the default parameters" - ], - "offline": true, - "options": [ - "-h, --help this help", - "-n, --keyno key number", - "-t, --algo crypt algo: des, 2tdea, 3tdea, aes", - "-k, --key key for authenticate (hex 8(des), 16(2tdea or aes) or 24(3tdea) bytes)", - "-f, --kdf key derivation function (kdf): none, an10922, gallagher", - "-i, --kdfi kdf input (hex 1-31 bytes)", - "-m, --cmode communicaton mode: plain/mac/encrypt", - "-c, --ccset communicaton command set: native/niso/iso", - "-s, --schann secure channel: d40/ev1/ev2" - ], - "usage": "hf mfdes default [-h] [-n ] [-t ] [-k ] [-f ] [-i ] [-m ] [-c ] [-s ]" - }, - "hf mfdes info": { - "command": "hf mfdes info", - "description": "get info from mifare desfire tags", + "description": "help this help list list desfire (iso 14443a) history test test crypto --------------------------------------------------------------------------------------- hf mfdes info available offline: no get info from mifare desfire tags", "notes": [ "hf mfdes info" ], - "offline": false, + "offline": true, "options": [ "-h, --help this help" ], @@ -4720,6 +4714,32 @@ ], "usage": "hf mfdes list [-h1fcrux] [--dict ]..." }, + "hf mfdes lsapp": { + "command": "hf mfdes lsapp", + "description": "show application list. master key needs to be provided or flag --no-auth set (depend on cards settings).", + "notes": [ + "hf mfdes lsapp -> show application list with defaults from `default` command", + "hf mfdes lsapp --files -> show application list and show each file type/settings/etc for each application" + ], + "offline": false, + "options": [ + "-h, --help this help", + "-a, --apdu show apdu requests and responses", + "-v, --verbose show technical data", + "-n, --keyno key number", + "-t, --algo crypt algo: des, 2tdea, 3tdea, aes", + "-k, --key key for authenticate (hex 8(des), 16(2tdea or aes) or 24(3tdea) bytes)", + "-f, --kdf key derivation function (kdf): none, an10922, gallagher", + "-i, --kdfi kdf input (hex 1-31 bytes)", + "-m, --cmode communicaton mode: plain/mac/encrypt", + "-c, --ccset communicaton command set: native/niso/iso", + "-s, --schann secure channel: d40/ev1/ev2", + "--no-auth execute without authentication", + "--no-deep not to check authentication commands that avail for any application", + "--files scan files and print file settings for each application" + ], + "usage": "hf mfdes lsapp [-hav] [-n ] [-t ] [-k ] [-f ] [-i ] [-m ] [-c ] [-s ] [--no-auth] [--no-deep] [--files]" + }, "hf mfdes lsfiles": { "command": "hf mfdes lsfiles", "description": "show file list. master key needs to be provided or flag --no-auth set (depend on cards settings).", @@ -4748,8 +4768,17 @@ "command": "hf mfdes read", "description": "read data from file. key needs to be provided or flag --no-auth set (depend on file settings).", "notes": [ + "it reads file via all command sets.", + "for iso command set it can be read by specifying full 2-byte iso id or 1-byte short iso id (first byte of the full iso id). iso id lays in the data in big endian format.", + "iso record commands: offset - record number (0-current, 1..ff-number, 1-lastest), length - if 0 - all records, if 1 - one", + "", "hf mfdes read --aid 123456 --fid 01 -> read file: app=123456, file=01, offset=0, all the data. use default channel settings from `default` command", - "hf mfdes read --aid 123456 --fid 01 --type record --offset 000000 --length 000001 -> read one last record from record file. use default channel settings from `default` command" + "hf mfdes read --aid 123456 --fid 01 --type record --offset 000000 --length 000001 -> read one last record from record file. use default channel settings from `default` command", + "hf mfdes read --aid 123456 --fid 10 --type data -c iso -> read file via iso channel: app=123456, short iso id=10, offset=0.", + "hf mfdes read --aid 123456 --fileisoid 1000 --type data -c iso -> read file via iso channel: app=123456, iso id=1000, offset=0. select via native iso wrapper", + "hf mfdes read --appisoid 0102 --fileisoid 1000 --type data -c iso -> read file via iso channel: app iso id=0102, iso id=1000, offset=0. select via iso commands", + "hf mfdes read --appisoid 0102 --fileisoid 1100 --type record -c iso --offset 000005 --length 000001 -> get one record (number 5) from file 1100 via iso commands", + "hf mfdes read --appisoid 0102 --fileisoid 1100 --type record -c iso --offset 000005 --length 000000 -> get all record (from 5 to 1) from file 1100 via iso commands" ], "offline": false, "options": [ @@ -4769,15 +4798,20 @@ "--no-auth execute without authentication", "--type file type auto/data(standard/backup)/value/record(linear/cyclic)/mac). auto - check file settings and then read. default: auto", "-o, --offset file offset (3 hex bytes, big endian). for records - record number (0 - lastest record). default 0", - "-l, --length length to read (3 hex bytes, big endian -> 000000 = read all data). for records - records count (0 - all). default 0." + "-l, --length length to read (3 hex bytes, big endian -> 000000 = read all data). for records - records count (0 - all). default 0.", + "--appisoid application iso id (iso df id) (2 hex bytes, big endian). works only for iso read commands.", + "--fileisoid file iso id (iso df id) (2 hex bytes, big endian). works only for iso read commands." ], - "usage": "hf mfdes read [-hav] [-n ] [-t ] [-k ] [-f ] [-i ] [-m ] [-c ] [-s ] [--aid ] [--fid ] [--no-auth] [--type ] [-o ] [-l ]" + "usage": "hf mfdes read [-hav] [-n ] [-t ] [-k ] [-f ] [-i ] [-m ] [-c ] [-s ] [--aid ] [--fid ] [--no-auth] [--type ] [-o ] [-l ] [--appisoid ] [--fileisoid ]" }, "hf mfdes selectapp": { "command": "hf mfdes selectapp", "description": "select application on the card. it selects app if it is a valid one or returns an error.", "notes": [ - "hf mfdes selectapp --aid 123456 -> select application 123456" + "hf mfdes selectapp --aid 123456 -> select application 123456", + "hf mfdes selectapp --mf -> select master file (picc level)", + "hf mfdes selectapp --dfname aid123456 -> select application aid123456 by df name", + "hf mfdes selectapp --isoid 1111 -> select application 1111 by iso id" ], "offline": false, "options": [ @@ -4793,16 +4827,30 @@ "-c, --ccset communicaton command set: native/niso/iso", "-s, --schann secure channel: d40/ev1/ev2", "--aid application id of application for some parameters (3 hex bytes, big endian)", - "--dfname application df name (string, max 16 chars). selects application via iso select command" + "--dfname application df name (string, max 16 chars). selects application via iso select command", + "--isoid application iso id (iso df id) (2 hex bytes, big endian)", + "--mf select mf (master file) via iso channel" ], - "usage": "hf mfdes selectapp [-hav] [-n ] [-t ] [-k ] [-f ] [-i ] [-m ] [-c ] [-s ] [--aid ] [--dfname ]" + "usage": "hf mfdes selectapp [-hav] [-n ] [-t ] [-k ] [-f ] [-i ] [-m ] [-c ] [-s ] [--aid ] [--dfname ] [--isoid ] [--mf]" }, "hf mfdes setconfig": { "command": "hf mfdes setconfig", "description": "set card configuration. warning! danger zone! needs to provide card's master key and works if not blocked by config.", "notes": [ - "hf mfdes setconfig --param 03 --data 0428 -> set parameter 03", - "hf mfdes setconfig --param 02 --data 0875778102637264 -> set parameter 02" + "more about options mf2dlhx0.pdf. options list:", + "00h picc configuration.", + "02h ats update.", + "03h sak update", + "04h secure messaging configuration.", + "05h capability data. (here change for lrp in the desfire light)", + "06h df name renaming", + "08h file renaming", + "09h value file configuration", + "0ah failed authentication counter setting", + "0bh hw configuration", + "", + "hf mfdes setconfig --param 03 --data 0428 -> set sak", + "hf mfdes setconfig --param 02 --data 0875778102637264 -> set ats (first byte - length)" ], "offline": false, "options": [ @@ -4825,7 +4873,7 @@ }, "hf mfdes test": { "command": "hf mfdes test", - "description": "[=] ------ desfire tests ------ [!] no space for crc. pos: 1 [=] crc16............. passed [!] no space for crc. pos: 2 [=] crc32............. passed [=] cmac 3tdea........ passed [=] cmac 2tdea........ passed [=] cmac des.......... passed [=] ev2 session keys.. passed [=] ev2 iv calc....... passed [=] --------------------------- [+] tests [ ok ] ======================================================================================= hf seos { seos rfids... } --------------------------------------------------------------------------------------- hf seos help available offline: yes help this help list list seos history --------------------------------------------------------------------------------------- hf seos info available offline: no get info from seos tags", + "description": "[=] ------ desfire tests ------ [!] no space for crc. pos: 1 [=] crc16............. passed [!] no space for crc. pos: 2 [=] crc32............. passed [=] cmac 3tdea........ passed [=] cmac 2tdea........ passed [=] cmac des.......... passed [=] ev2 session keys.. passed [=] ev2 iv calc....... passed [=] ev2 mac calc...... passed [=] --------------------------- [+] tests [ ok ] ======================================================================================= hf seos { seos rfids... } --------------------------------------------------------------------------------------- hf seos help available offline: yes help this help list list seos history --------------------------------------------------------------------------------------- hf seos info available offline: no get info from seos tags", "notes": [ "hf seos info" ], @@ -4876,7 +4924,9 @@ "hf mfdes write --aid 123456 --fid 01 -d 01020304 -> write data to record file with `auto` type", "hf mfdes write --aid 123456 --fid 01 --type record -d 01020304 -> write data to record file", "hf mfdes write --aid 123456 --fid 01 --type record -d 01020304 --updaterec 0 -> update record in the record file. record 0 - lastest record.", - "hf mfdes write --aid 123456 --fid 01 --type record --offset 000000 -d 11223344 -> write record to record file. use default channel settings from `default` command" + "hf mfdes write --aid 123456 --fid 01 --type record --offset 000000 -d 11223344 -> write record to record file. use default channel settings from `default` command", + "hf mfdes write --appisoid 1234 --fileisoid 1000 --type data -c iso -d 01020304 -> write data to std/backup file iso commandset", + "hf mfdes write --appisoid 1234 --fileisoid 2000 --type record -c iso -d 01020304 -> aend record to record file via iso commandset" ], "offline": false, "options": [ @@ -4899,9 +4949,11 @@ "-d, --data data for write (data/record file), credit/debit(value file)", "--debit use for value file debit operation instead of credit", "--commit commit needs for backup file only. for the other file types and in the `auto` mode - command set it automatically.", - "--updaterec record number for update record command. updates record instead of write. lastest record - 0" + "--updaterec record number for update record command. updates record instead of write. lastest record - 0", + "--appisoid application iso id (iso df id) (2 hex bytes, big endian). works only for iso read commands.", + "--fileisoid file iso id (iso df id) (2 hex bytes, big endian). works only for iso read commands." ], - "usage": "hf mfdes write [-hav] [-n ] [-t ] [-k ] [-f ] [-i ] [-m ] [-c ] [-s ] [--aid ] [--fid ] [--no-auth] [--type ] [-o ] [-d ] [--debit] [--commit] [--updaterec ]" + "usage": "hf mfdes write [-hav] [-n ] [-t ] [-k ] [-f ] [-i ] [-m ] [-c ] [-s ] [--aid ] [--fid ] [--no-auth] [--type ] [-o ] [-d ] [--debit] [--commit] [--updaterec ] [--appisoid ] [--fileisoid ]" }, "hf mfp auth": { "command": "hf mfp auth", @@ -9839,6 +9891,6 @@ "metadata": { "commands_extracted": 587, "extracted_by": "PM3Help2JSON v1.00", - "extracted_on": "2021-07-31T13:44:52" + "extracted_on": "2021-08-06T20:40:15" } } \ No newline at end of file diff --git a/doc/commands.md b/doc/commands.md index 830fab232..a5dbcfb3e 100644 --- a/doc/commands.md +++ b/doc/commands.md @@ -503,26 +503,26 @@ Check column "offline" for their availability. |command |offline |description |------- |------- |----------- |`hf mfdes help `|Y |`This help` +|`hf mfdes info `|N |`Tag information` +|`hf mfdes getuid `|N |`Get uid from card` |`hf mfdes default `|N |`Set defaults for all the commands` |`hf mfdes auth `|N |`MIFARE DesFire Authentication` |`hf mfdes chk `|N |`[old]Check keys` -|`hf mfdes enum `|N |`[old]Tries enumerate all applications` -|`hf mfdes formatpicc `|N |`Format PICC` |`hf mfdes freemem `|N |`Get free memory size` -|`hf mfdes getuid `|N |`Get uid from card` |`hf mfdes setconfig `|N |`Set card configuration` -|`hf mfdes info `|N |`[old]Tag information` +|`hf mfdes formatpicc `|N |`Format PICC` |`hf mfdes list `|Y |`List DESFire (ISO 14443A) history` -|`hf mfdes changekey `|N |`Change Key` -|`hf mfdes chkeysettings `|N |`Change Key Settings` -|`hf mfdes getkeysettings`|N |`Get Key Settings` -|`hf mfdes getkeyversions`|N |`Get Key Versions` +|`hf mfdes lsapp `|N |`Show all applications with files list` +|`hf mfdes getaids `|N |`Get Application IDs list` +|`hf mfdes getappnames `|N |`Get Applications list` |`hf mfdes bruteaid `|N |`Recover AIDs by bruteforce` |`hf mfdes createapp `|N |`Create Application` |`hf mfdes deleteapp `|N |`Delete Application` |`hf mfdes selectapp `|N |`Select Application ID` -|`hf mfdes getaids `|N |`Get Application IDs list` -|`hf mfdes getappnames `|N |`Get Applications list` +|`hf mfdes changekey `|N |`Change Key` +|`hf mfdes chkeysettings `|N |`Change Key Settings` +|`hf mfdes getkeysettings`|N |`Get Key Settings` +|`hf mfdes getkeyversions`|N |`Get Key Versions` |`hf mfdes getfileids `|N |`Get File IDs list` |`hf mfdes getfileisoids `|N |`Get File ISO IDs list` |`hf mfdes lsfiles `|N |`Show all files list`