NFC refactoring (#3050)

"A long time ago in a galaxy far, far away...." we started NFC subsystem refactoring.

Starring:

- @gornekich - NFC refactoring project lead, architect, senior developer
- @gsurkov - architect, senior developer
- @RebornedBrain - senior developer

Supporting roles:

- @skotopes, @DrZlo13, @hedger - general architecture advisors, code review
- @Astrrra, @doomwastaken, @Hellitron, @ImagineVagon333 - quality assurance

Special thanks:

@bettse, @pcunning, @nxv, @noproto, @AloneLiberty and everyone else who has been helping us all this time and contributing valuable knowledges, ideas and source code.
This commit is contained in:
gornekich
2023-10-24 07:08:09 +04:00
committed by GitHub
parent 35c903494c
commit d92b0a82cc
514 changed files with 41488 additions and 68125 deletions

View File

@@ -0,0 +1,290 @@
#include "mf_desfire_i.h"
#include <furi.h>
#define MF_DESFIRE_PROTOCOL_NAME "Mifare DESFire"
const NfcDeviceBase nfc_device_mf_desfire = {
.protocol_name = MF_DESFIRE_PROTOCOL_NAME,
.alloc = (NfcDeviceAlloc)mf_desfire_alloc,
.free = (NfcDeviceFree)mf_desfire_free,
.reset = (NfcDeviceReset)mf_desfire_reset,
.copy = (NfcDeviceCopy)mf_desfire_copy,
.verify = (NfcDeviceVerify)mf_desfire_verify,
.load = (NfcDeviceLoad)mf_desfire_load,
.save = (NfcDeviceSave)mf_desfire_save,
.is_equal = (NfcDeviceEqual)mf_desfire_is_equal,
.get_name = (NfcDeviceGetName)mf_desfire_get_device_name,
.get_uid = (NfcDeviceGetUid)mf_desfire_get_uid,
.set_uid = (NfcDeviceSetUid)mf_desfire_set_uid,
.get_base_data = (NfcDeviceGetBaseData)mf_desfire_get_base_data,
};
MfDesfireData* mf_desfire_alloc() {
MfDesfireData* data = malloc(sizeof(MfDesfireData));
data->iso14443_4a_data = iso14443_4a_alloc();
data->master_key_versions = simple_array_alloc(&mf_desfire_key_version_array_config);
data->application_ids = simple_array_alloc(&mf_desfire_app_id_array_config);
data->applications = simple_array_alloc(&mf_desfire_application_array_config);
return data;
}
void mf_desfire_free(MfDesfireData* data) {
furi_assert(data);
mf_desfire_reset(data);
simple_array_free(data->applications);
simple_array_free(data->application_ids);
simple_array_free(data->master_key_versions);
iso14443_4a_free(data->iso14443_4a_data);
free(data);
}
void mf_desfire_reset(MfDesfireData* data) {
furi_assert(data);
iso14443_4a_reset(data->iso14443_4a_data);
memset(&data->version, 0, sizeof(MfDesfireVersion));
memset(&data->free_memory, 0, sizeof(MfDesfireFreeMemory));
simple_array_reset(data->master_key_versions);
simple_array_reset(data->application_ids);
simple_array_reset(data->applications);
}
void mf_desfire_copy(MfDesfireData* data, const MfDesfireData* other) {
furi_assert(data);
furi_assert(other);
mf_desfire_reset(data);
iso14443_4a_copy(data->iso14443_4a_data, other->iso14443_4a_data);
data->version = other->version;
data->free_memory = other->free_memory;
data->master_key_settings = other->master_key_settings;
simple_array_copy(data->master_key_versions, other->master_key_versions);
simple_array_copy(data->application_ids, other->application_ids);
simple_array_copy(data->applications, other->applications);
}
bool mf_desfire_verify(MfDesfireData* data, const FuriString* device_type) {
UNUSED(data);
return furi_string_equal_str(device_type, MF_DESFIRE_PROTOCOL_NAME);
}
bool mf_desfire_load(MfDesfireData* data, FlipperFormat* ff, uint32_t version) {
furi_assert(data);
FuriString* prefix = furi_string_alloc();
bool success = false;
do {
if(!iso14443_4a_load(data->iso14443_4a_data, ff, version)) break;
if(!mf_desfire_version_load(&data->version, ff)) break;
if(!mf_desfire_free_memory_load(&data->free_memory, ff)) break;
if(!mf_desfire_key_settings_load(
&data->master_key_settings, MF_DESFIRE_FFF_PICC_PREFIX, ff))
break;
const uint32_t master_key_version_count = data->master_key_settings.max_keys;
simple_array_init(data->master_key_versions, master_key_version_count);
uint32_t i;
for(i = 0; i < master_key_version_count; ++i) {
if(!mf_desfire_key_version_load(
simple_array_get(data->master_key_versions, i),
MF_DESFIRE_FFF_PICC_PREFIX,
i,
ff))
break;
}
if(i != master_key_version_count) break;
uint32_t application_count;
if(!mf_desfire_application_count_load(&application_count, ff)) break;
if(application_count > 0) {
simple_array_init(data->application_ids, application_count);
if(!mf_desfire_application_ids_load(
simple_array_get_data(data->application_ids), application_count, ff))
break;
simple_array_init(data->applications, application_count);
for(i = 0; i < application_count; ++i) {
const MfDesfireApplicationId* app_id = simple_array_cget(data->application_ids, i);
furi_string_printf(
prefix,
"%s %02x%02x%02x",
MF_DESFIRE_FFF_APP_PREFIX,
app_id->data[0],
app_id->data[1],
app_id->data[2]);
if(!mf_desfire_application_load(
simple_array_get(data->applications, i), furi_string_get_cstr(prefix), ff))
break;
}
if(i != application_count) break;
}
success = true;
} while(false);
furi_string_free(prefix);
return success;
}
bool mf_desfire_save(const MfDesfireData* data, FlipperFormat* ff) {
furi_assert(data);
FuriString* prefix = furi_string_alloc();
bool success = false;
do {
if(!iso14443_4a_save(data->iso14443_4a_data, ff)) break;
if(!flipper_format_write_comment_cstr(ff, MF_DESFIRE_PROTOCOL_NAME " specific data"))
break;
if(!mf_desfire_version_save(&data->version, ff)) break;
if(!mf_desfire_free_memory_save(&data->free_memory, ff)) break;
if(!mf_desfire_key_settings_save(
&data->master_key_settings, MF_DESFIRE_FFF_PICC_PREFIX, ff))
break;
const uint32_t master_key_version_count =
simple_array_get_count(data->master_key_versions);
uint32_t i;
for(i = 0; i < master_key_version_count; ++i) {
if(!mf_desfire_key_version_save(
simple_array_cget(data->master_key_versions, i),
MF_DESFIRE_FFF_PICC_PREFIX,
i,
ff))
break;
}
if(i != master_key_version_count) break;
const uint32_t application_count = simple_array_get_count(data->application_ids);
if(!mf_desfire_application_count_save(&application_count, ff)) break;
if(application_count > 0) {
if(!mf_desfire_application_ids_save(
simple_array_cget_data(data->application_ids), application_count, ff))
break;
for(i = 0; i < application_count; ++i) {
const MfDesfireApplicationId* app_id = simple_array_cget(data->application_ids, i);
furi_string_printf(
prefix,
"%s %02x%02x%02x",
MF_DESFIRE_FFF_APP_PREFIX,
app_id->data[0],
app_id->data[1],
app_id->data[2]);
const MfDesfireApplication* app = simple_array_cget(data->applications, i);
if(!mf_desfire_application_save(app, furi_string_get_cstr(prefix), ff)) break;
}
if(i != application_count) break;
}
success = true;
} while(false);
furi_string_free(prefix);
return success;
}
bool mf_desfire_is_equal(const MfDesfireData* data, const MfDesfireData* other) {
furi_assert(data);
furi_assert(other);
return iso14443_4a_is_equal(data->iso14443_4a_data, other->iso14443_4a_data) &&
memcmp(&data->version, &other->version, sizeof(MfDesfireVersion)) == 0 &&
memcmp(&data->free_memory, &other->free_memory, sizeof(MfDesfireFreeMemory)) == 0 &&
memcmp(
&data->master_key_settings,
&other->master_key_settings,
sizeof(MfDesfireKeySettings)) == 0 &&
simple_array_is_equal(data->master_key_versions, other->master_key_versions) &&
simple_array_is_equal(data->application_ids, other->application_ids) &&
simple_array_is_equal(data->applications, other->applications);
}
const char* mf_desfire_get_device_name(const MfDesfireData* data, NfcDeviceNameType name_type) {
UNUSED(data);
UNUSED(name_type);
return MF_DESFIRE_PROTOCOL_NAME;
}
const uint8_t* mf_desfire_get_uid(const MfDesfireData* data, size_t* uid_len) {
furi_assert(data);
return iso14443_4a_get_uid(data->iso14443_4a_data, uid_len);
}
bool mf_desfire_set_uid(MfDesfireData* data, const uint8_t* uid, size_t uid_len) {
furi_assert(data);
return iso14443_4a_set_uid(data->iso14443_4a_data, uid, uid_len);
}
Iso14443_4aData* mf_desfire_get_base_data(const MfDesfireData* data) {
furi_assert(data);
return data->iso14443_4a_data;
}
const MfDesfireApplication*
mf_desfire_get_application(const MfDesfireData* data, const MfDesfireApplicationId* app_id) {
MfDesfireApplication* app = NULL;
for(uint32_t i = 0; i < simple_array_get_count(data->application_ids); ++i) {
const MfDesfireApplicationId* current_app_id = simple_array_cget(data->application_ids, i);
if(memcmp(app_id, current_app_id, sizeof(MfDesfireApplicationId)) == 0) {
app = simple_array_get(data->applications, i);
}
}
return app;
}
const MfDesfireFileSettings*
mf_desfire_get_file_settings(const MfDesfireApplication* data, const MfDesfireFileId* file_id) {
MfDesfireFileSettings* file_settings = NULL;
for(uint32_t i = 0; i < simple_array_get_count(data->file_ids); ++i) {
const MfDesfireFileId* current_file_id = simple_array_cget(data->file_ids, i);
if(*file_id == *current_file_id) {
file_settings = simple_array_get(data->file_settings, i);
}
}
return file_settings;
}
const MfDesfireFileData*
mf_desfire_get_file_data(const MfDesfireApplication* data, const MfDesfireFileId* file_id) {
MfDesfireFileData* file_data = NULL;
for(uint32_t i = 0; i < simple_array_get_count(data->file_ids); ++i) {
const MfDesfireFileId* current_file_id = simple_array_cget(data->file_ids, i);
if(*file_id == *current_file_id) {
file_data = simple_array_get(data->file_data, i);
}
}
return file_data;
}

View File

@@ -0,0 +1,191 @@
#pragma once
#include <lib/nfc/protocols/iso14443_4a/iso14443_4a.h>
#include <lib/toolbox/simple_array.h>
#ifdef __cplusplus
extern "C" {
#endif
#define MF_DESFIRE_CMD_GET_VERSION (0x60)
#define MF_DESFIRE_CMD_GET_FREE_MEMORY (0x6E)
#define MF_DESFIRE_CMD_GET_KEY_SETTINGS (0x45)
#define MF_DESFIRE_CMD_GET_KEY_VERSION (0x64)
#define MF_DESFIRE_CMD_GET_APPLICATION_IDS (0x6A)
#define MF_DESFIRE_CMD_SELECT_APPLICATION (0x5A)
#define MF_DESFIRE_CMD_GET_FILE_IDS (0x6F)
#define MF_DESFIRE_CMD_GET_FILE_SETTINGS (0xF5)
#define MF_DESFIRE_CMD_READ_DATA (0xBD)
#define MF_DESFIRE_CMD_GET_VALUE (0x6C)
#define MF_DESFIRE_CMD_READ_RECORDS (0xBB)
#define MF_DESFIRE_FLAG_HAS_NEXT (0xAF)
#define MF_DESFIRE_MAX_KEYS (14)
#define MF_DESFIRE_MAX_FILES (32)
#define MF_DESFIRE_UID_SIZE (7)
#define MF_DESFIRE_BATCH_SIZE (5)
#define MF_DESFIRE_APP_ID_SIZE (3)
typedef struct {
uint8_t hw_vendor;
uint8_t hw_type;
uint8_t hw_subtype;
uint8_t hw_major;
uint8_t hw_minor;
uint8_t hw_storage;
uint8_t hw_proto;
uint8_t sw_vendor;
uint8_t sw_type;
uint8_t sw_subtype;
uint8_t sw_major;
uint8_t sw_minor;
uint8_t sw_storage;
uint8_t sw_proto;
uint8_t uid[MF_DESFIRE_UID_SIZE];
uint8_t batch[MF_DESFIRE_BATCH_SIZE];
uint8_t prod_week;
uint8_t prod_year;
} MfDesfireVersion;
typedef struct {
uint32_t bytes_free;
bool is_present;
} MfDesfireFreeMemory; // EV1+ only
typedef struct {
bool is_master_key_changeable;
bool is_free_directory_list;
bool is_free_create_delete;
bool is_config_changeable;
uint8_t change_key_id;
uint8_t max_keys;
uint8_t flags;
} MfDesfireKeySettings;
typedef uint8_t MfDesfireKeyVersion;
typedef struct {
MfDesfireKeySettings key_settings;
SimpleArray* key_versions;
} MfDesfireKeyConfiguration;
typedef enum {
MfDesfireFileTypeStandard = 0,
MfDesfireFileTypeBackup = 1,
MfDesfireFileTypeValue = 2,
MfDesfireFileTypeLinearRecord = 3,
MfDesfireFileTypeCyclicRecord = 4,
} MfDesfireFileType;
typedef enum {
MfDesfireFileCommunicationSettingsPlaintext = 0,
MfDesfireFileCommunicationSettingsAuthenticated = 1,
MfDesfireFileCommunicationSettingsEnciphered = 3,
} MfDesfireFileCommunicationSettings;
typedef uint8_t MfDesfireFileId;
typedef uint16_t MfDesfireFileAccessRights;
typedef struct {
MfDesfireFileType type;
MfDesfireFileCommunicationSettings comm;
MfDesfireFileAccessRights access_rights;
union {
struct {
uint32_t size;
} data;
struct {
uint32_t lo_limit;
uint32_t hi_limit;
uint32_t limited_credit_value;
bool limited_credit_enabled;
} value;
struct {
uint32_t size;
uint32_t max;
uint32_t cur;
} record;
};
} MfDesfireFileSettings;
typedef struct {
SimpleArray* data;
} MfDesfireFileData;
typedef struct {
uint8_t data[MF_DESFIRE_APP_ID_SIZE];
} MfDesfireApplicationId;
typedef struct MfDesfireApplication {
MfDesfireKeySettings key_settings;
SimpleArray* key_versions;
SimpleArray* file_ids;
SimpleArray* file_settings;
SimpleArray* file_data;
} MfDesfireApplication;
typedef enum {
MfDesfireErrorNone,
MfDesfireErrorNotPresent,
MfDesfireErrorProtocol,
MfDesfireErrorTimeout,
} MfDesfireError;
typedef struct {
Iso14443_4aData* iso14443_4a_data;
MfDesfireVersion version;
MfDesfireFreeMemory free_memory;
MfDesfireKeySettings master_key_settings;
SimpleArray* master_key_versions;
SimpleArray* application_ids;
SimpleArray* applications;
} MfDesfireData;
extern const NfcDeviceBase nfc_device_mf_desfire;
// Virtual methods
MfDesfireData* mf_desfire_alloc();
void mf_desfire_free(MfDesfireData* data);
void mf_desfire_reset(MfDesfireData* data);
void mf_desfire_copy(MfDesfireData* data, const MfDesfireData* other);
bool mf_desfire_verify(MfDesfireData* data, const FuriString* device_type);
bool mf_desfire_load(MfDesfireData* data, FlipperFormat* ff, uint32_t version);
bool mf_desfire_save(const MfDesfireData* data, FlipperFormat* ff);
bool mf_desfire_is_equal(const MfDesfireData* data, const MfDesfireData* other);
const char* mf_desfire_get_device_name(const MfDesfireData* data, NfcDeviceNameType name_type);
const uint8_t* mf_desfire_get_uid(const MfDesfireData* data, size_t* uid_len);
bool mf_desfire_set_uid(MfDesfireData* data, const uint8_t* uid, size_t uid_len);
Iso14443_4aData* mf_desfire_get_base_data(const MfDesfireData* data);
// Getters and tests
const MfDesfireApplication*
mf_desfire_get_application(const MfDesfireData* data, const MfDesfireApplicationId* app_id);
const MfDesfireFileSettings*
mf_desfire_get_file_settings(const MfDesfireApplication* data, const MfDesfireFileId* file_id);
const MfDesfireFileData*
mf_desfire_get_file_data(const MfDesfireApplication* data, const MfDesfireFileId* file_id);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,790 @@
#include "mf_desfire_i.h"
#define BITS_IN_BYTE (8U)
#define MF_DESFIRE_FFF_VERSION_KEY \
MF_DESFIRE_FFF_PICC_PREFIX " " \
"Version"
#define MF_DESFIRE_FFF_FREE_MEM_KEY \
MF_DESFIRE_FFF_PICC_PREFIX " " \
"Free Memory"
#define MF_DESFIRE_FFF_CHANGE_KEY_ID_KEY "Change Key ID"
#define MF_DESFIRE_FFF_CONFIG_CHANGEABLE_KEY "Config Changeable"
#define MF_DESFIRE_FFF_FREE_CREATE_DELETE_KEY "Free Create Delete"
#define MF_DESFIRE_FFF_FREE_DIR_LIST_KEY "Free Directory List"
#define MF_DESFIRE_FFF_KEY_CHANGEABLE_KEY "Key Changeable"
#define MF_DESFIRE_FFF_FLAGS_KEY "Flags"
#define MF_DESFIRE_FFF_MAX_KEYS_KEY "Max Keys"
#define MF_DESFIRE_FFF_KEY_SUB_PREFIX "Key"
#define MF_DESFIRE_FFF_KEY_VERSION_KEY "Version"
#define MF_DESFIRE_FFF_APPLICATION_COUNT_KEY \
MF_DESFIRE_FFF_APP_PREFIX " " \
"Count"
#define MF_DESFIRE_FFF_APPLICATION_IDS_KEY \
MF_DESFIRE_FFF_APP_PREFIX " " \
"IDs"
#define MF_DESFIRE_FFF_FILE_SUB_PREFIX "File"
#define MF_DESFIRE_FFF_FILE_IDS_KEY \
MF_DESFIRE_FFF_FILE_SUB_PREFIX " " \
"IDs"
#define MF_DESFIRE_FFF_FILE_TYPE_KEY "Type"
#define MF_DESFIRE_FFF_FILE_COMM_SETTINGS_KEY "Communication Settings"
#define MF_DESFIRE_FFF_FILE_ACCESS_RIGHTS_KEY "Access Rights"
#define MF_DESFIRE_FFF_FILE_SIZE_KEY "Size"
#define MF_DESFIRE_FFF_FILE_HI_LIMIT_KEY "Hi Limit"
#define MF_DESFIRE_FFF_FILE_LO_LIMIT_KEY "Lo Limit"
#define MF_DESFIRE_FFF_FILE_LIMIT_CREDIT_VALUE_KEY "Limited Credit Value"
#define MF_DESFIRE_FFF_FILE_LIMIT_CREDIT_ENABLED_KEY "Limited Credit Enabled"
#define MF_DESFIRE_FFF_FILE_MAX_KEY "Max"
#define MF_DESFIRE_FFF_FILE_CUR_KEY "Cur"
bool mf_desfire_version_parse(MfDesfireVersion* data, const BitBuffer* buf) {
const bool can_parse = bit_buffer_get_size_bytes(buf) == sizeof(MfDesfireVersion);
if(can_parse) {
bit_buffer_write_bytes(buf, data, sizeof(MfDesfireVersion));
}
return can_parse;
}
bool mf_desfire_free_memory_parse(MfDesfireFreeMemory* data, const BitBuffer* buf) {
typedef struct __attribute__((packed)) {
uint32_t bytes_free : 3 * BITS_IN_BYTE;
} MfDesfireFreeMemoryLayout;
const bool can_parse = bit_buffer_get_size_bytes(buf) == sizeof(MfDesfireFreeMemoryLayout);
if(can_parse) {
MfDesfireFreeMemoryLayout layout;
bit_buffer_write_bytes(buf, &layout, sizeof(MfDesfireFreeMemoryLayout));
data->bytes_free = layout.bytes_free;
}
data->is_present = can_parse;
return can_parse;
}
bool mf_desfire_key_settings_parse(MfDesfireKeySettings* data, const BitBuffer* buf) {
typedef struct __attribute__((packed)) {
bool is_master_key_changeable : 1;
bool is_free_directory_list : 1;
bool is_free_create_delete : 1;
bool is_config_changeable : 1;
uint8_t change_key_id : 4;
uint8_t max_keys : 4;
uint8_t flags : 4;
} MfDesfireKeySettingsLayout;
const bool can_parse = bit_buffer_get_size_bytes(buf) == sizeof(MfDesfireKeySettingsLayout);
if(can_parse) {
MfDesfireKeySettingsLayout layout;
bit_buffer_write_bytes(buf, &layout, sizeof(MfDesfireKeySettingsLayout));
data->is_master_key_changeable = layout.is_master_key_changeable;
data->is_free_directory_list = layout.is_free_directory_list;
data->is_free_create_delete = layout.is_free_create_delete;
data->is_config_changeable = layout.is_config_changeable;
data->change_key_id = layout.change_key_id;
data->max_keys = layout.max_keys;
data->flags = layout.flags;
}
return can_parse;
}
bool mf_desfire_key_version_parse(MfDesfireKeyVersion* data, const BitBuffer* buf) {
const bool can_parse = bit_buffer_get_size_bytes(buf) == sizeof(MfDesfireKeyVersion);
if(can_parse) {
bit_buffer_write_bytes(buf, data, sizeof(MfDesfireKeyVersion));
}
return can_parse;
}
bool mf_desfire_application_id_parse(
MfDesfireApplicationId* data,
uint32_t index,
const BitBuffer* buf) {
const bool can_parse =
bit_buffer_get_size_bytes(buf) >=
(index * sizeof(MfDesfireApplicationId) + sizeof(MfDesfireApplicationId));
if(can_parse) {
bit_buffer_write_bytes_mid(
buf, data, index * sizeof(MfDesfireApplicationId), sizeof(MfDesfireApplicationId));
}
return can_parse;
}
bool mf_desfire_file_id_parse(MfDesfireFileId* data, uint32_t index, const BitBuffer* buf) {
const bool can_parse = bit_buffer_get_size_bytes(buf) >=
(index * sizeof(MfDesfireFileId) + sizeof(MfDesfireFileId));
if(can_parse) {
bit_buffer_write_bytes_mid(
buf, data, index * sizeof(MfDesfireFileId), sizeof(MfDesfireFileId));
}
return can_parse;
}
bool mf_desfire_file_settings_parse(MfDesfireFileSettings* data, const BitBuffer* buf) {
bool parsed = false;
typedef struct __attribute__((packed)) {
uint8_t type;
uint8_t comm;
uint16_t access_rights;
} MfDesfireFileSettingsHeader;
typedef struct __attribute__((packed)) {
uint32_t size : 3 * BITS_IN_BYTE;
} MfDesfireFileSettingsData;
typedef struct __attribute__((packed)) {
uint32_t lo_limit;
uint32_t hi_limit;
uint32_t limited_credit_value;
uint8_t limited_credit_enabled;
} MfDesfireFileSettingsValue;
typedef struct __attribute__((packed)) {
uint32_t size : 3 * BITS_IN_BYTE;
uint32_t max : 3 * BITS_IN_BYTE;
uint32_t cur : 3 * BITS_IN_BYTE;
} MfDesfireFileSettingsRecord;
typedef struct __attribute__((packed)) {
MfDesfireFileSettingsHeader header;
union {
MfDesfireFileSettingsData data;
MfDesfireFileSettingsValue value;
MfDesfireFileSettingsRecord record;
};
} MfDesfireFileSettingsLayout;
do {
const size_t data_size = bit_buffer_get_size_bytes(buf);
const size_t min_data_size =
sizeof(MfDesfireFileSettingsHeader) + sizeof(MfDesfireFileSettingsData);
if(data_size < min_data_size) break;
MfDesfireFileSettingsLayout layout;
bit_buffer_write_bytes(buf, &layout, sizeof(MfDesfireFileSettingsLayout));
data->type = layout.header.type;
data->comm = layout.header.comm;
data->access_rights = layout.header.access_rights;
if(data->type == MfDesfireFileTypeStandard || data->type == MfDesfireFileTypeBackup) {
if(data_size != min_data_size) break;
data->data.size = layout.data.size;
} else if(data->type == MfDesfireFileTypeValue) {
if(data_size !=
sizeof(MfDesfireFileSettingsHeader) + sizeof(MfDesfireFileSettingsValue))
break;
data->value.lo_limit = layout.value.lo_limit;
data->value.hi_limit = layout.value.hi_limit;
data->value.limited_credit_value = layout.value.hi_limit;
data->value.limited_credit_enabled = layout.value.limited_credit_enabled;
} else if(
data->type == MfDesfireFileTypeLinearRecord ||
data->type == MfDesfireFileTypeCyclicRecord) {
if(data_size !=
sizeof(MfDesfireFileSettingsHeader) + sizeof(MfDesfireFileSettingsRecord))
break;
data->record.size = layout.record.size;
data->record.max = layout.record.max;
data->record.cur = layout.record.cur;
} else {
break;
}
parsed = true;
} while(false);
return parsed;
}
bool mf_desfire_file_data_parse(MfDesfireFileData* data, const BitBuffer* buf) {
const size_t data_size = bit_buffer_get_size_bytes(buf);
if(data_size > 0) {
simple_array_init(data->data, data_size);
bit_buffer_write_bytes(buf, simple_array_get_data(data->data), data_size);
}
// Success no matter whether there is data or not
return true;
}
void mf_desfire_file_data_init(MfDesfireFileData* data) {
data->data = simple_array_alloc(&simple_array_config_uint8_t);
}
void mf_desfire_application_init(MfDesfireApplication* data) {
data->key_versions = simple_array_alloc(&mf_desfire_key_version_array_config);
data->file_ids = simple_array_alloc(&mf_desfire_file_id_array_config);
data->file_settings = simple_array_alloc(&mf_desfire_file_settings_array_config);
data->file_data = simple_array_alloc(&mf_desfire_file_data_array_config);
}
void mf_desfire_file_data_reset(MfDesfireFileData* data) {
simple_array_free(data->data);
memset(data, 0, sizeof(MfDesfireFileData));
}
void mf_desfire_application_reset(MfDesfireApplication* data) {
simple_array_free(data->key_versions);
simple_array_free(data->file_ids);
simple_array_free(data->file_settings);
simple_array_free(data->file_data);
memset(data, 0, sizeof(MfDesfireApplication));
}
void mf_desfire_file_data_copy(MfDesfireFileData* data, const MfDesfireFileData* other) {
simple_array_copy(data->data, other->data);
}
void mf_desfire_application_copy(MfDesfireApplication* data, const MfDesfireApplication* other) {
data->key_settings = other->key_settings;
simple_array_copy(data->key_versions, other->key_versions);
simple_array_copy(data->file_ids, other->file_ids);
simple_array_copy(data->file_settings, other->file_settings);
simple_array_copy(data->file_data, other->file_data);
}
bool mf_desfire_version_load(MfDesfireVersion* data, FlipperFormat* ff) {
return flipper_format_read_hex(
ff, MF_DESFIRE_FFF_VERSION_KEY, (uint8_t*)data, sizeof(MfDesfireVersion));
}
bool mf_desfire_free_memory_load(MfDesfireFreeMemory* data, FlipperFormat* ff) {
data->is_present = flipper_format_key_exist(ff, MF_DESFIRE_FFF_FREE_MEM_KEY);
return data->is_present ?
flipper_format_read_uint32(ff, MF_DESFIRE_FFF_FREE_MEM_KEY, &data->bytes_free, 1) :
true;
}
bool mf_desfire_key_settings_load(
MfDesfireKeySettings* data,
const char* prefix,
FlipperFormat* ff) {
bool success = false;
FuriString* key = furi_string_alloc();
do {
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_CHANGE_KEY_ID_KEY);
if(!flipper_format_read_hex(ff, furi_string_get_cstr(key), &data->change_key_id, 1)) break;
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_CONFIG_CHANGEABLE_KEY);
if(!flipper_format_read_bool(ff, furi_string_get_cstr(key), &data->is_config_changeable, 1))
break;
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FREE_CREATE_DELETE_KEY);
if(!flipper_format_read_bool(
ff, furi_string_get_cstr(key), &data->is_free_create_delete, 1))
break;
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FREE_DIR_LIST_KEY);
if(!flipper_format_read_bool(
ff, furi_string_get_cstr(key), &data->is_free_directory_list, 1))
break;
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_KEY_CHANGEABLE_KEY);
if(!flipper_format_read_bool(
ff, furi_string_get_cstr(key), &data->is_master_key_changeable, 1))
break;
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FLAGS_KEY);
if(flipper_format_key_exist(ff, furi_string_get_cstr(key))) {
if(!flipper_format_read_hex(ff, furi_string_get_cstr(key), &data->flags, 1)) break;
}
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_MAX_KEYS_KEY);
if(!flipper_format_read_hex(ff, furi_string_get_cstr(key), &data->max_keys, 1)) break;
success = true;
} while(false);
furi_string_free(key);
return success;
}
bool mf_desfire_key_version_load(
MfDesfireKeyVersion* data,
const char* prefix,
uint32_t index,
FlipperFormat* ff) {
FuriString* key = furi_string_alloc_printf(
"%s %s %lu %s",
prefix,
MF_DESFIRE_FFF_KEY_SUB_PREFIX,
index,
MF_DESFIRE_FFF_KEY_VERSION_KEY);
const bool success = flipper_format_read_hex(ff, furi_string_get_cstr(key), data, 1);
furi_string_free(key);
return success;
}
bool mf_desfire_file_count_load(uint32_t* data, const char* prefix, FlipperFormat* ff) {
FuriString* key = furi_string_alloc_printf("%s %s", prefix, MF_DESFIRE_FFF_FILE_IDS_KEY);
const bool success = flipper_format_get_value_count(ff, furi_string_get_cstr(key), data);
furi_string_free(key);
return success;
}
bool mf_desfire_file_ids_load(
MfDesfireFileId* data,
uint32_t count,
const char* prefix,
FlipperFormat* ff) {
FuriString* key = furi_string_alloc_printf("%s %s", prefix, MF_DESFIRE_FFF_FILE_IDS_KEY);
const bool success = flipper_format_read_hex(ff, furi_string_get_cstr(key), data, count);
furi_string_free(key);
return success;
}
bool mf_desfire_file_settings_load(
MfDesfireFileSettings* data,
const char* prefix,
FlipperFormat* ff) {
bool success = false;
FuriString* key = furi_string_alloc();
do {
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_TYPE_KEY);
if(!flipper_format_read_hex(ff, furi_string_get_cstr(key), (uint8_t*)&data->type, 1))
break;
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_COMM_SETTINGS_KEY);
if(!flipper_format_read_hex(ff, furi_string_get_cstr(key), (uint8_t*)&data->comm, 1))
break;
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_ACCESS_RIGHTS_KEY);
if(!flipper_format_read_hex(
ff,
furi_string_get_cstr(key),
(uint8_t*)&data->access_rights,
sizeof(MfDesfireFileAccessRights)))
break;
if(data->type == MfDesfireFileTypeStandard || data->type == MfDesfireFileTypeBackup) {
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_SIZE_KEY);
if(!flipper_format_read_uint32(ff, furi_string_get_cstr(key), &data->data.size, 1))
break;
} else if(data->type == MfDesfireFileTypeValue) {
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_HI_LIMIT_KEY);
if(!flipper_format_read_uint32(ff, furi_string_get_cstr(key), &data->value.hi_limit, 1))
break;
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_LO_LIMIT_KEY);
if(!flipper_format_read_uint32(ff, furi_string_get_cstr(key), &data->value.lo_limit, 1))
break;
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_LIMIT_CREDIT_VALUE_KEY);
if(!flipper_format_read_uint32(
ff, furi_string_get_cstr(key), &data->value.limited_credit_value, 1))
break;
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_LIMIT_CREDIT_ENABLED_KEY);
if(!flipper_format_read_bool(
ff, furi_string_get_cstr(key), &data->value.limited_credit_enabled, 1))
break;
} else if(
data->type == MfDesfireFileTypeLinearRecord ||
data->type == MfDesfireFileTypeCyclicRecord) {
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_SIZE_KEY);
if(!flipper_format_read_uint32(ff, furi_string_get_cstr(key), &data->record.size, 1))
break;
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_MAX_KEY);
if(!flipper_format_read_uint32(ff, furi_string_get_cstr(key), &data->record.max, 1))
break;
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))
break;
}
success = true;
} while(false);
furi_string_free(key);
return success;
}
bool mf_desfire_file_data_load(MfDesfireFileData* data, const char* prefix, FlipperFormat* ff) {
bool success = false;
do {
if(!flipper_format_key_exist(ff, prefix)) {
success = true;
break;
}
uint32_t data_size;
if(!flipper_format_get_value_count(ff, prefix, &data_size)) break;
simple_array_init(data->data, data_size);
if(!flipper_format_read_hex(ff, prefix, simple_array_get_data(data->data), data_size))
break;
success = true;
} while(false);
return success;
}
bool mf_desfire_application_count_load(uint32_t* data, FlipperFormat* ff) {
return flipper_format_read_uint32(ff, MF_DESFIRE_FFF_APPLICATION_COUNT_KEY, data, 1);
}
bool mf_desfire_application_ids_load(
MfDesfireApplicationId* data,
uint32_t count,
FlipperFormat* ff) {
return flipper_format_read_hex(
ff, MF_DESFIRE_FFF_APPLICATION_IDS_KEY, data->data, count * sizeof(MfDesfireApplicationId));
}
bool mf_desfire_application_load(MfDesfireApplication* data, const char* prefix, FlipperFormat* ff) {
FuriString* sub_prefix = furi_string_alloc();
bool success = false;
do {
if(!mf_desfire_key_settings_load(&data->key_settings, prefix, ff)) break;
const uint32_t key_version_count = data->key_settings.max_keys;
simple_array_init(data->key_versions, key_version_count);
uint32_t i;
for(i = 0; i < key_version_count; ++i) {
if(!mf_desfire_key_version_load(simple_array_get(data->key_versions, i), prefix, i, ff))
break;
}
if(i != key_version_count) break;
uint32_t file_count;
if(!mf_desfire_file_count_load(&file_count, prefix, ff)) break;
simple_array_init(data->file_ids, file_count);
if(!mf_desfire_file_ids_load(simple_array_get_data(data->file_ids), file_count, prefix, ff))
break;
simple_array_init(data->file_settings, file_count);
simple_array_init(data->file_data, file_count);
for(i = 0; i < file_count; ++i) {
const MfDesfireFileId* file_id = simple_array_cget(data->file_ids, i);
furi_string_printf(
sub_prefix, "%s %s %u", prefix, MF_DESFIRE_FFF_FILE_SUB_PREFIX, *file_id);
MfDesfireFileSettings* file_settings = simple_array_get(data->file_settings, i);
if(!mf_desfire_file_settings_load(file_settings, furi_string_get_cstr(sub_prefix), ff))
break;
MfDesfireFileData* file_data = simple_array_get(data->file_data, i);
if(!mf_desfire_file_data_load(file_data, furi_string_get_cstr(sub_prefix), ff)) break;
}
if(i != file_count) break;
success = true;
} while(false);
furi_string_free(sub_prefix);
return success;
}
bool mf_desfire_version_save(const MfDesfireVersion* data, FlipperFormat* ff) {
return flipper_format_write_hex(
ff, MF_DESFIRE_FFF_VERSION_KEY, (const uint8_t*)data, sizeof(MfDesfireVersion));
}
bool mf_desfire_free_memory_save(const MfDesfireFreeMemory* data, FlipperFormat* ff) {
return data->is_present ?
flipper_format_write_uint32(ff, MF_DESFIRE_FFF_FREE_MEM_KEY, &data->bytes_free, 1) :
true;
}
bool mf_desfire_key_settings_save(
const MfDesfireKeySettings* data,
const char* prefix,
FlipperFormat* ff) {
bool success = false;
FuriString* key = furi_string_alloc();
do {
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_CHANGE_KEY_ID_KEY);
if(!flipper_format_write_hex(ff, furi_string_get_cstr(key), &data->change_key_id, 1))
break;
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_CONFIG_CHANGEABLE_KEY);
if(!flipper_format_write_bool(
ff, furi_string_get_cstr(key), &data->is_config_changeable, 1))
break;
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FREE_CREATE_DELETE_KEY);
if(!flipper_format_write_bool(
ff, furi_string_get_cstr(key), &data->is_free_create_delete, 1))
break;
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FREE_DIR_LIST_KEY);
if(!flipper_format_write_bool(
ff, furi_string_get_cstr(key), &data->is_free_directory_list, 1))
break;
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_KEY_CHANGEABLE_KEY);
if(!flipper_format_write_bool(
ff, furi_string_get_cstr(key), &data->is_master_key_changeable, 1))
break;
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FLAGS_KEY);
if(!flipper_format_write_hex(ff, furi_string_get_cstr(key), &data->flags, 1)) break;
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_MAX_KEYS_KEY);
if(!flipper_format_write_hex(ff, furi_string_get_cstr(key), &data->max_keys, 1)) break;
success = true;
} while(false);
furi_string_free(key);
return success;
}
bool mf_desfire_key_version_save(
const MfDesfireKeyVersion* data,
const char* prefix,
uint32_t index,
FlipperFormat* ff) {
FuriString* key = furi_string_alloc_printf(
"%s %s %lu %s",
prefix,
MF_DESFIRE_FFF_KEY_SUB_PREFIX,
index,
MF_DESFIRE_FFF_KEY_VERSION_KEY);
const bool success = flipper_format_write_hex(ff, furi_string_get_cstr(key), data, 1);
furi_string_free(key);
return success;
}
bool mf_desfire_file_ids_save(
const MfDesfireFileId* data,
uint32_t count,
const char* prefix,
FlipperFormat* ff) {
FuriString* key = furi_string_alloc_printf("%s %s", prefix, MF_DESFIRE_FFF_FILE_IDS_KEY);
const bool success = flipper_format_write_hex(ff, furi_string_get_cstr(key), data, count);
furi_string_free(key);
return success;
}
bool mf_desfire_file_settings_save(
const MfDesfireFileSettings* data,
const char* prefix,
FlipperFormat* ff) {
bool success = false;
FuriString* key = furi_string_alloc();
do {
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_TYPE_KEY);
if(!flipper_format_write_hex(ff, furi_string_get_cstr(key), (const uint8_t*)&data->type, 1))
break;
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_COMM_SETTINGS_KEY);
if(!flipper_format_write_hex(ff, furi_string_get_cstr(key), (const uint8_t*)&data->comm, 1))
break;
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_ACCESS_RIGHTS_KEY);
if(!flipper_format_write_hex(
ff,
furi_string_get_cstr(key),
(const uint8_t*)&data->access_rights,
sizeof(MfDesfireFileAccessRights)))
break;
if(data->type == MfDesfireFileTypeStandard || data->type == MfDesfireFileTypeBackup) {
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_SIZE_KEY);
if(!flipper_format_write_uint32(ff, furi_string_get_cstr(key), &data->data.size, 1))
break;
} else if(data->type == MfDesfireFileTypeValue) {
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_HI_LIMIT_KEY);
if(!flipper_format_write_uint32(
ff, furi_string_get_cstr(key), &data->value.hi_limit, 1))
break;
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_LO_LIMIT_KEY);
if(!flipper_format_write_uint32(
ff, furi_string_get_cstr(key), &data->value.lo_limit, 1))
break;
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_LIMIT_CREDIT_VALUE_KEY);
if(!flipper_format_write_uint32(
ff, furi_string_get_cstr(key), &data->value.limited_credit_value, 1))
break;
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_LIMIT_CREDIT_ENABLED_KEY);
if(!flipper_format_write_bool(
ff, furi_string_get_cstr(key), &data->value.limited_credit_enabled, 1))
break;
} else if(
data->type == MfDesfireFileTypeLinearRecord ||
data->type == MfDesfireFileTypeCyclicRecord) {
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_SIZE_KEY);
if(!flipper_format_write_uint32(ff, furi_string_get_cstr(key), &data->record.size, 1))
break;
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_MAX_KEY);
if(!flipper_format_write_uint32(ff, furi_string_get_cstr(key), &data->record.max, 1))
break;
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))
break;
}
success = true;
} while(false);
furi_string_free(key);
return success;
}
bool mf_desfire_file_data_save(
const MfDesfireFileData* data,
const char* prefix,
FlipperFormat* ff) {
const uint32_t data_size = simple_array_get_count(data->data);
return data_size > 0 ? flipper_format_write_hex(
ff, prefix, simple_array_cget_data(data->data), data_size) :
true;
}
bool mf_desfire_application_count_save(const uint32_t* data, FlipperFormat* ff) {
return flipper_format_write_uint32(ff, MF_DESFIRE_FFF_APPLICATION_COUNT_KEY, data, 1);
}
bool mf_desfire_application_ids_save(
const MfDesfireApplicationId* data,
uint32_t count,
FlipperFormat* ff) {
return flipper_format_write_hex(
ff, MF_DESFIRE_FFF_APPLICATION_IDS_KEY, data->data, count * sizeof(MfDesfireApplicationId));
}
bool mf_desfire_application_save(
const MfDesfireApplication* data,
const char* prefix,
FlipperFormat* ff) {
FuriString* sub_prefix = furi_string_alloc();
bool success = false;
do {
if(!mf_desfire_key_settings_save(&data->key_settings, prefix, ff)) break;
const uint32_t key_version_count = data->key_settings.max_keys;
uint32_t i;
for(i = 0; i < key_version_count; ++i) {
if(!mf_desfire_key_version_save(
simple_array_cget(data->key_versions, i), prefix, i, ff))
break;
}
if(i != key_version_count) break;
const uint32_t file_count = simple_array_get_count(data->file_ids);
if(!mf_desfire_file_ids_save(simple_array_get_data(data->file_ids), file_count, prefix, ff))
break;
for(i = 0; i < file_count; ++i) {
const MfDesfireFileId* file_id = simple_array_cget(data->file_ids, i);
furi_string_printf(
sub_prefix, "%s %s %u", prefix, MF_DESFIRE_FFF_FILE_SUB_PREFIX, *file_id);
const MfDesfireFileSettings* file_settings = simple_array_cget(data->file_settings, i);
if(!mf_desfire_file_settings_save(file_settings, furi_string_get_cstr(sub_prefix), ff))
break;
const MfDesfireFileData* file_data = simple_array_cget(data->file_data, i);
if(!mf_desfire_file_data_save(file_data, furi_string_get_cstr(sub_prefix), ff)) break;
}
if(i != file_count) break;
success = true;
} while(false);
furi_string_free(sub_prefix);
return success;
}
const SimpleArrayConfig mf_desfire_key_version_array_config = {
.init = NULL,
.copy = NULL,
.reset = NULL,
.type_size = sizeof(MfDesfireKeyVersion),
};
const SimpleArrayConfig mf_desfire_app_id_array_config = {
.init = NULL,
.copy = NULL,
.reset = NULL,
.type_size = sizeof(MfDesfireApplicationId),
};
const SimpleArrayConfig mf_desfire_file_id_array_config = {
.init = NULL,
.copy = NULL,
.reset = NULL,
.type_size = sizeof(MfDesfireFileId),
};
const SimpleArrayConfig mf_desfire_file_settings_array_config = {
.init = NULL,
.copy = NULL,
.reset = NULL,
.type_size = sizeof(MfDesfireFileSettings),
};
const SimpleArrayConfig mf_desfire_file_data_array_config = {
.init = (SimpleArrayInit)mf_desfire_file_data_init,
.copy = (SimpleArrayCopy)mf_desfire_file_data_copy,
.reset = (SimpleArrayReset)mf_desfire_file_data_reset,
.type_size = sizeof(MfDesfireData),
};
const SimpleArrayConfig mf_desfire_application_array_config = {
.init = (SimpleArrayInit)mf_desfire_application_init,
.copy = (SimpleArrayCopy)mf_desfire_application_copy,
.reset = (SimpleArrayReset)mf_desfire_application_reset,
.type_size = sizeof(MfDesfireApplication),
};

