1
mirror of https://github.com/flipperdevices/flipperzero-firmware.git synced 2025-12-12 12:51:22 +04:00

NFC: Support DESFire Transaction MAC file type (#4159)

* NFC: Support DESFire Transaction MAC file type

* Fix typo

---------

Co-authored-by: hedger <hedger@users.noreply.github.com>
This commit is contained in:
WillyJL
2025-03-31 17:22:16 +00:00
committed by GitHub
parent 17759a9e4b
commit 8871df863b
4 changed files with 89 additions and 13 deletions

View File

@@ -180,6 +180,9 @@ void nfc_render_mf_desfire_file_settings_data(
case MfDesfireFileTypeCyclicRecord: case MfDesfireFileTypeCyclicRecord:
type = "cyclic"; type = "cyclic";
break; break;
case MfDesfireFileTypeTransactionMac:
type = "txn-mac";
break;
default: default:
type = "unknown"; type = "unknown";
} }
@@ -237,6 +240,15 @@ void nfc_render_mf_desfire_file_settings_data(
furi_string_cat_printf(str, "size %lu\n", record_size); furi_string_cat_printf(str, "size %lu\n", record_size);
furi_string_cat_printf(str, "num %lu max %lu\n", record_count, settings->record.max); furi_string_cat_printf(str, "num %lu max %lu\n", record_count, settings->record.max);
break; break;
case MfDesfireFileTypeTransactionMac:
record_count = 0;
furi_string_cat_printf(
str,
"key opt %02X ver %02X\n",
settings->transaction_mac.key_option,
settings->transaction_mac.key_version);
furi_string_cat_printf(str, "cnt limit %lu\n", settings->transaction_mac.counter_limit);
break;
} }
bool is_auth_required = true; bool is_auth_required = true;

View File

@@ -97,6 +97,7 @@ typedef enum {
MfDesfireFileTypeValue = 2, MfDesfireFileTypeValue = 2,
MfDesfireFileTypeLinearRecord = 3, MfDesfireFileTypeLinearRecord = 3,
MfDesfireFileTypeCyclicRecord = 4, MfDesfireFileTypeCyclicRecord = 4,
MfDesfireFileTypeTransactionMac = 5,
} MfDesfireFileType; } MfDesfireFileType;
typedef enum { typedef enum {
@@ -128,6 +129,11 @@ typedef struct {
uint32_t max; uint32_t max;
uint32_t cur; uint32_t cur;
} record; } record;
struct {
uint8_t key_option;
uint8_t key_version;
uint32_t counter_limit;
} transaction_mac;
}; };
} MfDesfireFileSettings; } MfDesfireFileSettings;

View File

