mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-04-29 04:09:58 -07:00
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:
290
lib/nfc/protocols/mf_desfire/mf_desfire.c
Normal file
290
lib/nfc/protocols/mf_desfire/mf_desfire.c
Normal 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;
|
||||
}
|
||||
191
lib/nfc/protocols/mf_desfire/mf_desfire.h
Normal file
191
lib/nfc/protocols/mf_desfire/mf_desfire.h
Normal 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
|
||||
790
lib/nfc/protocols/mf_desfire/mf_desfire_i.c
Normal file
790
lib/nfc/protocols/mf_desfire/mf_desfire_i.c
Normal 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),
|
||||
};
|
||||
140
lib/nfc/protocols/mf_desfire/mf_desfire_i.h
Normal file
140
lib/nfc/protocols/mf_desfire/mf_desfire_i.h
Normal 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);
|
||||
243
lib/nfc/protocols/mf_desfire/mf_desfire_poller.c
Normal file
243
lib/nfc/protocols/mf_desfire/mf_desfire_poller.c
Normal 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,
|
||||
};
|
||||
31
lib/nfc/protocols/mf_desfire/mf_desfire_poller.h
Normal file
31
lib/nfc/protocols/mf_desfire/mf_desfire_poller.h
Normal 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
|
||||
5
lib/nfc/protocols/mf_desfire/mf_desfire_poller_defs.h
Normal file
5
lib/nfc/protocols/mf_desfire/mf_desfire_poller_defs.h
Normal file
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include <nfc/protocols/nfc_poller_base.h>
|
||||
|
||||
extern const NfcPollerBase mf_desfire_poller;
|
||||
474
lib/nfc/protocols/mf_desfire/mf_desfire_poller_i.c
Normal file
474
lib/nfc/protocols/mf_desfire/mf_desfire_poller_i.c
Normal 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;
|
||||
}
|
||||
126
lib/nfc/protocols/mf_desfire/mf_desfire_poller_i.h
Normal file
126
lib/nfc/protocols/mf_desfire/mf_desfire_poller_i.h
Normal 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
|
||||
Reference in New Issue
Block a user