View File

@@ -0,0 +1,140 @@
#pragma once
#include "mf_desfire.h"
#define MF_DESFIRE_FFF_PICC_PREFIX "PICC"
#define MF_DESFIRE_FFF_APP_PREFIX "Application"
// SimpleArray configurations
extern const SimpleArrayConfig mf_desfire_key_version_array_config;
extern const SimpleArrayConfig mf_desfire_app_id_array_config;
extern const SimpleArrayConfig mf_desfire_file_id_array_config;
extern const SimpleArrayConfig mf_desfire_file_settings_array_config;
extern const SimpleArrayConfig mf_desfire_file_data_array_config;
extern const SimpleArrayConfig mf_desfire_application_array_config;
// Parse internal MfDesfire structures
bool mf_desfire_version_parse(MfDesfireVersion* data, const BitBuffer* buf);
bool mf_desfire_free_memory_parse(MfDesfireFreeMemory* data, const BitBuffer* buf);
bool mf_desfire_key_settings_parse(MfDesfireKeySettings* data, const BitBuffer* buf);
bool mf_desfire_key_version_parse(MfDesfireKeyVersion* data, const BitBuffer* buf);
bool mf_desfire_application_id_parse(
MfDesfireApplicationId* data,
uint32_t index,
const BitBuffer* buf);
bool mf_desfire_file_id_parse(MfDesfireFileId* data, uint32_t index, const BitBuffer* buf);
bool mf_desfire_file_settings_parse(MfDesfireFileSettings* data, const BitBuffer* buf);
bool mf_desfire_file_data_parse(MfDesfireFileData* data, const BitBuffer* buf);
// Init internal MfDesfire structures
void mf_desfire_file_data_init(MfDesfireFileData* data);
void mf_desfire_application_init(MfDesfireApplication* data);
// Reset internal MfDesfire structures
void mf_desfire_file_data_reset(MfDesfireFileData* data);
void mf_desfire_application_reset(MfDesfireApplication* data);
// Copy internal MfDesfire structures
void mf_desfire_file_data_copy(MfDesfireFileData* data, const MfDesfireFileData* other);
void mf_desfire_application_copy(MfDesfireApplication* data, const MfDesfireApplication* other);
// Load internal MfDesfire structures
bool mf_desfire_version_load(MfDesfireVersion* data, FlipperFormat* ff);
bool mf_desfire_free_memory_load(MfDesfireFreeMemory* data, FlipperFormat* ff);
bool mf_desfire_key_settings_load(
MfDesfireKeySettings* data,
const char* prefix,
FlipperFormat* ff);
bool mf_desfire_key_version_load(
MfDesfireKeyVersion* data,
const char* prefix,
uint32_t index,
FlipperFormat* ff);
bool mf_desfire_file_count_load(uint32_t* data, const char* prefix, FlipperFormat* ff);
bool mf_desfire_file_ids_load(
MfDesfireFileId* data,
uint32_t count,
const char* prefix,
FlipperFormat* ff);
bool mf_desfire_file_settings_load(
MfDesfireFileSettings* data,
const char* prefix,
FlipperFormat* ff);
bool mf_desfire_file_data_load(MfDesfireFileData* data, const char* prefix, FlipperFormat* ff);
bool mf_desfire_application_count_load(uint32_t* data, FlipperFormat* ff);
bool mf_desfire_application_ids_load(
MfDesfireApplicationId* data,
uint32_t count,
FlipperFormat* ff);
bool mf_desfire_application_load(MfDesfireApplication* data, const char* prefix, FlipperFormat* ff);
// Save internal MFDesfire structures
bool mf_desfire_version_save(const MfDesfireVersion* data, FlipperFormat* ff);
bool mf_desfire_free_memory_save(const MfDesfireFreeMemory* data, FlipperFormat* ff);
bool mf_desfire_key_settings_save(
const MfDesfireKeySettings* data,
const char* prefix,
FlipperFormat* ff);
bool mf_desfire_key_version_save(
const MfDesfireKeyVersion* data,
const char* prefix,
uint32_t index,
FlipperFormat* ff);
bool mf_desfire_file_ids_save(
const MfDesfireFileId* data,
uint32_t count,
const char* prefix,
FlipperFormat* ff);
bool mf_desfire_file_settings_save(
const MfDesfireFileSettings* data,
const char* prefix,
FlipperFormat* ff);
bool mf_desfire_file_data_save(
const MfDesfireFileData* data,
const char* prefix,
FlipperFormat* ff);
bool mf_desfire_application_count_save(const uint32_t* data, FlipperFormat* ff);
bool mf_desfire_application_ids_save(
const MfDesfireApplicationId* data,
uint32_t count,
FlipperFormat* ff);
bool mf_desfire_application_save(
const MfDesfireApplication* data,
const char* prefix,
FlipperFormat* ff);

View File

@@ -0,0 +1,243 @@
#include "mf_desfire_poller_i.h"
#include <nfc/protocols/nfc_poller_base.h>
#include <furi.h>
#define TAG "MfDesfirePoller"
#define MF_DESFIRE_BUF_SIZE_MAX (64U)
typedef NfcCommand (*MfDesfirePollerReadHandler)(MfDesfirePoller* instance);
const MfDesfireData* mf_desfire_poller_get_data(MfDesfirePoller* instance) {
furi_assert(instance);
return instance->data;
}
static MfDesfirePoller* mf_desfire_poller_alloc(Iso14443_4aPoller* iso14443_4a_poller) {
MfDesfirePoller* instance = malloc(sizeof(MfDesfirePoller));
instance->iso14443_4a_poller = iso14443_4a_poller;
instance->data = mf_desfire_alloc();
instance->tx_buffer = bit_buffer_alloc(MF_DESFIRE_BUF_SIZE_MAX);
instance->rx_buffer = bit_buffer_alloc(MF_DESFIRE_BUF_SIZE_MAX);
instance->input_buffer = bit_buffer_alloc(MF_DESFIRE_BUF_SIZE_MAX);
instance->result_buffer = bit_buffer_alloc(MF_DESFIRE_BUF_SIZE_MAX);
instance->mf_desfire_event.data = &instance->mf_desfire_event_data;
instance->general_event.protocol = NfcProtocolMfDesfire;
instance->general_event.event_data = &instance->mf_desfire_event;
instance->general_event.instance = instance;
return instance;
}
static void mf_desfire_poller_free(MfDesfirePoller* instance) {
furi_assert(instance);
mf_desfire_free(instance->data);
bit_buffer_free(instance->tx_buffer);
bit_buffer_free(instance->rx_buffer);
bit_buffer_free(instance->input_buffer);
bit_buffer_free(instance->result_buffer);
free(instance);
}
static NfcCommand mf_desfire_poller_handler_idle(MfDesfirePoller* instance) {
bit_buffer_reset(instance->input_buffer);
bit_buffer_reset(instance->result_buffer);
bit_buffer_reset(instance->tx_buffer);
bit_buffer_reset(instance->rx_buffer);
iso14443_4a_copy(
instance->data->iso14443_4a_data,
iso14443_4a_poller_get_data(instance->iso14443_4a_poller));
instance->state = MfDesfirePollerStateReadVersion;
return NfcCommandContinue;
}
static NfcCommand mf_desfire_poller_handler_read_version(MfDesfirePoller* instance) {
instance->error = mf_desfire_poller_async_read_version(instance, &instance->data->version);
if(instance->error == MfDesfireErrorNone) {
FURI_LOG_D(TAG, "Read version success");
instance->state = MfDesfirePollerStateReadFreeMemory;
} else {
FURI_LOG_E(TAG, "Failed to read version");
iso14443_4a_poller_halt(instance->iso14443_4a_poller);
instance->state = MfDesfirePollerStateReadFailed;
}
return NfcCommandContinue;
}
static NfcCommand mf_desfire_poller_handler_read_free_memory(MfDesfirePoller* instance) {
instance->error =
mf_desfire_poller_async_read_free_memory(instance, &instance->data->free_memory);
if(instance->error == MfDesfireErrorNone) {
FURI_LOG_D(TAG, "Read free memory success");
instance->state = MfDesfirePollerStateReadMasterKeySettings;
} else {
FURI_LOG_E(TAG, "Failed to read free memory");
iso14443_4a_poller_halt(instance->iso14443_4a_poller);
instance->state = MfDesfirePollerStateReadFailed;
}
return NfcCommandContinue;
}
static NfcCommand mf_desfire_poller_handler_read_master_key_settings(MfDesfirePoller* instance) {
instance->error =
mf_desfire_poller_async_read_key_settings(instance, &instance->data->master_key_settings);
if(instance->error == MfDesfireErrorNone) {
FURI_LOG_D(TAG, "Read master key settings success");
instance->state = MfDesfirePollerStateReadMasterKeyVersion;
} else {
FURI_LOG_E(TAG, "Failed to read master key settings");
iso14443_4a_poller_halt(instance->iso14443_4a_poller);
instance->state = MfDesfirePollerStateReadFailed;
}
return NfcCommandContinue;
}
static NfcCommand mf_desfire_poller_handler_read_master_key_version(MfDesfirePoller* instance) {
instance->error = mf_desfire_poller_async_read_key_versions(
instance,
instance->data->master_key_versions,
instance->data->master_key_settings.max_keys);
if(instance->error == MfDesfireErrorNone) {
FURI_LOG_D(TAG, "Read master key version success");
instance->state = MfDesfirePollerStateReadApplicationIds;
} else {
FURI_LOG_E(TAG, "Failed to read master key version");
iso14443_4a_poller_halt(instance->iso14443_4a_poller);
instance->state = MfDesfirePollerStateReadFailed;
}
return NfcCommandContinue;
}
static NfcCommand mf_desfire_poller_handler_read_application_ids(MfDesfirePoller* instance) {
instance->error =
mf_desfire_poller_async_read_application_ids(instance, instance->data->application_ids);
if(instance->error == MfDesfireErrorNone) {
FURI_LOG_D(TAG, "Read application ids success");
instance->state = MfDesfirePollerStateReadApplications;
} else {
FURI_LOG_E(TAG, "Failed to read application ids");
iso14443_4a_poller_halt(instance->iso14443_4a_poller);
instance->state = MfDesfirePollerStateReadFailed;
}
return NfcCommandContinue;
}
static NfcCommand mf_desfire_poller_handler_read_applications(MfDesfirePoller* instance) {
instance->error = mf_desfire_poller_async_read_applications(
instance, instance->data->application_ids, instance->data->applications);
if(instance->error == MfDesfireErrorNone) {
FURI_LOG_D(TAG, "Read applications success");
instance->state = MfDesfirePollerStateReadSuccess;
} else {
FURI_LOG_E(TAG, "Failed to read applications");
iso14443_4a_poller_halt(instance->iso14443_4a_poller);
instance->state = MfDesfirePollerStateReadFailed;
}
return NfcCommandContinue;
}
static NfcCommand mf_desfire_poller_handler_read_fail(MfDesfirePoller* instance) {
FURI_LOG_D(TAG, "Read Failed");
iso14443_4a_poller_halt(instance->iso14443_4a_poller);
instance->mf_desfire_event.data->error = instance->error;
NfcCommand command = instance->callback(instance->general_event, instance->context);
instance->state = MfDesfirePollerStateIdle;
return command;
}
static NfcCommand mf_desfire_poller_handler_read_success(MfDesfirePoller* instance) {
FURI_LOG_D(TAG, "Read success.");
iso14443_4a_poller_halt(instance->iso14443_4a_poller);
instance->mf_desfire_event.type = MfDesfirePollerEventTypeReadSuccess;
NfcCommand command = instance->callback(instance->general_event, instance->context);
return command;
}
static const MfDesfirePollerReadHandler mf_desfire_poller_read_handler[MfDesfirePollerStateNum] = {
[MfDesfirePollerStateIdle] = mf_desfire_poller_handler_idle,
[MfDesfirePollerStateReadVersion] = mf_desfire_poller_handler_read_version,
[MfDesfirePollerStateReadFreeMemory] = mf_desfire_poller_handler_read_free_memory,
[MfDesfirePollerStateReadMasterKeySettings] =
mf_desfire_poller_handler_read_master_key_settings,
[MfDesfirePollerStateReadMasterKeyVersion] = mf_desfire_poller_handler_read_master_key_version,
[MfDesfirePollerStateReadApplicationIds] = mf_desfire_poller_handler_read_application_ids,
[MfDesfirePollerStateReadApplications] = mf_desfire_poller_handler_read_applications,
[MfDesfirePollerStateReadFailed] = mf_desfire_poller_handler_read_fail,
[MfDesfirePollerStateReadSuccess] = mf_desfire_poller_handler_read_success,
};
static void mf_desfire_poller_set_callback(
MfDesfirePoller* instance,
NfcGenericCallback callback,
void* context) {
furi_assert(instance);
furi_assert(callback);
instance->callback = callback;
instance->context = context;
}
static NfcCommand mf_desfire_poller_run(NfcGenericEvent event, void* context) {
furi_assert(event.protocol == NfcProtocolIso14443_4a);
MfDesfirePoller* instance = context;
furi_assert(instance);
furi_assert(instance->callback);
const Iso14443_4aPollerEvent* iso14443_4a_event = event.event_data;
furi_assert(iso14443_4a_event);
NfcCommand command = NfcCommandContinue;
if(iso14443_4a_event->type == Iso14443_4aPollerEventTypeReady) {
command = mf_desfire_poller_read_handler[instance->state](instance);
} else if(iso14443_4a_event->type == Iso14443_4aPollerEventTypeError) {
instance->mf_desfire_event.type = MfDesfirePollerEventTypeReadFailed;
command = instance->callback(instance->general_event, instance->context);
}
return command;
}
static bool mf_desfire_poller_detect(NfcGenericEvent event, void* context) {
furi_assert(event.protocol == NfcProtocolIso14443_4a);
MfDesfirePoller* instance = context;
furi_assert(instance);
const Iso14443_4aPollerEvent* iso14443_4a_event = event.event_data;
furi_assert(iso14443_4a_event);
bool protocol_detected = false;
if(iso14443_4a_event->type == Iso14443_4aPollerEventTypeReady) {
MfDesfireVersion version = {};
const MfDesfireError error = mf_desfire_poller_async_read_version(instance, &version);
protocol_detected = (error == MfDesfireErrorNone);
}
return protocol_detected;
}
const NfcPollerBase mf_desfire_poller = {
.alloc = (NfcPollerAlloc)mf_desfire_poller_alloc,
.free = (NfcPollerFree)mf_desfire_poller_free,
.set_callback = (NfcPollerSetCallback)mf_desfire_poller_set_callback,
.run = (NfcPollerRun)mf_desfire_poller_run,
.detect = (NfcPollerDetect)mf_desfire_poller_detect,
.get_data = (NfcPollerGetData)mf_desfire_poller_get_data,
};

View File

@@ -0,0 +1,31 @@
#pragma once
#include "mf_desfire.h"
#include <lib/nfc/protocols/iso14443_4a/iso14443_4a_poller.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct MfDesfirePoller MfDesfirePoller;
typedef enum {
MfDesfirePollerEventTypeReadSuccess,
MfDesfirePollerEventTypeReadFailed,
} MfDesfirePollerEventType;
typedef struct {
union {
MfDesfireError error;
};
} MfDesfirePollerEventData;
typedef struct {
MfDesfirePollerEventType type;
MfDesfirePollerEventData* data;
} MfDesfirePollerEvent;
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,5 @@
#pragma once
#include <nfc/protocols/nfc_poller_base.h>
extern const NfcPollerBase mf_desfire_poller;

View File

@@ -0,0 +1,474 @@
#include "mf_desfire_poller_i.h"
#include <furi.h>
#include "mf_desfire_i.h"
#define TAG "MfDesfirePoller"
MfDesfireError mf_desfire_process_error(Iso14443_4aError error) {
switch(error) {
case Iso14443_4aErrorNone:
return MfDesfireErrorNone;
case Iso14443_4aErrorNotPresent:
return MfDesfireErrorNotPresent;
case Iso14443_4aErrorTimeout:
return MfDesfireErrorTimeout;
default:
return MfDesfireErrorProtocol;
}
}
MfDesfireError mf_desfire_send_chunks(
MfDesfirePoller* instance,
const BitBuffer* tx_buffer,
BitBuffer* rx_buffer) {
furi_assert(instance);
furi_assert(instance->iso14443_4a_poller);
furi_assert(instance->tx_buffer);
furi_assert(instance->rx_buffer);
furi_assert(tx_buffer);
furi_assert(rx_buffer);
MfDesfireError error = MfDesfireErrorNone;
do {
Iso14443_4aError iso14443_4a_error = iso14443_4a_poller_send_block(
instance->iso14443_4a_poller, tx_buffer, instance->rx_buffer);
if(iso14443_4a_error != Iso14443_4aErrorNone) {
error = mf_desfire_process_error(iso14443_4a_error);
break;
}
bit_buffer_reset(instance->tx_buffer);
bit_buffer_append_byte(instance->tx_buffer, MF_DESFIRE_FLAG_HAS_NEXT);
if(bit_buffer_get_size_bytes(instance->rx_buffer) > sizeof(uint8_t)) {
bit_buffer_copy_right(rx_buffer, instance->rx_buffer, sizeof(uint8_t));
} else {
bit_buffer_reset(rx_buffer);
}
while(bit_buffer_starts_with_byte(instance->rx_buffer, MF_DESFIRE_FLAG_HAS_NEXT)) {
Iso14443_4aError iso14443_4a_error = iso14443_4a_poller_send_block(
instance->iso14443_4a_poller, instance->tx_buffer, instance->rx_buffer);
if(iso14443_4a_error != Iso14443_4aErrorNone) {
error = mf_desfire_process_error(iso14443_4a_error);
break;
}
bit_buffer_append_right(rx_buffer, instance->rx_buffer, sizeof(uint8_t));
}
} while(false);
return error;
}
MfDesfireError
mf_desfire_poller_async_read_version(MfDesfirePoller* instance, MfDesfireVersion* data) {
furi_assert(instance);
bit_buffer_reset(instance->input_buffer);
bit_buffer_append_byte(instance->input_buffer, MF_DESFIRE_CMD_GET_VERSION);
MfDesfireError error;
do {
error = mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer);
if(error != MfDesfireErrorNone) break;
if(!mf_desfire_version_parse(data, instance->result_buffer)) {
error = MfDesfireErrorProtocol;
}
} while(false);
return error;
}
MfDesfireError
mf_desfire_poller_async_read_free_memory(MfDesfirePoller* instance, MfDesfireFreeMemory* data) {
furi_assert(instance);
bit_buffer_reset(instance->input_buffer);
bit_buffer_append_byte(instance->input_buffer, MF_DESFIRE_CMD_GET_FREE_MEMORY);
MfDesfireError error;
do {
error = mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer);
if(error != MfDesfireErrorNone) break;
if(!mf_desfire_free_memory_parse(data, instance->result_buffer)) {
error = MfDesfireErrorProtocol;
}
} while(false);
return error;
}
MfDesfireError mf_desfire_poller_async_read_key_settings(
MfDesfirePoller* instance,
MfDesfireKeySettings* data) {
furi_assert(instance);
bit_buffer_reset(instance->input_buffer);
bit_buffer_append_byte(instance->input_buffer, MF_DESFIRE_CMD_GET_KEY_SETTINGS);
MfDesfireError error;
do {
error = mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer);
if(error != MfDesfireErrorNone) break;
if(!mf_desfire_key_settings_parse(data, instance->result_buffer)) {
error = MfDesfireErrorProtocol;
}
} while(false);
return error;
}
MfDesfireError mf_desfire_poller_async_read_key_versions(
MfDesfirePoller* instance,
SimpleArray* data,
uint32_t count) {
furi_assert(instance);
furi_assert(count > 0);
simple_array_init(data, count);
bit_buffer_set_size_bytes(instance->input_buffer, sizeof(uint8_t) * 2);
bit_buffer_set_byte(instance->input_buffer, 0, MF_DESFIRE_CMD_GET_KEY_VERSION);
MfDesfireError error = MfDesfireErrorNone;
for(uint32_t i = 0; i < count; ++i) {
bit_buffer_set_byte(instance->input_buffer, 1, i);
error = mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer);
if(error != MfDesfireErrorNone) break;
if(!mf_desfire_key_version_parse(simple_array_get(data, i), instance->result_buffer)) {
error = MfDesfireErrorProtocol;
break;
}
}
return error;
}
MfDesfireError
mf_desfire_poller_async_read_application_ids(MfDesfirePoller* instance, SimpleArray* data) {
furi_assert(instance);
bit_buffer_reset(instance->input_buffer);
bit_buffer_append_byte(instance->input_buffer, MF_DESFIRE_CMD_GET_APPLICATION_IDS);
MfDesfireError error;
do {
error = mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer);
if(error != MfDesfireErrorNone) break;
const uint32_t app_id_count =
bit_buffer_get_size_bytes(instance->result_buffer) / sizeof(MfDesfireApplicationId);
if(app_id_count == 0) break;
simple_array_init(data, app_id_count);
for(uint32_t i = 0; i < app_id_count; ++i) {
if(!mf_desfire_application_id_parse(
simple_array_get(data, i), i, instance->result_buffer)) {
error = MfDesfireErrorProtocol;
break;
}
}
} while(false);
return error;
}
MfDesfireError mf_desfire_poller_async_select_application(
MfDesfirePoller* instance,
const MfDesfireApplicationId* id) {
furi_assert(instance);
bit_buffer_reset(instance->input_buffer);
bit_buffer_append_byte(instance->input_buffer, MF_DESFIRE_CMD_SELECT_APPLICATION);
bit_buffer_append_bytes(
instance->input_buffer, (const uint8_t*)id, sizeof(MfDesfireApplicationId));
MfDesfireError error =
mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer);
return error;
}
MfDesfireError
mf_desfire_poller_async_read_file_ids(MfDesfirePoller* instance, SimpleArray* data) {
furi_assert(instance);
bit_buffer_reset(instance->input_buffer);
bit_buffer_append_byte(instance->input_buffer, MF_DESFIRE_CMD_GET_FILE_IDS);
MfDesfireError error;
do {
error = mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer);
if(error != MfDesfireErrorNone) break;
const uint32_t id_count =
bit_buffer_get_size_bytes(instance->result_buffer) / sizeof(MfDesfireFileId);
if(id_count == 0) break;
simple_array_init(data, id_count);
for(uint32_t i = 0; i < id_count; ++i) {
if(!mf_desfire_file_id_parse(simple_array_get(data, i), i, instance->result_buffer)) {
error = MfDesfireErrorProtocol;
break;
}
}
} while(false);
return error;
}
MfDesfireError mf_desfire_poller_async_read_file_settings(
MfDesfirePoller* instance,
MfDesfireFileId id,
MfDesfireFileSettings* data) {
furi_assert(instance);
bit_buffer_reset(instance->input_buffer);
bit_buffer_append_byte(instance->input_buffer, MF_DESFIRE_CMD_GET_FILE_SETTINGS);
bit_buffer_append_byte(instance->input_buffer, id);
MfDesfireError error;
do {
error = mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer);
if(error != MfDesfireErrorNone) break;
if(!mf_desfire_file_settings_parse(data, instance->result_buffer)) {
error = MfDesfireErrorProtocol;
}
} while(false);
return error;
}
MfDesfireError mf_desfire_poller_async_read_file_settings_multi(
MfDesfirePoller* instance,
const SimpleArray* file_ids,
SimpleArray* data) {
furi_assert(instance);
MfDesfireError error = MfDesfireErrorNone;
const uint32_t file_id_count = simple_array_get_count(file_ids);
if(file_id_count > 0) {
simple_array_init(data, file_id_count);
}
for(uint32_t i = 0; i < file_id_count; ++i) {
const MfDesfireFileId file_id = *(const MfDesfireFileId*)simple_array_cget(file_ids, i);
error = mf_desfire_poller_async_read_file_settings(
instance, file_id, simple_array_get(data, i));
if(error != MfDesfireErrorNone) break;
}
return error;
}
MfDesfireError mf_desfire_poller_async_read_file_data(
MfDesfirePoller* instance,
MfDesfireFileId id,
uint32_t offset,
size_t size,
MfDesfireFileData* data) {
furi_assert(instance);
bit_buffer_reset(instance->input_buffer);
bit_buffer_append_byte(instance->input_buffer, MF_DESFIRE_CMD_READ_DATA);
bit_buffer_append_byte(instance->input_buffer, id);
bit_buffer_append_bytes(instance->input_buffer, (const uint8_t*)&offset, 3);
bit_buffer_append_bytes(instance->input_buffer, (const uint8_t*)&size, 3);
MfDesfireError error;
do {
error = mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer);
if(error != MfDesfireErrorNone) break;
if(!mf_desfire_file_data_parse(data, instance->result_buffer)) {
error = MfDesfireErrorProtocol;
}
} while(false);
return error;
}
MfDesfireError mf_desfire_poller_async_read_file_value(
MfDesfirePoller* instance,
MfDesfireFileId id,
MfDesfireFileData* data) {
furi_assert(instance);
bit_buffer_reset(instance->input_buffer);
bit_buffer_append_byte(instance->input_buffer, MF_DESFIRE_CMD_GET_VALUE);
bit_buffer_append_byte(instance->input_buffer, id);
MfDesfireError error;
do {
error = mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer);
if(error != MfDesfireErrorNone) break;
if(!mf_desfire_file_data_parse(data, instance->result_buffer)) {
error = MfDesfireErrorProtocol;
}
} while(false);
return error;
}
MfDesfireError mf_desfire_poller_async_read_file_records(
MfDesfirePoller* instance,
MfDesfireFileId id,
uint32_t offset,
size_t size,
MfDesfireFileData* data) {
furi_assert(instance);
bit_buffer_reset(instance->input_buffer);
bit_buffer_append_byte(instance->input_buffer, MF_DESFIRE_CMD_READ_DATA);
bit_buffer_append_byte(instance->input_buffer, id);
bit_buffer_append_bytes(instance->input_buffer, (const uint8_t*)&offset, 3);
bit_buffer_append_bytes(instance->input_buffer, (const uint8_t*)&size, 3);
MfDesfireError error;
do {
error = mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer);
if(error != MfDesfireErrorNone) break;
if(!mf_desfire_file_data_parse(data, instance->result_buffer)) {
error = MfDesfireErrorProtocol;
}
} while(false);
return error;
}
MfDesfireError mf_desfire_poller_async_read_file_data_multi(
MfDesfirePoller* instance,
const SimpleArray* file_ids,
const SimpleArray* file_settings,
SimpleArray* data) {
furi_assert(instance);
furi_assert(simple_array_get_count(file_ids) == simple_array_get_count(file_settings));
MfDesfireError error = MfDesfireErrorNone;
const uint32_t file_id_count = simple_array_get_count(file_ids);
if(file_id_count > 0) {
simple_array_init(data, file_id_count);
}
for(uint32_t i = 0; i < file_id_count; ++i) {
const MfDesfireFileId file_id = *(const MfDesfireFileId*)simple_array_cget(file_ids, i);
const MfDesfireFileSettings* file_settings_cur = simple_array_cget(file_settings, i);
const MfDesfireFileType file_type = file_settings_cur->type;
MfDesfireFileData* file_data = simple_array_get(data, i);
if(file_type == MfDesfireFileTypeStandard || file_type == MfDesfireFileTypeBackup) {
error = mf_desfire_poller_async_read_file_data(
instance, file_id, 0, file_settings_cur->data.size, file_data);
} else if(file_type == MfDesfireFileTypeValue) {
error = mf_desfire_poller_async_read_file_value(instance, file_id, file_data);
} else if(
file_type == MfDesfireFileTypeLinearRecord ||
file_type == MfDesfireFileTypeCyclicRecord) {
error = mf_desfire_poller_async_read_file_records(
instance, file_id, 0, file_settings_cur->data.size, file_data);
}
if(error != MfDesfireErrorNone) break;
}
return error;
}
MfDesfireError mf_desfire_poller_async_read_application(
MfDesfirePoller* instance,
MfDesfireApplication* data) {
furi_assert(instance);
furi_assert(data);
MfDesfireError error;
do {
error = mf_desfire_poller_async_read_key_settings(instance, &data->key_settings);
if(error != MfDesfireErrorNone) break;
error = mf_desfire_poller_async_read_key_versions(
instance, data->key_versions, data->key_settings.max_keys);
if(error != MfDesfireErrorNone) break;
error = mf_desfire_poller_async_read_file_ids(instance, data->file_ids);
if(error != MfDesfireErrorNone) break;
error = mf_desfire_poller_async_read_file_settings_multi(
instance, data->file_ids, data->file_settings);
if(error != MfDesfireErrorNone) break;
error = mf_desfire_poller_async_read_file_data_multi(
instance, data->file_ids, data->file_settings, data->file_data);
if(error != MfDesfireErrorNone) break;
} while(false);
return error;
}
MfDesfireError mf_desfire_poller_async_read_applications(
MfDesfirePoller* instance,
const SimpleArray* app_ids,
SimpleArray* data) {
furi_assert(instance);
MfDesfireError error = MfDesfireErrorNone;
const uint32_t app_id_count = simple_array_get_count(app_ids);
if(app_id_count > 0) {
simple_array_init(data, app_id_count);
}
for(uint32_t i = 0; i < app_id_count; ++i) {
do {
error = mf_desfire_poller_async_select_application(
instance, simple_array_cget(app_ids, i));
if(error != MfDesfireErrorNone) break;
MfDesfireApplication* current_app = simple_array_get(data, i);
error = mf_desfire_poller_async_read_application(instance, current_app);
} while(false);
}
return error;
}