@@ -1,5 +1,7 @@
#include "mf_desfire_i.h" #include "mf_desfire_i.h"
#include <bit_lib/bit_lib.h>
#define TAG "MfDesfire" #define TAG "MfDesfire"
#define BITS_IN_BYTE (8U) #define BITS_IN_BYTE (8U)
@@ -47,6 +49,10 @@
#define MF_DESFIRE_FFF_FILE_MAX_KEY "Max" #define MF_DESFIRE_FFF_FILE_MAX_KEY "Max"
#define MF_DESFIRE_FFF_FILE_CUR_KEY "Cur" #define MF_DESFIRE_FFF_FILE_CUR_KEY "Cur"
#define MF_DESFIRE_FFF_FILE_KEY_OPTION_KEY "Key Option"
#define MF_DESFIRE_FFF_FILE_KEY_VERSION_KEY "Key Version"
#define MF_DESFIRE_FFF_FILE_COUNTER_LIMIT_KEY "Counter Limit"
bool mf_desfire_version_parse(MfDesfireVersion* data, const BitBuffer* buf) { bool mf_desfire_version_parse(MfDesfireVersion* data, const BitBuffer* buf) {
const bool can_parse = bit_buffer_get_size_bytes(buf) == sizeof(MfDesfireVersion); const bool can_parse = bit_buffer_get_size_bytes(buf) == sizeof(MfDesfireVersion);
@@ -168,12 +174,19 @@ bool mf_desfire_file_settings_parse(MfDesfireFileSettings* data, const BitBuffer
uint32_t cur : 3 * BITS_IN_BYTE; uint32_t cur : 3 * BITS_IN_BYTE;
} MfDesfireFileSettingsRecord; } MfDesfireFileSettingsRecord;
typedef struct FURI_PACKED {
uint8_t key_option;
uint8_t key_version;
uint8_t counter_limit[];
} MfDesfireFileSettingsTransactionMac;
typedef struct FURI_PACKED { typedef struct FURI_PACKED {
MfDesfireFileSettingsHeader header; MfDesfireFileSettingsHeader header;
union { union {
MfDesfireFileSettingsData data; MfDesfireFileSettingsData data;
MfDesfireFileSettingsValue value; MfDesfireFileSettingsValue value;
MfDesfireFileSettingsRecord record; MfDesfireFileSettingsRecord record;
MfDesfireFileSettingsTransactionMac transaction_mac;
}; };
} MfDesfireFileSettingsLayout; } MfDesfireFileSettingsLayout;
@@ -182,7 +195,7 @@ bool mf_desfire_file_settings_parse(MfDesfireFileSettings* data, const BitBuffer
const size_t data_size = bit_buffer_get_size_bytes(buf); const size_t data_size = bit_buffer_get_size_bytes(buf);
const uint8_t* data_ptr = bit_buffer_get_data(buf); const uint8_t* data_ptr = bit_buffer_get_data(buf);
const size_t min_data_size = const size_t min_data_size =
sizeof(MfDesfireFileSettingsHeader) + sizeof(MfDesfireFileSettingsData); sizeof(MfDesfireFileSettingsHeader) + sizeof(MfDesfireFileSettingsTransactionMac);
if(data_size < min_data_size) { if(data_size < min_data_size) {
FURI_LOG_E( FURI_LOG_E(
@@ -202,17 +215,11 @@ bool mf_desfire_file_settings_parse(MfDesfireFileSettings* data, const BitBuffer
if(file_settings_temp.type == MfDesfireFileTypeStandard || if(file_settings_temp.type == MfDesfireFileTypeStandard ||
file_settings_temp.type == MfDesfireFileTypeBackup) { file_settings_temp.type == MfDesfireFileTypeBackup) {
memcpy( memcpy(&layout.data, &data_ptr[bytes_processed], sizeof(MfDesfireFileSettingsData));
&layout.data,
&data_ptr[sizeof(MfDesfireFileSettingsHeader)],
sizeof(MfDesfireFileSettingsData));
file_settings_temp.data.size = layout.data.size; file_settings_temp.data.size = layout.data.size;
bytes_processed += sizeof(MfDesfireFileSettingsData); bytes_processed += sizeof(MfDesfireFileSettingsData);
} else if(file_settings_temp.type == MfDesfireFileTypeValue) { } else if(file_settings_temp.type == MfDesfireFileTypeValue) {
memcpy( memcpy(&layout.value, &data_ptr[bytes_processed], sizeof(MfDesfireFileSettingsValue));
&layout.value,
&data_ptr[sizeof(MfDesfireFileSettingsHeader)],
sizeof(MfDesfireFileSettingsValue));
file_settings_temp.value.lo_limit = layout.value.lo_limit; file_settings_temp.value.lo_limit = layout.value.lo_limit;
file_settings_temp.value.hi_limit = layout.value.hi_limit; file_settings_temp.value.hi_limit = layout.value.hi_limit;
file_settings_temp.value.limited_credit_value = layout.value.limited_credit_value; file_settings_temp.value.limited_credit_value = layout.value.limited_credit_value;
@@ -222,13 +229,34 @@ bool mf_desfire_file_settings_parse(MfDesfireFileSettings* data, const BitBuffer
file_settings_temp.type == MfDesfireFileTypeLinearRecord || file_settings_temp.type == MfDesfireFileTypeLinearRecord ||
file_settings_temp.type == MfDesfireFileTypeCyclicRecord) { file_settings_temp.type == MfDesfireFileTypeCyclicRecord) {
memcpy( memcpy(
&layout.record, &layout.record, &data_ptr[bytes_processed], sizeof(MfDesfireFileSettingsRecord));
&data_ptr[sizeof(MfDesfireFileSettingsHeader)],
sizeof(MfDesfireFileSettingsRecord));
file_settings_temp.record.size = layout.record.size; file_settings_temp.record.size = layout.record.size;
file_settings_temp.record.max = layout.record.max; file_settings_temp.record.max = layout.record.max;
file_settings_temp.record.cur = layout.record.cur; file_settings_temp.record.cur = layout.record.cur;
bytes_processed += sizeof(MfDesfireFileSettingsRecord); bytes_processed += sizeof(MfDesfireFileSettingsRecord);
} else if(file_settings_temp.type == MfDesfireFileTypeTransactionMac) {
const bool has_counter_limit = (layout.header.comm & 0x20) != 0;
memcpy(
&layout.transaction_mac,
&data_ptr[bytes_processed],
sizeof(MfDesfireFileSettingsTransactionMac));
file_settings_temp.transaction_mac.key_option = layout.transaction_mac.key_option;
file_settings_temp.transaction_mac.key_version = layout.transaction_mac.key_version;
if(!has_counter_limit) {
file_settings_temp.transaction_mac.counter_limit = 0;
} else {
// AES (4b) or LRP (2b)
const size_t counter_limit_size = (layout.transaction_mac.key_option & 0x02) ? 4 :
2;
memcpy(
&layout.transaction_mac,
&data_ptr[bytes_processed],
sizeof(MfDesfireFileSettingsTransactionMac) + counter_limit_size);
file_settings_temp.transaction_mac.counter_limit = bit_lib_bytes_to_num_be(
layout.transaction_mac.counter_limit, counter_limit_size);
bytes_processed += counter_limit_size;
}
bytes_processed += sizeof(MfDesfireFileSettingsTransactionMac);
} else { } else {
FURI_LOG_W(TAG, "Unknown file type: %02x", file_settings_temp.type); FURI_LOG_W(TAG, "Unknown file type: %02x", file_settings_temp.type);
break; break;
@@ -468,6 +496,21 @@ bool mf_desfire_file_settings_load(
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_CUR_KEY); furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_CUR_KEY);
if(!flipper_format_read_uint32(ff, furi_string_get_cstr(key), &data->record.cur, 1)) if(!flipper_format_read_uint32(ff, furi_string_get_cstr(key), &data->record.cur, 1))
break; break;
} else if(data->type == MfDesfireFileTypeTransactionMac) {
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_KEY_OPTION_KEY);
if(!flipper_format_read_hex(
ff, furi_string_get_cstr(key), &data->transaction_mac.key_option, 1))
break;
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_KEY_VERSION_KEY);
if(!flipper_format_read_hex(
ff, furi_string_get_cstr(key), &data->transaction_mac.key_version, 1))
break;
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_COUNTER_LIMIT_KEY);
if(!flipper_format_read_uint32(
ff, furi_string_get_cstr(key), &data->transaction_mac.counter_limit, 1))
break;
} }
success = true; success = true;
@@ -716,6 +759,21 @@ bool mf_desfire_file_settings_save(
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_CUR_KEY); furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_CUR_KEY);
if(!flipper_format_write_uint32(ff, furi_string_get_cstr(key), &data->record.cur, 1)) if(!flipper_format_write_uint32(ff, furi_string_get_cstr(key), &data->record.cur, 1))
break; break;
} else if(data->type == MfDesfireFileTypeTransactionMac) {
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_KEY_OPTION_KEY);
if(!flipper_format_write_hex(
ff, furi_string_get_cstr(key), &data->transaction_mac.key_option, 1))
break;
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_KEY_VERSION_KEY);
if(!flipper_format_write_hex(
ff, furi_string_get_cstr(key), &data->transaction_mac.key_version, 1))
break;
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_COUNTER_LIMIT_KEY);
if(!flipper_format_write_uint32(
ff, furi_string_get_cstr(key), &data->transaction_mac.counter_limit, 1))
break;
} }
success = true; success = true;

View File

@@ -468,7 +468,7 @@ MfDesfireError mf_desfire_poller_read_file_data_multi(
file_type == MfDesfireFileTypeLinearRecord || file_type == MfDesfireFileTypeLinearRecord ||
file_type == MfDesfireFileTypeCyclicRecord) { file_type == MfDesfireFileTypeCyclicRecord) {
error = mf_desfire_poller_read_file_records( error = mf_desfire_poller_read_file_records(
instance, file_id, 0, file_settings_cur->data.size, file_data); instance, file_id, 0, file_settings_cur->record.size, file_data);
} }
} }