mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2025-08-21 05:43:48 -07:00
Merge branch 'master' of https://github.com/RfidResearchGroup/proxmark3 into new-standalone-mode
This commit is contained in:
commit
f317fc38f5
26 changed files with 2140 additions and 1288 deletions
|
@ -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...
|
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]
|
## [unreleased][unreleased]
|
||||||
|
- Added support to demodulate Electra tags and column parity check for EM410x (@doegox)
|
||||||
- Fix demod plot for various demodulations (@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)
|
- 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)
|
- Added `hf iclass encode --wiegand/--fc/--cn` - direct fmt/fc/cn support (@bettse)
|
||||||
|
|
|
@ -96,8 +96,9 @@ static uint32_t IceEM410xdemod(void) {
|
||||||
return PM3_ESOFT;
|
return PM3_ESOFT;
|
||||||
}
|
}
|
||||||
|
|
||||||
errCnt = Em410xDecode(dest, &size, &idx, &hi, &lo);
|
int type = Em410xDecode(dest, &size, &idx, &hi, &lo);
|
||||||
if (errCnt != 1) {
|
// Did we find a Short EM or a Long EM?
|
||||||
|
if ((type & (0x1 | 0x2)) == 0) {
|
||||||
BigBuf_free();
|
BigBuf_free();
|
||||||
return PM3_ESOFT;
|
return PM3_ESOFT;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1476,17 +1476,8 @@ int lf_em410x_watch(int findone, uint32_t *high, uint64_t *low) {
|
||||||
|
|
||||||
WDT_HIT();
|
WDT_HIT();
|
||||||
|
|
||||||
errCnt = Em410xDecode(dest, &size, &idx, &hi, &lo);
|
int type = Em410xDecode(dest, &size, &idx, &hi, &lo);
|
||||||
if (errCnt == 1) {
|
if (type & 0x1) {
|
||||||
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));
|
|
||||||
} else {
|
|
||||||
Dbprintf("EM TAG ID: " _GREEN_("%02x%08x") " - ( %05d_%03d_%08d )",
|
Dbprintf("EM TAG ID: " _GREEN_("%02x%08x") " - ( %05d_%03d_%08d )",
|
||||||
(uint32_t)(lo >> 32),
|
(uint32_t)(lo >> 32),
|
||||||
(uint32_t)lo,
|
(uint32_t)lo,
|
||||||
|
@ -1494,13 +1485,43 @@ int lf_em410x_watch(int findone, uint32_t *high, uint64_t *low) {
|
||||||
(uint32_t)((lo >> 16LL) & 0xFF),
|
(uint32_t)((lo >> 16LL) & 0xFF),
|
||||||
(uint32_t)(lo & 0xFFFFFF));
|
(uint32_t)(lo & 0xFFFFFF));
|
||||||
}
|
}
|
||||||
|
if (type & 0x2) {
|
||||||
if (findone) {
|
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 ) 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;
|
*high = hi;
|
||||||
*low = lo;
|
*low = lo;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
hi = lo = size = idx = 0;
|
hi = lo = size = idx = 0;
|
||||||
clk = invert = 0;
|
clk = invert = 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -222,6 +222,7 @@ set (TARGET_SOURCES
|
||||||
${PM3_ROOT}/client/src/loclass/hash1_brute.c
|
${PM3_ROOT}/client/src/loclass/hash1_brute.c
|
||||||
${PM3_ROOT}/client/src/loclass/ikeys.c
|
${PM3_ROOT}/client/src/loclass/ikeys.c
|
||||||
${PM3_ROOT}/client/src/mifare/mad.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/mfkey.c
|
||||||
${PM3_ROOT}/client/src/mifare/mifare4.c
|
${PM3_ROOT}/client/src/mifare/mifare4.c
|
||||||
${PM3_ROOT}/client/src/mifare/mifaredefault.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/uart/uart_win32.c
|
||||||
${PM3_ROOT}/client/src/ui/overlays.ui
|
${PM3_ROOT}/client/src/ui/overlays.ui
|
||||||
${PM3_ROOT}/client/src/ui/image.ui
|
${PM3_ROOT}/client/src/ui/image.ui
|
||||||
${PM3_ROOT}/client/src/aiddesfire.c
|
|
||||||
${PM3_ROOT}/client/src/aidsearch.c
|
${PM3_ROOT}/client/src/aidsearch.c
|
||||||
${PM3_ROOT}/client/src/cmdanalyse.c
|
${PM3_ROOT}/client/src/cmdanalyse.c
|
||||||
${PM3_ROOT}/client/src/cmdcrc.c
|
${PM3_ROOT}/client/src/cmdcrc.c
|
||||||
|
|
|
@ -474,7 +474,7 @@ POSTCOMPILE = $(MV) -f $(OBJDIR)/$*.Td $(OBJDIR)/$*.d && $(TOUCH) $@
|
||||||
# enumerations #
|
# enumerations #
|
||||||
################
|
################
|
||||||
|
|
||||||
SRCS = aiddesfire.c \
|
SRCS = mifare/aiddesfire.c \
|
||||||
aidsearch.c \
|
aidsearch.c \
|
||||||
cmdanalyse.c \
|
cmdanalyse.c \
|
||||||
cmdcrc.c \
|
cmdcrc.c \
|
||||||
|
|
|
@ -7875,6 +7875,13 @@
|
||||||
"service_provider": "Atelei Engineering",
|
"service_provider": "Atelei Engineering",
|
||||||
"system_integrator": "Atelei Engineering"
|
"system_integrator": "Atelei Engineering"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"application": "Access control",
|
||||||
|
"company": "LEAF Identity",
|
||||||
|
"mad": "0x51CD",
|
||||||
|
"service_provider": "",
|
||||||
|
"system_integrator": ""
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"application": "Access & biometrics application",
|
"application": "Access & biometrics application",
|
||||||
"company": "Manufacture Francaise des Pneumatiques MICHELIN",
|
"company": "Manufacture Francaise des Pneumatiques MICHELIN",
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
|
@ -592,10 +592,11 @@ static int Cmdmandecoderaw(const char *Cmd) {
|
||||||
uint64_t id = 0;
|
uint64_t id = 0;
|
||||||
uint32_t hi = 0;
|
uint32_t hi = 0;
|
||||||
size_t idx = 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
|
//need to adjust to set bitstream back to manchester encoded data
|
||||||
//setDemodBuff(bits, size, idx);
|
//setDemodBuff(bits, size, idx);
|
||||||
printEM410x(hi, id, false);
|
printEM410x(hi, id, false, res);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setDemodBuff(bits, size, 0);
|
setDemodBuff(bits, size, 0);
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1269,6 +1269,7 @@ uint32_t GetHF14AMfU_Type(void) {
|
||||||
NT2H1001G0DUx 0004040202000B03
|
NT2H1001G0DUx 0004040202000B03
|
||||||
NT2H1311TTDUx 0004040203000F03
|
NT2H1311TTDUx 0004040203000F03
|
||||||
Micron UL 0034210101000E03
|
Micron UL 0034210101000E03
|
||||||
|
Feiju NTAG 0053040201000F03
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (memcmp(version, "\x00\x04\x03\x01\x01\x00\x0B", 7) == 0) { tagtype = UL_EV1_48; break; }
|
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\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\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\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\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\x11", 7) == 0) { tagtype = NTAG_215; break; }
|
||||||
else if (memcmp(version, "\x00\x04\x04\x02\x01\x00\x13", 7) == 0) { tagtype = NTAG_216; break; }
|
else if (memcmp(version, "\x00\x04\x04\x02\x01\x00\x13", 7) == 0) { tagtype = NTAG_216; break; }
|
||||||
|
|
|
@ -100,10 +100,47 @@ static void em410x_construct_emul_graph(uint8_t *uid, uint8_t clock, uint8_t gap
|
||||||
}
|
}
|
||||||
|
|
||||||
//print 64 bit EM410x ID in multiple formats
|
//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;
|
if (!id && !hi) return;
|
||||||
|
|
||||||
|
if (verbose == false) {
|
||||||
|
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 (type & 0x2) { // Long ID
|
||||||
|
//output 88 bit em id
|
||||||
|
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 n = 1;
|
||||||
uint64_t id2lo = 0;
|
uint64_t id2lo = 0;
|
||||||
uint8_t m, i;
|
uint8_t m, i;
|
||||||
|
@ -112,23 +149,6 @@ void printEM410x(uint32_t hi, uint64_t id, bool verbose) {
|
||||||
id2lo = (id2lo << 1LL) | ((id & (n << (i + ((m - 1) * 8)))) >> (i + ((m - 1) * 8)));
|
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 {
|
|
||||||
PrintAndLogEx(SUCCESS, "EM 410x ID "_GREEN_("%010" PRIX64), id);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hi) {
|
|
||||||
//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 {
|
|
||||||
//output 40 bit em id
|
|
||||||
PrintAndLogEx(SUCCESS, "EM 410x ID "_GREEN_("%010" PRIX64), id);
|
PrintAndLogEx(SUCCESS, "EM 410x ID "_GREEN_("%010" PRIX64), id);
|
||||||
PrintAndLogEx(SUCCESS, "EM410x ( RF/%d )", g_DemodClock);
|
PrintAndLogEx(SUCCESS, "EM410x ( RF/%d )", g_DemodClock);
|
||||||
PrintAndLogEx(INFO, "-------- " _CYAN_("Possible de-scramble patterns") " ---------");
|
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);
|
printDemodBuff(0, false, false, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
printEM410x(*hi, *lo, verbose);
|
printEM410x(*hi, *lo, verbose, ans);
|
||||||
g_em410xid = *lo;
|
g_em410xid = *lo;
|
||||||
return PM3_SUCCESS;
|
return PM3_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
int CmdLFEM410X(const char *Cmd);
|
int CmdLFEM410X(const char *Cmd);
|
||||||
|
|
||||||
int demodEM410x(bool verbose);
|
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 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);
|
int AskEm410xDemod(int clk, int invert, int maxErr, size_t maxLen, bool amplify, uint32_t *hi, uint64_t *lo, bool verbose);
|
||||||
|
|
|
@ -198,12 +198,17 @@ int demodIndalaEx(int clk, int invert, int maxErr, bool verbose) {
|
||||||
parity |= DemodBuffer[34] << 1; // b2
|
parity |= DemodBuffer[34] << 1; // b2
|
||||||
parity |= DemodBuffer[38] << 0; // b1
|
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")
|
PrintAndLogEx(SUCCESS, "Fmt " _GREEN_("26") " FC: " _GREEN_("%u") " Card: " _GREEN_("%u") " Parity: " _GREEN_("%1d%1d")
|
||||||
, fc
|
, fc
|
||||||
, csn
|
, csn
|
||||||
, parity >> 1 & 0x01
|
, parity >> 1 & 0x01
|
||||||
, parity & 0x01
|
, parity & 0x01
|
||||||
);
|
);
|
||||||
|
PrintAndLogEx(DEBUG, "two bit checksum... " _GREEN_("%1d%1d"), checksum >> 1 & 0x01, checksum & 0x01);
|
||||||
|
|
||||||
PrintAndLogEx(SUCCESS, "Possible de-scramble patterns");
|
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
|
// 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
|
||||||
|
|
|
@ -327,7 +327,7 @@ bool asn1_tag_dump(const struct tlv *tlv, int level, bool *candump) {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
PrintAndLogEx(INFO,
|
PrintAndLogEx(INFO,
|
||||||
"%*s-- %02X [%02ZX] '"_YELLOW_("%s") "'" NOLF
|
"%*s-- %02X [%02zX] '"_YELLOW_("%s") "'" NOLF
|
||||||
, (level * 4)
|
, (level * 4)
|
||||||
, " "
|
, " "
|
||||||
, tlv->tag
|
, tlv->tag
|
||||||
|
|
329
client/src/mifare/aiddesfire.c
Normal file
329
client/src/mifare/aiddesfire.c
Normal file
|
@ -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;
|
||||||
|
}
|
|
@ -11,6 +11,7 @@
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
|
||||||
|
const char *nxp_cluster_to_text(uint8_t cluster);
|
||||||
int AIDDFDecodeAndPrint(uint8_t aid[3]);
|
int AIDDFDecodeAndPrint(uint8_t aid[3]);
|
||||||
|
|
||||||
#endif // _AIDDESFIRE_H_
|
#endif // _AIDDESFIRE_H_
|
|
@ -32,6 +32,8 @@
|
||||||
#include "util_posix.h" // msleep
|
#include "util_posix.h" // msleep
|
||||||
#include "mifare/desfire_crypto.h"
|
#include "mifare/desfire_crypto.h"
|
||||||
#include "desfiresecurechan.h"
|
#include "desfiresecurechan.h"
|
||||||
|
#include "mifare/mad.h"
|
||||||
|
#include "mifare/aiddesfire.h"
|
||||||
|
|
||||||
const CLIParserOption DesfireAlgoOpts[] = {
|
const CLIParserOption DesfireAlgoOpts[] = {
|
||||||
{T_DES, "des"},
|
{T_DES, "des"},
|
||||||
|
@ -261,6 +263,10 @@ const char *DesfireAuthErrorToStr(int error) {
|
||||||
return "Can't select application.";
|
return "Can't select application.";
|
||||||
case 201:
|
case 201:
|
||||||
return "Authentication retured no error but channel not authenticated.";
|
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:
|
case 301:
|
||||||
return "ISO Get challenge error.";
|
return "ISO Get challenge error.";
|
||||||
case 302:
|
case 302:
|
||||||
|
@ -347,7 +353,7 @@ void DesfirePrintContext(DesfireContext *ctx) {
|
||||||
desfire_get_key_block_length(ctx->keyType),
|
desfire_get_key_block_length(ctx->keyType),
|
||||||
sprint_hex(ctx->IV, desfire_get_key_block_length(ctx->keyType)));
|
sprint_hex(ctx->IV, desfire_get_key_block_length(ctx->keyType)));
|
||||||
if (ctx->secureChannel == DACEV2) {
|
if (ctx->secureChannel == DACEV2) {
|
||||||
PrintAndLogEx(INFO, " TI: %s cmdCntr: 0x%08x",
|
PrintAndLogEx(INFO, " TI: %s cmdCntr: 0x%04x",
|
||||||
sprint_hex(ctx->TI, 4),
|
sprint_hex(ctx->TI, 4),
|
||||||
ctx->cmdCntr);
|
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) {
|
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;
|
uint8_t data[1050] = {0};
|
||||||
int res = DESFIRESendApduEx(activate_field, apdu, le, resp, 255, &rlen, sw);
|
uint32_t datalen = 0;
|
||||||
|
int res = DESFIRESendApduEx(activate_field, apdu, le, data, sizeof(data), &datalen, sw);
|
||||||
|
|
||||||
if (res == PM3_SUCCESS)
|
if (res == PM3_SUCCESS)
|
||||||
*resplen = rlen;
|
DesfireSecureChannelDecode(ctx, data, datalen, 0, resp, resplen);
|
||||||
|
|
||||||
return res;
|
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) {
|
static void DesfireSplitBytesToBlock(uint8_t *blockdata, size_t *blockdatacount, size_t blockdatasize, uint8_t *dstdata, size_t dstdatalen) {
|
||||||
size_t len = 0;
|
size_t len = 0;
|
||||||
for (int i = 0; i < *blockdatacount; i++) {
|
for (int i = 0; i < *blockdatacount; i++) {
|
||||||
|
memset(&blockdata[i * blockdatasize + 1], 0, blockdatasize - 1);
|
||||||
size_t tlen = len + blockdata[i * blockdatasize];
|
size_t tlen = len + blockdata[i * blockdatasize];
|
||||||
if (tlen > dstdatalen)
|
if (tlen > dstdatalen) {
|
||||||
tlen = dstdatalen;
|
tlen = dstdatalen;
|
||||||
|
if (tlen >= len)
|
||||||
|
blockdata[i * blockdatasize] = tlen - len;
|
||||||
|
else
|
||||||
|
blockdata[i * blockdatasize] = 0;
|
||||||
|
}
|
||||||
if (len == tlen) {
|
if (len == tlen) {
|
||||||
*blockdatacount = i;
|
*blockdatacount = i;
|
||||||
break;
|
break;
|
||||||
|
@ -764,6 +777,7 @@ int DesfireSelectAID(DesfireContext *ctx, uint8_t *aid1, uint8_t *aid2) {
|
||||||
size_t resplen = 0;
|
size_t resplen = 0;
|
||||||
uint8_t respcode = 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);
|
int res = DesfireExchangeEx(true, ctx, MFDES_SELECT_APPLICATION, data, (aid2 == NULL) ? 3 : 6, &respcode, resp, &resplen, true, 0);
|
||||||
if (res == PM3_SUCCESS) {
|
if (res == PM3_SUCCESS) {
|
||||||
if (resplen != 0)
|
if (resplen != 0)
|
||||||
|
@ -800,6 +814,7 @@ int DesfireSelectAIDHexNoFieldOn(DesfireContext *ctx, uint32_t aid) {
|
||||||
size_t resplen = 0;
|
size_t resplen = 0;
|
||||||
uint8_t respcode = 0;
|
uint8_t respcode = 0;
|
||||||
|
|
||||||
|
ctx->secureChannel = DACNone;
|
||||||
int res = DesfireExchangeEx(false, ctx, MFDES_SELECT_APPLICATION, data, 3, &respcode, resp, &resplen, true, 0);
|
int res = DesfireExchangeEx(false, ctx, MFDES_SELECT_APPLICATION, data, 3, &respcode, resp, &resplen, true, 0);
|
||||||
if (res == PM3_SUCCESS) {
|
if (res == PM3_SUCCESS) {
|
||||||
if (resplen != 0)
|
if (resplen != 0)
|
||||||
|
@ -809,11 +824,28 @@ int DesfireSelectAIDHexNoFieldOn(DesfireContext *ctx, uint32_t aid) {
|
||||||
if (respcode != MFDES_S_OPERATION_OK)
|
if (respcode != MFDES_S_OPERATION_OK)
|
||||||
return PM3_EAPDU_FAIL;
|
return PM3_EAPDU_FAIL;
|
||||||
|
|
||||||
|
DesfireClearSession(ctx);
|
||||||
|
ctx->appSelected = (aid != 0x000000);
|
||||||
|
|
||||||
return PM3_SUCCESS;
|
return PM3_SUCCESS;
|
||||||
}
|
}
|
||||||
return res;
|
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) {
|
int DesfireSelectAndAuthenticateEx(DesfireContext *dctx, DesfireSecureChannel secureChannel, uint32_t aid, bool noauth, bool verbose) {
|
||||||
if (verbose)
|
if (verbose)
|
||||||
DesfirePrintContext(dctx);
|
DesfirePrintContext(dctx);
|
||||||
|
@ -859,6 +891,62 @@ int DesfireSelectAndAuthenticate(DesfireContext *dctx, DesfireSecureChannel secu
|
||||||
return DesfireSelectAndAuthenticateEx(dctx, secureChannel, aid, false, verbose);
|
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) {
|
static int DesfireAuthenticateEV1(DesfireContext *dctx, DesfireSecureChannel secureChannel, bool verbose) {
|
||||||
// 3 different way to authenticate AUTH (CRC16) , AUTH_ISO (CRC32) , AUTH_AES (CRC32)
|
// 3 different way to authenticate AUTH (CRC16) , AUTH_ISO (CRC32) , AUTH_AES (CRC32)
|
||||||
// 4 different crypto arg1 DES, 3DES, 3K3DES, AES
|
// 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 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 RndB[CRYPTO_AES_BLOCK_SIZE] = {0};
|
||||||
uint8_t encRndB[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 rotRndB[CRYPTO_AES_BLOCK_SIZE] = {0}; //RndB'
|
||||||
uint8_t both[CRYPTO_AES_BLOCK_SIZE * 2 + 1] = {0}; // ek/dk_keyNo(RndA+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);
|
memcpy(rotRndB, RndB, CRYPTO_AES_BLOCK_SIZE);
|
||||||
rol(rotRndB, CRYPTO_AES_BLOCK_SIZE);
|
rol(rotRndB, CRYPTO_AES_BLOCK_SIZE);
|
||||||
|
|
||||||
uint8_t encRndA[16] = {0x00};
|
|
||||||
|
|
||||||
// - Encrypt our response
|
// - Encrypt our response
|
||||||
uint8_t tmp[32] = {0x00};
|
uint8_t tmp[32] = {0x00};
|
||||||
memcpy(tmp, RndA, CRYPTO_AES_BLOCK_SIZE);
|
memcpy(tmp, RndA, CRYPTO_AES_BLOCK_SIZE);
|
||||||
|
@ -1232,20 +1319,21 @@ static int DesfireAuthenticateEV2(DesfireContext *dctx, DesfireSecureChannel sec
|
||||||
}
|
}
|
||||||
|
|
||||||
// Part 4
|
// Part 4
|
||||||
memcpy(encRndA, recv_data, CRYPTO_AES_BLOCK_SIZE);
|
|
||||||
|
|
||||||
uint8_t data[32] = {0};
|
uint8_t data[32] = {0};
|
||||||
|
|
||||||
if (aes_decode(IV, key, recv_data, data, recv_len))
|
if (aes_decode(IV, key, recv_data, data, recv_len))
|
||||||
return 10;
|
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;
|
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) {
|
if (g_debugMode > 1) {
|
||||||
PrintAndLogEx(DEBUG, "Expected_RndA : %s", sprint_hex(RndA, 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));
|
PrintAndLogEx(DEBUG, "Generated_RndA' : %s", sprint_hex(recRndA, CRYPTO_AES_BLOCK_SIZE));
|
||||||
}
|
}
|
||||||
return 11;
|
return 11;
|
||||||
}
|
}
|
||||||
|
@ -1295,7 +1383,7 @@ static int DesfireAuthenticateISO(DesfireContext *dctx, DesfireSecureChannel sec
|
||||||
|
|
||||||
// encode
|
// encode
|
||||||
DesfireClearIV(dctx);
|
DesfireClearIV(dctx);
|
||||||
DesfireCryptoEncDec(dctx, false, both, rndlen * 2, both, true); // error 303
|
DesfireCryptoEncDec(dctx, DCOMainKey, both, rndlen * 2, both, true); // error 303
|
||||||
|
|
||||||
// external authenticate
|
// external authenticate
|
||||||
res = DesfireISOExternalAuth(dctx, dctx->appSelected, dctx->keyNum, dctx->keyType, both);
|
res = DesfireISOExternalAuth(dctx, dctx->appSelected, dctx->keyNum, dctx->keyType, both);
|
||||||
|
@ -1314,7 +1402,7 @@ static int DesfireAuthenticateISO(DesfireContext *dctx, DesfireSecureChannel sec
|
||||||
|
|
||||||
// decode rnddata
|
// decode rnddata
|
||||||
uint8_t piccrnd2[64] = {0};
|
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
|
// check
|
||||||
if (memcmp(hostrnd2, &piccrnd2[rndlen], rndlen) != 0)
|
if (memcmp(hostrnd2, &piccrnd2[rndlen], rndlen) != 0)
|
||||||
|
@ -1344,6 +1432,279 @@ int DesfireAuthenticate(DesfireContext *dctx, DesfireSecureChannel secureChannel
|
||||||
return 100;
|
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) {
|
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)
|
if (resplen)
|
||||||
*resplen = 0;
|
*resplen = 0;
|
||||||
|
@ -1397,6 +1758,26 @@ int DesfireGetFreeMem(DesfireContext *dctx, uint32_t *freemem) {
|
||||||
return res;
|
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) {
|
int DesfireGetUID(DesfireContext *dctx, uint8_t *resp, size_t *resplen) {
|
||||||
return DesfireCommandRxData(dctx, MFDES_GET_UID, resp, resplen, -1);
|
return DesfireCommandRxData(dctx, MFDES_GET_UID, resp, resplen, -1);
|
||||||
}
|
}
|
||||||
|
@ -1502,8 +1883,7 @@ int DesfireFillFileList(DesfireContext *dctx, FileListS FileList, size_t *filesc
|
||||||
isoindx++;
|
isoindx++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (isoindx > 0)
|
|
||||||
isoindx--;
|
|
||||||
if (isoindx * 2 != buflen)
|
if (isoindx * 2 != buflen)
|
||||||
PrintAndLogEx(WARNING, "Wrong ISO ID list length. must be %zu but %zu", buflen, isoindx * 2);
|
PrintAndLogEx(WARNING, "Wrong ISO ID list length. must be %zu but %zu", buflen, isoindx * 2);
|
||||||
} else {
|
} else {
|
||||||
|
@ -1610,47 +1990,19 @@ int DesfireUpdateRecord(DesfireContext *dctx, uint8_t fnum, uint32_t recnum, uin
|
||||||
return DesfireCommandTxData(dctx, MFDES_UPDATE_RECORD, xdata, 10 + len);
|
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) {
|
static void PrintKeySettingsPICC(uint8_t keysettings, uint8_t numkeys, bool print2ndbyte) {
|
||||||
PrintAndLogEx(SUCCESS, "PICC level rights:");
|
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..] 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.] 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, "");
|
PrintAndLogEx(SUCCESS, "");
|
||||||
|
|
||||||
if (print2ndbyte)
|
if (print2ndbyte) {
|
||||||
|
DesfirePrintCardKeyType(numkeys >> 6);
|
||||||
PrintAndLogEx(SUCCESS, "key count: %d", numkeys & 0x0f);
|
PrintAndLogEx(SUCCESS, "key count: %d", numkeys & 0x0f);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void PrintKeySettingsApp(uint8_t keysettings, uint8_t numkeys, bool print2ndbyte) {
|
static void PrintKeySettingsApp(uint8_t keysettings, uint8_t numkeys, bool print2ndbyte) {
|
||||||
// Access rights.
|
// Access rights.
|
||||||
|
@ -1676,14 +2028,14 @@ static void PrintKeySettingsApp(uint8_t keysettings, uint8_t numkeys, bool print
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
PrintAndLogEx(SUCCESS, "[%c...] AMK Configuration changeable : %s", (keysettings & (1 << 3)) ? '1' : '0', (keysettings & (1 << 3)) ? _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)) ? "NO" : "YES");
|
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)) ? "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") : "NO (frozen)");
|
PrintAndLogEx(SUCCESS, "[...%c] AMK is changeable : %s", (keysettings & (1 << 0)) ? '1' : '0', (keysettings & (1 << 0)) ? _GREEN_("YES") : _RED_("NO (frozen)"));
|
||||||
PrintAndLogEx(SUCCESS, "");
|
PrintAndLogEx(SUCCESS, "");
|
||||||
|
|
||||||
if (print2ndbyte) {
|
if (print2ndbyte) {
|
||||||
PrintKeyType(numkeys >> 6);
|
DesfirePrintCardKeyType(numkeys >> 6);
|
||||||
PrintAndLogEx(SUCCESS, "key count: %d", numkeys & 0x0f);
|
PrintAndLogEx(SUCCESS, "key count: %d", numkeys & 0x0f);
|
||||||
if (numkeys & 0x20)
|
if (numkeys & 0x20)
|
||||||
PrintAndLogEx(SUCCESS, "iso file id: enabled");
|
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 *DesfireUnknownStr = "unknown";
|
||||||
static const char *DesfireDisabledStr = "disabled";
|
static const char *DesfireDisabledStr = "disabled";
|
||||||
static const char *DesfireFreeStr = "free";
|
static const char *DesfireFreeStr = "free";
|
||||||
|
static const char *DesfireNAStr = "n/a";
|
||||||
static const DesfireCreateFileCommandsS DesfireFileCommands[] = {
|
static const DesfireCreateFileCommandsS DesfireFileCommands[] = {
|
||||||
{0x00, "Standard data", MFDES_CREATE_STD_DATA_FILE, 6, 6, true},
|
{0x00, "Standard data", MFDES_CREATE_STD_DATA_FILE, 6, 6, true},
|
||||||
{0x01, "Backup data", MFDES_CREATE_BACKUP_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;
|
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) {
|
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[0] = (ch & 0x0f) | ((rw << 4) & 0xf0);
|
||||||
mode[1] = (w & 0x0f) | ((r << 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) {
|
static void DesfirePrintShortFileTypeSettings(FileSettingsS *fsettings) {
|
||||||
PrintAndLogEx(NORMAL, "(%-5s) " NOLF, GetDesfireCommunicationMode(fsettings->fileCommMode));
|
|
||||||
PrintAndLogEx(NORMAL, "[0x%02x] " _CYAN_("%-13s ") NOLF, fsettings->fileType, GetDesfireFileType(fsettings->fileType));
|
|
||||||
|
|
||||||
switch (fsettings->fileType) {
|
switch (fsettings->fileType) {
|
||||||
case 0x00:
|
case 0x00:
|
||||||
case 0x01: {
|
case 0x01: {
|
||||||
|
@ -1874,13 +2250,13 @@ void DesfirePrintFileSettingsOneLine(FileSettingsS *fsettings) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 0x02: {
|
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);
|
fsettings->lowerLimit, fsettings->upperLimit, fsettings->limitedCredit, fsettings->value, fsettings->value);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 0x03:
|
case 0x03:
|
||||||
case 0x04: {
|
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);
|
fsettings->curRecordCount, fsettings->maxRecordCount, fsettings->recordSize, fsettings->recordSize);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1892,12 +2268,49 @@ void DesfirePrintFileSettingsOneLine(FileSettingsS *fsettings) {
|
||||||
break;
|
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)",
|
PrintAndLogEx(NORMAL, "(%s %s %s %s)",
|
||||||
GetDesfireAccessRightStr(fsettings->rAccess),
|
GetDesfireAccessRightShortStr(fsettings->rAccess),
|
||||||
GetDesfireAccessRightStr(fsettings->wAccess),
|
GetDesfireAccessRightShortStr(fsettings->wAccess),
|
||||||
GetDesfireAccessRightStr(fsettings->rwAccess),
|
GetDesfireAccessRightShortStr(fsettings->rwAccess),
|
||||||
GetDesfireAccessRightStr(fsettings->chAccess));
|
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) {
|
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]);
|
iso14443a_crc(nkeybuf, nkeylen, &cdata[cdatalen]);
|
||||||
cdatalen += 2;
|
cdatalen += 2;
|
||||||
}
|
}
|
||||||
} else {
|
} else if (dctx->secureChannel == DACEV1) {
|
||||||
// EV1 Checksum must cover : <KeyNo> <PrevKey XOR Newkey> [<AES NewKeyVer>]
|
// EV1 Checksum must cover : <KeyNo> <PrevKey XOR Newkey> [<AES NewKeyVer>]
|
||||||
desfire_crc32_append(pckcdata, cdatalen + 2);
|
desfire_crc32_append(pckcdata, cdatalen + 2);
|
||||||
cdatalen += 4;
|
cdatalen += 4;
|
||||||
|
@ -2166,12 +2579,18 @@ int DesfireChangeKey(DesfireContext *dctx, bool change_master_key, uint8_t newke
|
||||||
desfire_crc32(nkeybuf, nkeylen, &cdata[cdatalen]);
|
desfire_crc32(nkeybuf, nkeylen, &cdata[cdatalen]);
|
||||||
cdatalen += 4;
|
cdatalen += 4;
|
||||||
}
|
}
|
||||||
|
} else if (dctx->secureChannel == DACEV2) {
|
||||||
|
// EV2 : <PrevKey XOR Newkey> [<AES NewKeyVer>]
|
||||||
|
if (newkeynum != dctx->keyNum) {
|
||||||
|
desfire_crc32(nkeybuf, nkeylen, &cdata[cdatalen]);
|
||||||
|
cdatalen += 4;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// send command
|
// send command
|
||||||
uint8_t resp[257] = {0};
|
uint8_t resp[257] = {0};
|
||||||
size_t resplen = 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
|
// check response
|
||||||
if (res == 0 && resplen > 0)
|
if (res == 0 && resplen > 0)
|
||||||
|
@ -2192,21 +2611,9 @@ int DesfireSetConfiguration(DesfireContext *dctx, uint8_t paramid, uint8_t *para
|
||||||
memcpy(&data[1], param, paramlen);
|
memcpy(&data[1], param, paramlen);
|
||||||
size_t datalen = 1 + 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
|
// dynamic length
|
||||||
if (paramid == 0x02) {
|
if (paramid == 0x02 && dctx->commMode == DCMEncrypted)
|
||||||
data[datalen] = 0x80;
|
dctx->commMode = DCMEncryptedWithPadding;
|
||||||
datalen++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// send command
|
// send command
|
||||||
uint8_t resp[257] = {0};
|
uint8_t resp[257] = {0};
|
||||||
|
@ -2220,11 +2627,11 @@ int DesfireSetConfiguration(DesfireContext *dctx, uint8_t paramid, uint8_t *para
|
||||||
return res;
|
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};
|
uint8_t xresp[250] = {0};
|
||||||
size_t xresplen = 0;
|
size_t xresplen = 0;
|
||||||
uint16_t sw = 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)
|
if (res == PM3_SUCCESS && sw != 0x9000)
|
||||||
return PM3_ESOFT;
|
return PM3_ESOFT;
|
||||||
|
|
||||||
|
@ -2239,6 +2646,10 @@ int DesfireISOSelect(DesfireContext *dctx, DesfireISOSelectControl cntr, uint8_t
|
||||||
return res;
|
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) {
|
int DesfireISOSelectDF(DesfireContext *dctx, char *dfname, uint8_t *resp, size_t *resplen) {
|
||||||
return DesfireISOSelect(dctx, ISSDFName, (uint8_t *)dfname, strnlen(dfname, 16), resp, 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;
|
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);
|
||||||
|
}
|
||||||
|
|
|
@ -30,6 +30,14 @@ enum DesfireISOSelectControlEnum {
|
||||||
};
|
};
|
||||||
typedef enum DesfireISOSelectControlEnum DesfireISOSelectControl;
|
typedef enum DesfireISOSelectControlEnum DesfireISOSelectControl;
|
||||||
|
|
||||||
|
enum DesfireISOSelectWayEnum {
|
||||||
|
ISW6bAID,
|
||||||
|
ISWMF,
|
||||||
|
ISWIsoID,
|
||||||
|
ISWDFName
|
||||||
|
};
|
||||||
|
typedef enum DesfireISOSelectWayEnum DesfireISOSelectWay;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
const uint8_t id;
|
const uint8_t id;
|
||||||
const char *text;
|
const char *text;
|
||||||
|
@ -85,6 +93,48 @@ typedef struct {
|
||||||
|
|
||||||
typedef FileListElmS FileListS[32];
|
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 {
|
typedef enum {
|
||||||
RFTAuto,
|
RFTAuto,
|
||||||
RFTData,
|
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 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 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 DesfireSelectAID(DesfireContext *ctx, uint8_t *aid1, uint8_t *aid2);
|
||||||
int DesfireSelectAIDHex(DesfireContext *ctx, uint32_t aid1, bool select_two, uint32_t aid2);
|
int DesfireSelectAIDHex(DesfireContext *ctx, uint32_t aid1, bool select_two, uint32_t aid2);
|
||||||
int DesfireSelectAIDHexNoFieldOn(DesfireContext *ctx, uint32_t aid);
|
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);
|
const char *DesfireAuthErrorToStr(int error);
|
||||||
int DesfireSelectAndAuthenticate(DesfireContext *dctx, DesfireSecureChannel secureChannel, uint32_t aid, bool verbose);
|
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 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);
|
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 DesfireFormatPICC(DesfireContext *dctx);
|
||||||
int DesfireGetFreeMem(DesfireContext *dctx, uint32_t *freemem);
|
int DesfireGetFreeMem(DesfireContext *dctx, uint32_t *freemem);
|
||||||
int DesfireGetUID(DesfireContext *dctx, uint8_t *resp, size_t *resplen);
|
int DesfireGetUID(DesfireContext *dctx, uint8_t *resp, size_t *resplen);
|
||||||
int DesfireGetAIDList(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 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 DesfireCreateApplication(DesfireContext *dctx, uint8_t *appdata, size_t appdatalen);
|
||||||
int DesfireDeleteApplication(DesfireContext *dctx, uint32_t aid);
|
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 DesfireGetKeySettings(DesfireContext *dctx, uint8_t *resp, size_t *resplen);
|
||||||
int DesfireChangeKeySettings(DesfireContext *dctx, uint8_t *data, size_t len);
|
int DesfireChangeKeySettings(DesfireContext *dctx, uint8_t *data, size_t len);
|
||||||
void PrintKeySettings(uint8_t keysettings, uint8_t numkeys, bool applevel, bool print2ndbyte);
|
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 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);
|
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 DesfireFillFileSettings(uint8_t *data, size_t datalen, FileSettingsS *fsettings);
|
||||||
void DesfirePrintFileSettingsOneLine(FileSettingsS *fsettings);
|
void DesfirePrintFileSettingsOneLine(FileSettingsS *fsettings);
|
||||||
|
void DesfirePrintFileSettingsTable(bool printheader, uint8_t id, bool isoidavail, uint16_t isoid, FileSettingsS *fsettings);
|
||||||
void DesfirePrintFileSettingsExtended(FileSettingsS *fsettings);
|
void DesfirePrintFileSettingsExtended(FileSettingsS *fsettings);
|
||||||
int DesfireGetFileSettings(DesfireContext *dctx, uint8_t fileid, uint8_t *resp, size_t *resplen);
|
int DesfireGetFileSettings(DesfireContext *dctx, uint8_t fileid, uint8_t *resp, size_t *resplen);
|
||||||
int DesfireGetFileSettingsStruct(DesfireContext *dctx, uint8_t fileid, FileSettingsS *fsettings);
|
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 DesfireCreateFileCommandsS *GetDesfireFileCmdRec(uint8_t type);
|
||||||
const char *GetDesfireAccessRightStr(uint8_t right);
|
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 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 DesfireDecodeFileAcessMode(uint8_t *mode, uint8_t *r, uint8_t *w, uint8_t *rw, uint8_t *ch);
|
||||||
void DesfirePrintAccessRight(uint8_t *data);
|
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 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 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 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 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 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
|
#endif // __DESFIRECORE_H
|
||||||
|
|
|
@ -217,7 +217,7 @@ static void DesfireCryptoEncDecSingleBlock(uint8_t *key, DesfireCryptoAlgorythm
|
||||||
memcpy(dstdata, edata, block_size);
|
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 data[1024] = {0};
|
||||||
uint8_t xiv[DESFIRE_MAX_CRYPTO_BLOCK_SIZE] = {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;
|
size_t offset = 0;
|
||||||
while (offset < srcdatalen) {
|
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);
|
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);
|
DesfireCryptoEncDecSingleBlock(ctx->key, ctx->keyType, srcdata + offset, data + offset, xiv, dir_to_send, encode);
|
||||||
|
}
|
||||||
offset += block_size;
|
offset += block_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -250,13 +253,13 @@ void DesfireCryptoEncDecEx(DesfireContext *ctx, bool use_session_key, uint8_t *s
|
||||||
memcpy(dstdata, data, srcdatalen);
|
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 dir_to_send = encode;
|
||||||
bool xencode = encode;
|
bool xencode = encode;
|
||||||
if (ctx->secureChannel == DACd40)
|
if (ctx->secureChannel == DACd40)
|
||||||
xencode = false;
|
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) {
|
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];
|
uint8_t ivect[kbs];
|
||||||
memset(ivect, 0, 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;
|
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);
|
bin_xor(buffer + len - kbs, sk1, kbs);
|
||||||
}
|
}
|
||||||
|
|
||||||
DesfireCryptoEncDec(ctx, true, buffer, len, NULL, true);
|
DesfireCryptoEncDec(ctx, DCOSessionKeyMac, buffer, len, NULL, true);
|
||||||
|
|
||||||
if (cmac != NULL)
|
if (cmac != NULL)
|
||||||
memcpy(cmac, ctx->IV, kbs);
|
memcpy(cmac, ctx->IV, kbs);
|
||||||
|
@ -353,6 +356,50 @@ uint8_t DesfireDESKeyGetVersion(uint8_t *key) {
|
||||||
return version;
|
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 DesfireFileCommModeToCommMode(uint8_t file_comm_mode) {
|
||||||
DesfireCommunicationMode mode = DCMNone;
|
DesfireCommunicationMode mode = DCMNone;
|
||||||
switch (file_comm_mode & 0x03) {
|
switch (file_comm_mode & 0x03) {
|
||||||
|
@ -382,6 +429,7 @@ uint8_t DesfireCommModeToFileCommMode(DesfireCommunicationMode comm_mode) {
|
||||||
fmode = 0x01;
|
fmode = 0x01;
|
||||||
break;
|
break;
|
||||||
case DCMEncrypted:
|
case DCMEncrypted:
|
||||||
|
case DCMEncryptedWithPadding:
|
||||||
case DCMEncryptedPlain:
|
case DCMEncryptedPlain:
|
||||||
fmode = 0x11;
|
fmode = 0x11;
|
||||||
break;
|
break;
|
||||||
|
@ -474,6 +522,19 @@ void DesfireEV2FillIV(DesfireContext *ctx, bool ivforcommand, uint8_t *iv) {
|
||||||
memcpy(iv, xiv, CRYPTO_AES_BLOCK_SIZE);
|
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) {
|
void desfire_crc32(const uint8_t *data, const size_t len, uint8_t *crc) {
|
||||||
crc32_ex(data, len, crc);
|
crc32_ex(data, len, crc);
|
||||||
|
|
|
@ -58,9 +58,15 @@ typedef enum {
|
||||||
DCMPlain,
|
DCMPlain,
|
||||||
DCMMACed,
|
DCMMACed,
|
||||||
DCMEncrypted,
|
DCMEncrypted,
|
||||||
|
DCMEncryptedWithPadding,
|
||||||
DCMEncryptedPlain
|
DCMEncryptedPlain
|
||||||
} DesfireCommunicationMode;
|
} DesfireCommunicationMode;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
DCOMainKey,
|
||||||
|
DCOSessionKeyMac,
|
||||||
|
DCOSessionKeyEnc
|
||||||
|
} DesfireCryptoOpKeyType;
|
||||||
|
|
||||||
typedef struct DesfireContextS {
|
typedef struct DesfireContextS {
|
||||||
uint8_t keyNum;
|
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);
|
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 DesfireCryptoEncDec(DesfireContext *ctx, DesfireCryptoOpKeyType key_type, 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 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 DesfireCryptoCMAC(DesfireContext *ctx, uint8_t *srcdata, size_t srcdatalen, uint8_t *cmac);
|
||||||
|
|
||||||
void DesfireDESKeySetVersion(uint8_t *key, DesfireCryptoAlgorythm keytype, uint8_t version);
|
void DesfireDESKeySetVersion(uint8_t *key, DesfireCryptoAlgorythm keytype, uint8_t version);
|
||||||
uint8_t DesfireDESKeyGetVersion(uint8_t *key);
|
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);
|
DesfireCommunicationMode DesfireFileCommModeToCommMode(uint8_t file_comm_mode);
|
||||||
uint8_t DesfireCommModeToFileCommMode(DesfireCommunicationMode comm_mode);
|
uint8_t DesfireCommModeToFileCommMode(DesfireCommunicationMode comm_mode);
|
||||||
|
|
||||||
void DesfireGenSessionKeyEV1(const uint8_t rnda[], const uint8_t rndb[], DesfireCryptoAlgorythm keytype, uint8_t *key);
|
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 DesfireGenSessionKeyEV2(uint8_t *key, uint8_t *rndA, uint8_t *rndB, bool enckey, uint8_t *sessionkey);
|
||||||
void DesfireEV2FillIV(DesfireContext *ctx, bool ivforcommand, uint8_t *iv);
|
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(const uint8_t *data, const size_t len, uint8_t *crc);
|
||||||
void desfire_crc32_append(uint8_t *data, const size_t len);
|
void desfire_crc32_append(uint8_t *data, const size_t len);
|
||||||
|
|
|
@ -41,27 +41,24 @@ static bool CommandCanUseAnyChannel(uint8_t cmd) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static const AllowedChannelModesS AllowedChannelModes[] = {
|
static const AllowedChannelModesS AllowedChannelModes[] = {
|
||||||
|
// D40 channel
|
||||||
{MFDES_SELECT_APPLICATION, DACd40, DCCNative, DCMPlain},
|
{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_GET_VALUE, DACd40, DCCNative, DCMMACed},
|
||||||
{MFDES_CREDIT, DACd40, DCCNative, DCMMACed},
|
{MFDES_CREDIT, DACd40, DCCNative, DCMMACed},
|
||||||
{MFDES_DEBIT, 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, DACd40, DCCNative, DCMEncryptedPlain},
|
||||||
{MFDES_CHANGE_KEY_EV2, DACd40, DCCNative, DCMEncryptedPlain},
|
{MFDES_CHANGE_KEY_EV2, DACd40, DCCNative, DCMEncryptedPlain},
|
||||||
|
|
||||||
{MFDES_GET_KEY_VERSION, DACEV1, DCCNative, DCMPlain},
|
// EV1 and EV2 channel
|
||||||
{MFDES_GET_FREE_MEMORY, DACEV1, DCCNative, DCMPlain},
|
|
||||||
{MFDES_SELECT_APPLICATION, DACEV1, DCCNative, DCMPlain},
|
{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_CREATE_APPLICATION, DACEV1, DCCNative, DCMMACed},
|
||||||
{MFDES_DELETE_APPLICATION, DACEV1, DCCNative, DCMMACed},
|
{MFDES_DELETE_APPLICATION, DACEV1, DCCNative, DCMMACed},
|
||||||
{MFDES_GET_APPLICATION_IDS, 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, DACEV1, DCCNative, DCMEncryptedPlain},
|
||||||
{MFDES_CHANGE_KEY_EV2, DACEV1, DCCNative, DCMEncryptedPlain},
|
{MFDES_CHANGE_KEY_EV2, DACEV1, DCCNative, DCMEncryptedPlain},
|
||||||
|
|
||||||
|
// EV2 channel separately
|
||||||
{MFDES_AUTHENTICATE_EV2F, DACEV2, DCCNative, DCMPlain},
|
{MFDES_AUTHENTICATE_EV2F, DACEV2, DCCNative, DCMPlain},
|
||||||
{MFDES_AUTHENTICATE_EV2NF, 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
|
#define CMD_HEADER_LEN_ALL 0xffff
|
||||||
|
@ -147,6 +163,68 @@ static uint8_t DesfireGetCmdHeaderLen(uint8_t cmd) {
|
||||||
return 0;
|
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) {
|
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};
|
uint8_t data[1024] = {0};
|
||||||
size_t rlen = 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));
|
size_t srcmaclen = padded_data_length(srcdatalen - hdrlen, desfire_get_key_block_length(ctx->keyType));
|
||||||
|
|
||||||
uint8_t mac[32] = {0};
|
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);
|
if (DesfireEV1D40TransmitMAC(ctx, cmd)) {
|
||||||
memcpy(&dstdata[srcdatalen], mac, DesfireGetMACLength(ctx));
|
memcpy(&dstdata[srcdatalen], mac, DesfireGetMACLength(ctx));
|
||||||
*dstdatalen = rlen;
|
*dstdatalen = rlen;
|
||||||
} else if (ctx->commMode == DCMEncrypted) {
|
}
|
||||||
|
} else if (ctx->commMode == DCMEncrypted || ctx->commMode == DCMEncryptedWithPadding) {
|
||||||
if (srcdatalen <= hdrlen)
|
if (srcdatalen <= hdrlen)
|
||||||
return;
|
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);
|
memcpy(data, &srcdata[hdrlen], srcdatalen - hdrlen);
|
||||||
iso14443a_crc_append(data, srcdatalen - hdrlen);
|
iso14443a_crc_append(data, srcdatalen - hdrlen);
|
||||||
|
|
||||||
|
// add padding
|
||||||
|
if (paddinglen > 0)
|
||||||
|
data[srcdatalen - hdrlen + 2] = 0x80;
|
||||||
|
|
||||||
memcpy(dstdata, srcdata, hdrlen);
|
memcpy(dstdata, srcdata, hdrlen);
|
||||||
//PrintAndLogEx(INFO, "src[%d]: %s", srcdatalen - hdrlen + 2, sprint_hex(data, srcdatalen - hdrlen + 2));
|
//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;
|
*dstdatalen = rlen;
|
||||||
} else if (ctx->commMode == DCMEncryptedPlain) {
|
} 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;
|
rlen = padded_data_length(srcdatalen - hdrlen, desfire_get_key_block_length(ctx->keyType)) + hdrlen;
|
||||||
memcpy(data, srcdata, srcdatalen);
|
memcpy(data, srcdata, srcdatalen);
|
||||||
memcpy(dstdata, srcdata, hdrlen);
|
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;
|
*dstdatalen = rlen;
|
||||||
ctx->commMode = DCMEncrypted;
|
ctx->commMode = DCMEncrypted;
|
||||||
}
|
}
|
||||||
|
@ -216,20 +300,28 @@ static void DesfireSecureChannelEncodeEV1(DesfireContext *ctx, uint8_t cmd, uint
|
||||||
|
|
||||||
memcpy(dstdata, srcdata, srcdatalen);
|
memcpy(dstdata, srcdata, srcdatalen);
|
||||||
*dstdatalen = srcdatalen;
|
*dstdatalen = srcdatalen;
|
||||||
if (srcdatalen > hdrlen && ctx->commMode == DCMMACed) {
|
if (ctx->commMode == DCMMACed && DesfireEV1D40TransmitMAC(ctx, cmd)) {
|
||||||
memcpy(&dstdata[srcdatalen], cmac, DesfireGetMACLength(ctx));
|
memcpy(&dstdata[srcdatalen], cmac, DesfireGetMACLength(ctx));
|
||||||
*dstdatalen = srcdatalen + DesfireGetMACLength(ctx);
|
*dstdatalen = srcdatalen + DesfireGetMACLength(ctx);
|
||||||
}
|
}
|
||||||
} else if (ctx->commMode == DCMEncrypted) {
|
} else if (ctx->commMode == DCMEncrypted || ctx->commMode == DCMEncryptedWithPadding) {
|
||||||
rlen = padded_data_length(srcdatalen + 4 - hdrlen, desfire_get_key_block_length(ctx->keyType));
|
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;
|
data[0] = cmd;
|
||||||
|
|
||||||
|
// crc
|
||||||
memcpy(&data[1], srcdata, srcdatalen);
|
memcpy(&data[1], srcdata, srcdatalen);
|
||||||
desfire_crc32_append(data, srcdatalen + 1);
|
desfire_crc32_append(data, srcdatalen + 1);
|
||||||
|
|
||||||
|
// add padding
|
||||||
|
if (paddinglen > 0)
|
||||||
|
data[srcdatalen + 1 + 4] = 0x80;
|
||||||
|
|
||||||
memcpy(dstdata, srcdata, hdrlen);
|
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;
|
*dstdatalen = hdrlen + rlen;
|
||||||
|
ctx->commMode = DCMEncrypted;
|
||||||
} else if (ctx->commMode == DCMEncryptedPlain) {
|
} else if (ctx->commMode == DCMEncryptedPlain) {
|
||||||
if (srcdatalen <= hdrlen)
|
if (srcdatalen <= hdrlen)
|
||||||
return;
|
return;
|
||||||
|
@ -237,16 +329,47 @@ static void DesfireSecureChannelEncodeEV1(DesfireContext *ctx, uint8_t cmd, uint
|
||||||
memcpy(dstdata, srcdata, hdrlen);
|
memcpy(dstdata, srcdata, hdrlen);
|
||||||
memcpy(data, &srcdata[hdrlen], srcdatalen);
|
memcpy(data, &srcdata[hdrlen], srcdatalen);
|
||||||
rlen = padded_data_length(srcdatalen - hdrlen, desfire_get_key_block_length(ctx->keyType));
|
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;
|
*dstdatalen = hdrlen + rlen;
|
||||||
ctx->commMode = DCMEncrypted;
|
ctx->commMode = DCMEncrypted;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void DesfireSecureChannelEncodeEV2(DesfireContext *ctx, uint8_t cmd, uint8_t *srcdata, size_t srcdatalen, uint8_t *dstdata, size_t *dstdatalen) {
|
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);
|
memcpy(dstdata, srcdata, srcdatalen);
|
||||||
*dstdatalen = 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) {
|
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) {
|
switch (ctx->commMode) {
|
||||||
case DCMMACed: {
|
case DCMMACed: {
|
||||||
size_t maclen = DesfireGetMACLength(ctx);
|
size_t maclen = DesfireGetMACLength(ctx);
|
||||||
if (srcdatalen > maclen) {
|
if (srcdatalen > maclen && DesfireEV1D40ReceiveMAC(ctx, ctx->lastCommand)) {
|
||||||
uint8_t mac[16] = {0};
|
uint8_t mac[16] = {0};
|
||||||
rlen = padded_data_length(srcdatalen - maclen, desfire_get_key_block_length(ctx->keyType));
|
rlen = padded_data_length(srcdatalen - maclen, desfire_get_key_block_length(ctx->keyType));
|
||||||
memcpy(data, srcdata, srcdatalen - maclen);
|
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) {
|
if (memcmp(mac, &srcdata[srcdatalen - maclen], maclen) == 0) {
|
||||||
*dstdatalen = srcdatalen - maclen;
|
*dstdatalen = srcdatalen - maclen;
|
||||||
|
if (GetAPDULogging())
|
||||||
|
PrintAndLogEx(INFO, "Received MAC OK");
|
||||||
} else {
|
} else {
|
||||||
PrintAndLogEx(WARNING, "Received MAC is not match with calculated");
|
PrintAndLogEx(WARNING, "Received MAC is not match with calculated");
|
||||||
//PrintAndLogEx(INFO, " received MAC: %s", sprint_hex(&srcdata[srcdatalen - maclen], maclen));
|
PrintAndLogEx(INFO, " received MAC: %s", sprint_hex(&srcdata[srcdatalen - maclen], maclen));
|
||||||
//PrintAndLogEx(INFO, " calculated MAC: %s", sprint_hex(mac, maclen));
|
PrintAndLogEx(INFO, " calculated MAC: %s", sprint_hex(mac, maclen));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DCMEncrypted:
|
case DCMEncrypted:
|
||||||
|
case DCMEncryptedWithPadding:
|
||||||
if (srcdatalen < desfire_get_key_block_length(ctx->keyType)) {
|
if (srcdatalen < desfire_get_key_block_length(ctx->keyType)) {
|
||||||
memcpy(dstdata, srcdata, srcdatalen);
|
memcpy(dstdata, srcdata, srcdatalen);
|
||||||
*dstdatalen = srcdatalen;
|
*dstdatalen = srcdatalen;
|
||||||
return;
|
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));
|
//PrintAndLogEx(INFO, "decoded[%d]: %s", srcdatalen, sprint_hex(dstdata, srcdatalen));
|
||||||
|
|
||||||
size_t puredatalen = DesfireSearchCRCPos(dstdata, srcdatalen, respcode, 2);
|
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);
|
DesfireCryptoCMAC(ctx, data, *dstdatalen + 1, cmac);
|
||||||
if (memcmp(&srcdata[*dstdatalen], cmac, DesfireGetMACLength(ctx)) != 0) {
|
if (memcmp(&srcdata[*dstdatalen], cmac, DesfireGetMACLength(ctx)) != 0) {
|
||||||
PrintAndLogEx(WARNING, "Received MAC is not match with calculated");
|
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, " received MAC: %s", sprint_hex(&srcdata[*dstdatalen], DesfireGetMACLength(ctx)));
|
||||||
PrintAndLogEx(INFO, " calculated MAC: %s", sprint_hex(cmac, desfire_get_key_block_length(ctx->keyType)));
|
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)) {
|
if (srcdatalen < desfire_get_key_block_length(ctx->keyType)) {
|
||||||
memcpy(dstdata, srcdata, srcdatalen);
|
memcpy(dstdata, srcdata, srcdatalen);
|
||||||
*dstdatalen = srcdatalen;
|
*dstdatalen = srcdatalen;
|
||||||
return;
|
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));
|
//PrintAndLogEx(INFO, "decoded[%d]: %s", srcdatalen, sprint_hex(dstdata, srcdatalen));
|
||||||
|
|
||||||
size_t puredatalen = DesfireSearchCRCPos(dstdata, srcdatalen, respcode, 4);
|
size_t puredatalen = DesfireSearchCRCPos(dstdata, srcdatalen, respcode, 4);
|
||||||
|
@ -377,9 +506,92 @@ static void DesfireSecureChannelDecodeEV2(DesfireContext *ctx, uint8_t *srcdata,
|
||||||
|
|
||||||
memcpy(dstdata, srcdata, srcdatalen);
|
memcpy(dstdata, srcdata, srcdatalen);
|
||||||
*dstdatalen = 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) {
|
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) {
|
switch (ctx->secureChannel) {
|
||||||
case DACd40:
|
case DACd40:
|
||||||
DesfireSecureChannelDecodeD40(ctx, srcdata, srcdatalen, respcode, dstdata, dstdatalen);
|
DesfireSecureChannelDecodeD40(ctx, srcdata, srcdatalen, respcode, dstdata, dstdatalen);
|
||||||
|
@ -406,9 +618,17 @@ bool PrintChannelModeWarning(uint8_t cmd, DesfireSecureChannel secureChannel, De
|
||||||
// no security set
|
// no security set
|
||||||
if (secureChannel == DACNone)
|
if (secureChannel == DACNone)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (CommandCanUseAnyChannel(cmd))
|
if (CommandCanUseAnyChannel(cmd))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
// ISO commands
|
||||||
|
if (cmdSet == DCCISO) {
|
||||||
|
bool res = DesfireISOChannelValidCmd(cmd);
|
||||||
|
if (!res)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool found = false;
|
bool found = false;
|
||||||
for (int i = 0; i < ARRAY_LENGTH(AllowedChannelModes); i++)
|
for (int i = 0; i < ARRAY_LENGTH(AllowedChannelModes); i++)
|
||||||
if (AllowedChannelModes[i].cmd == cmd) {
|
if (AllowedChannelModes[i].cmd == cmd) {
|
||||||
|
@ -431,6 +651,15 @@ bool PrintChannelModeWarning(uint8_t cmd, DesfireSecureChannel secureChannel, De
|
||||||
break;
|
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)
|
if (!found)
|
||||||
|
|
|
@ -269,6 +269,66 @@ static bool TestEV2IVEncode(void) {
|
||||||
return res;
|
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 DesfireTest(bool verbose) {
|
||||||
bool res = true;
|
bool res = true;
|
||||||
|
|
||||||
|
@ -281,6 +341,7 @@ bool DesfireTest(bool verbose) {
|
||||||
res = res && TestCMACDES();
|
res = res && TestCMACDES();
|
||||||
res = res && TestEV2SessionKeys();
|
res = res && TestEV2SessionKeys();
|
||||||
res = res && TestEV2IVEncode();
|
res = res && TestEV2IVEncode();
|
||||||
|
res = res && TestEV2MAC();
|
||||||
|
|
||||||
PrintAndLogEx(INFO, "---------------------------");
|
PrintAndLogEx(INFO, "---------------------------");
|
||||||
if (res)
|
if (res)
|
||||||
|
|
|
@ -135,7 +135,7 @@ static int check_comm(void) {
|
||||||
char prompt_filtered[PROXPROMPT_MAX_SIZE] = {0};
|
char prompt_filtered[PROXPROMPT_MAX_SIZE] = {0};
|
||||||
memcpy_filter_ansi(prompt_filtered, prompt, sizeof(prompt_filtered), !session.supports_colors);
|
memcpy_filter_ansi(prompt_filtered, prompt, sizeof(prompt_filtered), !session.supports_colors);
|
||||||
rl_set_prompt(prompt_filtered);
|
rl_set_prompt(prompt_filtered);
|
||||||
rl_forced_update_display();
|
rl_redisplay();
|
||||||
#endif
|
#endif
|
||||||
CloseProxmark(session.current_device);
|
CloseProxmark(session.current_device);
|
||||||
}
|
}
|
||||||
|
|
|
@ -253,6 +253,55 @@ size_t removeParity(uint8_t *bits, size_t startIdx, uint8_t pLen, uint8_t pType,
|
||||||
return bitCnt;
|
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
|
// by marshmellow
|
||||||
// takes a array of binary values, length of bits per parity (includes parity bit),
|
// 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)
|
// 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 (bits[1] > 1) return -1;
|
||||||
if (*size < 64) return -2;
|
if (*size < 64) return -2;
|
||||||
|
|
||||||
uint8_t fmtlen;
|
|
||||||
*start_idx = 0;
|
*start_idx = 0;
|
||||||
|
|
||||||
// preamble 0111111111
|
// preamble 0111111111
|
||||||
|
@ -2131,31 +2179,28 @@ 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))
|
if (!preambleSearch(bits, preamble, sizeof(preamble), size, start_idx))
|
||||||
return -4;
|
return -4;
|
||||||
|
|
||||||
// (iceman) if the preamble doesn't find two occuriences, this identification fails.
|
bool validShort = false;
|
||||||
fmtlen = (*size == 128) ? 22 : 10;
|
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
|
if (validShort) {
|
||||||
*size = removeParity(bits, *start_idx + sizeof(preamble), 5, 0, fmtlen * 5);
|
|
||||||
|
|
||||||
switch (*size) {
|
|
||||||
case 40: {
|
|
||||||
// std em410x format
|
// std em410x format
|
||||||
*hi = 0;
|
*hi = 0;
|
||||||
*lo = ((uint64_t)(bytebits_to_byte(bits, 8)) << 32) | (bytebits_to_byte(bits + 8, 32));
|
*lo = ((uint64_t)(bytebits_to_byte(bits, 8)) << 32) | (bytebits_to_byte(bits + 8, 32));
|
||||||
break;
|
// 1 = Short
|
||||||
}
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
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
|
// 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) {
|
int HIDdemodFSK(uint8_t *dest, size_t *size, uint32_t *hi2, uint32_t *hi, uint32_t *lo, int *waveStartIdx) {
|
||||||
|
|
|
@ -973,7 +973,7 @@
|
||||||
},
|
},
|
||||||
"help": {
|
"help": {
|
||||||
"command": "help",
|
"command": "help",
|
||||||
"description": "help use `<command> 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 `<command> 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": [
|
"notes": [
|
||||||
"auto"
|
"auto"
|
||||||
],
|
],
|
||||||
|
@ -3990,7 +3990,13 @@
|
||||||
"command": "hf mfdes changekey",
|
"command": "hf mfdes changekey",
|
||||||
"description": "change picc/application key. needs to provide keynum/key for a valid authentication (may get from default parameters).",
|
"description": "change picc/application key. needs to provide keynum/key for a valid authentication (may get from default parameters).",
|
||||||
"notes": [
|
"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,
|
"offline": false,
|
||||||
"options": [
|
"options": [
|
||||||
|
@ -4342,6 +4348,26 @@
|
||||||
],
|
],
|
||||||
"usage": "hf mfdes createvaluefile [-hav] [-n <keyno>] [-t <des/2tdea/3tdea/aes>] [-k <key>] [-f <none/an10922/gallagher>] [-i <kdfi>] [-m <plain/mac/encrypt>] [-c <native/niso/iso>] [-s <d40/ev1/ev2>] [--aid <app id hex>] [--fid <file id hex>] [--amode <plain/mac/encrypt>] [--rawrights <access rights hex>] [--rrights <key0/../key13/free/deny>] [--wrights <key0/../key13/free/deny>] [--rwrights <key0/../key13/free/deny>] [--chrights <key0/../key13/free/deny>] [--no-auth] [--lower <hex>] [--upper <hex>] [--value <hex>] [--lcredit <dec>]"
|
"usage": "hf mfdes createvaluefile [-hav] [-n <keyno>] [-t <des/2tdea/3tdea/aes>] [-k <key>] [-f <none/an10922/gallagher>] [-i <kdfi>] [-m <plain/mac/encrypt>] [-c <native/niso/iso>] [-s <d40/ev1/ev2>] [--aid <app id hex>] [--fid <file id hex>] [--amode <plain/mac/encrypt>] [--rawrights <access rights hex>] [--rrights <key0/../key13/free/deny>] [--wrights <key0/../key13/free/deny>] [--rwrights <key0/../key13/free/deny>] [--chrights <key0/../key13/free/deny>] [--no-auth] [--lower <hex>] [--upper <hex>] [--value <hex>] [--lcredit <dec>]"
|
||||||
},
|
},
|
||||||
|
"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 <keyno> key number",
|
||||||
|
"-t, --algo <des/2tdea/3tdea/aes> crypt algo: des, 2tdea, 3tdea, aes",
|
||||||
|
"-k, --key <key> key for authenticate (hex 8(des), 16(2tdea or aes) or 24(3tdea) bytes)",
|
||||||
|
"-f, --kdf <none/an10922/gallagher> key derivation function (kdf): none, an10922, gallagher",
|
||||||
|
"-i, --kdfi <kdfi> kdf input (hex 1-31 bytes)",
|
||||||
|
"-m, --cmode <plain/mac/encrypt> communicaton mode: plain/mac/encrypt",
|
||||||
|
"-c, --ccset <native/niso/iso> communicaton command set: native/niso/iso",
|
||||||
|
"-s, --schann <d40/ev1/ev2> secure channel: d40/ev1/ev2"
|
||||||
|
],
|
||||||
|
"usage": "hf mfdes default [-h] [-n <keyno>] [-t <des/2tdea/3tdea/aes>] [-k <key>] [-f <none/an10922/gallagher>] [-i <kdfi>] [-m <plain/mac/encrypt>] [-c <native/niso/iso>] [-s <d40/ev1/ev2>]"
|
||||||
|
},
|
||||||
"hf mfdes deleteapp": {
|
"hf mfdes deleteapp": {
|
||||||
"command": "hf mfdes deleteapp",
|
"command": "hf mfdes deleteapp",
|
||||||
"description": "delete application by its 3-byte aid. master key needs to be provided.",
|
"description": "delete application by its 3-byte aid. master key needs to be provided.",
|
||||||
|
@ -4414,18 +4440,6 @@
|
||||||
],
|
],
|
||||||
"usage": "hf mfdes dump [-hav] [-n <keyno>] [-t <des/2tdea/3tdea/aes>] [-k <key>] [-f <none/an10922/gallagher>] [-i <kdfi>] [-m <plain/mac/encrypt>] [-c <native/niso/iso>] [-s <d40/ev1/ev2>] [--aid <app id hex>] [--no-auth]"
|
"usage": "hf mfdes dump [-hav] [-n <keyno>] [-t <des/2tdea/3tdea/aes>] [-k <key>] [-f <none/an10922/gallagher>] [-i <kdfi>] [-m <plain/mac/encrypt>] [-c <native/niso/iso>] [-s <d40/ev1/ev2>] [--aid <app id hex>] [--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": {
|
"hf mfdes formatpicc": {
|
||||||
"command": "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.",
|
"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": {
|
"hf mfdes help": {
|
||||||
"command": "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.",
|
"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 default -n 0 -t des -k 0000000000000000 -f none -> save to the default parameters"
|
|
||||||
],
|
|
||||||
"offline": true,
|
|
||||||
"options": [
|
|
||||||
"-h, --help this help",
|
|
||||||
"-n, --keyno <keyno> key number",
|
|
||||||
"-t, --algo <des/2tdea/3tdea/aes> crypt algo: des, 2tdea, 3tdea, aes",
|
|
||||||
"-k, --key <key> key for authenticate (hex 8(des), 16(2tdea or aes) or 24(3tdea) bytes)",
|
|
||||||
"-f, --kdf <none/an10922/gallagher> key derivation function (kdf): none, an10922, gallagher",
|
|
||||||
"-i, --kdfi <kdfi> kdf input (hex 1-31 bytes)",
|
|
||||||
"-m, --cmode <plain/mac/encrypt> communicaton mode: plain/mac/encrypt",
|
|
||||||
"-c, --ccset <native/niso/iso> communicaton command set: native/niso/iso",
|
|
||||||
"-s, --schann <d40/ev1/ev2> secure channel: d40/ev1/ev2"
|
|
||||||
],
|
|
||||||
"usage": "hf mfdes default [-h] [-n <keyno>] [-t <des/2tdea/3tdea/aes>] [-k <key>] [-f <none/an10922/gallagher>] [-i <kdfi>] [-m <plain/mac/encrypt>] [-c <native/niso/iso>] [-s <d40/ev1/ev2>]"
|
|
||||||
},
|
|
||||||
"hf mfdes info": {
|
|
||||||
"command": "hf mfdes info",
|
|
||||||
"description": "get info from mifare desfire tags",
|
|
||||||
"notes": [
|
"notes": [
|
||||||
"hf mfdes info"
|
"hf mfdes info"
|
||||||
],
|
],
|
||||||
"offline": false,
|
"offline": true,
|
||||||
"options": [
|
"options": [
|
||||||
"-h, --help this help"
|
"-h, --help this help"
|
||||||
],
|
],
|
||||||
|
@ -4720,6 +4714,32 @@
|
||||||
],
|
],
|
||||||
"usage": "hf mfdes list [-h1fcrux] [--dict <file>]..."
|
"usage": "hf mfdes list [-h1fcrux] [--dict <file>]..."
|
||||||
},
|
},
|
||||||
|
"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 <keyno> key number",
|
||||||
|
"-t, --algo <des/2tdea/3tdea/aes> crypt algo: des, 2tdea, 3tdea, aes",
|
||||||
|
"-k, --key <key> key for authenticate (hex 8(des), 16(2tdea or aes) or 24(3tdea) bytes)",
|
||||||
|
"-f, --kdf <none/an10922/gallagher> key derivation function (kdf): none, an10922, gallagher",
|
||||||
|
"-i, --kdfi <kdfi> kdf input (hex 1-31 bytes)",
|
||||||
|
"-m, --cmode <plain/mac/encrypt> communicaton mode: plain/mac/encrypt",
|
||||||
|
"-c, --ccset <native/niso/iso> communicaton command set: native/niso/iso",
|
||||||
|
"-s, --schann <d40/ev1/ev2> 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 <keyno>] [-t <des/2tdea/3tdea/aes>] [-k <key>] [-f <none/an10922/gallagher>] [-i <kdfi>] [-m <plain/mac/encrypt>] [-c <native/niso/iso>] [-s <d40/ev1/ev2>] [--no-auth] [--no-deep] [--files]"
|
||||||
|
},
|
||||||
"hf mfdes lsfiles": {
|
"hf mfdes lsfiles": {
|
||||||
"command": "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).",
|
"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",
|
"command": "hf mfdes read",
|
||||||
"description": "read data from file. key needs to be provided or flag --no-auth set (depend on file settings).",
|
"description": "read data from file. key needs to be provided or flag --no-auth set (depend on file settings).",
|
||||||
"notes": [
|
"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 -> 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,
|
"offline": false,
|
||||||
"options": [
|
"options": [
|
||||||
|
@ -4769,15 +4798,20 @@
|
||||||
"--no-auth execute without authentication",
|
"--no-auth execute without authentication",
|
||||||
"--type <auto/data/value/record/mac> file type auto/data(standard/backup)/value/record(linear/cyclic)/mac). auto - check file settings and then read. default: auto",
|
"--type <auto/data/value/record/mac> file type auto/data(standard/backup)/value/record(linear/cyclic)/mac). auto - check file settings and then read. default: auto",
|
||||||
"-o, --offset <hex> file offset (3 hex bytes, big endian). for records - record number (0 - lastest record). default 0",
|
"-o, --offset <hex> file offset (3 hex bytes, big endian). for records - record number (0 - lastest record). default 0",
|
||||||
"-l, --length <hex> length to read (3 hex bytes, big endian -> 000000 = read all data). for records - records count (0 - all). default 0."
|
"-l, --length <hex> length to read (3 hex bytes, big endian -> 000000 = read all data). for records - records count (0 - all). default 0.",
|
||||||
|
"--appisoid <isoid hex> application iso id (iso df id) (2 hex bytes, big endian). works only for iso read commands.",
|
||||||
|
"--fileisoid <isoid hex> file iso id (iso df id) (2 hex bytes, big endian). works only for iso read commands."
|
||||||
],
|
],
|
||||||
"usage": "hf mfdes read [-hav] [-n <keyno>] [-t <des/2tdea/3tdea/aes>] [-k <key>] [-f <none/an10922/gallagher>] [-i <kdfi>] [-m <plain/mac/encrypt>] [-c <native/niso/iso>] [-s <d40/ev1/ev2>] [--aid <app id hex>] [--fid <file id hex>] [--no-auth] [--type <auto/data/value/record/mac>] [-o <hex>] [-l <hex>]"
|
"usage": "hf mfdes read [-hav] [-n <keyno>] [-t <des/2tdea/3tdea/aes>] [-k <key>] [-f <none/an10922/gallagher>] [-i <kdfi>] [-m <plain/mac/encrypt>] [-c <native/niso/iso>] [-s <d40/ev1/ev2>] [--aid <app id hex>] [--fid <file id hex>] [--no-auth] [--type <auto/data/value/record/mac>] [-o <hex>] [-l <hex>] [--appisoid <isoid hex>] [--fileisoid <isoid hex>]"
|
||||||
},
|
},
|
||||||
"hf mfdes selectapp": {
|
"hf mfdes selectapp": {
|
||||||
"command": "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.",
|
"description": "select application on the card. it selects app if it is a valid one or returns an error.",
|
||||||
"notes": [
|
"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,
|
"offline": false,
|
||||||
"options": [
|
"options": [
|
||||||
|
@ -4793,16 +4827,30 @@
|
||||||
"-c, --ccset <native/niso/iso> communicaton command set: native/niso/iso",
|
"-c, --ccset <native/niso/iso> communicaton command set: native/niso/iso",
|
||||||
"-s, --schann <d40/ev1/ev2> secure channel: d40/ev1/ev2",
|
"-s, --schann <d40/ev1/ev2> secure channel: d40/ev1/ev2",
|
||||||
"--aid <app id hex> application id of application for some parameters (3 hex bytes, big endian)",
|
"--aid <app id hex> application id of application for some parameters (3 hex bytes, big endian)",
|
||||||
"--dfname <df name str> application df name (string, max 16 chars). selects application via iso select command"
|
"--dfname <df name str> application df name (string, max 16 chars). selects application via iso select command",
|
||||||
|
"--isoid <isoid hex> 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 <keyno>] [-t <des/2tdea/3tdea/aes>] [-k <key>] [-f <none/an10922/gallagher>] [-i <kdfi>] [-m <plain/mac/encrypt>] [-c <native/niso/iso>] [-s <d40/ev1/ev2>] [--aid <app id hex>] [--dfname <df name str>]"
|
"usage": "hf mfdes selectapp [-hav] [-n <keyno>] [-t <des/2tdea/3tdea/aes>] [-k <key>] [-f <none/an10922/gallagher>] [-i <kdfi>] [-m <plain/mac/encrypt>] [-c <native/niso/iso>] [-s <d40/ev1/ev2>] [--aid <app id hex>] [--dfname <df name str>] [--isoid <isoid hex>] [--mf]"
|
||||||
},
|
},
|
||||||
"hf mfdes setconfig": {
|
"hf mfdes setconfig": {
|
||||||
"command": "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.",
|
"description": "set card configuration. warning! danger zone! needs to provide card's master key and works if not blocked by config.",
|
||||||
"notes": [
|
"notes": [
|
||||||
"hf mfdes setconfig --param 03 --data 0428 -> set parameter 03",
|
"more about options mf2dlhx0.pdf. options list:",
|
||||||
"hf mfdes setconfig --param 02 --data 0875778102637264 -> set parameter 02"
|
"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,
|
"offline": false,
|
||||||
"options": [
|
"options": [
|
||||||
|
@ -4825,7 +4873,7 @@
|
||||||
},
|
},
|
||||||
"hf mfdes test": {
|
"hf mfdes test": {
|
||||||
"command": "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": [
|
"notes": [
|
||||||
"hf seos info"
|
"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 -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 -> 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 -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,
|
"offline": false,
|
||||||
"options": [
|
"options": [
|
||||||
|
@ -4899,9 +4949,11 @@
|
||||||
"-d, --data <hex> data for write (data/record file), credit/debit(value file)",
|
"-d, --data <hex> data for write (data/record file), credit/debit(value file)",
|
||||||
"--debit use for value file debit operation instead of credit",
|
"--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.",
|
"--commit commit needs for backup file only. for the other file types and in the `auto` mode - command set it automatically.",
|
||||||
"--updaterec <record number dec> record number for update record command. updates record instead of write. lastest record - 0"
|
"--updaterec <record number dec> record number for update record command. updates record instead of write. lastest record - 0",
|
||||||
|
"--appisoid <isoid hex> application iso id (iso df id) (2 hex bytes, big endian). works only for iso read commands.",
|
||||||
|
"--fileisoid <isoid hex> file iso id (iso df id) (2 hex bytes, big endian). works only for iso read commands."
|
||||||
],
|
],
|
||||||
"usage": "hf mfdes write [-hav] [-n <keyno>] [-t <des/2tdea/3tdea/aes>] [-k <key>] [-f <none/an10922/gallagher>] [-i <kdfi>] [-m <plain/mac/encrypt>] [-c <native/niso/iso>] [-s <d40/ev1/ev2>] [--aid <app id hex>] [--fid <file id hex>] [--no-auth] [--type <auto/data/value/record/mac>] [-o <hex>] [-d <hex>] [--debit] [--commit] [--updaterec <record number dec>]"
|
"usage": "hf mfdes write [-hav] [-n <keyno>] [-t <des/2tdea/3tdea/aes>] [-k <key>] [-f <none/an10922/gallagher>] [-i <kdfi>] [-m <plain/mac/encrypt>] [-c <native/niso/iso>] [-s <d40/ev1/ev2>] [--aid <app id hex>] [--fid <file id hex>] [--no-auth] [--type <auto/data/value/record/mac>] [-o <hex>] [-d <hex>] [--debit] [--commit] [--updaterec <record number dec>] [--appisoid <isoid hex>] [--fileisoid <isoid hex>]"
|
||||||
},
|
},
|
||||||
"hf mfp auth": {
|
"hf mfp auth": {
|
||||||
"command": "hf mfp auth",
|
"command": "hf mfp auth",
|
||||||
|
@ -9839,6 +9891,6 @@
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"commands_extracted": 587,
|
"commands_extracted": 587,
|
||||||
"extracted_by": "PM3Help2JSON v1.00",
|
"extracted_by": "PM3Help2JSON v1.00",
|
||||||
"extracted_on": "2021-07-31T13:44:52"
|
"extracted_on": "2021-08-06T20:40:15"
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -503,26 +503,26 @@ Check column "offline" for their availability.
|
||||||
|command |offline |description
|
|command |offline |description
|
||||||
|------- |------- |-----------
|
|------- |------- |-----------
|
||||||
|`hf mfdes help `|Y |`This help`
|
|`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 default `|N |`Set defaults for all the commands`
|
||||||
|`hf mfdes auth `|N |`MIFARE DesFire Authentication`
|
|`hf mfdes auth `|N |`MIFARE DesFire Authentication`
|
||||||
|`hf mfdes chk `|N |`[old]Check keys`
|
|`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 freemem `|N |`Get free memory size`
|
||||||
|`hf mfdes getuid `|N |`Get uid from card`
|
|
||||||
|`hf mfdes setconfig `|N |`Set card configuration`
|
|`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 list `|Y |`List DESFire (ISO 14443A) history`
|
||||||
|`hf mfdes changekey `|N |`Change Key`
|
|`hf mfdes lsapp `|N |`Show all applications with files list`
|
||||||
|`hf mfdes chkeysettings `|N |`Change Key Settings`
|
|`hf mfdes getaids `|N |`Get Application IDs list`
|
||||||
|`hf mfdes getkeysettings`|N |`Get Key Settings`
|
|`hf mfdes getappnames `|N |`Get Applications list`
|
||||||
|`hf mfdes getkeyversions`|N |`Get Key Versions`
|
|
||||||
|`hf mfdes bruteaid `|N |`Recover AIDs by bruteforce`
|
|`hf mfdes bruteaid `|N |`Recover AIDs by bruteforce`
|
||||||
|`hf mfdes createapp `|N |`Create Application`
|
|`hf mfdes createapp `|N |`Create Application`
|
||||||
|`hf mfdes deleteapp `|N |`Delete Application`
|
|`hf mfdes deleteapp `|N |`Delete Application`
|
||||||
|`hf mfdes selectapp `|N |`Select Application ID`
|
|`hf mfdes selectapp `|N |`Select Application ID`
|
||||||
|`hf mfdes getaids `|N |`Get Application IDs list`
|
|`hf mfdes changekey `|N |`Change Key`
|
||||||
|`hf mfdes getappnames `|N |`Get Applications list`
|
|`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 getfileids `|N |`Get File IDs list`
|
||||||
|`hf mfdes getfileisoids `|N |`Get File ISO IDs list`
|
|`hf mfdes getfileisoids `|N |`Get File ISO IDs list`
|
||||||
|`hf mfdes lsfiles `|N |`Show all files list`
|
|`hf mfdes lsfiles `|N |`Show all files list`
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue