mirror of
https://github.com/DarkFlippers/unleashed-firmware.git
synced 2024-12-29 16:25:47 +03:00
c00776ca22
* drivers: expose st25r3916 driver API * nfc poller: add start with custom callback * mf classic: rework sync API with poller custom start * mf ultralight: rework sync API with poller custom start * iso14443_3a poller: remove unused col res state * nfc: rework nfc poller custom start * mf ultralight: rename sync API * mf classic: rename sync API * iso14443-3a: rename sync API * nfc: remove async prefix in internal functions * nfc: expose internal API * nfc: fix sync api include and docs * targets: fix f18 build * nfc: rework NfcGenericEventEx type * nfc poller: add documentation * iso14443-3a poller: add documentation * felica poller: add documentation * iso14443_3b poller: add documentation * so14443_4a poller: add documentation * iso14443_4b poller: add documentation * iso15693 poller: add documentation * slix poller: add documentation * mf desfire poller: add documentation * mf ultralight poller: fix API and add documentation * mf classic poller: add documentation Co-authored-by: あく <alleteam@gmail.com>
477 lines
15 KiB
C
477 lines
15 KiB
C
#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;
|
|
}
|
|
|
|
const size_t rx_size = bit_buffer_get_size_bytes(instance->rx_buffer);
|
|
const size_t rx_capacity_remaining =
|
|
bit_buffer_get_capacity_bytes(rx_buffer) - bit_buffer_get_size_bytes(rx_buffer);
|
|
|
|
if(rx_size <= rx_capacity_remaining) {
|
|
bit_buffer_append_right(rx_buffer, instance->rx_buffer, sizeof(uint8_t));
|
|
} else {
|
|
FURI_LOG_W(TAG, "RX buffer overflow: ignoring %zu bytes", rx_size);
|
|
}
|
|
}
|
|
} while(false);
|
|
|
|
return error;
|
|
}
|
|
|
|
MfDesfireError mf_desfire_poller_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_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_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_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_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_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_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_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_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_read_file_settings(instance, file_id, simple_array_get(data, i));
|
|
if(error != MfDesfireErrorNone) break;
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
MfDesfireError mf_desfire_poller_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_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_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_RECORDS);
|
|
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_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_read_file_data(
|
|
instance, file_id, 0, file_settings_cur->data.size, file_data);
|
|
} else if(file_type == MfDesfireFileTypeValue) {
|
|
error = mf_desfire_poller_read_file_value(instance, file_id, file_data);
|
|
} else if(
|
|
file_type == MfDesfireFileTypeLinearRecord ||
|
|
file_type == MfDesfireFileTypeCyclicRecord) {
|
|
error = mf_desfire_poller_read_file_records(
|
|
instance, file_id, 0, file_settings_cur->data.size, file_data);
|
|
}
|
|
|
|
if(error != MfDesfireErrorNone) break;
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
MfDesfireError
|
|
mf_desfire_poller_read_application(MfDesfirePoller* instance, MfDesfireApplication* data) {
|
|
furi_assert(instance);
|
|
furi_assert(data);
|
|
|
|
MfDesfireError error;
|
|
|
|
do {
|
|
error = mf_desfire_poller_read_key_settings(instance, &data->key_settings);
|
|
if(error != MfDesfireErrorNone) break;
|
|
|
|
error = mf_desfire_poller_read_key_versions(
|
|
instance, data->key_versions, data->key_settings.max_keys);
|
|
if(error != MfDesfireErrorNone) break;
|
|
|
|
error = mf_desfire_poller_read_file_ids(instance, data->file_ids);
|
|
if(error != MfDesfireErrorNone) break;
|
|
|
|
error = mf_desfire_poller_read_file_settings_multi(
|
|
instance, data->file_ids, data->file_settings);
|
|
if(error != MfDesfireErrorNone) 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);
|
|
|
|
return error;
|
|
}
|
|
|
|
MfDesfireError mf_desfire_poller_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_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_read_application(instance, current_app);
|
|
|
|
} while(false);
|
|
}
|
|
|
|
return error;
|
|
}
|