mirror of
https://github.com/DarkFlippers/unleashed-firmware
synced 2024-11-22 20:43:07 +00:00
[FL-3750] Mf Desfire multiple file rights support (#3576)
* mf desfire: remove unused type * mf desfire: continue reading after failed get free mem cmd * mf desfire: fix processing read master key settings command * mf desfire: don't read applications if they are auth protected * mf desfire: handle multiple rights * mf desfire: fix PVS warnings * mf desfire: fix print format * mf desfire: fix logs * mf classic: add send frame functions to poller * unit tests: add test from mfc crypto frame exchange * mf classic: add documentation * mf classic: fix incorrect name * target: fix api version
This commit is contained in:
parent
fb9728d570
commit
1a40fae003
14 changed files with 576 additions and 113 deletions
|
@ -7,10 +7,13 @@
|
|||
#include <nfc/nfc_poller.h>
|
||||
#include <nfc/nfc_listener.h>
|
||||
#include <nfc/protocols/iso14443_3a/iso14443_3a.h>
|
||||
#include <nfc/protocols/iso14443_3a/iso14443_3a_poller.h>
|
||||
#include <nfc/protocols/iso14443_3a/iso14443_3a_poller_sync.h>
|
||||
#include <nfc/protocols/mf_ultralight/mf_ultralight.h>
|
||||
#include <nfc/protocols/mf_ultralight/mf_ultralight_poller_sync.h>
|
||||
#include <nfc/protocols/mf_classic/mf_classic_poller_sync.h>
|
||||
#include <nfc/protocols/mf_classic/mf_classic_poller.h>
|
||||
#include <nfc/nfc_poller.h>
|
||||
|
||||
#include <toolbox/keys_dict.h>
|
||||
#include <nfc/nfc.h>
|
||||
|
@ -22,6 +25,23 @@
|
|||
#define NFC_TEST_NFC_DEV_PATH EXT_PATH("unit_tests/nfc/nfc_device_test.nfc")
|
||||
#define NFC_APP_MF_CLASSIC_DICT_UNIT_TEST_PATH EXT_PATH("unit_tests/mf_dict.nfc")
|
||||
|
||||
#define NFC_TEST_FLAG_WORKER_DONE (1)
|
||||
|
||||
typedef enum {
|
||||
NfcTestMfClassicSendFrameTestStateAuth,
|
||||
NfcTestMfClassicSendFrameTestStateReadBlock,
|
||||
|
||||
NfcTestMfClassicSendFrameTestStateFail,
|
||||
NfcTestMfClassicSendFrameTestStateSuccess,
|
||||
} NfcTestMfClassicSendFrameTestState;
|
||||
|
||||
typedef struct {
|
||||
NfcTestMfClassicSendFrameTestState state;
|
||||
BitBuffer* tx_buf;
|
||||
BitBuffer* rx_buf;
|
||||
FuriThreadId thread_id;
|
||||
} NfcTestMfClassicSendFrameTest;
|
||||
|
||||
typedef struct {
|
||||
Storage* storage;
|
||||
} NfcTest;
|
||||
|
@ -435,6 +455,109 @@ static void mf_classic_value_block(void) {
|
|||
nfc_free(poller);
|
||||
}
|
||||
|
||||
NfcCommand mf_classic_poller_send_frame_callback(NfcGenericEventEx event, void* context) {
|
||||
furi_check(event.poller);
|
||||
furi_check(event.parent_event_data);
|
||||
furi_check(context);
|
||||
|
||||
NfcCommand command = NfcCommandContinue;
|
||||
MfClassicPoller* instance = event.poller;
|
||||
NfcTestMfClassicSendFrameTest* frame_test = context;
|
||||
Iso14443_3aPollerEvent* iso3_event = event.parent_event_data;
|
||||
|
||||
MfClassicError error = MfClassicErrorNone;
|
||||
if(iso3_event->type == Iso14443_3aPollerEventTypeReady) {
|
||||
if(frame_test->state == NfcTestMfClassicSendFrameTestStateAuth) {
|
||||
MfClassicKey key = {
|
||||
.data = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
|
||||
};
|
||||
error = mf_classic_poller_auth(instance, 0, &key, MfClassicKeyTypeA, NULL);
|
||||
frame_test->state = (error == MfClassicErrorNone) ?
|
||||
NfcTestMfClassicSendFrameTestStateReadBlock :
|
||||
NfcTestMfClassicSendFrameTestStateFail;
|
||||
} else if(frame_test->state == NfcTestMfClassicSendFrameTestStateReadBlock) {
|
||||
do {
|
||||
const uint8_t read_block_cmd[] = {
|
||||
0x30,
|
||||
0x01,
|
||||
0x8b,
|
||||
0xb9,
|
||||
};
|
||||
bit_buffer_copy_bytes(frame_test->tx_buf, read_block_cmd, sizeof(read_block_cmd));
|
||||
|
||||
error = mf_classic_poller_send_encrypted_frame(
|
||||
instance, frame_test->tx_buf, frame_test->rx_buf, 200000);
|
||||
if(error != MfClassicErrorNone) break;
|
||||
if(bit_buffer_get_size_bytes(frame_test->rx_buf) != 18) {
|
||||
error = MfClassicErrorProtocol;
|
||||
break;
|
||||
}
|
||||
|
||||
const uint8_t* rx_data = bit_buffer_get_data(frame_test->rx_buf);
|
||||
const uint8_t rx_data_ref[16] = {0};
|
||||
if(memcmp(rx_data, rx_data_ref, sizeof(rx_data_ref)) != 0) {
|
||||
error = MfClassicErrorProtocol;
|
||||
break;
|
||||
}
|
||||
} while(false);
|
||||
|
||||
frame_test->state = (error == MfClassicErrorNone) ?
|
||||
NfcTestMfClassicSendFrameTestStateSuccess :
|
||||
NfcTestMfClassicSendFrameTestStateFail;
|
||||
} else if(frame_test->state == NfcTestMfClassicSendFrameTestStateSuccess) {
|
||||
command = NfcCommandStop;
|
||||
} else if(frame_test->state == NfcTestMfClassicSendFrameTestStateFail) {
|
||||
command = NfcCommandStop;
|
||||
}
|
||||
} else {
|
||||
frame_test->state = NfcTestMfClassicSendFrameTestStateFail;
|
||||
command = NfcCommandStop;
|
||||
}
|
||||
|
||||
if(command == NfcCommandStop) {
|
||||
furi_thread_flags_set(frame_test->thread_id, NFC_TEST_FLAG_WORKER_DONE);
|
||||
}
|
||||
|
||||
return command;
|
||||
}
|
||||
|
||||
MU_TEST(mf_classic_send_frame_test) {
|
||||
Nfc* poller = nfc_alloc();
|
||||
Nfc* listener = nfc_alloc();
|
||||
|
||||
NfcDevice* nfc_device = nfc_device_alloc();
|
||||
nfc_data_generator_fill_data(NfcDataGeneratorTypeMfClassic4k_7b, nfc_device);
|
||||
NfcListener* mfc_listener = nfc_listener_alloc(
|
||||
listener, NfcProtocolMfClassic, nfc_device_get_data(nfc_device, NfcProtocolMfClassic));
|
||||
nfc_listener_start(mfc_listener, NULL, NULL);
|
||||
|
||||
NfcPoller* mfc_poller = nfc_poller_alloc(poller, NfcProtocolMfClassic);
|
||||
NfcTestMfClassicSendFrameTest context = {
|
||||
.state = NfcTestMfClassicSendFrameTestStateAuth,
|
||||
.thread_id = furi_thread_get_current_id(),
|
||||
.tx_buf = bit_buffer_alloc(32),
|
||||
.rx_buf = bit_buffer_alloc(32),
|
||||
};
|
||||
nfc_poller_start_ex(mfc_poller, mf_classic_poller_send_frame_callback, &context);
|
||||
|
||||
uint32_t flag =
|
||||
furi_thread_flags_wait(NFC_TEST_FLAG_WORKER_DONE, FuriFlagWaitAny, FuriWaitForever);
|
||||
mu_assert(flag == NFC_TEST_FLAG_WORKER_DONE, "Wrong thread flag");
|
||||
nfc_poller_stop(mfc_poller);
|
||||
nfc_poller_free(mfc_poller);
|
||||
|
||||
mu_assert(
|
||||
context.state == NfcTestMfClassicSendFrameTestStateSuccess, "Wrong test state at the end");
|
||||
|
||||
bit_buffer_free(context.tx_buf);
|
||||
bit_buffer_free(context.rx_buf);
|
||||
nfc_listener_stop(mfc_listener);
|
||||
nfc_listener_free(mfc_listener);
|
||||
nfc_device_free(nfc_device);
|
||||
nfc_free(listener);
|
||||
nfc_free(poller);
|
||||
}
|
||||
|
||||
MU_TEST(mf_classic_dict_test) {
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
if(storage_common_stat(storage, NFC_APP_MF_CLASSIC_DICT_UNIT_TEST_PATH, NULL) == FSE_OK) {
|
||||
|
@ -538,11 +661,11 @@ MU_TEST_SUITE(nfc) {
|
|||
MU_RUN_TEST(mf_classic_1k_7b_file_test);
|
||||
MU_RUN_TEST(mf_classic_4k_4b_file_test);
|
||||
MU_RUN_TEST(mf_classic_4k_7b_file_test);
|
||||
MU_RUN_TEST(mf_classic_reader);
|
||||
|
||||
MU_RUN_TEST(mf_classic_reader);
|
||||
MU_RUN_TEST(mf_classic_write);
|
||||
MU_RUN_TEST(mf_classic_value_block);
|
||||
|
||||
MU_RUN_TEST(mf_classic_send_frame_test);
|
||||
MU_RUN_TEST(mf_classic_dict_test);
|
||||
|
||||
nfc_test_free();
|
||||
|
|
|
@ -34,6 +34,8 @@ static void nfc_scene_more_info_on_enter_mf_desfire(NfcApp* instance) {
|
|||
static NfcCommand nfc_scene_read_poller_callback_mf_desfire(NfcGenericEvent event, void* context) {
|
||||
furi_assert(event.protocol == NfcProtocolMfDesfire);
|
||||
|
||||
NfcCommand command = NfcCommandContinue;
|
||||
|
||||
NfcApp* instance = context;
|
||||
const MfDesfirePollerEvent* mf_desfire_event = event.event_data;
|
||||
|
||||
|
@ -41,10 +43,12 @@ static NfcCommand nfc_scene_read_poller_callback_mf_desfire(NfcGenericEvent even
|
|||
nfc_device_set_data(
|
||||
instance->nfc_device, NfcProtocolMfDesfire, nfc_poller_get_data(instance->poller));
|
||||
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess);
|
||||
return NfcCommandStop;
|
||||
command = NfcCommandStop;
|
||||
} else if(mf_desfire_event->type == MfDesfirePollerEventTypeReadFailed) {
|
||||
command = NfcCommandReset;
|
||||
}
|
||||
|
||||
return NfcCommandContinue;
|
||||
return command;
|
||||
}
|
||||
|
||||
static void nfc_scene_read_on_enter_mf_desfire(NfcApp* instance) {
|
||||
|
|
|
@ -11,24 +11,29 @@ void nfc_render_mf_desfire_info(
|
|||
const uint32_t bytes_total = 1UL << (data->version.sw_storage >> 1);
|
||||
const uint32_t bytes_free = data->free_memory.is_present ? data->free_memory.bytes_free : 0;
|
||||
|
||||
if(data->master_key_settings.is_free_directory_list) {
|
||||
const uint32_t app_count = simple_array_get_count(data->applications);
|
||||
uint32_t file_count = 0;
|
||||
|
||||
for(uint32_t i = 0; i < app_count; ++i) {
|
||||
const MfDesfireApplication* app = simple_array_cget(data->applications, i);
|
||||
if(app->key_settings.is_free_directory_list) {
|
||||
file_count += simple_array_get_count(app->file_ids);
|
||||
}
|
||||
}
|
||||
|
||||
furi_string_cat_printf(str, "\n%lu Application%s", app_count, app_count != 1 ? "s" : "");
|
||||
furi_string_cat_printf(str, ", %lu File%s", file_count, file_count != 1 ? "s" : "");
|
||||
} else {
|
||||
furi_string_cat_printf(str, "\nAuth required to read apps!");
|
||||
}
|
||||
|
||||
furi_string_cat_printf(str, "\n%lu", bytes_total);
|
||||
|
||||
if(data->version.sw_storage & 1) {
|
||||
furi_string_push_back(str, '+');
|
||||
}
|
||||
|
||||
furi_string_cat_printf(str, " bytes, %lu bytes free\n", bytes_free);
|
||||
|
||||
const uint32_t app_count = simple_array_get_count(data->applications);
|
||||
uint32_t file_count = 0;
|
||||
|
||||
for(uint32_t i = 0; i < app_count; ++i) {
|
||||
const MfDesfireApplication* app = simple_array_cget(data->applications, i);
|
||||
file_count += simple_array_get_count(app->file_ids);
|
||||
}
|
||||
|
||||
furi_string_cat_printf(str, "%lu Application%s", app_count, app_count != 1 ? "s" : "");
|
||||
furi_string_cat_printf(str, ", %lu File%s", file_count, file_count != 1 ? "s" : "");
|
||||
furi_string_cat_printf(str, " bytes, %lu bytes free", bytes_free);
|
||||
|
||||
if(format_type != NfcProtocolFormatTypeFull) return;
|
||||
|
||||
|
@ -101,17 +106,29 @@ void nfc_render_mf_desfire_free_memory(const MfDesfireFreeMemory* data, FuriStri
|
|||
}
|
||||
|
||||
void nfc_render_mf_desfire_key_settings(const MfDesfireKeySettings* data, FuriString* str) {
|
||||
furi_string_cat_printf(str, "changeKeyID %d\n", data->change_key_id);
|
||||
furi_string_cat_printf(str, "configChangeable %d\n", data->is_config_changeable);
|
||||
furi_string_cat_printf(str, "freeCreateDelete %d\n", data->is_free_create_delete);
|
||||
furi_string_cat_printf(str, "freeDirectoryList %d\n", data->is_free_directory_list);
|
||||
furi_string_cat_printf(str, "masterChangeable %d\n", data->is_master_key_changeable);
|
||||
if(data->is_free_directory_list) {
|
||||
furi_string_cat_printf(str, "changeKeyID %d\n", data->change_key_id);
|
||||
furi_string_cat_printf(str, "configChangeable %d\n", data->is_config_changeable);
|
||||
furi_string_cat_printf(str, "freeCreateDelete %d\n", data->is_free_create_delete);
|
||||
furi_string_cat_printf(str, "freeDirectoryList %d\n", data->is_free_directory_list);
|
||||
furi_string_cat_printf(str, "masterChangeable %d\n", data->is_master_key_changeable);
|
||||
} else {
|
||||
furi_string_cat_printf(str, "changeKeyID ??\n");
|
||||
furi_string_cat_printf(str, "configChangeable ??\n");
|
||||
furi_string_cat_printf(str, "freeCreateDelete ??\n");
|
||||
furi_string_cat_printf(str, "freeDirectoryList 0\n");
|
||||
furi_string_cat_printf(str, "masterChangeable ??\n");
|
||||
}
|
||||
|
||||
if(data->flags) {
|
||||
furi_string_cat_printf(str, "flags %d\n", data->flags);
|
||||
}
|
||||
|
||||
furi_string_cat_printf(str, "maxKeys %d\n", data->max_keys);
|
||||
if(data->is_free_directory_list) {
|
||||
furi_string_cat_printf(str, "maxKeys %d\n", data->max_keys);
|
||||
} else {
|
||||
furi_string_cat_printf(str, "maxKeys ??\n");
|
||||
}
|
||||
}
|
||||
|
||||
void nfc_render_mf_desfire_key_version(
|
||||
|
@ -123,14 +140,16 @@ void nfc_render_mf_desfire_key_version(
|
|||
|
||||
void nfc_render_mf_desfire_application_id(const MfDesfireApplicationId* data, FuriString* str) {
|
||||
const uint8_t* app_id = data->data;
|
||||
furi_string_cat_printf(str, "Application %02x%02x%02x\n", app_id[0], app_id[1], app_id[2]);
|
||||
furi_string_cat_printf(str, "Application %02x%02x%02x\n", app_id[2], app_id[1], app_id[0]);
|
||||
}
|
||||
|
||||
void nfc_render_mf_desfire_application(const MfDesfireApplication* data, FuriString* str) {
|
||||
nfc_render_mf_desfire_key_settings(&data->key_settings, str);
|
||||
|
||||
for(uint32_t i = 0; i < simple_array_get_count(data->key_versions); ++i) {
|
||||
nfc_render_mf_desfire_key_version(simple_array_cget(data->key_versions, i), i, str);
|
||||
if(data->key_settings.is_free_directory_list) {
|
||||
for(uint32_t i = 0; i < simple_array_get_count(data->key_versions); ++i) {
|
||||
nfc_render_mf_desfire_key_version(simple_array_cget(data->key_versions, i), i, str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -179,13 +198,16 @@ void nfc_render_mf_desfire_file_settings_data(
|
|||
}
|
||||
|
||||
furi_string_cat_printf(str, "%s %s\n", type, comm);
|
||||
furi_string_cat_printf(
|
||||
str,
|
||||
"r %d w %d rw %d c %d\n",
|
||||
settings->access_rights >> 12 & 0xF,
|
||||
settings->access_rights >> 8 & 0xF,
|
||||
settings->access_rights >> 4 & 0xF,
|
||||
settings->access_rights & 0xF);
|
||||
|
||||
for(size_t i = 0; i < settings->access_rights_len; i++) {
|
||||
furi_string_cat_printf(
|
||||
str,
|
||||
"r %d w %d rw %d c %d\n",
|
||||
settings->access_rights[i] >> 12 & 0xF,
|
||||
settings->access_rights[i] >> 8 & 0xF,
|
||||
settings->access_rights[i] >> 4 & 0xF,
|
||||
settings->access_rights[i] & 0xF);
|
||||
}
|
||||
|
||||
uint32_t record_count = 1;
|
||||
uint32_t record_size = 0;
|
||||
|
@ -217,6 +239,20 @@ void nfc_render_mf_desfire_file_settings_data(
|
|||
break;
|
||||
}
|
||||
|
||||
bool is_auth_required = true;
|
||||
for(size_t i = 0; i < settings->access_rights_len; i++) {
|
||||
uint8_t read_rights = (settings->access_rights[i] >> 12) & 0x0f;
|
||||
uint8_t read_write_rights = (settings->access_rights[i] >> 4) & 0x0f;
|
||||
if((read_rights == 0x0e) || (read_write_rights == 0x0e)) {
|
||||
is_auth_required = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(is_auth_required) {
|
||||
furi_string_cat_printf(str, "Auth required to read file data\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if(simple_array_get_count(data->data) == 0) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@ void nfc_scene_mf_desfire_more_info_on_enter(void* context) {
|
|||
for(uint32_t i = 0; i < simple_array_get_count(data->application_ids); ++i) {
|
||||
const MfDesfireApplicationId* app_id = simple_array_cget(data->application_ids, i);
|
||||
furi_string_printf(
|
||||
label, "App %02x%02x%02x", app_id->data[0], app_id->data[1], app_id->data[2]);
|
||||
label, "App %02x%02x%02x", app_id->data[2], app_id->data[1], app_id->data[0]);
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
furi_string_get_cstr(label),
|
||||
|
|
|
@ -315,6 +315,89 @@ MfClassicError mf_classic_poller_value_cmd(
|
|||
*/
|
||||
MfClassicError mf_classic_poller_value_transfer(MfClassicPoller* instance, uint8_t block_num);
|
||||
|
||||
/**
|
||||
* @brief Transmit and receive Iso14443_3a standard frames in poller mode.
|
||||
*
|
||||
* Must ONLY be used inside the callback function.
|
||||
*
|
||||
* The rx_buffer will be filled with any data received as a response to data
|
||||
* sent from tx_buffer, with a timeout defined by the fwt parameter.
|
||||
*
|
||||
* @param[in, out] instance pointer to the instance to be used in the transaction.
|
||||
* @param[in] tx_buffer pointer to the buffer containing the data to be transmitted.
|
||||
* @param[out] rx_buffer pointer to the buffer to be filled with received data.
|
||||
* @param[in] fwt frame wait time (response timeout), in carrier cycles.
|
||||
* @return MfClassicErrorNone on success, an error code on failure.
|
||||
*/
|
||||
MfClassicError mf_classic_poller_send_standard_frame(
|
||||
MfClassicPoller* instance,
|
||||
const BitBuffer* tx_buffer,
|
||||
BitBuffer* rx_buffer,
|
||||
uint32_t fwt_fc);
|
||||
|
||||
/**
|
||||
* @brief Transmit and receive Iso14443_3a frames in poller mode.
|
||||
*
|
||||
* Must ONLY be used inside the callback function.
|
||||
*
|
||||
* The rx_buffer will be filled with any data received as a response to data
|
||||
* sent from tx_buffer, with a timeout defined by the fwt parameter.
|
||||
*
|
||||
* @param[in, out] instance pointer to the instance to be used in the transaction.
|
||||
* @param[in] tx_buffer pointer to the buffer containing the data to be transmitted.
|
||||
* @param[out] rx_buffer pointer to the buffer to be filled with received data.
|
||||
* @param[in] fwt frame wait time (response timeout), in carrier cycles.
|
||||
* @return MfClassicErrorNone on success, an error code on failure.
|
||||
*/
|
||||
MfClassicError mf_classic_poller_send_frame(
|
||||
MfClassicPoller* instance,
|
||||
const BitBuffer* tx_buffer,
|
||||
BitBuffer* rx_buffer,
|
||||
uint32_t fwt_fc);
|
||||
|
||||
/**
|
||||
* @brief Transmit and receive Iso14443_3a frames with custom parity bits in poller mode.
|
||||
*
|
||||
* Must ONLY be used inside the callback function.
|
||||
*
|
||||
* The rx_buffer will be filled with any data received as a response to data
|
||||
* sent from tx_buffer, with a timeout defined by the fwt parameter.
|
||||
*
|
||||
* Custom parity bits must be set in the tx_buffer. The rx_buffer will contain
|
||||
* the received data with the parity bits.
|
||||
*
|
||||
* @param[in, out] instance pointer to the instance to be used in the transaction.
|
||||
* @param[in] tx_buffer pointer to the buffer containing the data to be transmitted.
|
||||
* @param[out] rx_buffer pointer to the buffer to be filled with received data.
|
||||
* @param[in] fwt frame wait time (response timeout), in carrier cycles.
|
||||
* @return MfClassicErrorNone on success, an error code on failure.
|
||||
*/
|
||||
MfClassicError mf_classic_poller_send_custom_parity_frame(
|
||||
MfClassicPoller* instance,
|
||||
const BitBuffer* tx_buffer,
|
||||
BitBuffer* rx_buffer,
|
||||
uint32_t fwt_fc);
|
||||
|
||||
/**
|
||||
* @brief Transmit and receive Mifare Classic encrypted frames with custom parity bits in poller mode.
|
||||
*
|
||||
* Must ONLY be used inside the callback function.
|
||||
*
|
||||
* The rx_buffer will be filled with any data received as a response to data
|
||||
* sent from tx_buffer, with a timeout defined by the fwt parameter.
|
||||
*
|
||||
* @param[in, out] instance pointer to the instance to be used in the transaction.
|
||||
* @param[in] tx_buffer pointer to the buffer containing the plain data to be transmitted.
|
||||
* @param[out] rx_buffer pointer to the buffer to be filled with decyphered received data.
|
||||
* @param[in] fwt frame wait time (response timeout), in carrier cycles.
|
||||
* @return MfClassicErrorNone on success, an error code on failure.
|
||||
*/
|
||||
MfClassicError mf_classic_poller_send_encrypted_frame(
|
||||
MfClassicPoller* instance,
|
||||
const BitBuffer* tx_buffer,
|
||||
BitBuffer* rx_buffer,
|
||||
uint32_t fwt_fc);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -465,3 +465,77 @@ MfClassicError mf_classic_poller_value_transfer(MfClassicPoller* instance, uint8
|
|||
|
||||
return ret;
|
||||
}
|
||||
|
||||
MfClassicError mf_classic_poller_send_standard_frame(
|
||||
MfClassicPoller* instance,
|
||||
const BitBuffer* tx_buffer,
|
||||
BitBuffer* rx_buffer,
|
||||
uint32_t fwt_fc) {
|
||||
furi_check(instance);
|
||||
furi_check(tx_buffer);
|
||||
furi_check(rx_buffer);
|
||||
|
||||
Iso14443_3aError error = iso14443_3a_poller_send_standard_frame(
|
||||
instance->iso14443_3a_poller, tx_buffer, rx_buffer, fwt_fc);
|
||||
|
||||
return mf_classic_process_error(error);
|
||||
}
|
||||
|
||||
MfClassicError mf_classic_poller_send_frame(
|
||||
MfClassicPoller* instance,
|
||||
const BitBuffer* tx_buffer,
|
||||
BitBuffer* rx_buffer,
|
||||
uint32_t fwt_fc) {
|
||||
furi_check(instance);
|
||||
furi_check(tx_buffer);
|
||||
furi_check(rx_buffer);
|
||||
|
||||
Iso14443_3aError error =
|
||||
iso14443_3a_poller_txrx(instance->iso14443_3a_poller, tx_buffer, rx_buffer, fwt_fc);
|
||||
|
||||
return mf_classic_process_error(error);
|
||||
}
|
||||
|
||||
MfClassicError mf_classic_poller_send_custom_parity_frame(
|
||||
MfClassicPoller* instance,
|
||||
const BitBuffer* tx_buffer,
|
||||
BitBuffer* rx_buffer,
|
||||
uint32_t fwt_fc) {
|
||||
furi_check(instance);
|
||||
furi_check(tx_buffer);
|
||||
furi_check(rx_buffer);
|
||||
|
||||
Iso14443_3aError error = iso14443_3a_poller_txrx_custom_parity(
|
||||
instance->iso14443_3a_poller, tx_buffer, rx_buffer, fwt_fc);
|
||||
|
||||
return mf_classic_process_error(error);
|
||||
}
|
||||
|
||||
MfClassicError mf_classic_poller_send_encrypted_frame(
|
||||
MfClassicPoller* instance,
|
||||
const BitBuffer* tx_buffer,
|
||||
BitBuffer* rx_buffer,
|
||||
uint32_t fwt_fc) {
|
||||
furi_check(instance);
|
||||
furi_check(tx_buffer);
|
||||
furi_check(rx_buffer);
|
||||
|
||||
MfClassicError ret = MfClassicErrorNone;
|
||||
do {
|
||||
crypto1_encrypt(instance->crypto, NULL, tx_buffer, instance->tx_encrypted_buffer);
|
||||
|
||||
Iso14443_3aError error = iso14443_3a_poller_txrx_custom_parity(
|
||||
instance->iso14443_3a_poller,
|
||||
instance->tx_encrypted_buffer,
|
||||
instance->rx_encrypted_buffer,
|
||||
fwt_fc);
|
||||
if(error != Iso14443_3aErrorNone) {
|
||||
ret = mf_classic_process_error(error);
|
||||
break;
|
||||
}
|
||||
|
||||
crypto1_decrypt(instance->crypto, instance->rx_encrypted_buffer, rx_buffer);
|
||||
} while(false);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -21,8 +21,6 @@ extern "C" {
|
|||
#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)
|
||||
|
||||
|
@ -71,11 +69,6 @@ typedef struct {
|
|||
|
||||
typedef uint8_t MfDesfireKeyVersion;
|
||||
|
||||
typedef struct {
|
||||
MfDesfireKeySettings key_settings;
|
||||
SimpleArray* key_versions;
|
||||
} MfDesfireKeyConfiguration;
|
||||
|
||||
typedef enum {
|
||||
MfDesfireFileTypeStandard = 0,
|
||||
MfDesfireFileTypeBackup = 1,
|
||||
|
@ -96,7 +89,8 @@ typedef uint16_t MfDesfireFileAccessRights;
|
|||
typedef struct {
|
||||
MfDesfireFileType type;
|
||||
MfDesfireFileCommunicationSettings comm;
|
||||
MfDesfireFileAccessRights access_rights;
|
||||
MfDesfireFileAccessRights access_rights[MF_DESFIRE_MAX_KEYS];
|
||||
uint8_t access_rights_len;
|
||||
union {
|
||||
struct {
|
||||
uint32_t size;
|
||||
|
@ -136,6 +130,7 @@ typedef enum {
|
|||
MfDesfireErrorNotPresent,
|
||||
MfDesfireErrorProtocol,
|
||||
MfDesfireErrorTimeout,
|
||||
MfDesfireErrorAuthentication,
|
||||
} MfDesfireError;
|
||||
|
||||
typedef struct {
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
#include "mf_desfire_i.h"
|
||||
|
||||
#define TAG "MfDesfire"
|
||||
|
||||
#define BITS_IN_BYTE (8U)
|
||||
|
||||
#define MF_DESFIRE_FFF_VERSION_KEY \
|
||||
|
@ -175,59 +177,88 @@ bool mf_desfire_file_settings_parse(MfDesfireFileSettings* data, const BitBuffer
|
|||
};
|
||||
} MfDesfireFileSettingsLayout;
|
||||
|
||||
MfDesfireFileSettings file_settings_temp = {};
|
||||
do {
|
||||
const size_t data_size = bit_buffer_get_size_bytes(buf);
|
||||
const uint8_t* data_ptr = bit_buffer_get_data(buf);
|
||||
const size_t min_data_size =
|
||||
sizeof(MfDesfireFileSettingsHeader) + sizeof(MfDesfireFileSettingsData);
|
||||
const size_t max_data_size =
|
||||
sizeof(MfDesfireFileSettingsHeader) + sizeof(MfDesfireFileSettingsValue);
|
||||
|
||||
if(data_size < min_data_size) break;
|
||||
if(data_size <= max_data_size) {
|
||||
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.limited_credit_value;
|
||||
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;
|
||||
}
|
||||
} else {
|
||||
// TODO FL-3750: process HID Desfire command response here
|
||||
// Set default fields for now
|
||||
data->type = 0;
|
||||
data->comm = 0;
|
||||
data->access_rights = 0;
|
||||
data->data.size = 0;
|
||||
if(data_size < min_data_size) {
|
||||
FURI_LOG_E(
|
||||
TAG, "File settings size %zu less than minimum %zu", data_size, min_data_size);
|
||||
break;
|
||||
}
|
||||
|
||||
size_t bytes_processed = sizeof(MfDesfireFileSettingsHeader);
|
||||
MfDesfireFileSettingsLayout layout = {};
|
||||
memcpy(&layout.header, data_ptr, sizeof(MfDesfireFileSettingsHeader));
|
||||
bool has_additional_access_rights = (layout.header.comm & 0x80) != 0;
|
||||
|
||||
file_settings_temp.type = layout.header.type;
|
||||
file_settings_temp.comm = layout.header.comm & 0x03;
|
||||
file_settings_temp.access_rights_len = 1;
|
||||
file_settings_temp.access_rights[0] = layout.header.access_rights;
|
||||
|
||||
if(file_settings_temp.type == MfDesfireFileTypeStandard ||
|
||||
file_settings_temp.type == MfDesfireFileTypeBackup) {
|
||||
memcpy(
|
||||
&layout.data,
|
||||
&data_ptr[sizeof(MfDesfireFileSettingsHeader)],
|
||||
sizeof(MfDesfireFileSettingsData));
|
||||
file_settings_temp.data.size = layout.data.size;
|
||||
bytes_processed += sizeof(MfDesfireFileSettingsData);
|
||||
} else if(file_settings_temp.type == MfDesfireFileTypeValue) {
|
||||
memcpy(
|
||||
&layout.value,
|
||||
&data_ptr[sizeof(MfDesfireFileSettingsHeader)],
|
||||
sizeof(MfDesfireFileSettingsValue));
|
||||
file_settings_temp.value.lo_limit = layout.value.lo_limit;
|
||||
file_settings_temp.value.hi_limit = layout.value.hi_limit;
|
||||
file_settings_temp.value.limited_credit_value = layout.value.limited_credit_value;
|
||||
file_settings_temp.value.limited_credit_enabled = layout.value.limited_credit_enabled;
|
||||
bytes_processed += sizeof(MfDesfireFileSettingsValue);
|
||||
} else if(
|
||||
file_settings_temp.type == MfDesfireFileTypeLinearRecord ||
|
||||
file_settings_temp.type == MfDesfireFileTypeCyclicRecord) {
|
||||
memcpy(
|
||||
&layout.record,
|
||||
&data_ptr[sizeof(MfDesfireFileSettingsHeader)],
|
||||
sizeof(MfDesfireFileSettingsRecord));
|
||||
file_settings_temp.record.size = layout.record.size;
|
||||
file_settings_temp.record.max = layout.record.max;
|
||||
file_settings_temp.record.cur = layout.record.cur;
|
||||
bytes_processed += sizeof(MfDesfireFileSettingsRecord);
|
||||
} else {
|
||||
FURI_LOG_W(TAG, "Unknown file type: %02x", file_settings_temp.type);
|
||||
break;
|
||||
}
|
||||
|
||||
if(has_additional_access_rights) {
|
||||
uint8_t additional_access_rights_len = bit_buffer_get_byte(buf, bytes_processed);
|
||||
FURI_LOG_D(TAG, "Has additional rights: %d", additional_access_rights_len);
|
||||
if(data_size != bytes_processed +
|
||||
additional_access_rights_len * sizeof(MfDesfireFileAccessRights) +
|
||||
1) {
|
||||
FURI_LOG_W(TAG, "Unexpected command length: %zu", data_size);
|
||||
for(size_t i = 0; i < bit_buffer_get_size_bytes(buf); i++) {
|
||||
printf("%02X ", bit_buffer_get_byte(buf, i));
|
||||
}
|
||||
printf("\r\n");
|
||||
break;
|
||||
}
|
||||
if(additional_access_rights_len >
|
||||
MF_DESFIRE_MAX_KEYS * sizeof(MfDesfireFileAccessRights))
|
||||
break;
|
||||
|
||||
memcpy(
|
||||
&file_settings_temp.access_rights[1],
|
||||
&data_ptr[bytes_processed],
|
||||
additional_access_rights_len * sizeof(MfDesfireFileAccessRights));
|
||||
file_settings_temp.access_rights_len += additional_access_rights_len;
|
||||
}
|
||||
|
||||
*data = file_settings_temp;
|
||||
parsed = true;
|
||||
} while(false);
|
||||
|
||||
|
@ -392,18 +423,19 @@ bool mf_desfire_file_settings_load(
|
|||
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)))
|
||||
uint32_t access_rights_len = 0;
|
||||
if(!flipper_format_get_value_count(ff, furi_string_get_cstr(key), &access_rights_len))
|
||||
break;
|
||||
if((access_rights_len == 0) || ((access_rights_len % 2) != 0)) break;
|
||||
if(!flipper_format_read_hex(
|
||||
ff, furi_string_get_cstr(key), (uint8_t*)&data->access_rights, access_rights_len))
|
||||
break;
|
||||
data->access_rights_len = access_rights_len / sizeof(MfDesfireFileAccessRights);
|
||||
|
||||
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))
|
||||
|
@ -641,8 +673,8 @@ bool mf_desfire_file_settings_save(
|
|||
if(!flipper_format_write_hex(
|
||||
ff,
|
||||
furi_string_get_cstr(key),
|
||||
(const uint8_t*)&data->access_rights,
|
||||
sizeof(MfDesfireFileAccessRights)))
|
||||
(const uint8_t*)data->access_rights,
|
||||
data->access_rights_len * sizeof(MfDesfireFileAccessRights)))
|
||||
break;
|
||||
|
||||
if(data->type == MfDesfireFileTypeStandard || data->type == MfDesfireFileTypeBackup) {
|
||||
|
@ -737,8 +769,11 @@ bool mf_desfire_application_save(
|
|||
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;
|
||||
if(file_count > 0) {
|
||||
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);
|
||||
|
|
|
@ -5,6 +5,52 @@
|
|||
#define MF_DESFIRE_FFF_PICC_PREFIX "PICC"
|
||||
#define MF_DESFIRE_FFF_APP_PREFIX "Application"
|
||||
|
||||
// Successful operation
|
||||
#define MF_DESFIRE_STATUS_OPERATION_OK (0x00)
|
||||
// No changes done to backup files, CommitTransaction / AbortTransaction not necessary
|
||||
#define MF_DESFIRE_STATUS_NO_CHANGES (0x0C)
|
||||
// Insufficient NV-Memory to complete command
|
||||
#define MF_DESFIRE_STATUS_OUT_OF_EEPROM_ERROR (0x0E)
|
||||
// Command code not supported
|
||||
#define MF_DESFIRE_STATUS_ILLEGAL_COMMAND_CODE (0x1C)
|
||||
// CRC or MAC does not match data Padding bytes not valid
|
||||
#define MF_DESFIRE_STATUS_INTEGRITY_ERROR (0x1E)
|
||||
// Invalid key number specified
|
||||
#define MF_DESFIRE_STATUS_NO_SUCH_KEY (0x40)
|
||||
// Length of command string invalid
|
||||
#define MF_DESFIRE_STATUS_LENGTH_ERROR (0x7E)
|
||||
// Current configuration / status does not allow the requested command
|
||||
#define MF_DESFIRE_STATUS_PERMISSION_DENIED (0x9D)
|
||||
// Value of the parameter(s) invalid
|
||||
#define MF_DESFIRE_STATUS_PARAMETER_ERROR (0x9E)
|
||||
// Requested AID not present on PICC
|
||||
#define MF_DESFIRE_STATUS_APPLICATION_NOT_FOUND (0xA0)
|
||||
// Unrecoverable error within application, application will be disabled
|
||||
#define MF_DESFIRE_STATUS_APPL_INTEGRITY_ERROR (0xA1)
|
||||
// Current authentication status does not allow the requested command
|
||||
#define MF_DESFIRE_STATUS_AUTHENTICATION_ERROR (0xAE)
|
||||
// Additional data frame is expected to be sent
|
||||
#define MF_DESFIRE_STATUS_ADDITIONAL_FRAME (0xAF)
|
||||
// Attempt to read/write data from/to beyond the file's/record's limits
|
||||
// Attempt to exceed the limits of a value file.
|
||||
#define MF_DESFIRE_STATUS_BOUNDARY_ERROR (0xBE)
|
||||
// Unrecoverable error within PICC, PICC will be disabled
|
||||
#define MF_DESFIRE_STATUS_PICC_INTEGRITY_ERROR (0xC1)
|
||||
// Previous Command was not fully completed. Not all Frames were requested or provided by the PCD
|
||||
#define MF_DESFIRE_STATUS_COMMAND_ABORTED (0xCA)
|
||||
// PICC was disabled by an unrecoverable error
|
||||
#define MF_DESFIRE_STATUS_PICC_DISABLED_ERROR (0xCD)
|
||||
// Number of Applications limited to 28, no additional CreateApplication possible
|
||||
#define MF_DESFIRE_STATUS_COUNT_ERROR (0xCE)
|
||||
// Creation of file/application failed because file/application with same number already exists
|
||||
#define MF_DESFIRE_STATUS_DUBLICATE_ERROR (0xDE)
|
||||
// Could not complete NV-write operation due to loss of power, internal backup/rollback mechanism activated
|
||||
#define MF_DESFIRE_STATUS_EEPROM_ERROR (0xEE)
|
||||
// Specified file number does not exist
|
||||
#define MF_DESFIRE_STATUS_FILE_NOT_FOUND (0xF0)
|
||||
// Unrecoverable error within file, file will be disabled
|
||||
#define MF_DESFIRE_STATUS_FILE_INTEGRITY_ERROR (0xF1)
|
||||
|
||||
// SimpleArray configurations
|
||||
|
||||
extern const SimpleArrayConfig mf_desfire_key_version_array_config;
|
||||
|
|
|
@ -75,17 +75,23 @@ static NfcCommand mf_desfire_poller_handler_read_version(MfDesfirePoller* instan
|
|||
}
|
||||
|
||||
static NfcCommand mf_desfire_poller_handler_read_free_memory(MfDesfirePoller* instance) {
|
||||
NfcCommand command = NfcCommandContinue;
|
||||
|
||||
instance->error = mf_desfire_poller_read_free_memory(instance, &instance->data->free_memory);
|
||||
if(instance->error == MfDesfireErrorNone) {
|
||||
FURI_LOG_D(TAG, "Read free memory success");
|
||||
instance->state = MfDesfirePollerStateReadMasterKeySettings;
|
||||
} else if(instance->error == MfDesfireErrorNotPresent) {
|
||||
FURI_LOG_D(TAG, "Read free memoty is unsupported");
|
||||
instance->state = MfDesfirePollerStateReadMasterKeySettings;
|
||||
command = NfcCommandReset;
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "Failed to read free memory");
|
||||
iso14443_4a_poller_halt(instance->iso14443_4a_poller);
|
||||
instance->state = MfDesfirePollerStateReadFailed;
|
||||
}
|
||||
|
||||
return NfcCommandContinue;
|
||||
return command;
|
||||
}
|
||||
|
||||
static NfcCommand mf_desfire_poller_handler_read_master_key_settings(MfDesfirePoller* instance) {
|
||||
|
@ -94,6 +100,11 @@ static NfcCommand mf_desfire_poller_handler_read_master_key_settings(MfDesfirePo
|
|||
if(instance->error == MfDesfireErrorNone) {
|
||||
FURI_LOG_D(TAG, "Read master key settings success");
|
||||
instance->state = MfDesfirePollerStateReadMasterKeyVersion;
|
||||
} else if(instance->error == MfDesfireErrorAuthentication) {
|
||||
FURI_LOG_D(TAG, "Auth is required to read master key settings and app ids");
|
||||
instance->data->master_key_settings.is_free_directory_list = false;
|
||||
instance->data->master_key_settings.max_keys = 1;
|
||||
instance->state = MfDesfirePollerStateReadMasterKeyVersion;
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "Failed to read master key settings");
|
||||
iso14443_4a_poller_halt(instance->iso14443_4a_poller);
|
||||
|
@ -110,7 +121,11 @@ static NfcCommand mf_desfire_poller_handler_read_master_key_version(MfDesfirePol
|
|||
instance->data->master_key_settings.max_keys);
|
||||
if(instance->error == MfDesfireErrorNone) {
|
||||
FURI_LOG_D(TAG, "Read master key version success");
|
||||
instance->state = MfDesfirePollerStateReadApplicationIds;
|
||||
if(instance->data->master_key_settings.is_free_directory_list) {
|
||||
instance->state = MfDesfirePollerStateReadApplicationIds;
|
||||
} else {
|
||||
instance->state = MfDesfirePollerStateReadSuccess;
|
||||
}
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "Failed to read master key version");
|
||||
iso14443_4a_poller_halt(instance->iso14443_4a_poller);
|
||||
|
@ -126,6 +141,9 @@ static NfcCommand mf_desfire_poller_handler_read_application_ids(MfDesfirePoller
|
|||
if(instance->error == MfDesfireErrorNone) {
|
||||
FURI_LOG_D(TAG, "Read application ids success");
|
||||
instance->state = MfDesfirePollerStateReadApplications;
|
||||
} else if(instance->error == MfDesfireErrorAuthentication) {
|
||||
FURI_LOG_D(TAG, "Read application ids impossible without authentication");
|
||||
instance->state = MfDesfirePollerStateReadSuccess;
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "Failed to read application ids");
|
||||
iso14443_4a_poller_halt(instance->iso14443_4a_poller);
|
||||
|
|
|
@ -19,6 +19,17 @@ MfDesfireError mf_desfire_process_error(Iso14443_4aError error) {
|
|||
}
|
||||
}
|
||||
|
||||
MfDesfireError mf_desfire_process_status_code(uint8_t status_code) {
|
||||
switch(status_code) {
|
||||
case MF_DESFIRE_STATUS_OPERATION_OK:
|
||||
return MfDesfireErrorNone;
|
||||
case MF_DESFIRE_STATUS_AUTHENTICATION_ERROR:
|
||||
return MfDesfireErrorAuthentication;
|
||||
default:
|
||||
return MfDesfireErrorProtocol;
|
||||
}
|
||||
}
|
||||
|
||||
MfDesfireError mf_desfire_send_chunks(
|
||||
MfDesfirePoller* instance,
|
||||
const BitBuffer* tx_buffer,
|
||||
|
@ -42,7 +53,7 @@ MfDesfireError mf_desfire_send_chunks(
|
|||
}
|
||||
|
||||
bit_buffer_reset(instance->tx_buffer);
|
||||
bit_buffer_append_byte(instance->tx_buffer, MF_DESFIRE_FLAG_HAS_NEXT);
|
||||
bit_buffer_append_byte(instance->tx_buffer, MF_DESFIRE_STATUS_ADDITIONAL_FRAME);
|
||||
|
||||
if(bit_buffer_get_size_bytes(instance->rx_buffer) > sizeof(uint8_t)) {
|
||||
bit_buffer_copy_right(rx_buffer, instance->rx_buffer, sizeof(uint8_t));
|
||||
|
@ -50,7 +61,8 @@ MfDesfireError mf_desfire_send_chunks(
|
|||
bit_buffer_reset(rx_buffer);
|
||||
}
|
||||
|
||||
while(bit_buffer_starts_with_byte(instance->rx_buffer, MF_DESFIRE_FLAG_HAS_NEXT)) {
|
||||
while(
|
||||
bit_buffer_starts_with_byte(instance->rx_buffer, MF_DESFIRE_STATUS_ADDITIONAL_FRAME)) {
|
||||
Iso14443_4aError iso14443_4a_error = iso14443_4a_poller_send_block(
|
||||
instance->iso14443_4a_poller, instance->tx_buffer, instance->rx_buffer);
|
||||
|
||||
|
@ -71,6 +83,11 @@ MfDesfireError mf_desfire_send_chunks(
|
|||
}
|
||||
} while(false);
|
||||
|
||||
if(error == MfDesfireErrorNone) {
|
||||
uint8_t err_code = bit_buffer_get_byte(instance->rx_buffer, 0);
|
||||
error = mf_desfire_process_status_code(err_code);
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
|
@ -110,7 +127,7 @@ MfDesfireError
|
|||
if(error != MfDesfireErrorNone) break;
|
||||
|
||||
if(!mf_desfire_free_memory_parse(data, instance->result_buffer)) {
|
||||
error = MfDesfireErrorProtocol;
|
||||
error = MfDesfireErrorNotPresent;
|
||||
}
|
||||
} while(false);
|
||||
|
||||
|
@ -414,13 +431,25 @@ MfDesfireError mf_desfire_poller_read_file_data_multi(
|
|||
simple_array_init(data, file_id_count);
|
||||
}
|
||||
|
||||
for(uint32_t i = 0; i < file_id_count; ++i) {
|
||||
for(size_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);
|
||||
|
||||
bool can_read_data = false;
|
||||
for(size_t j = 0; j < file_settings_cur->access_rights_len; j++) {
|
||||
uint8_t read_access = (file_settings_cur->access_rights[j] >> 12) & 0x0f;
|
||||
uint8_t read_write_access = (file_settings_cur->access_rights[j] >> 4) & 0x0f;
|
||||
can_read_data = (read_access == 0x0e) || (read_write_access == 0x0e);
|
||||
if(can_read_data) break;
|
||||
}
|
||||
if(!can_read_data) {
|
||||
FURI_LOG_D(TAG, "Can't read file %zu data without authentication", i);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(file_type == MfDesfireFileTypeStandard || file_type == MfDesfireFileTypeBackup) {
|
||||
error = mf_desfire_poller_read_file_data(
|
||||
instance, file_id, 0, file_settings_cur->data.size, file_data);
|
||||
|
@ -432,8 +461,6 @@ MfDesfireError mf_desfire_poller_read_file_data_multi(
|
|||
error = mf_desfire_poller_read_file_records(
|
||||
instance, file_id, 0, file_settings_cur->data.size, file_data);
|
||||
}
|
||||
|
||||
if(error != MfDesfireErrorNone) break;
|
||||
}
|
||||
|
||||
return error;
|
||||
|
@ -448,22 +475,36 @@ MfDesfireError
|
|||
|
||||
do {
|
||||
error = mf_desfire_poller_read_key_settings(instance, &data->key_settings);
|
||||
if(error == MfDesfireErrorAuthentication) {
|
||||
FURI_LOG_D(TAG, "Auth is required to read master key settings and app ids");
|
||||
data->key_settings.is_free_directory_list = false;
|
||||
error = MfDesfireErrorNone;
|
||||
break;
|
||||
}
|
||||
if(error != MfDesfireErrorNone) break;
|
||||
|
||||
error = mf_desfire_poller_read_key_versions(
|
||||
instance, data->key_versions, data->key_settings.max_keys);
|
||||
if(error != MfDesfireErrorNone) break;
|
||||
if(error != MfDesfireErrorNone) {
|
||||
FURI_LOG_E(TAG, "Failed to read key version: %d", error);
|
||||
break;
|
||||
}
|
||||
|
||||
error = mf_desfire_poller_read_file_ids(instance, data->file_ids);
|
||||
if(error != MfDesfireErrorNone) break;
|
||||
if(error != MfDesfireErrorNone) {
|
||||
FURI_LOG_E(TAG, "Failed to read file ids: %d", error);
|
||||
break;
|
||||
}
|
||||
|
||||
error = mf_desfire_poller_read_file_settings_multi(
|
||||
instance, data->file_ids, data->file_settings);
|
||||
if(error != MfDesfireErrorNone) break;
|
||||
if(error != MfDesfireErrorNone) {
|
||||
FURI_LOG_E(TAG, "Failed to read file settings: %d", error);
|
||||
break;
|
||||
}
|
||||
|
||||
error = mf_desfire_poller_read_file_data_multi(
|
||||
instance, data->file_ids, data->file_settings, data->file_data);
|
||||
if(error != MfDesfireErrorNone) break;
|
||||
|
||||
} while(false);
|
||||
|
||||
|
@ -484,11 +525,13 @@ MfDesfireError mf_desfire_poller_read_applications(
|
|||
simple_array_init(data, app_id_count);
|
||||
}
|
||||
|
||||
for(uint32_t i = 0; i < app_id_count; ++i) {
|
||||
for(size_t i = 0; i < app_id_count; ++i) {
|
||||
do {
|
||||
FURI_LOG_D(TAG, "Selecting app %zu", i);
|
||||
error = mf_desfire_poller_select_application(instance, simple_array_cget(app_ids, i));
|
||||
if(error != MfDesfireErrorNone) break;
|
||||
|
||||
FURI_LOG_D(TAG, "Reading app %zu", i);
|
||||
MfDesfireApplication* current_app = simple_array_get(data, i);
|
||||
error = mf_desfire_poller_read_application(instance, current_app);
|
||||
|
||||
|
|
|
@ -47,6 +47,8 @@ struct MfDesfirePoller {
|
|||
|
||||
MfDesfireError mf_desfire_process_error(Iso14443_4aError error);
|
||||
|
||||
MfDesfireError mf_desfire_process_status_code(uint8_t status_code);
|
||||
|
||||
const MfDesfireData* mf_desfire_poller_get_data(MfDesfirePoller* instance);
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
entry,status,name,type,params
|
||||
Version,+,60.7,,
|
||||
Version,+,60.8,,
|
||||
Header,+,applications/services/bt/bt_service/bt.h,,
|
||||
Header,+,applications/services/cli/cli.h,,
|
||||
Header,+,applications/services/cli/cli_vcp.h,,
|
||||
|
|
|
|
@ -1,5 +1,5 @@
|
|||
entry,status,name,type,params
|
||||
Version,+,60.7,,
|
||||
Version,+,60.8,,
|
||||
Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,,
|
||||
Header,+,applications/services/bt/bt_service/bt.h,,
|
||||
Header,+,applications/services/cli/cli.h,,
|
||||
|
@ -2435,6 +2435,10 @@ Function,+,mf_classic_poller_get_nt,MfClassicError,"MfClassicPoller*, uint8_t, M
|
|||
Function,+,mf_classic_poller_get_nt_nested,MfClassicError,"MfClassicPoller*, uint8_t, MfClassicKeyType, MfClassicNt*"
|
||||
Function,+,mf_classic_poller_halt,MfClassicError,MfClassicPoller*
|
||||
Function,+,mf_classic_poller_read_block,MfClassicError,"MfClassicPoller*, uint8_t, MfClassicBlock*"
|
||||
Function,+,mf_classic_poller_send_custom_parity_frame,MfClassicError,"MfClassicPoller*, const BitBuffer*, BitBuffer*, uint32_t"
|
||||
Function,+,mf_classic_poller_send_encrypted_frame,MfClassicError,"MfClassicPoller*, const BitBuffer*, BitBuffer*, uint32_t"
|
||||
Function,+,mf_classic_poller_send_frame,MfClassicError,"MfClassicPoller*, const BitBuffer*, BitBuffer*, uint32_t"
|
||||
Function,+,mf_classic_poller_send_standard_frame,MfClassicError,"MfClassicPoller*, const BitBuffer*, BitBuffer*, uint32_t"
|
||||
Function,+,mf_classic_poller_sync_auth,MfClassicError,"Nfc*, uint8_t, MfClassicKey*, MfClassicKeyType, MfClassicAuthContext*"
|
||||
Function,+,mf_classic_poller_sync_change_value,MfClassicError,"Nfc*, uint8_t, MfClassicKey*, MfClassicKeyType, int32_t, int32_t*"
|
||||
Function,+,mf_classic_poller_sync_collect_nt,MfClassicError,"Nfc*, uint8_t, MfClassicKeyType, MfClassicNt*"
|
||||
|
|
|
Loading…
Reference in a new issue