View File

@@ -0,0 +1,126 @@
#pragma once
#include "mf_desfire_poller.h"
#include <lib/nfc/protocols/iso14443_4a/iso14443_4a_poller_i.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
MfDesfirePollerStateIdle,
MfDesfirePollerStateReadVersion,
MfDesfirePollerStateReadFreeMemory,
MfDesfirePollerStateReadMasterKeySettings,
MfDesfirePollerStateReadMasterKeyVersion,
MfDesfirePollerStateReadApplicationIds,
MfDesfirePollerStateReadApplications,
MfDesfirePollerStateReadFailed,
MfDesfirePollerStateReadSuccess,
MfDesfirePollerStateNum,
} MfDesfirePollerState;
typedef enum {
MfDesfirePollerSessionStateIdle,
MfDesfirePollerSessionStateActive,
MfDesfirePollerSessionStateStopRequest,
} MfDesfirePollerSessionState;
struct MfDesfirePoller {
Iso14443_4aPoller* iso14443_4a_poller;
MfDesfirePollerSessionState session_state;
MfDesfirePollerState state;
MfDesfireError error;
MfDesfireData* data;
BitBuffer* tx_buffer;
BitBuffer* rx_buffer;
BitBuffer* input_buffer;
BitBuffer* result_buffer;
MfDesfirePollerEventData mf_desfire_event_data;
MfDesfirePollerEvent mf_desfire_event;
NfcGenericEvent general_event;
NfcGenericCallback callback;
void* context;
};
MfDesfireError mf_desfire_process_error(Iso14443_4aError error);
const MfDesfireData* mf_desfire_poller_get_data(MfDesfirePoller* instance);
MfDesfireError mf_desfire_send_chunks(
MfDesfirePoller* instance,
const BitBuffer* tx_buffer,
BitBuffer* rx_buffer);
MfDesfireError
mf_desfire_poller_async_read_version(MfDesfirePoller* instance, MfDesfireVersion* data);
MfDesfireError
mf_desfire_poller_async_read_free_memory(MfDesfirePoller* instance, MfDesfireFreeMemory* data);
MfDesfireError mf_desfire_poller_async_read_key_settings(
MfDesfirePoller* instance,
MfDesfireKeySettings* data);
MfDesfireError mf_desfire_poller_async_read_key_versions(
MfDesfirePoller* instance,
SimpleArray* data,
uint32_t count);
MfDesfireError
mf_desfire_poller_async_read_application_ids(MfDesfirePoller* instance, SimpleArray* data);
MfDesfireError mf_desfire_poller_async_select_application(
MfDesfirePoller* instance,
const MfDesfireApplicationId* id);
MfDesfireError mf_desfire_poller_async_read_file_ids(MfDesfirePoller* instance, SimpleArray* data);
MfDesfireError mf_desfire_poller_async_read_file_settings(
MfDesfirePoller* instance,
MfDesfireFileId id,
MfDesfireFileSettings* data);
MfDesfireError mf_desfire_poller_async_read_file_settings_multi(
MfDesfirePoller* instance,
const SimpleArray* file_ids,
SimpleArray* data);
MfDesfireError mf_desfire_poller_async_read_file_data(
MfDesfirePoller* instance,
MfDesfireFileId id,
uint32_t offset,
size_t size,
MfDesfireFileData* data);
MfDesfireError mf_desfire_poller_async_read_file_value(
MfDesfirePoller* instance,
MfDesfireFileId id,
MfDesfireFileData* data);
MfDesfireError mf_desfire_poller_async_read_file_records(
MfDesfirePoller* instance,
MfDesfireFileId id,
uint32_t offset,
size_t size,
MfDesfireFileData* data);
MfDesfireError mf_desfire_poller_async_read_file_data_multi(
MfDesfirePoller* instance,
const SimpleArray* file_ids,
const SimpleArray* file_settings,
SimpleArray* data);
MfDesfireError
mf_desfire_poller_async_read_application(MfDesfirePoller* instance, MfDesfireApplication* data);
MfDesfireError mf_desfire_poller_async_read_applications(
MfDesfirePoller* instance,
const SimpleArray* app_ids,
SimpleArray* data);
#ifdef __cplusplus
}
#endif