From 358631ec864d4a7793b43ae06a2c6d8518ba4381 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Wed, 5 Mar 2025 08:10:03 +0000 Subject: [PATCH 01/92] NFC: Implement Type 4 Tag reading --- .../nfc_protocol_support_defs.c | 2 + .../protocol_support/type_4_tag/type_4_tag.c | 220 +++++++++++++++ .../protocol_support/type_4_tag/type_4_tag.h | 5 + .../type_4_tag/type_4_tag_render.c | 50 ++++ .../type_4_tag/type_4_tag_render.h | 14 + lib/nfc/SConscript | 2 + lib/nfc/protocols/nfc_device_defs.c | 2 + lib/nfc/protocols/nfc_listener_defs.c | 3 +- lib/nfc/protocols/nfc_poller_defs.c | 4 +- lib/nfc/protocols/nfc_protocol.c | 11 +- lib/nfc/protocols/nfc_protocol.h | 1 + lib/nfc/protocols/type_4_tag/type_4_tag.c | 158 +++++++++++ lib/nfc/protocols/type_4_tag/type_4_tag.h | 68 +++++ lib/nfc/protocols/type_4_tag/type_4_tag_i.c | 3 + lib/nfc/protocols/type_4_tag/type_4_tag_i.h | 54 ++++ .../protocols/type_4_tag/type_4_tag_poller.c | 187 ++++++++++++ .../protocols/type_4_tag/type_4_tag_poller.h | 43 +++ .../type_4_tag/type_4_tag_poller_defs.h | 5 + .../type_4_tag/type_4_tag_poller_i.c | 265 ++++++++++++++++++ .../type_4_tag/type_4_tag_poller_i.h | 48 ++++ targets/f7/api_symbols.csv | 15 + 21 files changed, 1157 insertions(+), 3 deletions(-) create mode 100644 applications/main/nfc/helpers/protocol_support/type_4_tag/type_4_tag.c create mode 100644 applications/main/nfc/helpers/protocol_support/type_4_tag/type_4_tag.h create mode 100644 applications/main/nfc/helpers/protocol_support/type_4_tag/type_4_tag_render.c create mode 100644 applications/main/nfc/helpers/protocol_support/type_4_tag/type_4_tag_render.h create mode 100644 lib/nfc/protocols/type_4_tag/type_4_tag.c create mode 100644 lib/nfc/protocols/type_4_tag/type_4_tag.h create mode 100644 lib/nfc/protocols/type_4_tag/type_4_tag_i.c create mode 100644 lib/nfc/protocols/type_4_tag/type_4_tag_i.h create mode 100644 lib/nfc/protocols/type_4_tag/type_4_tag_poller.c create mode 100644 lib/nfc/protocols/type_4_tag/type_4_tag_poller.h create mode 100644 lib/nfc/protocols/type_4_tag/type_4_tag_poller_defs.h create mode 100644 lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c create mode 100644 lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.h diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_defs.c b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_defs.c index 66839aacc..dd724b13a 100644 --- a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_defs.c +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_defs.c @@ -21,6 +21,7 @@ #include "mf_desfire/mf_desfire.h" #include "slix/slix.h" #include "st25tb/st25tb.h" +#include "type_4_tag/type_4_tag.h" /** * @brief Array of pointers to concrete protocol support implementations. @@ -43,5 +44,6 @@ const NfcProtocolSupportBase* nfc_protocol_support[NfcProtocolNum] = { [NfcProtocolMfDesfire] = &nfc_protocol_support_mf_desfire, [NfcProtocolSlix] = &nfc_protocol_support_slix, [NfcProtocolSt25tb] = &nfc_protocol_support_st25tb, + [NfcProtocolType4Tag] = &nfc_protocol_support_type_4_tag, /* Add new protocol support implementations here */ }; diff --git a/applications/main/nfc/helpers/protocol_support/type_4_tag/type_4_tag.c b/applications/main/nfc/helpers/protocol_support/type_4_tag/type_4_tag.c new file mode 100644 index 000000000..aefd095c1 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/type_4_tag/type_4_tag.c @@ -0,0 +1,220 @@ +#include "type_4_tag.h" +#include "type_4_tag_render.h" + +#include +#include + +#include "nfc/nfc_app_i.h" + +#include "../nfc_protocol_support_common.h" +#include "../nfc_protocol_support_gui_common.h" +#include "../iso14443_4a/iso14443_4a_i.h" + +enum { + SubmenuIndexWrite = SubmenuIndexCommonMax, +}; + +enum { + NfcSceneMoreInfoStateASCII, + NfcSceneMoreInfoStateRawData, +}; + +static void nfc_scene_info_on_enter_type_4_tag(NfcApp* instance) { + const NfcDevice* device = instance->nfc_device; + const Type4TagData* data = nfc_device_get_data(device, NfcProtocolType4Tag); + + FuriString* temp_str = furi_string_alloc(); + nfc_append_filename_string_when_present(instance, temp_str); + furi_string_cat_printf( + temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); + nfc_render_type_4_tag_info(data, NfcProtocolFormatTypeFull, temp_str); + + widget_add_text_scroll_element( + instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str)); + + furi_string_free(temp_str); +} + +static void nfc_scene_more_info_on_enter_type_4_tag(NfcApp* instance) { + const NfcDevice* device = instance->nfc_device; + const Type4TagData* data = nfc_device_get_data(device, NfcProtocolType4Tag); + + furi_string_reset(instance->text_box_store); + uint32_t scene_state = + scene_manager_get_scene_state(instance->scene_manager, NfcSceneMoreInfo); + + if(scene_state == NfcSceneMoreInfoStateASCII) { + pretty_format_bytes_hex_canonical( + instance->text_box_store, + TYPE_4_TAG_RENDER_BYTES_PER_LINE, + PRETTY_FORMAT_FONT_MONOSPACE, + simple_array_cget_data(data->ndef_data), + simple_array_get_count(data->ndef_data)); + + widget_add_text_scroll_element( + instance->widget, 0, 0, 128, 48, furi_string_get_cstr(instance->text_box_store)); + widget_add_button_element( + instance->widget, + GuiButtonTypeRight, + "Raw Data", + nfc_protocol_support_common_widget_callback, + instance); + + widget_add_button_element( + instance->widget, + GuiButtonTypeLeft, + "Info", + nfc_protocol_support_common_widget_callback, + instance); + } else if(scene_state == NfcSceneMoreInfoStateRawData) { + nfc_render_type_4_tag_dump(data, instance->text_box_store); + widget_add_text_scroll_element( + instance->widget, 0, 0, 128, 48, furi_string_get_cstr(instance->text_box_store)); + + widget_add_button_element( + instance->widget, + GuiButtonTypeLeft, + "ASCII", + nfc_protocol_support_common_widget_callback, + instance); + } +} + +static bool nfc_scene_more_info_on_event_type_4_tag(NfcApp* instance, SceneManagerEvent event) { + bool consumed = false; + + if((event.type == SceneManagerEventTypeCustom && event.event == GuiButtonTypeLeft) || + (event.type == SceneManagerEventTypeBack)) { + scene_manager_set_scene_state( + instance->scene_manager, NfcSceneMoreInfo, NfcSceneMoreInfoStateASCII); + scene_manager_previous_scene(instance->scene_manager); + consumed = true; + } else if(event.type == SceneManagerEventTypeCustom && event.event == GuiButtonTypeRight) { + scene_manager_set_scene_state( + instance->scene_manager, NfcSceneMoreInfo, NfcSceneMoreInfoStateRawData); + scene_manager_next_scene(instance->scene_manager, NfcSceneMoreInfo); + consumed = true; + } + return consumed; +} + +static NfcCommand nfc_scene_read_poller_callback_type_4_tag(NfcGenericEvent event, void* context) { + furi_assert(event.protocol == NfcProtocolType4Tag); + + NfcCommand command = NfcCommandContinue; + + NfcApp* instance = context; + const Type4TagPollerEvent* type_4_tag_event = event.event_data; + + if(type_4_tag_event->type == Type4TagPollerEventTypeReadSuccess) { + nfc_device_set_data( + instance->nfc_device, NfcProtocolType4Tag, nfc_poller_get_data(instance->poller)); + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess); + command = NfcCommandStop; + } else if(type_4_tag_event->type == Type4TagPollerEventTypeReadFailed) { + command = NfcCommandReset; + } + + return command; +} + +static void nfc_scene_read_on_enter_type_4_tag(NfcApp* instance) { + nfc_poller_start(instance->poller, nfc_scene_read_poller_callback_type_4_tag, instance); +} + +static void nfc_scene_read_and_saved_menu_on_enter_type_4_tag(NfcApp* instance) { + Submenu* submenu = instance->submenu; + + submenu_add_item( + submenu, + "Write (Not Implemented)", + SubmenuIndexWrite, + nfc_protocol_support_common_submenu_callback, + instance); +} + +static bool + nfc_scene_read_and_saved_menu_on_event_type_4_tag(NfcApp* instance, SceneManagerEvent event) { + bool consumed = false; + UNUSED(instance); + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuIndexWrite) { + // TODO: Implement write + // scene_manager_next_scene(instance->scene_manager, NfcSceneType4TagWrite); + consumed = true; + } + } + return consumed; +} + +static void nfc_scene_read_success_on_enter_type_4_tag(NfcApp* instance) { + const NfcDevice* device = instance->nfc_device; + const Type4TagData* data = nfc_device_get_data(device, NfcProtocolType4Tag); + + FuriString* temp_str = furi_string_alloc(); + furi_string_cat_printf( + temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); + nfc_render_type_4_tag_info(data, NfcProtocolFormatTypeShort, temp_str); + + widget_add_text_scroll_element( + instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str)); + + furi_string_free(temp_str); +} + +static void nfc_scene_emulate_on_enter_type_4_tag(NfcApp* instance) { + // TODO: Implement full emulation + const Iso14443_4aData* iso14443_4a_data = + nfc_device_get_data(instance->nfc_device, NfcProtocolIso14443_4a); + + instance->listener = + nfc_listener_alloc(instance->nfc, NfcProtocolIso14443_4a, iso14443_4a_data); + nfc_listener_start( + instance->listener, nfc_scene_emulate_listener_callback_iso14443_4a, instance); +} + +const NfcProtocolSupportBase nfc_protocol_support_type_4_tag = { + .features = NfcProtocolFeatureEmulateUid | NfcProtocolFeatureMoreInfo, + + .scene_info = + { + .on_enter = nfc_scene_info_on_enter_type_4_tag, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_more_info = + { + .on_enter = nfc_scene_more_info_on_enter_type_4_tag, + .on_event = nfc_scene_more_info_on_event_type_4_tag, + }, + .scene_read = + { + .on_enter = nfc_scene_read_on_enter_type_4_tag, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_read_menu = + { + .on_enter = nfc_scene_read_and_saved_menu_on_enter_type_4_tag, + .on_event = nfc_scene_read_and_saved_menu_on_event_type_4_tag, + }, + .scene_read_success = + { + .on_enter = nfc_scene_read_success_on_enter_type_4_tag, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_saved_menu = + { + .on_enter = nfc_scene_read_and_saved_menu_on_enter_type_4_tag, + .on_event = nfc_scene_read_and_saved_menu_on_event_type_4_tag, + }, + .scene_save_name = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_emulate = + { + .on_enter = nfc_scene_emulate_on_enter_type_4_tag, + .on_event = nfc_protocol_support_common_on_event_empty, + }, +}; diff --git a/applications/main/nfc/helpers/protocol_support/type_4_tag/type_4_tag.h b/applications/main/nfc/helpers/protocol_support/type_4_tag/type_4_tag.h new file mode 100644 index 000000000..e9d43a3b4 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/type_4_tag/type_4_tag.h @@ -0,0 +1,5 @@ +#pragma once + +#include "../nfc_protocol_support_base.h" + +extern const NfcProtocolSupportBase nfc_protocol_support_type_4_tag; diff --git a/applications/main/nfc/helpers/protocol_support/type_4_tag/type_4_tag_render.c b/applications/main/nfc/helpers/protocol_support/type_4_tag/type_4_tag_render.c new file mode 100644 index 000000000..bf85feb78 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/type_4_tag/type_4_tag_render.c @@ -0,0 +1,50 @@ +#include "type_4_tag_render.h" + +#include "../iso14443_4a/iso14443_4a_render.h" + +#define TYPE_4_TAG_RENDER_MAX_RECORD_SIZE (256U) + +void nfc_render_type_4_tag_info( + const Type4TagData* data, + NfcProtocolFormatType format_type, + FuriString* str) { + nfc_render_iso14443_4a_brief(type_4_tag_get_base_data(data), str); + + furi_string_cat(str, "\n::::::::::::::::::[Tag Specs]::::::::::::::::::\n"); + furi_string_cat_printf( + str, "T4T Mapping Version: %u.%u\n", data->t4t_version.major, data->t4t_version.minor); + furi_string_cat_printf(str, "NDEF File ID: 0x%04X\n", data->ndef_file_id); + furi_string_cat_printf(str, "Max NDEF Size: %u\n", data->ndef_max_len); + furi_string_cat_printf( + str, "APDU Sizes: R:%u W:%u\n", data->chunk_max_read, data->chunk_max_write); + furi_string_cat_printf( + str, + "Read Lock: 0x%02X%s\n", + data->ndef_read_lock, + data->ndef_read_lock == 0 ? " (unlocked)" : ""); + furi_string_cat_printf( + str, + "Write Lock: 0x%02X%s\n", + data->ndef_write_lock, + data->ndef_write_lock == 0 ? " (unlocked)" : ""); + + furi_string_cat(str, ":::::::::::::::[Stored NDEF]:::::::::::::::\n"); + furi_string_cat_printf(str, "Current NDEF Size: %lu", simple_array_get_count(data->ndef_data)); + + if(format_type != NfcProtocolFormatTypeFull) return; + + furi_string_cat(str, "\n\e#ISO14443-4 data"); + nfc_render_iso14443_4a_extra(type_4_tag_get_base_data(data), str); +} + +void nfc_render_type_4_tag_dump(const Type4TagData* data, FuriString* str) { + furi_string_cat_printf(str, "\e*"); + const uint8_t* ndef_data = simple_array_cget_data(data->ndef_data); + size_t ndef_len = simple_array_get_count(data->ndef_data); + for(size_t i = 0; i < ndef_len; i += TYPE_4_TAG_RENDER_BYTES_PER_LINE) { + const uint8_t* line_data = &ndef_data[i]; + for(size_t j = 0; j < TYPE_4_TAG_RENDER_BYTES_PER_LINE; j += 2) { + furi_string_cat_printf(str, " %02X%02X", line_data[j], line_data[j + 1]); + } + } +} diff --git a/applications/main/nfc/helpers/protocol_support/type_4_tag/type_4_tag_render.h b/applications/main/nfc/helpers/protocol_support/type_4_tag/type_4_tag_render.h new file mode 100644 index 000000000..91eaeb735 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/type_4_tag/type_4_tag_render.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +#include "../nfc_protocol_support_render_common.h" + +#define TYPE_4_TAG_RENDER_BYTES_PER_LINE (4) + +void nfc_render_type_4_tag_info( + const Type4TagData* data, + NfcProtocolFormatType format_type, + FuriString* str); + +void nfc_render_type_4_tag_dump(const Type4TagData* data, FuriString* str); diff --git a/lib/nfc/SConscript b/lib/nfc/SConscript index 82b87ea2a..9aef970c8 100644 --- a/lib/nfc/SConscript +++ b/lib/nfc/SConscript @@ -26,6 +26,7 @@ env.Append( File("protocols/slix/slix.h"), File("protocols/st25tb/st25tb.h"), File("protocols/felica/felica.h"), + File("protocols/type_4_tag/type_4_tag.h"), # Pollers File("protocols/iso14443_3a/iso14443_3a_poller.h"), File("protocols/iso14443_3b/iso14443_3b_poller.h"), @@ -38,6 +39,7 @@ env.Append( File("protocols/slix/slix_poller.h"), File("protocols/st25tb/st25tb_poller.h"), File("protocols/felica/felica_poller.h"), + File("protocols/type_4_tag/type_4_tag_poller.h"), # Listeners File("protocols/iso14443_3a/iso14443_3a_listener.h"), File("protocols/iso14443_4a/iso14443_4a_listener.h"), diff --git a/lib/nfc/protocols/nfc_device_defs.c b/lib/nfc/protocols/nfc_device_defs.c index 6a145445c..df7ebde40 100644 --- a/lib/nfc/protocols/nfc_device_defs.c +++ b/lib/nfc/protocols/nfc_device_defs.c @@ -24,6 +24,7 @@ #include #include #include +#include /** * @brief List of registered NFC device implementations. @@ -44,5 +45,6 @@ const NfcDeviceBase* nfc_devices[NfcProtocolNum] = { [NfcProtocolMfDesfire] = &nfc_device_mf_desfire, [NfcProtocolSlix] = &nfc_device_slix, [NfcProtocolSt25tb] = &nfc_device_st25tb, + [NfcProtocolType4Tag] = &nfc_device_type_4_tag, /* Add new protocols here */ }; diff --git a/lib/nfc/protocols/nfc_listener_defs.c b/lib/nfc/protocols/nfc_listener_defs.c index 2a6167e9c..69320bd79 100644 --- a/lib/nfc/protocols/nfc_listener_defs.c +++ b/lib/nfc/protocols/nfc_listener_defs.c @@ -14,10 +14,11 @@ const NfcListenerBase* nfc_listeners_api[NfcProtocolNum] = { [NfcProtocolIso14443_4a] = &nfc_listener_iso14443_4a, [NfcProtocolIso14443_4b] = NULL, [NfcProtocolIso15693_3] = &nfc_listener_iso15693_3, + [NfcProtocolFelica] = &nfc_listener_felica, [NfcProtocolMfUltralight] = &mf_ultralight_listener, [NfcProtocolMfClassic] = &mf_classic_listener, [NfcProtocolMfDesfire] = NULL, [NfcProtocolSlix] = &nfc_listener_slix, [NfcProtocolSt25tb] = NULL, - [NfcProtocolFelica] = &nfc_listener_felica, + [NfcProtocolType4Tag] = NULL, }; diff --git a/lib/nfc/protocols/nfc_poller_defs.c b/lib/nfc/protocols/nfc_poller_defs.c index c007740b7..d11b74eb6 100644 --- a/lib/nfc/protocols/nfc_poller_defs.c +++ b/lib/nfc/protocols/nfc_poller_defs.c @@ -12,6 +12,7 @@ #include #include #include +#include const NfcPollerBase* nfc_pollers_api[NfcProtocolNum] = { [NfcProtocolIso14443_3a] = &nfc_poller_iso14443_3a, @@ -25,6 +26,7 @@ const NfcPollerBase* nfc_pollers_api[NfcProtocolNum] = { [NfcProtocolMfPlus] = &mf_plus_poller, [NfcProtocolMfDesfire] = &mf_desfire_poller, [NfcProtocolSlix] = &nfc_poller_slix, - /* Add new pollers here */ [NfcProtocolSt25tb] = &nfc_poller_st25tb, + [NfcProtocolType4Tag] = &type_4_tag_poller, + /* Add new pollers here */ }; diff --git a/lib/nfc/protocols/nfc_protocol.c b/lib/nfc/protocols/nfc_protocol.c index 40201843e..fd64a7b0c 100644 --- a/lib/nfc/protocols/nfc_protocol.c +++ b/lib/nfc/protocols/nfc_protocol.c @@ -22,7 +22,9 @@ * | | | * ISO14443-4A Mf Ultralight Mf Classic * | - * Mf Desfire + * +-----+------+----------+ + * | | | + * Mf Desfire Type 4 Tag Mf Plus * ``` * * When implementing a new protocol, its place in the tree must be determined first. @@ -62,6 +64,7 @@ static const NfcProtocol nfc_protocol_iso14443_3b_children_protocol[] = { static const NfcProtocol nfc_protocol_iso14443_4a_children_protocol[] = { NfcProtocolMfDesfire, NfcProtocolMfPlus, + NfcProtocolType4Tag, }; /** List of ISO115693-3 child protocols. */ @@ -153,6 +156,12 @@ static const NfcProtocolTreeNode nfc_protocol_nodes[NfcProtocolNum] = { .children_num = 0, .children_protocol = NULL, }, + [NfcProtocolType4Tag] = + { + .parent_protocol = NfcProtocolIso14443_4a, + .children_num = 0, + .children_protocol = NULL, + }, /* Add new protocols here */ }; diff --git a/lib/nfc/protocols/nfc_protocol.h b/lib/nfc/protocols/nfc_protocol.h index cf74972f7..db03c74e7 100644 --- a/lib/nfc/protocols/nfc_protocol.h +++ b/lib/nfc/protocols/nfc_protocol.h @@ -188,6 +188,7 @@ typedef enum { NfcProtocolMfDesfire, NfcProtocolSlix, NfcProtocolSt25tb, + NfcProtocolType4Tag, /* Add new protocols here */ NfcProtocolNum, /**< Special value representing the number of available protocols. */ diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag.c b/lib/nfc/protocols/type_4_tag/type_4_tag.c new file mode 100644 index 000000000..ec268b618 --- /dev/null +++ b/lib/nfc/protocols/type_4_tag/type_4_tag.c @@ -0,0 +1,158 @@ +#include "type_4_tag_i.h" + +#include + +#define TYPE_4_TAG_PROTOCOL_NAME "Type 4 Tag" + +const NfcDeviceBase nfc_device_type_4_tag = { + .protocol_name = TYPE_4_TAG_PROTOCOL_NAME, + .alloc = (NfcDeviceAlloc)type_4_tag_alloc, + .free = (NfcDeviceFree)type_4_tag_free, + .reset = (NfcDeviceReset)type_4_tag_reset, + .copy = (NfcDeviceCopy)type_4_tag_copy, + .verify = (NfcDeviceVerify)type_4_tag_verify, + .load = (NfcDeviceLoad)type_4_tag_load, + .save = (NfcDeviceSave)type_4_tag_save, + .is_equal = (NfcDeviceEqual)type_4_tag_is_equal, + .get_name = (NfcDeviceGetName)type_4_tag_get_device_name, + .get_uid = (NfcDeviceGetUid)type_4_tag_get_uid, + .set_uid = (NfcDeviceSetUid)type_4_tag_set_uid, + .get_base_data = (NfcDeviceGetBaseData)type_4_tag_get_base_data, +}; + +Type4TagData* type_4_tag_alloc(void) { + Type4TagData* data = malloc(sizeof(Type4TagData)); + data->iso14443_4a_data = iso14443_4a_alloc(); + data->ndef_data = simple_array_alloc(&simple_array_config_uint8_t); + return data; +} + +void type_4_tag_free(Type4TagData* data) { + furi_check(data); + + type_4_tag_reset(data); + simple_array_free(data->ndef_data); + iso14443_4a_free(data->iso14443_4a_data); + free(data); +} + +void type_4_tag_reset(Type4TagData* data) { + furi_check(data); + + iso14443_4a_reset(data->iso14443_4a_data); + + data->t4t_version.value = 0; + data->chunk_max_read = 0; + data->chunk_max_write = 0; + data->ndef_file_id = 0; + data->ndef_max_len = 0; + data->ndef_read_lock = 0; + data->ndef_write_lock = 0; + + simple_array_reset(data->ndef_data); +} + +void type_4_tag_copy(Type4TagData* data, const Type4TagData* other) { + furi_check(data); + furi_check(other); + + type_4_tag_reset(data); + + iso14443_4a_copy(data->iso14443_4a_data, other->iso14443_4a_data); + + data->t4t_version.value = other->t4t_version.value; + data->chunk_max_read = other->chunk_max_read; + data->chunk_max_write = other->chunk_max_write; + data->ndef_file_id = other->ndef_file_id; + data->ndef_max_len = other->ndef_max_len; + data->ndef_read_lock = other->ndef_read_lock; + data->ndef_write_lock = other->ndef_write_lock; + + simple_array_copy(data->ndef_data, other->ndef_data); +} + +bool type_4_tag_verify(Type4TagData* data, const FuriString* device_type) { + UNUSED(data); + UNUSED(device_type); + + return false; +} + +bool type_4_tag_load(Type4TagData* data, FlipperFormat* ff, uint32_t version) { + furi_check(data); + furi_check(ff); + + FuriString* prefix = furi_string_alloc(); + + bool success = false; + + do { + if(!iso14443_4a_load(data->iso14443_4a_data, ff, version)) break; + + success = true; + } while(false); + + furi_string_free(prefix); + return success; +} + +bool type_4_tag_save(const Type4TagData* data, FlipperFormat* ff) { + furi_check(data); + furi_check(ff); + + 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, TYPE_4_TAG_PROTOCOL_NAME " specific data")) + break; + + success = true; + } while(false); + + furi_string_free(prefix); + return success; +} + +bool type_4_tag_is_equal(const Type4TagData* data, const Type4TagData* other) { + furi_check(data); + furi_check(other); + + return iso14443_4a_is_equal(data->iso14443_4a_data, other->iso14443_4a_data) && + data->t4t_version.value == other->t4t_version.value && + data->chunk_max_read == other->chunk_max_read && + data->chunk_max_write == other->chunk_max_write && + data->ndef_file_id == other->ndef_file_id && + data->ndef_max_len == other->ndef_max_len && + data->ndef_read_lock == other->ndef_read_lock && + data->ndef_write_lock == other->ndef_write_lock && + simple_array_is_equal(data->ndef_data, other->ndef_data); +} + +const char* type_4_tag_get_device_name(const Type4TagData* data, NfcDeviceNameType name_type) { + UNUSED(data); + UNUSED(name_type); + return TYPE_4_TAG_PROTOCOL_NAME; +} + +const uint8_t* type_4_tag_get_uid(const Type4TagData* data, size_t* uid_len) { + furi_check(data); + furi_check(uid_len); + + return iso14443_4a_get_uid(data->iso14443_4a_data, uid_len); +} + +bool type_4_tag_set_uid(Type4TagData* data, const uint8_t* uid, size_t uid_len) { + furi_check(data); + + return iso14443_4a_set_uid(data->iso14443_4a_data, uid, uid_len); +} + +Iso14443_4aData* type_4_tag_get_base_data(const Type4TagData* data) { + furi_check(data); + + return data->iso14443_4a_data; +} diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag.h b/lib/nfc/protocols/type_4_tag/type_4_tag.h new file mode 100644 index 000000000..99f58b7cc --- /dev/null +++ b/lib/nfc/protocols/type_4_tag/type_4_tag.h @@ -0,0 +1,68 @@ +#pragma once + +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + Type4TagErrorNone, + Type4TagErrorNotPresent, + Type4TagErrorProtocol, + Type4TagErrorTimeout, +} Type4TagError; + +typedef struct { + Iso14443_4aData* iso14443_4a_data; + // Tag specific + union { + struct { + uint8_t minor : 4; + uint8_t major : 4; + }; + uint8_t value; + } t4t_version; + uint16_t chunk_max_read; + uint16_t chunk_max_write; + uint16_t ndef_file_id; + uint16_t ndef_max_len; + uint8_t ndef_read_lock; + uint8_t ndef_write_lock; + // Data contained, not tag specific + SimpleArray* ndef_data; +} Type4TagData; + +extern const NfcDeviceBase nfc_device_type_4_tag; + +// Virtual methods + +Type4TagData* type_4_tag_alloc(void); + +void type_4_tag_free(Type4TagData* data); + +void type_4_tag_reset(Type4TagData* data); + +void type_4_tag_copy(Type4TagData* data, const Type4TagData* other); + +bool type_4_tag_verify(Type4TagData* data, const FuriString* device_type); + +bool type_4_tag_load(Type4TagData* data, FlipperFormat* ff, uint32_t version); + +bool type_4_tag_save(const Type4TagData* data, FlipperFormat* ff); + +bool type_4_tag_is_equal(const Type4TagData* data, const Type4TagData* other); + +const char* type_4_tag_get_device_name(const Type4TagData* data, NfcDeviceNameType name_type); + +const uint8_t* type_4_tag_get_uid(const Type4TagData* data, size_t* uid_len); + +bool type_4_tag_set_uid(Type4TagData* data, const uint8_t* uid, size_t uid_len); + +Iso14443_4aData* type_4_tag_get_base_data(const Type4TagData* data); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_i.c b/lib/nfc/protocols/type_4_tag/type_4_tag_i.c new file mode 100644 index 000000000..c2cbfd1af --- /dev/null +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_i.c @@ -0,0 +1,3 @@ +#include "type_4_tag_i.h" + +#define TAG "Type4Tag" diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_i.h b/lib/nfc/protocols/type_4_tag/type_4_tag_i.h new file mode 100644 index 000000000..0dcece22b --- /dev/null +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_i.h @@ -0,0 +1,54 @@ +#pragma once + +#include "type_4_tag.h" + +#define TYPE_4_TAG_ISO_SELECT_CMD 0x00, 0xA4 +#define TYPE_4_TAG_ISO_SELECT_P1_BY_NAME (0x04) +#define TYPE_4_TAG_ISO_SELECT_P1_BY_ID (0x00) +#define TYPE_4_TAG_ISO_SELECT_P2_EMPTY (0x0C) +#define TYPE_4_TAG_ISO_SELECT_LE_EMPTY (0x00) + +#define TYPE_4_TAG_ISO_READ_CMD 0x00, 0xB0 +#define TYPE_4_TAG_ISO_READ_P1_EMPTY (0x00) +#define TYPE_4_TAG_ISO_READ_P2_BEGINNING (0x00) +#define TYPE_4_TAG_ISO_READ_LE_FULL (0x00) + +#define TYPE_4_TAG_ISO_STATUS_LEN (2) +#define TYPE_4_TAG_ISO_STATUS_SUCCESS 0x90, 0x00 +#define TYPE_4_TAG_ISO_RW_CHUNK_LEN (255) +#define TYPE_4_TAG_ISO_APP_NAME_LEN (7) +#define TYPE_4_TAG_ISO_APP_NAME 0xD2, 0x76, 0x00, 0x00, 0x85, 0x01, 0x01 +#define TYPE_4_TAG_T4T_CC_FILE_ID_LEN (2) +#define TYPE_4_TAG_T4T_CC_FILE_ID 0xE1, 0x03 + +#define TYPE_4_TAG_T4T_CC_VNO 0x20 + +typedef enum FURI_PACKED { + Type4TagCcTlvTypeNdefFileCtrl = 0x04, + Type4TagCcTlvTypeProprietaryFileCtrl = 0x05, +} Type4TagCcTlvType; + +typedef struct FURI_PACKED { + uint16_t file_id; + uint16_t max_len; + uint8_t read_perm; + uint8_t write_perm; +} Type4TagCcTlvNdefFileCtrl; + +typedef union FURI_PACKED { + Type4TagCcTlvNdefFileCtrl ndef_file_ctrl; +} Type4TagCcTlvValue; + +typedef struct FURI_PACKED { + Type4TagCcTlvType type; + uint8_t len; + Type4TagCcTlvValue value; +} Type4TagCcTlv; + +typedef struct FURI_PACKED { + uint16_t len; + uint8_t t4t_vno; + uint16_t mle; + uint16_t mlc; + Type4TagCcTlv tlv[]; +} Type4TagCc; diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_poller.c b/lib/nfc/protocols/type_4_tag/type_4_tag_poller.c new file mode 100644 index 000000000..ee09d8a68 --- /dev/null +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_poller.c @@ -0,0 +1,187 @@ +#include "type_4_tag_poller_i.h" +#include "type_4_tag_i.h" + +#include + +#include + +#define TAG "Type4TagPoller" + +// Read returns 2 byte status trailer, write sends 5 byte command header +#define TYPE_4_TAG_BUF_SIZE (TYPE_4_TAG_ISO_RW_CHUNK_LEN + 5) + +typedef NfcCommand (*Type4TagPollerReadHandler)(Type4TagPoller* instance); + +const Type4TagData* type_4_tag_poller_get_data(Type4TagPoller* instance) { + furi_assert(instance); + + return instance->data; +} + +static Type4TagPoller* type_4_tag_poller_alloc(Iso14443_4aPoller* iso14443_4a_poller) { + Type4TagPoller* instance = malloc(sizeof(Type4TagPoller)); + instance->iso14443_4a_poller = iso14443_4a_poller; + instance->data = type_4_tag_alloc(); + instance->tx_buffer = bit_buffer_alloc(TYPE_4_TAG_BUF_SIZE); + instance->rx_buffer = bit_buffer_alloc(TYPE_4_TAG_BUF_SIZE); + + instance->type_4_tag_event.data = &instance->type_4_tag_event_data; + + instance->general_event.protocol = NfcProtocolType4Tag; + instance->general_event.event_data = &instance->type_4_tag_event; + instance->general_event.instance = instance; + + return instance; +} + +static void type_4_tag_poller_free(Type4TagPoller* instance) { + furi_assert(instance); + + type_4_tag_free(instance->data); + bit_buffer_free(instance->tx_buffer); + bit_buffer_free(instance->rx_buffer); + free(instance); +} + +static NfcCommand type_4_tag_poller_handler_idle(Type4TagPoller* instance) { + 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 = Type4TagPollerStateSelectApplication; + return NfcCommandContinue; +} + +static NfcCommand type_4_tag_poller_handler_select_app(Type4TagPoller* instance) { + instance->error = type_4_tag_poller_select_app(instance); + if(instance->error == Type4TagErrorNone) { + FURI_LOG_D(TAG, "Select application success"); + instance->state = Type4TagPollerStateReadCapabilityContainer; + } else { + FURI_LOG_E(TAG, "Failed to select application"); + iso14443_4a_poller_halt(instance->iso14443_4a_poller); + instance->state = Type4TagPollerStateReadFailed; + } + + return NfcCommandContinue; +} + +static NfcCommand type_4_tag_poller_handler_read_cc(Type4TagPoller* instance) { + instance->error = type_4_tag_poller_read_cc(instance); + if(instance->error == Type4TagErrorNone) { + FURI_LOG_D(TAG, "Read CC success"); + instance->state = Type4TagPollerStateReadNdefMessage; + } else { + FURI_LOG_E(TAG, "Failed to read CC"); + iso14443_4a_poller_halt(instance->iso14443_4a_poller); + instance->state = Type4TagPollerStateReadFailed; + } + + return NfcCommandContinue; +} + +static NfcCommand type_4_tag_poller_handler_read_ndef(Type4TagPoller* instance) { + instance->error = type_4_tag_poller_read_ndef(instance); + if(instance->error == Type4TagErrorNone) { + FURI_LOG_D(TAG, "Read NDEF success"); + instance->state = Type4TagPollerStateReadSuccess; + } else { + FURI_LOG_E(TAG, "Failed to read NDEF"); + iso14443_4a_poller_halt(instance->iso14443_4a_poller); + instance->state = Type4TagPollerStateReadFailed; + } + + return NfcCommandContinue; +} + +static NfcCommand type_4_tag_poller_handler_read_fail(Type4TagPoller* instance) { + FURI_LOG_D(TAG, "Read Failed"); + iso14443_4a_poller_halt(instance->iso14443_4a_poller); + instance->type_4_tag_event.data->error = instance->error; + NfcCommand command = instance->callback(instance->general_event, instance->context); + instance->state = Type4TagPollerStateIdle; + return command; +} + +static NfcCommand type_4_tag_poller_handler_read_success(Type4TagPoller* instance) { + FURI_LOG_D(TAG, "Read success."); + iso14443_4a_poller_halt(instance->iso14443_4a_poller); + instance->type_4_tag_event.type = Type4TagPollerEventTypeReadSuccess; + NfcCommand command = instance->callback(instance->general_event, instance->context); + return command; +} + +static const Type4TagPollerReadHandler type_4_tag_poller_read_handler[Type4TagPollerStateNum] = { + [Type4TagPollerStateIdle] = type_4_tag_poller_handler_idle, + [Type4TagPollerStateSelectApplication] = type_4_tag_poller_handler_select_app, + [Type4TagPollerStateReadCapabilityContainer] = type_4_tag_poller_handler_read_cc, + [Type4TagPollerStateReadNdefMessage] = type_4_tag_poller_handler_read_ndef, + [Type4TagPollerStateReadFailed] = type_4_tag_poller_handler_read_fail, + [Type4TagPollerStateReadSuccess] = type_4_tag_poller_handler_read_success, +}; + +static void type_4_tag_poller_set_callback( + Type4TagPoller* instance, + NfcGenericCallback callback, + void* context) { + furi_assert(instance); + furi_assert(callback); + + instance->callback = callback; + instance->context = context; +} + +static NfcCommand type_4_tag_poller_run(NfcGenericEvent event, void* context) { + furi_assert(event.protocol == NfcProtocolIso14443_4a); + + Type4TagPoller* 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 = type_4_tag_poller_read_handler[instance->state](instance); + } else if(iso14443_4a_event->type == Iso14443_4aPollerEventTypeError) { + instance->type_4_tag_event.type = Type4TagPollerEventTypeReadFailed; + command = instance->callback(instance->general_event, instance->context); + } + + return command; +} + +static bool type_4_tag_poller_detect(NfcGenericEvent event, void* context) { + furi_assert(event.protocol == NfcProtocolIso14443_4a); + + Type4TagPoller* 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) { + Type4TagError error = type_4_tag_poller_select_app(instance); + if(error == Type4TagErrorNone) { + protocol_detected = true; + } + } + + return protocol_detected; +} + +const NfcPollerBase type_4_tag_poller = { + .alloc = (NfcPollerAlloc)type_4_tag_poller_alloc, + .free = (NfcPollerFree)type_4_tag_poller_free, + .set_callback = (NfcPollerSetCallback)type_4_tag_poller_set_callback, + .run = (NfcPollerRun)type_4_tag_poller_run, + .detect = (NfcPollerDetect)type_4_tag_poller_detect, + .get_data = (NfcPollerGetData)type_4_tag_poller_get_data, +}; diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_poller.h b/lib/nfc/protocols/type_4_tag/type_4_tag_poller.h new file mode 100644 index 000000000..c3ba91c32 --- /dev/null +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_poller.h @@ -0,0 +1,43 @@ +#pragma once + +#include "type_4_tag.h" + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Type4TagPoller opaque type definition. + */ +typedef struct Type4TagPoller Type4TagPoller; + +/** + * @brief Enumeration of possible Type4Tag poller event types. + */ +typedef enum { + Type4TagPollerEventTypeReadSuccess, /**< Card was read successfully. */ + Type4TagPollerEventTypeReadFailed, /**< Poller failed to read card. */ +} Type4TagPollerEventType; + +/** + * @brief Type4Tag poller event data. + */ +typedef union { + Type4TagError error; /**< Error code indicating card reading fail reason. */ +} Type4TagPollerEventData; + +/** + * @brief Type4Tag poller event structure. + * + * Upon emission of an event, an instance of this struct will be passed to the callback. + */ +typedef struct { + Type4TagPollerEventType type; /**< Type of emmitted event. */ + Type4TagPollerEventData* data; /**< Pointer to event specific data. */ +} Type4TagPollerEvent; + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_poller_defs.h b/lib/nfc/protocols/type_4_tag/type_4_tag_poller_defs.h new file mode 100644 index 000000000..fe71b560a --- /dev/null +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_poller_defs.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +extern const NfcPollerBase type_4_tag_poller; diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c b/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c new file mode 100644 index 000000000..5d8c56d40 --- /dev/null +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c @@ -0,0 +1,265 @@ +#include "type_4_tag_poller_i.h" + +#include +#include + +#include "type_4_tag_i.h" + +#define TAG "Type4TagPoller" + +Type4TagError type_4_tag_process_error(Iso14443_4aError error) { + switch(error) { + case Iso14443_4aErrorNone: + return Type4TagErrorNone; + case Iso14443_4aErrorNotPresent: + return Type4TagErrorNotPresent; + case Iso14443_4aErrorTimeout: + return Type4TagErrorTimeout; + default: + return Type4TagErrorProtocol; + } +} + +Type4TagError type_4_tag_apdu_trx(Type4TagPoller* instance, BitBuffer* tx_buf, BitBuffer* rx_buf) { + furi_check(instance); + + bit_buffer_reset(rx_buf); + + Iso14443_4aError iso14443_4a_error = + iso14443_4a_poller_send_block(instance->iso14443_4a_poller, tx_buf, rx_buf); + + bit_buffer_reset(tx_buf); + + if(iso14443_4a_error != Iso14443_4aErrorNone) { + return type_4_tag_process_error(iso14443_4a_error); + } + + size_t response_len = bit_buffer_get_size_bytes(rx_buf); + if(response_len < TYPE_4_TAG_ISO_STATUS_LEN) { + return Type4TagErrorProtocol; + } + + const uint8_t success[TYPE_4_TAG_ISO_STATUS_LEN] = {TYPE_4_TAG_ISO_STATUS_SUCCESS}; + uint8_t status[TYPE_4_TAG_ISO_STATUS_LEN] = { + bit_buffer_get_byte(rx_buf, response_len - 2), + bit_buffer_get_byte(rx_buf, response_len - 1), + }; + bit_buffer_set_size_bytes(rx_buf, response_len - 2); + + if(memcmp(status, success, sizeof(status)) == 0) { + return Type4TagErrorNone; + } else { + FURI_LOG_E(TAG, "APDU failed: 0x%02X%02X", status[0], status[1]); + return Type4TagErrorProtocol; + } +} + +Type4TagError type_4_tag_poller_select_app(Type4TagPoller* instance) { + furi_check(instance); + + FURI_LOG_D(TAG, "Select application"); + const uint8_t type_4_tag_select_app_apdu[] = { + TYPE_4_TAG_ISO_SELECT_CMD, + TYPE_4_TAG_ISO_SELECT_P1_BY_NAME, + TYPE_4_TAG_ISO_SELECT_P2_EMPTY, + TYPE_4_TAG_ISO_APP_NAME_LEN, + TYPE_4_TAG_ISO_APP_NAME, + TYPE_4_TAG_ISO_SELECT_LE_EMPTY, + }; + + bit_buffer_append_bytes( + instance->tx_buffer, type_4_tag_select_app_apdu, sizeof(type_4_tag_select_app_apdu)); + + return type_4_tag_apdu_trx(instance, instance->tx_buffer, instance->rx_buffer); +} + +Type4TagError type_4_tag_poller_read_cc(Type4TagPoller* instance) { + furi_check(instance); + + Type4TagError error; + + do { + FURI_LOG_D(TAG, "Select CC"); + const uint8_t type_4_tag_select_cc_apdu[] = { + TYPE_4_TAG_ISO_SELECT_CMD, + TYPE_4_TAG_ISO_SELECT_P1_BY_ID, + TYPE_4_TAG_ISO_SELECT_P2_EMPTY, + TYPE_4_TAG_T4T_CC_FILE_ID_LEN, + TYPE_4_TAG_T4T_CC_FILE_ID, + TYPE_4_TAG_ISO_SELECT_LE_EMPTY, + }; + + bit_buffer_append_bytes( + instance->tx_buffer, type_4_tag_select_cc_apdu, sizeof(type_4_tag_select_cc_apdu)); + + error = type_4_tag_apdu_trx(instance, instance->tx_buffer, instance->rx_buffer); + if(error != Type4TagErrorNone) break; + + FURI_LOG_D(TAG, "Read CC"); + const uint8_t type_4_tag_read_cc_apdu[] = { + TYPE_4_TAG_ISO_READ_CMD, + TYPE_4_TAG_ISO_READ_P1_EMPTY, + TYPE_4_TAG_ISO_READ_P2_BEGINNING, + TYPE_4_TAG_ISO_READ_LE_FULL, + }; + + bit_buffer_append_bytes( + instance->tx_buffer, type_4_tag_read_cc_apdu, sizeof(type_4_tag_read_cc_apdu)); + + error = type_4_tag_apdu_trx(instance, instance->tx_buffer, instance->rx_buffer); + if(error != Type4TagErrorNone) break; + + const Type4TagCc* cc = (const Type4TagCc*)bit_buffer_get_data(instance->rx_buffer); + if(cc->t4t_vno != TYPE_4_TAG_T4T_CC_VNO) { + FURI_LOG_E(TAG, "Unsupported T4T version"); + error = Type4TagErrorProtocol; + break; + } + + const Type4TagCcTlv* tlv = cc->tlv; + const Type4TagCcTlvNdefFileCtrl* ndef_file_ctrl = NULL; + while((void*)tlv < (void*)cc + cc->len) { + if(tlv->type == Type4TagCcTlvTypeNdefFileCtrl) { + ndef_file_ctrl = &tlv->value.ndef_file_ctrl; + break; + } + + if(tlv->len < 0xFF) { + tlv = (void*)&tlv->value + tlv->len; + } else { + uint16_t len = bit_lib_bytes_to_num_be((void*)&tlv->len + 1, sizeof(uint16_t)); + tlv = (void*)&tlv->value + sizeof(len) + len; + } + } + if(!ndef_file_ctrl) { + FURI_LOG_E(TAG, "No NDEF file ctrl TLV"); + error = Type4TagErrorProtocol; + break; + } + + instance->data->t4t_version.value = cc->t4t_vno; + instance->data->chunk_max_read = bit_lib_bytes_to_num_be((void*)&cc->mle, sizeof(cc->mle)); + instance->data->chunk_max_write = + bit_lib_bytes_to_num_be((void*)&cc->mlc, sizeof(cc->mlc)); + instance->data->ndef_file_id = bit_lib_bytes_to_num_be( + (void*)&ndef_file_ctrl->file_id, sizeof(ndef_file_ctrl->file_id)); + instance->data->ndef_max_len = + bit_lib_bytes_to_num_be( + (void*)&ndef_file_ctrl->max_len, sizeof(ndef_file_ctrl->max_len)) - + sizeof(uint16_t); + instance->data->ndef_read_lock = ndef_file_ctrl->read_perm; + instance->data->ndef_write_lock = ndef_file_ctrl->write_perm; + + FURI_LOG_D(TAG, "Detected NDEF file ID 0x%04X", instance->data->ndef_file_id); + } while(false); + + return error; +} + +Type4TagError type_4_tag_poller_read_ndef(Type4TagPoller* instance) { + furi_check(instance); + + Type4TagError error; + + do { + FURI_LOG_D(TAG, "Select NDEF"); + const uint8_t type_4_tag_select_ndef_apdu_1[] = { + TYPE_4_TAG_ISO_SELECT_CMD, + TYPE_4_TAG_ISO_SELECT_P1_BY_ID, + TYPE_4_TAG_ISO_SELECT_P2_EMPTY, + sizeof(instance->data->ndef_file_id), + }; + uint8_t ndef_file_id_be[sizeof(instance->data->ndef_file_id)]; + bit_lib_num_to_bytes_be( + instance->data->ndef_file_id, sizeof(instance->data->ndef_file_id), ndef_file_id_be); + const uint8_t type_4_tag_select_ndef_apdu_2[] = { + TYPE_4_TAG_ISO_SELECT_LE_EMPTY, + }; + + bit_buffer_append_bytes( + instance->tx_buffer, + type_4_tag_select_ndef_apdu_1, + sizeof(type_4_tag_select_ndef_apdu_1)); + bit_buffer_append_bytes(instance->tx_buffer, ndef_file_id_be, sizeof(ndef_file_id_be)); + bit_buffer_append_bytes( + instance->tx_buffer, + type_4_tag_select_ndef_apdu_2, + sizeof(type_4_tag_select_ndef_apdu_2)); + + error = type_4_tag_apdu_trx(instance, instance->tx_buffer, instance->rx_buffer); + if(error != Type4TagErrorNone) break; + + FURI_LOG_D(TAG, "Read NDEF len"); + uint16_t ndef_len; + const uint8_t type_4_tag_read_ndef_len_apdu[] = { + TYPE_4_TAG_ISO_READ_CMD, + TYPE_4_TAG_ISO_READ_P1_EMPTY, + TYPE_4_TAG_ISO_READ_P2_BEGINNING, + sizeof(ndef_len), + }; + + bit_buffer_append_bytes( + instance->tx_buffer, + type_4_tag_read_ndef_len_apdu, + sizeof(type_4_tag_read_ndef_len_apdu)); + + error = type_4_tag_apdu_trx(instance, instance->tx_buffer, instance->rx_buffer); + if(error != Type4TagErrorNone) break; + + ndef_len = + bit_lib_bytes_to_num_be(bit_buffer_get_data(instance->rx_buffer), sizeof(ndef_len)); + simple_array_init(instance->data->ndef_data, ndef_len); + if(ndef_len > 510) { + // Both size and offset for READ BINARY are 1 byte uint + // So the furthest we can read is 255 bytes at offset 255 + // AKA 2 * 255 byte chunks = 510 bytes + // TODO: Surely there has to be another way? + FURI_LOG_E(TAG, "NDEF file too long: %zu bytes", ndef_len); + } + + FURI_LOG_D(TAG, "Read NDEF"); + const uint8_t type_4_tag_read_ndef_apdu_1[] = { + TYPE_4_TAG_ISO_READ_CMD, + TYPE_4_TAG_ISO_READ_P1_EMPTY, + }; + + uint16_t ndef_pos = 0; + uint8_t* ndef_data = simple_array_get_data(instance->data->ndef_data); + uint8_t chunk_max = MIN(instance->data->chunk_max_read, TYPE_4_TAG_ISO_RW_CHUNK_LEN); + while(ndef_len > 0) { + uint8_t chunk_len = MIN(ndef_len, chunk_max); + + bit_buffer_append_bytes( + instance->tx_buffer, + type_4_tag_read_ndef_apdu_1, + sizeof(type_4_tag_read_ndef_apdu_1)); + bit_buffer_append_byte(instance->tx_buffer, (uint8_t)ndef_pos); + bit_buffer_append_byte(instance->tx_buffer, chunk_len); + + error = type_4_tag_apdu_trx(instance, instance->tx_buffer, instance->rx_buffer); + if(error != Type4TagErrorNone) break; + if(bit_buffer_get_size_bytes(instance->rx_buffer) != chunk_len) { + FURI_LOG_E( + TAG, + "Wrong NDEF chunk len: %zu != %zu", + bit_buffer_get_size_bytes(instance->rx_buffer), + ndef_len); + error = Type4TagErrorProtocol; + break; + } + memcpy(&ndef_data[ndef_pos], bit_buffer_get_data(instance->rx_buffer), chunk_len); + + ndef_pos += chunk_len; + ndef_len -= chunk_len; + } + if(error != Type4TagErrorNone) break; + + FURI_LOG_D( + TAG, + "Read NDEF file 0x%04X of %lu bytes", + instance->data->ndef_file_id, + simple_array_get_count(instance->data->ndef_data)); + } while(false); + + return error; +} diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.h b/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.h new file mode 100644 index 000000000..c74135040 --- /dev/null +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.h @@ -0,0 +1,48 @@ +#pragma once + +#include "type_4_tag_poller.h" + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + Type4TagPollerStateIdle, + Type4TagPollerStateSelectApplication, + Type4TagPollerStateReadCapabilityContainer, + Type4TagPollerStateReadNdefMessage, + Type4TagPollerStateReadFailed, + Type4TagPollerStateReadSuccess, + + Type4TagPollerStateNum, +} Type4TagPollerState; + +struct Type4TagPoller { + Iso14443_4aPoller* iso14443_4a_poller; + Type4TagPollerState state; + Type4TagError error; + Type4TagData* data; + BitBuffer* tx_buffer; + BitBuffer* rx_buffer; + Type4TagPollerEventData type_4_tag_event_data; + Type4TagPollerEvent type_4_tag_event; + NfcGenericEvent general_event; + NfcGenericCallback callback; + void* context; +}; + +Type4TagError type_4_tag_process_error(Iso14443_4aError error); + +const Type4TagData* type_4_tag_poller_get_data(Type4TagPoller* instance); + +Type4TagError type_4_tag_poller_select_app(Type4TagPoller* instance); + +Type4TagError type_4_tag_poller_read_cc(Type4TagPoller* instance); + +Type4TagError type_4_tag_poller_read_ndef(Type4TagPoller* instance); + +#ifdef __cplusplus +} +#endif diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index 1168a6eea..9483b1719 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -169,6 +169,8 @@ Header,+,lib/nfc/protocols/slix/slix_poller.h,, Header,+,lib/nfc/protocols/st25tb/st25tb.h,, Header,+,lib/nfc/protocols/st25tb/st25tb_poller.h,, Header,+,lib/nfc/protocols/st25tb/st25tb_poller_sync.h,, +Header,+,lib/nfc/protocols/type_4_tag/type_4_tag.h,, +Header,+,lib/nfc/protocols/type_4_tag/type_4_tag_poller.h,, Header,+,lib/one_wire/maxim_crc.h,, Header,+,lib/one_wire/one_wire_host.h,, Header,+,lib/one_wire/one_wire_slave.h,, @@ -3625,6 +3627,18 @@ Function,-,toupper_l,int,"int, locale_t" Function,-,trunc,double,double Function,-,truncf,float,float Function,-,truncl,long double,long double +Function,+,type_4_tag_alloc,Type4TagData*, +Function,+,type_4_tag_copy,void,"Type4TagData*, const Type4TagData*" +Function,+,type_4_tag_free,void,Type4TagData* +Function,+,type_4_tag_get_base_data,Iso14443_4aData*,const Type4TagData* +Function,+,type_4_tag_get_device_name,const char*,"const Type4TagData*, NfcDeviceNameType" +Function,+,type_4_tag_get_uid,const uint8_t*,"const Type4TagData*, size_t*" +Function,+,type_4_tag_is_equal,_Bool,"const Type4TagData*, const Type4TagData*" +Function,+,type_4_tag_load,_Bool,"Type4TagData*, FlipperFormat*, uint32_t" +Function,+,type_4_tag_reset,void,Type4TagData* +Function,+,type_4_tag_save,_Bool,"const Type4TagData*, FlipperFormat*" +Function,+,type_4_tag_set_uid,_Bool,"Type4TagData*, const uint8_t*, size_t" +Function,+,type_4_tag_verify,_Bool,"Type4TagData*, const FuriString*" Function,+,uint8_to_hex_chars,void,"const uint8_t*, uint8_t*, int" Function,-,ungetc,int,"int, FILE*" Function,-,unsetenv,int,const char* @@ -4012,6 +4026,7 @@ Variable,-,nfc_device_mf_desfire,const NfcDeviceBase, Variable,-,nfc_device_mf_plus,const NfcDeviceBase, Variable,-,nfc_device_mf_ultralight,const NfcDeviceBase, Variable,-,nfc_device_st25tb,const NfcDeviceBase, +Variable,-,nfc_device_type_4_tag,const NfcDeviceBase, Variable,+,sequence_audiovisual_alert,const NotificationSequence, Variable,+,sequence_blink_blue_10,const NotificationSequence, Variable,+,sequence_blink_blue_100,const NotificationSequence, From d5161f080617acc4ba2a0d4406e340d57693f921 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 6 Mar 2025 01:40:46 +0000 Subject: [PATCH 02/92] Fix reading empty NDEF message --- .../protocol_support/type_4_tag/type_4_tag.c | 16 ++++++++++------ .../type_4_tag/type_4_tag_render.c | 19 ++++++++++++------- .../type_4_tag/type_4_tag_poller_i.c | 8 +++++++- 3 files changed, 29 insertions(+), 14 deletions(-) diff --git a/applications/main/nfc/helpers/protocol_support/type_4_tag/type_4_tag.c b/applications/main/nfc/helpers/protocol_support/type_4_tag/type_4_tag.c index aefd095c1..97a4872c7 100644 --- a/applications/main/nfc/helpers/protocol_support/type_4_tag/type_4_tag.c +++ b/applications/main/nfc/helpers/protocol_support/type_4_tag/type_4_tag.c @@ -44,12 +44,16 @@ static void nfc_scene_more_info_on_enter_type_4_tag(NfcApp* instance) { scene_manager_get_scene_state(instance->scene_manager, NfcSceneMoreInfo); if(scene_state == NfcSceneMoreInfoStateASCII) { - pretty_format_bytes_hex_canonical( - instance->text_box_store, - TYPE_4_TAG_RENDER_BYTES_PER_LINE, - PRETTY_FORMAT_FONT_MONOSPACE, - simple_array_cget_data(data->ndef_data), - simple_array_get_count(data->ndef_data)); + if(simple_array_get_count(data->ndef_data) == 0) { + furi_string_cat_str(instance->text_box_store, "No NDEF data to show"); + } else { + pretty_format_bytes_hex_canonical( + instance->text_box_store, + TYPE_4_TAG_RENDER_BYTES_PER_LINE, + PRETTY_FORMAT_FONT_MONOSPACE, + simple_array_cget_data(data->ndef_data), + simple_array_get_count(data->ndef_data)); + } widget_add_text_scroll_element( instance->widget, 0, 0, 128, 48, furi_string_get_cstr(instance->text_box_store)); diff --git a/applications/main/nfc/helpers/protocol_support/type_4_tag/type_4_tag_render.c b/applications/main/nfc/helpers/protocol_support/type_4_tag/type_4_tag_render.c index bf85feb78..8fdeecb14 100644 --- a/applications/main/nfc/helpers/protocol_support/type_4_tag/type_4_tag_render.c +++ b/applications/main/nfc/helpers/protocol_support/type_4_tag/type_4_tag_render.c @@ -10,7 +10,11 @@ void nfc_render_type_4_tag_info( FuriString* str) { nfc_render_iso14443_4a_brief(type_4_tag_get_base_data(data), str); - furi_string_cat(str, "\n::::::::::::::::::[Tag Specs]::::::::::::::::::\n"); + furi_string_cat(str, "\n:::::::::::::::[Stored NDEF]:::::::::::::::\n"); + furi_string_cat_printf( + str, "Current NDEF Size: %lu\n", simple_array_get_count(data->ndef_data)); + + furi_string_cat(str, "::::::::::::::::::[Tag Specs]::::::::::::::::::\n"); furi_string_cat_printf( str, "T4T Mapping Version: %u.%u\n", data->t4t_version.major, data->t4t_version.minor); furi_string_cat_printf(str, "NDEF File ID: 0x%04X\n", data->ndef_file_id); @@ -24,13 +28,10 @@ void nfc_render_type_4_tag_info( data->ndef_read_lock == 0 ? " (unlocked)" : ""); furi_string_cat_printf( str, - "Write Lock: 0x%02X%s\n", + "Write Lock: 0x%02X%s", data->ndef_write_lock, data->ndef_write_lock == 0 ? " (unlocked)" : ""); - furi_string_cat(str, ":::::::::::::::[Stored NDEF]:::::::::::::::\n"); - furi_string_cat_printf(str, "Current NDEF Size: %lu", simple_array_get_count(data->ndef_data)); - if(format_type != NfcProtocolFormatTypeFull) return; furi_string_cat(str, "\n\e#ISO14443-4 data"); @@ -38,9 +39,13 @@ void nfc_render_type_4_tag_info( } void nfc_render_type_4_tag_dump(const Type4TagData* data, FuriString* str) { - furi_string_cat_printf(str, "\e*"); - const uint8_t* ndef_data = simple_array_cget_data(data->ndef_data); size_t ndef_len = simple_array_get_count(data->ndef_data); + if(ndef_len == 0) { + furi_string_cat_str(str, "No NDEF data to show"); + return; + } + const uint8_t* ndef_data = simple_array_cget_data(data->ndef_data); + furi_string_cat_printf(str, "\e*"); for(size_t i = 0; i < ndef_len; i += TYPE_4_TAG_RENDER_BYTES_PER_LINE) { const uint8_t* line_data = &ndef_data[i]; for(size_t j = 0; j < TYPE_4_TAG_RENDER_BYTES_PER_LINE; j += 2) { diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c b/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c index 5d8c56d40..ab2f28285 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c @@ -208,14 +208,20 @@ Type4TagError type_4_tag_poller_read_ndef(Type4TagPoller* instance) { ndef_len = bit_lib_bytes_to_num_be(bit_buffer_get_data(instance->rx_buffer), sizeof(ndef_len)); - simple_array_init(instance->data->ndef_data, ndef_len); + if(ndef_len == 0) { + FURI_LOG_D(TAG, "NDEF file is empty"); + break; + } if(ndef_len > 510) { // Both size and offset for READ BINARY are 1 byte uint // So the furthest we can read is 255 bytes at offset 255 // AKA 2 * 255 byte chunks = 510 bytes // TODO: Surely there has to be another way? FURI_LOG_E(TAG, "NDEF file too long: %zu bytes", ndef_len); + error = Type4TagErrorProtocol; + break; } + simple_array_init(instance->data->ndef_data, ndef_len); FURI_LOG_D(TAG, "Read NDEF"); const uint8_t type_4_tag_read_ndef_apdu_1[] = { From aa38025977a90d46f3af4bc6bb884b9b98e052ee Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 6 Mar 2025 02:21:47 +0000 Subject: [PATCH 03/92] NFC: Implement Type 4 Tag saving --- .../type_4_tag/type_4_tag_render.c | 41 +++++++------ .../type_4_tag/type_4_tag_render.h | 2 +- lib/nfc/protocols/type_4_tag/type_4_tag.c | 6 ++ lib/nfc/protocols/type_4_tag/type_4_tag.h | 3 +- lib/nfc/protocols/type_4_tag/type_4_tag_i.c | 60 +++++++++++++++++++ lib/nfc/protocols/type_4_tag/type_4_tag_i.h | 22 +++++-- .../type_4_tag/type_4_tag_poller_i.c | 1 + 7 files changed, 107 insertions(+), 28 deletions(-) diff --git a/applications/main/nfc/helpers/protocol_support/type_4_tag/type_4_tag_render.c b/applications/main/nfc/helpers/protocol_support/type_4_tag/type_4_tag_render.c index 8fdeecb14..ac1f5bdbc 100644 --- a/applications/main/nfc/helpers/protocol_support/type_4_tag/type_4_tag_render.c +++ b/applications/main/nfc/helpers/protocol_support/type_4_tag/type_4_tag_render.c @@ -2,8 +2,6 @@ #include "../iso14443_4a/iso14443_4a_render.h" -#define TYPE_4_TAG_RENDER_MAX_RECORD_SIZE (256U) - void nfc_render_type_4_tag_info( const Type4TagData* data, NfcProtocolFormatType format_type, @@ -11,26 +9,27 @@ void nfc_render_type_4_tag_info( nfc_render_iso14443_4a_brief(type_4_tag_get_base_data(data), str); furi_string_cat(str, "\n:::::::::::::::[Stored NDEF]:::::::::::::::\n"); - furi_string_cat_printf( - str, "Current NDEF Size: %lu\n", simple_array_get_count(data->ndef_data)); + furi_string_cat_printf(str, "Current NDEF Size: %lu", simple_array_get_count(data->ndef_data)); - furi_string_cat(str, "::::::::::::::::::[Tag Specs]::::::::::::::::::\n"); - furi_string_cat_printf( - str, "T4T Mapping Version: %u.%u\n", data->t4t_version.major, data->t4t_version.minor); - furi_string_cat_printf(str, "NDEF File ID: 0x%04X\n", data->ndef_file_id); - furi_string_cat_printf(str, "Max NDEF Size: %u\n", data->ndef_max_len); - furi_string_cat_printf( - str, "APDU Sizes: R:%u W:%u\n", data->chunk_max_read, data->chunk_max_write); - furi_string_cat_printf( - str, - "Read Lock: 0x%02X%s\n", - data->ndef_read_lock, - data->ndef_read_lock == 0 ? " (unlocked)" : ""); - furi_string_cat_printf( - str, - "Write Lock: 0x%02X%s", - data->ndef_write_lock, - data->ndef_write_lock == 0 ? " (unlocked)" : ""); + if(data->is_tag_specific) { + furi_string_cat(str, "\n::::::::::::::::::[Tag Specs]::::::::::::::::::\n"); + furi_string_cat_printf( + str, "T4T Mapping Version: %u.%u\n", data->t4t_version.major, data->t4t_version.minor); + furi_string_cat_printf(str, "NDEF File ID: 0x%04X\n", data->ndef_file_id); + furi_string_cat_printf(str, "Max NDEF Size: %u\n", data->ndef_max_len); + furi_string_cat_printf( + str, "APDU Sizes: R:%u W:%u\n", data->chunk_max_read, data->chunk_max_write); + furi_string_cat_printf( + str, + "Read Lock: 0x%02X%s\n", + data->ndef_read_lock, + data->ndef_read_lock == 0 ? " (unlocked)" : ""); + furi_string_cat_printf( + str, + "Write Lock: 0x%02X%s", + data->ndef_write_lock, + data->ndef_write_lock == 0 ? " (unlocked)" : ""); + } if(format_type != NfcProtocolFormatTypeFull) return; diff --git a/applications/main/nfc/helpers/protocol_support/type_4_tag/type_4_tag_render.h b/applications/main/nfc/helpers/protocol_support/type_4_tag/type_4_tag_render.h index 91eaeb735..abb45317c 100644 --- a/applications/main/nfc/helpers/protocol_support/type_4_tag/type_4_tag_render.h +++ b/applications/main/nfc/helpers/protocol_support/type_4_tag/type_4_tag_render.h @@ -4,7 +4,7 @@ #include "../nfc_protocol_support_render_common.h" -#define TYPE_4_TAG_RENDER_BYTES_PER_LINE (4) +#define TYPE_4_TAG_RENDER_BYTES_PER_LINE (4U) void nfc_render_type_4_tag_info( const Type4TagData* data, diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag.c b/lib/nfc/protocols/type_4_tag/type_4_tag.c index ec268b618..47e8e7352 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag.c +++ b/lib/nfc/protocols/type_4_tag/type_4_tag.c @@ -41,6 +41,7 @@ void type_4_tag_reset(Type4TagData* data) { iso14443_4a_reset(data->iso14443_4a_data); + data->is_tag_specific = false; data->t4t_version.value = 0; data->chunk_max_read = 0; data->chunk_max_write = 0; @@ -60,6 +61,7 @@ void type_4_tag_copy(Type4TagData* data, const Type4TagData* other) { iso14443_4a_copy(data->iso14443_4a_data, other->iso14443_4a_data); + data->is_tag_specific = other->is_tag_specific; data->t4t_version.value = other->t4t_version.value; data->chunk_max_read = other->chunk_max_read; data->chunk_max_write = other->chunk_max_write; @@ -89,6 +91,8 @@ bool type_4_tag_load(Type4TagData* data, FlipperFormat* ff, uint32_t version) { do { if(!iso14443_4a_load(data->iso14443_4a_data, ff, version)) break; + if(!type_4_tag_ndef_data_load(data, ff)) break; + success = true; } while(false); @@ -109,6 +113,7 @@ bool type_4_tag_save(const Type4TagData* data, FlipperFormat* ff) { if(!flipper_format_write_comment_cstr(ff, TYPE_4_TAG_PROTOCOL_NAME " specific data")) break; + if(!type_4_tag_ndef_data_save(data, ff)) break; success = true; } while(false); @@ -122,6 +127,7 @@ bool type_4_tag_is_equal(const Type4TagData* data, const Type4TagData* other) { furi_check(other); return iso14443_4a_is_equal(data->iso14443_4a_data, other->iso14443_4a_data) && + data->is_tag_specific == other->is_tag_specific && data->t4t_version.value == other->t4t_version.value && data->chunk_max_read == other->chunk_max_read && data->chunk_max_write == other->chunk_max_write && diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag.h b/lib/nfc/protocols/type_4_tag/type_4_tag.h index 99f58b7cc..ec7013c48 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag.h +++ b/lib/nfc/protocols/type_4_tag/type_4_tag.h @@ -17,7 +17,8 @@ typedef enum { typedef struct { Iso14443_4aData* iso14443_4a_data; - // Tag specific + // Tag specific data + bool is_tag_specific; union { struct { uint8_t minor : 4; diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_i.c b/lib/nfc/protocols/type_4_tag/type_4_tag_i.c index c2cbfd1af..ef252f9a0 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag_i.c +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_i.c @@ -1,3 +1,63 @@ #include "type_4_tag_i.h" #define TAG "Type4Tag" + +#define TYPE_4_TAG_FFF_NDEF_DATA_SIZE_KEY "NDEF Data Size" +#define TYPE_4_TAG_FFF_NDEF_DATA_KEY "NDEF Data" + +#define TYPE_4_TAG_FFF_NDEF_DATA_PER_LINE (16U) + +bool type_4_tag_ndef_data_load(Type4TagData* data, FlipperFormat* ff) { + uint32_t ndef_data_size; + if(!flipper_format_read_uint32(ff, TYPE_4_TAG_FFF_NDEF_DATA_SIZE_KEY, &ndef_data_size, 1)) { + return false; + } + if(ndef_data_size == 0) { + return true; + } + + simple_array_init(data->ndef_data, ndef_data_size); + + uint32_t ndef_data_pos = 0; + uint8_t* ndef_data = simple_array_get_data(data->ndef_data); + while(ndef_data_size > 0) { + uint8_t ndef_line_size = MIN(ndef_data_size, TYPE_4_TAG_FFF_NDEF_DATA_PER_LINE); + + if(!flipper_format_read_hex( + ff, TYPE_4_TAG_FFF_NDEF_DATA_KEY, &ndef_data[ndef_data_pos], ndef_line_size)) { + simple_array_reset(data->ndef_data); + return false; + } + + ndef_data_pos += ndef_line_size; + ndef_data_size -= ndef_line_size; + } + + return true; +} + +bool type_4_tag_ndef_data_save(const Type4TagData* data, FlipperFormat* ff) { + uint32_t ndef_data_size = simple_array_get_count(data->ndef_data); + if(!flipper_format_write_uint32(ff, TYPE_4_TAG_FFF_NDEF_DATA_SIZE_KEY, &ndef_data_size, 1)) { + return false; + } + if(ndef_data_size == 0) { + return true; + } + + uint32_t ndef_data_pos = 0; + uint8_t* ndef_data = simple_array_get_data(data->ndef_data); + while(ndef_data_size > 0) { + uint8_t ndef_line_size = MIN(ndef_data_size, TYPE_4_TAG_FFF_NDEF_DATA_PER_LINE); + + if(!flipper_format_write_hex( + ff, TYPE_4_TAG_FFF_NDEF_DATA_KEY, &ndef_data[ndef_data_pos], ndef_line_size)) { + return false; + } + + ndef_data_pos += ndef_line_size; + ndef_data_size -= ndef_line_size; + } + + return true; +} diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_i.h b/lib/nfc/protocols/type_4_tag/type_4_tag_i.h index 0dcece22b..2fdd478ac 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag_i.h +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_i.h @@ -2,26 +2,30 @@ #include "type_4_tag.h" +// ISO SELECT FILE command and parameters #define TYPE_4_TAG_ISO_SELECT_CMD 0x00, 0xA4 #define TYPE_4_TAG_ISO_SELECT_P1_BY_NAME (0x04) #define TYPE_4_TAG_ISO_SELECT_P1_BY_ID (0x00) #define TYPE_4_TAG_ISO_SELECT_P2_EMPTY (0x0C) #define TYPE_4_TAG_ISO_SELECT_LE_EMPTY (0x00) +// ISO READ BINARY command and parameters #define TYPE_4_TAG_ISO_READ_CMD 0x00, 0xB0 #define TYPE_4_TAG_ISO_READ_P1_EMPTY (0x00) #define TYPE_4_TAG_ISO_READ_P2_BEGINNING (0x00) #define TYPE_4_TAG_ISO_READ_LE_FULL (0x00) -#define TYPE_4_TAG_ISO_STATUS_LEN (2) +// Common APDU parameters and values +#define TYPE_4_TAG_ISO_STATUS_LEN (2U) #define TYPE_4_TAG_ISO_STATUS_SUCCESS 0x90, 0x00 -#define TYPE_4_TAG_ISO_RW_CHUNK_LEN (255) -#define TYPE_4_TAG_ISO_APP_NAME_LEN (7) +#define TYPE_4_TAG_ISO_RW_CHUNK_LEN (255U) +#define TYPE_4_TAG_ISO_APP_NAME_LEN (7U) #define TYPE_4_TAG_ISO_APP_NAME 0xD2, 0x76, 0x00, 0x00, 0x85, 0x01, 0x01 -#define TYPE_4_TAG_T4T_CC_FILE_ID_LEN (2) +#define TYPE_4_TAG_T4T_CC_FILE_ID_LEN (2U) #define TYPE_4_TAG_T4T_CC_FILE_ID 0xE1, 0x03 +#define TYPE_4_TAG_T4T_CC_VNO (0x20) -#define TYPE_4_TAG_T4T_CC_VNO 0x20 +// Capability Container parsing structures typedef enum FURI_PACKED { Type4TagCcTlvTypeNdefFileCtrl = 0x04, @@ -52,3 +56,11 @@ typedef struct FURI_PACKED { uint16_t mlc; Type4TagCcTlv tlv[]; } Type4TagCc; + +// Load internal Type4Tag structures + +bool type_4_tag_ndef_data_load(Type4TagData* data, FlipperFormat* ff); + +// Save internal Type4Tag structures + +bool type_4_tag_ndef_data_save(const Type4TagData* data, FlipperFormat* ff); diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c b/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c index ab2f28285..496277bc9 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c @@ -137,6 +137,7 @@ Type4TagError type_4_tag_poller_read_cc(Type4TagPoller* instance) { break; } + instance->data->is_tag_specific = true; instance->data->t4t_version.value = cc->t4t_vno; instance->data->chunk_max_read = bit_lib_bytes_to_num_be((void*)&cc->mle, sizeof(cc->mle)); instance->data->chunk_max_write = From 0beee0c6a7e7014101f9061d1eb518d5a55abd1c Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 6 Mar 2025 02:52:02 +0000 Subject: [PATCH 04/92] Fix reading NLEN header in NDEF message --- lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c b/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c index 496277bc9..1edc793f7 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c @@ -213,10 +213,10 @@ Type4TagError type_4_tag_poller_read_ndef(Type4TagPoller* instance) { FURI_LOG_D(TAG, "NDEF file is empty"); break; } - if(ndef_len > 510) { + if(ndef_len > 510 - sizeof(ndef_len)) { // Both size and offset for READ BINARY are 1 byte uint // So the furthest we can read is 255 bytes at offset 255 - // AKA 2 * 255 byte chunks = 510 bytes + // AKA 2 * 255 byte chunks = 510 bytes -2 byte NDEF len header // TODO: Surely there has to be another way? FURI_LOG_E(TAG, "NDEF file too long: %zu bytes", ndef_len); error = Type4TagErrorProtocol; @@ -240,7 +240,7 @@ Type4TagError type_4_tag_poller_read_ndef(Type4TagPoller* instance) { instance->tx_buffer, type_4_tag_read_ndef_apdu_1, sizeof(type_4_tag_read_ndef_apdu_1)); - bit_buffer_append_byte(instance->tx_buffer, (uint8_t)ndef_pos); + bit_buffer_append_byte(instance->tx_buffer, (uint8_t)(sizeof(ndef_len) + ndef_pos)); bit_buffer_append_byte(instance->tx_buffer, chunk_len); error = type_4_tag_apdu_trx(instance, instance->tx_buffer, instance->rx_buffer); From e3f7e1c2d3c6db75428cf41376a69b0208650467 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 6 Mar 2025 02:54:40 +0000 Subject: [PATCH 05/92] NFC: Implement Type 4 Tag NDEF parsing --- applications/main/nfc/application.fam | 10 ++++ .../main/nfc/plugins/supported_cards/ndef.c | 51 ++++++++++++++++++- 2 files changed, 60 insertions(+), 1 deletion(-) diff --git a/applications/main/nfc/application.fam b/applications/main/nfc/application.fam index 29bdf390a..11dca519a 100644 --- a/applications/main/nfc/application.fam +++ b/applications/main/nfc/application.fam @@ -247,6 +247,16 @@ App( sources=["plugins/supported_cards/ndef.c"], ) +App( + appid="ndef_t4t_parser", + apptype=FlipperAppType.PLUGIN, + cdefines=[("NDEF_PROTO", "NDEF_PROTO_T4T")], + entry_point="ndef_plugin_ep", + targets=["f7"], + requires=["nfc"], + sources=["plugins/supported_cards/ndef.c"], +) + App( appid="disney_infinity_parser", apptype=FlipperAppType.PLUGIN, diff --git a/applications/main/nfc/plugins/supported_cards/ndef.c b/applications/main/nfc/plugins/supported_cards/ndef.c index fb2c4da48..2b7ea2dbc 100644 --- a/applications/main/nfc/plugins/supported_cards/ndef.c +++ b/applications/main/nfc/plugins/supported_cards/ndef.c @@ -20,6 +20,7 @@ #include #include #include +#include #include @@ -30,7 +31,8 @@ #define NDEF_PROTO_UL (1) #define NDEF_PROTO_MFC (2) #define NDEF_PROTO_SLIX (3) -#define NDEF_PROTO_TOTAL (4) +#define NDEF_PROTO_T4T (4) +#define NDEF_PROTO_TOTAL (5) #ifndef NDEF_PROTO #error Must specify what protocol to use with NDEF_PROTO define! @@ -150,6 +152,11 @@ typedef struct { const uint8_t* start; size_t size; } slix; +#elif NDEF_PROTO == NDEF_PROTO_T4T + struct { + const uint8_t* data; + size_t size; + } t4t; #endif } Ndef; @@ -225,6 +232,13 @@ static bool ndef_get(Ndef* ndef, size_t pos, size_t len, void* buf) { memcpy(buf, ndef->slix.start + pos, len); return true; +#elif NDEF_PROTO == NDEF_PROTO_T4T + + // Memory space is contiguous, simply need to remap to data pointer + if(pos + len > ndef->t4t.size) return false; + memcpy(buf, ndef->t4t.data + pos, len); + return true; + #else UNUSED(ndef); @@ -1030,6 +1044,38 @@ static bool ndef_slix_parse(const NfcDevice* device, FuriString* parsed_data) { return parsed > 0; } +#elif NDEF_PROTO == NDEF_PROTO_T4T + +static bool ndef_t4t_parse(const NfcDevice* device, FuriString* parsed_data) { + furi_assert(device); + furi_assert(parsed_data); + + const Type4TagData* data = nfc_device_get_data(device, NfcProtocolType4Tag); + size_t data_start = 0; + size_t data_size = simple_array_get_count(data->ndef_data); + + NDEF_TITLE(device, parsed_data); + + Ndef ndef = { + .output = parsed_data, + .t4t = + { + .data = data_size == 0 ? NULL : simple_array_cget_data(data->ndef_data), + .size = data_size, + }, + }; + size_t parsed = ndef_parse_message(&ndef, data_start, data_size - data_start, 1, false); + + if(parsed) { + furi_string_trim(parsed_data, "\n"); + furi_string_cat(parsed_data, "\n"); + } else { + furi_string_reset(parsed_data); + } + + return parsed > 0; +} + #endif // ---=== boilerplate ===--- @@ -1047,6 +1093,9 @@ static const NfcSupportedCardsPlugin ndef_plugin = { #elif NDEF_PROTO == NDEF_PROTO_SLIX .parse = ndef_slix_parse, .protocol = NfcProtocolSlix, +#elif NDEF_PROTO == NDEF_PROTO_T4T + .parse = ndef_t4t_parse, + .protocol = NfcProtocolType4Tag, #endif }; From f760d97e62ae78aac9d29ef91b59f6c7c101f6b2 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 9 Mar 2025 06:10:36 +0000 Subject: [PATCH 06/92] Sort and add missing protocol mentions --- lib/nfc/SConscript | 13 +++++++++---- lib/nfc/protocols/nfc_listener_defs.c | 3 ++- lib/nfc/protocols/nfc_protocol.c | 2 +- targets/f7/api_symbols.csv | 12 ++++++++++++ 4 files changed, 24 insertions(+), 6 deletions(-) diff --git a/lib/nfc/SConscript b/lib/nfc/SConscript index 9aef970c8..f0ca22aa7 100644 --- a/lib/nfc/SConscript +++ b/lib/nfc/SConscript @@ -19,39 +19,44 @@ env.Append( File("protocols/iso14443_3b/iso14443_3b.h"), File("protocols/iso14443_4a/iso14443_4a.h"), File("protocols/iso14443_4b/iso14443_4b.h"), + File("protocols/iso15693_3/iso15693_3.h"), + File("protocols/felica/felica.h"), File("protocols/mf_ultralight/mf_ultralight.h"), File("protocols/mf_classic/mf_classic.h"), File("protocols/mf_plus/mf_plus.h"), File("protocols/mf_desfire/mf_desfire.h"), File("protocols/slix/slix.h"), File("protocols/st25tb/st25tb.h"), - File("protocols/felica/felica.h"), File("protocols/type_4_tag/type_4_tag.h"), # Pollers File("protocols/iso14443_3a/iso14443_3a_poller.h"), File("protocols/iso14443_3b/iso14443_3b_poller.h"), File("protocols/iso14443_4a/iso14443_4a_poller.h"), File("protocols/iso14443_4b/iso14443_4b_poller.h"), + File("protocols/iso15693_3/iso15693_3_poller.h"), + File("protocols/felica/felica_poller.h"), File("protocols/mf_ultralight/mf_ultralight_poller.h"), File("protocols/mf_classic/mf_classic_poller.h"), File("protocols/mf_plus/mf_plus_poller.h"), File("protocols/mf_desfire/mf_desfire_poller.h"), File("protocols/slix/slix_poller.h"), File("protocols/st25tb/st25tb_poller.h"), - File("protocols/felica/felica_poller.h"), File("protocols/type_4_tag/type_4_tag_poller.h"), # Listeners File("protocols/iso14443_3a/iso14443_3a_listener.h"), File("protocols/iso14443_4a/iso14443_4a_listener.h"), + File("protocols/iso15693_3/iso15693_3_listener.h"), + File("protocols/felica/felica_listener.h"), File("protocols/mf_ultralight/mf_ultralight_listener.h"), File("protocols/mf_classic/mf_classic_listener.h"), - File("protocols/felica/felica_listener.h"), + File("protocols/slix/slix_listener.h"), + File("protocols/type_4_tag/type_4_tag_listener.h"), # Sync API File("protocols/iso14443_3a/iso14443_3a_poller_sync.h"), + File("protocols/felica/felica_poller_sync.h"), File("protocols/mf_ultralight/mf_ultralight_poller_sync.h"), File("protocols/mf_classic/mf_classic_poller_sync.h"), File("protocols/st25tb/st25tb_poller_sync.h"), - File("protocols/felica/felica_poller_sync.h"), # Misc File("helpers/nfc_util.h"), File("helpers/iso14443_crc.h"), diff --git a/lib/nfc/protocols/nfc_listener_defs.c b/lib/nfc/protocols/nfc_listener_defs.c index 69320bd79..017f6a571 100644 --- a/lib/nfc/protocols/nfc_listener_defs.c +++ b/lib/nfc/protocols/nfc_listener_defs.c @@ -3,10 +3,10 @@ #include #include #include +#include #include #include #include -#include const NfcListenerBase* nfc_listeners_api[NfcProtocolNum] = { [NfcProtocolIso14443_3a] = &nfc_listener_iso14443_3a, @@ -17,6 +17,7 @@ const NfcListenerBase* nfc_listeners_api[NfcProtocolNum] = { [NfcProtocolFelica] = &nfc_listener_felica, [NfcProtocolMfUltralight] = &mf_ultralight_listener, [NfcProtocolMfClassic] = &mf_classic_listener, + [NfcProtocolMfPlus] = NULL, [NfcProtocolMfDesfire] = NULL, [NfcProtocolSlix] = &nfc_listener_slix, [NfcProtocolSt25tb] = NULL, diff --git a/lib/nfc/protocols/nfc_protocol.c b/lib/nfc/protocols/nfc_protocol.c index fd64a7b0c..7fd7ffb6d 100644 --- a/lib/nfc/protocols/nfc_protocol.c +++ b/lib/nfc/protocols/nfc_protocol.c @@ -62,8 +62,8 @@ static const NfcProtocol nfc_protocol_iso14443_3b_children_protocol[] = { /** List of ISO14443-4A child protocols. */ static const NfcProtocol nfc_protocol_iso14443_4a_children_protocol[] = { - NfcProtocolMfDesfire, NfcProtocolMfPlus, + NfcProtocolMfDesfire, NfcProtocolType4Tag, }; diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index 9483b1719..7beadbb33 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -152,6 +152,9 @@ Header,+,lib/nfc/protocols/iso14443_4a/iso14443_4a_listener.h,, Header,+,lib/nfc/protocols/iso14443_4a/iso14443_4a_poller.h,, Header,+,lib/nfc/protocols/iso14443_4b/iso14443_4b.h,, Header,+,lib/nfc/protocols/iso14443_4b/iso14443_4b_poller.h,, +Header,+,lib/nfc/protocols/iso15693_3/iso15693_3.h,, +Header,+,lib/nfc/protocols/iso15693_3/iso15693_3_listener.h,, +Header,+,lib/nfc/protocols/iso15693_3/iso15693_3_poller.h,, Header,+,lib/nfc/protocols/mf_classic/mf_classic.h,, Header,+,lib/nfc/protocols/mf_classic/mf_classic_listener.h,, Header,+,lib/nfc/protocols/mf_classic/mf_classic_poller.h,, @@ -165,11 +168,13 @@ Header,+,lib/nfc/protocols/mf_ultralight/mf_ultralight_listener.h,, Header,+,lib/nfc/protocols/mf_ultralight/mf_ultralight_poller.h,, Header,+,lib/nfc/protocols/mf_ultralight/mf_ultralight_poller_sync.h,, Header,+,lib/nfc/protocols/slix/slix.h,, +Header,+,lib/nfc/protocols/slix/slix_listener.h,, Header,+,lib/nfc/protocols/slix/slix_poller.h,, Header,+,lib/nfc/protocols/st25tb/st25tb.h,, Header,+,lib/nfc/protocols/st25tb/st25tb_poller.h,, Header,+,lib/nfc/protocols/st25tb/st25tb_poller_sync.h,, Header,+,lib/nfc/protocols/type_4_tag/type_4_tag.h,, +Header,+,lib/nfc/protocols/type_4_tag/type_4_tag_listener.h,, Header,+,lib/nfc/protocols/type_4_tag/type_4_tag_poller.h,, Header,+,lib/one_wire/maxim_crc.h,, Header,+,lib/one_wire/one_wire_host.h,, @@ -2198,6 +2203,13 @@ Function,+,iso15693_3_get_uid,const uint8_t*,"const Iso15693_3Data*, size_t*" Function,+,iso15693_3_is_block_locked,_Bool,"const Iso15693_3Data*, uint8_t" Function,+,iso15693_3_is_equal,_Bool,"const Iso15693_3Data*, const Iso15693_3Data*" Function,+,iso15693_3_load,_Bool,"Iso15693_3Data*, FlipperFormat*, uint32_t" +Function,+,iso15693_3_poller_activate,Iso15693_3Error,"Iso15693_3Poller*, Iso15693_3Data*" +Function,+,iso15693_3_poller_get_blocks_security,Iso15693_3Error,"Iso15693_3Poller*, uint8_t*, uint16_t" +Function,+,iso15693_3_poller_get_system_info,Iso15693_3Error,"Iso15693_3Poller*, Iso15693_3SystemInfo*" +Function,+,iso15693_3_poller_inventory,Iso15693_3Error,"Iso15693_3Poller*, uint8_t*" +Function,+,iso15693_3_poller_read_block,Iso15693_3Error,"Iso15693_3Poller*, uint8_t*, uint8_t, uint8_t" +Function,+,iso15693_3_poller_read_blocks,Iso15693_3Error,"Iso15693_3Poller*, uint8_t*, uint16_t, uint8_t" +Function,+,iso15693_3_poller_send_frame,Iso15693_3Error,"Iso15693_3Poller*, const BitBuffer*, BitBuffer*, uint32_t" Function,+,iso15693_3_reset,void,Iso15693_3Data* Function,+,iso15693_3_save,_Bool,"const Iso15693_3Data*, FlipperFormat*" Function,+,iso15693_3_set_uid,_Bool,"Iso15693_3Data*, const uint8_t*, size_t" From 730f1b6ecf5fbcdbd3e7d53d6ae14974085c1264 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 10 Mar 2025 02:23:06 +0000 Subject: [PATCH 07/92] Fix event struct passed to callbacks --- lib/nfc/protocols/type_4_tag/type_4_tag_poller.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_poller.c b/lib/nfc/protocols/type_4_tag/type_4_tag_poller.c index ee09d8a68..79f20152c 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag_poller.c +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_poller.c @@ -100,6 +100,7 @@ static NfcCommand type_4_tag_poller_handler_read_ndef(Type4TagPoller* instance) static NfcCommand type_4_tag_poller_handler_read_fail(Type4TagPoller* instance) { FURI_LOG_D(TAG, "Read Failed"); iso14443_4a_poller_halt(instance->iso14443_4a_poller); + instance->type_4_tag_event.type = Type4TagPollerEventTypeReadFailed; instance->type_4_tag_event.data->error = instance->error; NfcCommand command = instance->callback(instance->general_event, instance->context); instance->state = Type4TagPollerStateIdle; @@ -150,6 +151,8 @@ static NfcCommand type_4_tag_poller_run(NfcGenericEvent event, void* context) { command = type_4_tag_poller_read_handler[instance->state](instance); } else if(iso14443_4a_event->type == Iso14443_4aPollerEventTypeError) { instance->type_4_tag_event.type = Type4TagPollerEventTypeReadFailed; + instance->type_4_tag_event.data->error = + type_4_tag_process_error(iso14443_4a_event->data->error); command = instance->callback(instance->general_event, instance->context); } From 385e8b3b1c608c82273da30b58dc73483a51b979 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 10 Mar 2025 02:57:01 +0000 Subject: [PATCH 08/92] Add more specific error types --- lib/nfc/protocols/type_4_tag/type_4_tag.h | 3 +++ lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c | 12 ++++++------ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag.h b/lib/nfc/protocols/type_4_tag/type_4_tag.h index ec7013c48..edbe320a4 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag.h +++ b/lib/nfc/protocols/type_4_tag/type_4_tag.h @@ -13,6 +13,9 @@ typedef enum { Type4TagErrorNotPresent, Type4TagErrorProtocol, Type4TagErrorTimeout, + Type4TagErrorWrongFormat, + Type4TagErrorNotSupported, + Type4TagErrorUnknown, } Type4TagError; typedef struct { diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c b/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c index 1edc793f7..0131b53c1 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c @@ -36,7 +36,7 @@ Type4TagError type_4_tag_apdu_trx(Type4TagPoller* instance, BitBuffer* tx_buf, B size_t response_len = bit_buffer_get_size_bytes(rx_buf); if(response_len < TYPE_4_TAG_ISO_STATUS_LEN) { - return Type4TagErrorProtocol; + return Type4TagErrorWrongFormat; } const uint8_t success[TYPE_4_TAG_ISO_STATUS_LEN] = {TYPE_4_TAG_ISO_STATUS_SUCCESS}; @@ -50,7 +50,7 @@ Type4TagError type_4_tag_apdu_trx(Type4TagPoller* instance, BitBuffer* tx_buf, B return Type4TagErrorNone; } else { FURI_LOG_E(TAG, "APDU failed: 0x%02X%02X", status[0], status[1]); - return Type4TagErrorProtocol; + return Type4TagErrorUnknown; } } @@ -112,7 +112,7 @@ Type4TagError type_4_tag_poller_read_cc(Type4TagPoller* instance) { const Type4TagCc* cc = (const Type4TagCc*)bit_buffer_get_data(instance->rx_buffer); if(cc->t4t_vno != TYPE_4_TAG_T4T_CC_VNO) { FURI_LOG_E(TAG, "Unsupported T4T version"); - error = Type4TagErrorProtocol; + error = Type4TagErrorNotSupported; break; } @@ -133,7 +133,7 @@ Type4TagError type_4_tag_poller_read_cc(Type4TagPoller* instance) { } if(!ndef_file_ctrl) { FURI_LOG_E(TAG, "No NDEF file ctrl TLV"); - error = Type4TagErrorProtocol; + error = Type4TagErrorWrongFormat; break; } @@ -219,7 +219,7 @@ Type4TagError type_4_tag_poller_read_ndef(Type4TagPoller* instance) { // AKA 2 * 255 byte chunks = 510 bytes -2 byte NDEF len header // TODO: Surely there has to be another way? FURI_LOG_E(TAG, "NDEF file too long: %zu bytes", ndef_len); - error = Type4TagErrorProtocol; + error = Type4TagErrorNotSupported; break; } simple_array_init(instance->data->ndef_data, ndef_len); @@ -251,7 +251,7 @@ Type4TagError type_4_tag_poller_read_ndef(Type4TagPoller* instance) { "Wrong NDEF chunk len: %zu != %zu", bit_buffer_get_size_bytes(instance->rx_buffer), ndef_len); - error = Type4TagErrorProtocol; + error = Type4TagErrorWrongFormat; break; } memcpy(&ndef_data[ndef_pos], bit_buffer_get_data(instance->rx_buffer), chunk_len); From 4dce8790cca3e8aec01974a6fa9f1a0d11fbe529 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 10 Mar 2025 04:15:14 +0000 Subject: [PATCH 09/92] Cleanup includes --- lib/nfc/protocols/type_4_tag/type_4_tag.c | 2 -- lib/nfc/protocols/type_4_tag/type_4_tag.h | 2 -- lib/nfc/protocols/type_4_tag/type_4_tag_poller.c | 5 +---- lib/nfc/protocols/type_4_tag/type_4_tag_poller.h | 2 -- lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c | 4 +--- 5 files changed, 2 insertions(+), 13 deletions(-) diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag.c b/lib/nfc/protocols/type_4_tag/type_4_tag.c index 47e8e7352..3bcb0ee41 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag.c +++ b/lib/nfc/protocols/type_4_tag/type_4_tag.c @@ -1,7 +1,5 @@ #include "type_4_tag_i.h" -#include - #define TYPE_4_TAG_PROTOCOL_NAME "Type 4 Tag" const NfcDeviceBase nfc_device_type_4_tag = { diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag.h b/lib/nfc/protocols/type_4_tag/type_4_tag.h index edbe320a4..bfa9f9478 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag.h +++ b/lib/nfc/protocols/type_4_tag/type_4_tag.h @@ -2,8 +2,6 @@ #include -#include - #ifdef __cplusplus extern "C" { #endif diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_poller.c b/lib/nfc/protocols/type_4_tag/type_4_tag_poller.c index 79f20152c..3ff58ea2e 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag_poller.c +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_poller.c @@ -1,10 +1,7 @@ #include "type_4_tag_poller_i.h" +#include "type_4_tag_poller_defs.h" #include "type_4_tag_i.h" -#include - -#include - #define TAG "Type4TagPoller" // Read returns 2 byte status trailer, write sends 5 byte command header diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_poller.h b/lib/nfc/protocols/type_4_tag/type_4_tag_poller.h index c3ba91c32..eb5e03a14 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag_poller.h +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_poller.h @@ -2,8 +2,6 @@ #include "type_4_tag.h" -#include - #ifdef __cplusplus extern "C" { #endif diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c b/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c index 0131b53c1..b356604c0 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c @@ -1,9 +1,7 @@ #include "type_4_tag_poller_i.h" +#include "type_4_tag_i.h" #include -#include - -#include "type_4_tag_i.h" #define TAG "Type4TagPoller" From b0690de06a40bf3a0abc5a6d17533466c7895dc9 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 10 Mar 2025 09:05:05 +0000 Subject: [PATCH 10/92] NFC: Expose iso14443_3a_listener functions (OFW issue 4059) --- .../iso14443_3a/iso14443_3a_listener.h | 41 +++++++++++++++++++ .../iso14443_3a/iso14443_3a_listener_i.h | 11 ----- targets/f7/api_symbols.csv | 3 ++ 3 files changed, 44 insertions(+), 11 deletions(-) diff --git a/lib/nfc/protocols/iso14443_3a/iso14443_3a_listener.h b/lib/nfc/protocols/iso14443_3a/iso14443_3a_listener.h index 8a550ca0a..7b2a4424d 100644 --- a/lib/nfc/protocols/iso14443_3a/iso14443_3a_listener.h +++ b/lib/nfc/protocols/iso14443_3a/iso14443_3a_listener.h @@ -26,6 +26,47 @@ typedef struct { Iso14443_3aListenerEventData* data; } Iso14443_3aListenerEvent; +/** + * @brief Transmit Iso14443_3a frames in listener mode. + * + * Must ONLY be used inside the callback function. + * + * + * @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. + * @return Iso14443_3aErrorNone on success, an error code on failure. + */ +Iso14443_3aError + iso14443_3a_listener_tx(Iso14443_3aListener* instance, const BitBuffer* tx_buffer); + +/** + * @brief Transmit Iso14443_3a frames with custom parity bits in listener mode. + * + * Must ONLY be used inside the callback function. + * + * Custom parity bits must be set in the tx_buffer. + * + * @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. + * @return Iso14443_3aErrorNone on success, an error code on failure. + */ +Iso14443_3aError iso14443_3a_listener_tx_with_custom_parity( + Iso14443_3aListener* instance, + const BitBuffer* tx_buffer); + +/** + * @brief Transmit Iso14443_3a standard frames in listener mode. + * + * Must ONLY be used inside the callback function. + * + * @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. + * @return Iso14443_3aErrorNone on success, an error code on failure. + */ +Iso14443_3aError iso14443_3a_listener_send_standard_frame( + Iso14443_3aListener* instance, + const BitBuffer* tx_buffer); + #ifdef __cplusplus } #endif diff --git a/lib/nfc/protocols/iso14443_3a/iso14443_3a_listener_i.h b/lib/nfc/protocols/iso14443_3a/iso14443_3a_listener_i.h index 0113a1cb8..ef9edf18f 100644 --- a/lib/nfc/protocols/iso14443_3a/iso14443_3a_listener_i.h +++ b/lib/nfc/protocols/iso14443_3a/iso14443_3a_listener_i.h @@ -26,17 +26,6 @@ struct Iso14443_3aListener { void* context; }; -Iso14443_3aError - iso14443_3a_listener_tx(Iso14443_3aListener* instance, const BitBuffer* tx_buffer); - -Iso14443_3aError iso14443_3a_listener_tx_with_custom_parity( - Iso14443_3aListener* instance, - const BitBuffer* tx_buffer); - -Iso14443_3aError iso14443_3a_listener_send_standard_frame( - Iso14443_3aListener* instance, - const BitBuffer* tx_buffer); - #ifdef __cplusplus } #endif diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index 7beadbb33..d20e5e12f 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -2114,6 +2114,9 @@ Function,+,iso14443_3a_get_device_name,const char*,"const Iso14443_3aData*, NfcD Function,+,iso14443_3a_get_sak,uint8_t,const Iso14443_3aData* Function,+,iso14443_3a_get_uid,const uint8_t*,"const Iso14443_3aData*, size_t*" Function,+,iso14443_3a_is_equal,_Bool,"const Iso14443_3aData*, const Iso14443_3aData*" +Function,+,iso14443_3a_listener_send_standard_frame,Iso14443_3aError,"Iso14443_3aListener*, const BitBuffer*" +Function,+,iso14443_3a_listener_tx,Iso14443_3aError,"Iso14443_3aListener*, const BitBuffer*" +Function,+,iso14443_3a_listener_tx_with_custom_parity,Iso14443_3aError,"Iso14443_3aListener*, const BitBuffer*" Function,+,iso14443_3a_load,_Bool,"Iso14443_3aData*, FlipperFormat*, uint32_t" Function,+,iso14443_3a_poller_activate,Iso14443_3aError,"Iso14443_3aPoller*, Iso14443_3aData*" Function,+,iso14443_3a_poller_check_presence,Iso14443_3aError,Iso14443_3aPoller* From cfeddbf8b6320a21749238d648eff7e69450d850 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 10 Mar 2025 09:06:08 +0000 Subject: [PATCH 11/92] BitBuffer: Allow copy right/left on same instance --- lib/toolbox/bit_buffer.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/toolbox/bit_buffer.c b/lib/toolbox/bit_buffer.c index e261e80d4..2d8057c3d 100644 --- a/lib/toolbox/bit_buffer.c +++ b/lib/toolbox/bit_buffer.c @@ -60,7 +60,7 @@ void bit_buffer_copy_right(BitBuffer* buf, const BitBuffer* other, size_t start_ furi_check(bit_buffer_get_size_bytes(other) > start_index); furi_check(buf->capacity_bytes >= bit_buffer_get_size_bytes(other) - start_index); - memcpy(buf->data, other->data + start_index, bit_buffer_get_size_bytes(other) - start_index); + memmove(buf->data, other->data + start_index, bit_buffer_get_size_bytes(other) - start_index); buf->size_bits = other->size_bits - start_index * BITS_IN_BYTE; } @@ -70,7 +70,9 @@ void bit_buffer_copy_left(BitBuffer* buf, const BitBuffer* other, size_t end_ind furi_check(bit_buffer_get_capacity_bytes(buf) >= end_index); furi_check(bit_buffer_get_size_bytes(other) >= end_index); - memcpy(buf->data, other->data, end_index); + if(buf != other) { + memcpy(buf->data, other->data, end_index); + } buf->size_bits = end_index * BITS_IN_BYTE; } From fbc176eb843bbfc1f5e799197552d3b180db41ee Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 10 Mar 2025 09:13:31 +0000 Subject: [PATCH 12/92] NFC: Rudimentary iso14443_4a_listener_send_block() (no PCB handling) (OFW issue 4059) --- .../iso14443_4a/iso14443_4a_listener.c | 10 +++++++++- .../iso14443_4a/iso14443_4a_listener.h | 12 ++++++++++++ .../iso14443_4a/iso14443_4a_listener_i.c | 17 +++++++++++++++++ .../iso14443_4a/iso14443_4a_listener_i.h | 1 + targets/f7/api_symbols.csv | 1 + 5 files changed, 40 insertions(+), 1 deletion(-) diff --git a/lib/nfc/protocols/iso14443_4a/iso14443_4a_listener.c b/lib/nfc/protocols/iso14443_4a/iso14443_4a_listener.c index 2519fb90c..b2e1a4e24 100644 --- a/lib/nfc/protocols/iso14443_4a/iso14443_4a_listener.c +++ b/lib/nfc/protocols/iso14443_4a/iso14443_4a_listener.c @@ -70,7 +70,15 @@ static NfcCommand iso14443_4a_listener_run(NfcGenericEvent event, void* context) instance->state = Iso14443_4aListenerStateActive; } } - } else { + } else if(bit_buffer_get_size_bytes(rx_buffer) > 1) { + // TODO: This is a rudimentary PCB implementation! + // Just ignores its meaning and saves it for sending blocks to reader, + // there is no handling of S, R, I blocks and their different flags. + // We have iso14443_4_layer helper but it is entirely designed for poller, + // will need large rework for listener, for now this works. + instance->pcb_prev = bit_buffer_get_byte(rx_buffer, 0); + bit_buffer_copy_right(rx_buffer, rx_buffer, 1); + instance->iso14443_4a_event.type = Iso14443_4aListenerEventTypeReceivedData; instance->iso14443_4a_event.data->buffer = rx_buffer; diff --git a/lib/nfc/protocols/iso14443_4a/iso14443_4a_listener.h b/lib/nfc/protocols/iso14443_4a/iso14443_4a_listener.h index 04f0b197a..ff4bad7e4 100644 --- a/lib/nfc/protocols/iso14443_4a/iso14443_4a_listener.h +++ b/lib/nfc/protocols/iso14443_4a/iso14443_4a_listener.h @@ -25,6 +25,18 @@ typedef struct { Iso14443_4aListenerEventData* data; } Iso14443_4aListenerEvent; +/** + * @brief Transmit Iso14443_4a blocks in listener mode. + * + * Must ONLY be used inside the callback function. + * + * @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. + * @return Iso14443_4aErrorNone on success, an error code on failure. + */ +Iso14443_4aError + iso14443_4a_listener_send_block(Iso14443_4aListener* instance, const BitBuffer* tx_buffer); + #ifdef __cplusplus } #endif diff --git a/lib/nfc/protocols/iso14443_4a/iso14443_4a_listener_i.c b/lib/nfc/protocols/iso14443_4a/iso14443_4a_listener_i.c index 8590c22ad..20ffa3542 100644 --- a/lib/nfc/protocols/iso14443_4a/iso14443_4a_listener_i.c +++ b/lib/nfc/protocols/iso14443_4a/iso14443_4a_listener_i.c @@ -30,3 +30,20 @@ Iso14443_4aError instance->iso14443_3a_listener, instance->tx_buffer); return iso14443_4a_process_error(error); } + +Iso14443_4aError + iso14443_4a_listener_send_block(Iso14443_4aListener* instance, const BitBuffer* tx_buffer) { + bit_buffer_reset(instance->tx_buffer); + + // TODO: This is a rudimentary PCB implementation! + // Just sends back same PCB that was last received from reader, + // there is no handling of S, R, I blocks and their different flags. + // We have iso14443_4_layer helper but it is entirely designed for poller, + // will need large rework for listener, for now this works. + bit_buffer_append_byte(instance->tx_buffer, instance->pcb_prev); + bit_buffer_append(instance->tx_buffer, tx_buffer); + + const Iso14443_3aError error = iso14443_3a_listener_send_standard_frame( + instance->iso14443_3a_listener, instance->tx_buffer); + return iso14443_4a_process_error(error); +} diff --git a/lib/nfc/protocols/iso14443_4a/iso14443_4a_listener_i.h b/lib/nfc/protocols/iso14443_4a/iso14443_4a_listener_i.h index d4e884f6f..9588d6849 100644 --- a/lib/nfc/protocols/iso14443_4a/iso14443_4a_listener_i.h +++ b/lib/nfc/protocols/iso14443_4a/iso14443_4a_listener_i.h @@ -17,6 +17,7 @@ typedef enum { struct Iso14443_4aListener { Iso14443_3aListener* iso14443_3a_listener; Iso14443_4aData* data; + uint8_t pcb_prev; Iso14443_4aListenerState state; BitBuffer* tx_buffer; diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index d20e5e12f..03df9a8c8 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -2163,6 +2163,7 @@ Function,+,iso14443_4a_get_fwt_fc_max,uint32_t,const Iso14443_4aData* Function,+,iso14443_4a_get_historical_bytes,const uint8_t*,"const Iso14443_4aData*, uint32_t*" Function,+,iso14443_4a_get_uid,const uint8_t*,"const Iso14443_4aData*, size_t*" Function,+,iso14443_4a_is_equal,_Bool,"const Iso14443_4aData*, const Iso14443_4aData*" +Function,+,iso14443_4a_listener_send_block,Iso14443_4aError,"Iso14443_4aListener*, const BitBuffer*" Function,+,iso14443_4a_load,_Bool,"Iso14443_4aData*, FlipperFormat*, uint32_t" Function,+,iso14443_4a_poller_halt,Iso14443_4aError,Iso14443_4aPoller* Function,+,iso14443_4a_poller_read_ats,Iso14443_4aError,"Iso14443_4aPoller*, Iso14443_4aAtsData*" From d174fa9505a9338d0b94f233927d17aa06efbc05 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 10 Mar 2025 09:19:38 +0000 Subject: [PATCH 13/92] NFC: Implement Type 4 Tag rudimentary emulation --- .../protocol_support/type_4_tag/type_4_tag.c | 41 ++- lib/nfc/protocols/nfc_listener_defs.c | 3 +- lib/nfc/protocols/type_4_tag/type_4_tag.h | 1 + lib/nfc/protocols/type_4_tag/type_4_tag_i.c | 13 + lib/nfc/protocols/type_4_tag/type_4_tag_i.h | 40 ++- .../type_4_tag/type_4_tag_listener.c | 88 +++++ .../type_4_tag/type_4_tag_listener.h | 26 ++ .../type_4_tag/type_4_tag_listener_defs.h | 5 + .../type_4_tag/type_4_tag_listener_i.c | 314 ++++++++++++++++++ .../type_4_tag/type_4_tag_listener_i.h | 37 +++ .../protocols/type_4_tag/type_4_tag_poller.c | 3 - .../type_4_tag/type_4_tag_poller_i.c | 15 +- .../type_4_tag/type_4_tag_poller_i.h | 2 - 13 files changed, 546 insertions(+), 42 deletions(-) create mode 100644 lib/nfc/protocols/type_4_tag/type_4_tag_listener.c create mode 100644 lib/nfc/protocols/type_4_tag/type_4_tag_listener.h create mode 100644 lib/nfc/protocols/type_4_tag/type_4_tag_listener_defs.h create mode 100644 lib/nfc/protocols/type_4_tag/type_4_tag_listener_i.c create mode 100644 lib/nfc/protocols/type_4_tag/type_4_tag_listener_i.h diff --git a/applications/main/nfc/helpers/protocol_support/type_4_tag/type_4_tag.c b/applications/main/nfc/helpers/protocol_support/type_4_tag/type_4_tag.c index 97a4872c7..043fd5f73 100644 --- a/applications/main/nfc/helpers/protocol_support/type_4_tag/type_4_tag.c +++ b/applications/main/nfc/helpers/protocol_support/type_4_tag/type_4_tag.c @@ -2,13 +2,13 @@ #include "type_4_tag_render.h" #include +#include #include #include "nfc/nfc_app_i.h" #include "../nfc_protocol_support_common.h" #include "../nfc_protocol_support_gui_common.h" -#include "../iso14443_4a/iso14443_4a_i.h" enum { SubmenuIndexWrite = SubmenuIndexCommonMax, @@ -167,19 +167,42 @@ static void nfc_scene_read_success_on_enter_type_4_tag(NfcApp* instance) { furi_string_free(temp_str); } -static void nfc_scene_emulate_on_enter_type_4_tag(NfcApp* instance) { - // TODO: Implement full emulation - const Iso14443_4aData* iso14443_4a_data = - nfc_device_get_data(instance->nfc_device, NfcProtocolIso14443_4a); +static NfcCommand + nfc_scene_emulate_listener_callback_type_4_tag(NfcGenericEvent event, void* context) { + furi_assert(context); + furi_assert(event.protocol == NfcProtocolType4Tag); + furi_assert(event.event_data); - instance->listener = - nfc_listener_alloc(instance->nfc, NfcProtocolIso14443_4a, iso14443_4a_data); + NfcApp* nfc = context; + Type4TagListenerEvent* type_4_tag_event = event.event_data; + + if(type_4_tag_event->type == Type4TagListenerEventTypeCustomCommand) { + if(furi_string_size(nfc->text_box_store) < NFC_LOG_SIZE_MAX) { + furi_string_cat_printf(nfc->text_box_store, "R:"); + for(size_t i = 0; i < bit_buffer_get_size_bytes(type_4_tag_event->data->buffer); i++) { + furi_string_cat_printf( + nfc->text_box_store, + " %02X", + bit_buffer_get_byte(type_4_tag_event->data->buffer, i)); + } + furi_string_push_back(nfc->text_box_store, '\n'); + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventListenerUpdate); + } + } + + return NfcCommandContinue; +} + +static void nfc_scene_emulate_on_enter_type_4_tag(NfcApp* instance) { + const Type4TagData* data = nfc_device_get_data(instance->nfc_device, NfcProtocolType4Tag); + + instance->listener = nfc_listener_alloc(instance->nfc, NfcProtocolType4Tag, data); nfc_listener_start( - instance->listener, nfc_scene_emulate_listener_callback_iso14443_4a, instance); + instance->listener, nfc_scene_emulate_listener_callback_type_4_tag, instance); } const NfcProtocolSupportBase nfc_protocol_support_type_4_tag = { - .features = NfcProtocolFeatureEmulateUid | NfcProtocolFeatureMoreInfo, + .features = NfcProtocolFeatureEmulateFull | NfcProtocolFeatureMoreInfo, .scene_info = { diff --git a/lib/nfc/protocols/nfc_listener_defs.c b/lib/nfc/protocols/nfc_listener_defs.c index 017f6a571..a4f9e225a 100644 --- a/lib/nfc/protocols/nfc_listener_defs.c +++ b/lib/nfc/protocols/nfc_listener_defs.c @@ -7,6 +7,7 @@ #include #include #include +#include const NfcListenerBase* nfc_listeners_api[NfcProtocolNum] = { [NfcProtocolIso14443_3a] = &nfc_listener_iso14443_3a, @@ -21,5 +22,5 @@ const NfcListenerBase* nfc_listeners_api[NfcProtocolNum] = { [NfcProtocolMfDesfire] = NULL, [NfcProtocolSlix] = &nfc_listener_slix, [NfcProtocolSt25tb] = NULL, - [NfcProtocolType4Tag] = NULL, + [NfcProtocolType4Tag] = &nfc_listener_type_4_tag, }; diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag.h b/lib/nfc/protocols/type_4_tag/type_4_tag.h index bfa9f9478..250f34edc 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag.h +++ b/lib/nfc/protocols/type_4_tag/type_4_tag.h @@ -14,6 +14,7 @@ typedef enum { Type4TagErrorWrongFormat, Type4TagErrorNotSupported, Type4TagErrorUnknown, + Type4TagErrorCustomCommand, } Type4TagError; typedef struct { diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_i.c b/lib/nfc/protocols/type_4_tag/type_4_tag_i.c index ef252f9a0..d90911212 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag_i.c +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_i.c @@ -7,6 +7,19 @@ #define TYPE_4_TAG_FFF_NDEF_DATA_PER_LINE (16U) +Type4TagError type_4_tag_process_error(Iso14443_4aError error) { + switch(error) { + case Iso14443_4aErrorNone: + return Type4TagErrorNone; + case Iso14443_4aErrorNotPresent: + return Type4TagErrorNotPresent; + case Iso14443_4aErrorTimeout: + return Type4TagErrorTimeout; + default: + return Type4TagErrorProtocol; + } +} + bool type_4_tag_ndef_data_load(Type4TagData* data, FlipperFormat* ff) { uint32_t ndef_data_size; if(!flipper_format_read_uint32(ff, TYPE_4_TAG_FFF_NDEF_DATA_SIZE_KEY, &ndef_data_size, 1)) { diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_i.h b/lib/nfc/protocols/type_4_tag/type_4_tag_i.h index 2fdd478ac..9a1add6ee 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag_i.h +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_i.h @@ -3,27 +3,37 @@ #include "type_4_tag.h" // ISO SELECT FILE command and parameters -#define TYPE_4_TAG_ISO_SELECT_CMD 0x00, 0xA4 -#define TYPE_4_TAG_ISO_SELECT_P1_BY_NAME (0x04) -#define TYPE_4_TAG_ISO_SELECT_P1_BY_ID (0x00) -#define TYPE_4_TAG_ISO_SELECT_P2_EMPTY (0x0C) -#define TYPE_4_TAG_ISO_SELECT_LE_EMPTY (0x00) +#define TYPE_4_TAG_ISO_SELECT_CMD 0x00, 0xA4 +#define TYPE_4_TAG_ISO_SELECT_P1_BY_DF_NAME (0x04) +#define TYPE_4_TAG_ISO_SELECT_P1_BY_EF_ID (0x02) +#define TYPE_4_TAG_ISO_SELECT_P1_BY_ID (0x00) +#define TYPE_4_TAG_ISO_SELECT_P2_EMPTY (0x0C) +#define TYPE_4_TAG_ISO_SELECT_LE_EMPTY (0x00) // ISO READ BINARY command and parameters #define TYPE_4_TAG_ISO_READ_CMD 0x00, 0xB0 #define TYPE_4_TAG_ISO_READ_P1_EMPTY (0x00) +#define TYPE_4_TAG_ISO_READ_P1_ID_MASK (1 << 7) #define TYPE_4_TAG_ISO_READ_P2_BEGINNING (0x00) #define TYPE_4_TAG_ISO_READ_LE_FULL (0x00) // Common APDU parameters and values -#define TYPE_4_TAG_ISO_STATUS_LEN (2U) -#define TYPE_4_TAG_ISO_STATUS_SUCCESS 0x90, 0x00 -#define TYPE_4_TAG_ISO_RW_CHUNK_LEN (255U) -#define TYPE_4_TAG_ISO_APP_NAME_LEN (7U) -#define TYPE_4_TAG_ISO_APP_NAME 0xD2, 0x76, 0x00, 0x00, 0x85, 0x01, 0x01 -#define TYPE_4_TAG_T4T_CC_FILE_ID_LEN (2U) -#define TYPE_4_TAG_T4T_CC_FILE_ID 0xE1, 0x03 -#define TYPE_4_TAG_T4T_CC_VNO (0x20) +#define TYPE_4_TAG_ISO_STATUS_LEN (2U) +#define TYPE_4_TAG_ISO_STATUS_SUCCESS 0x90, 0x00 +#define TYPE_4_TAG_ISO_STATUS_OFFSET_ERR 0x6B, 0x00 +#define TYPE_4_TAG_ISO_STATUS_NOT_FOUND 0x6A, 0x82 +#define TYPE_4_TAG_ISO_STATUS_NO_SUPPORT 0x6A, 0x81 +#define TYPE_4_TAG_ISO_STATUS_BAD_PARAMS 0x6A, 0x86 +#define TYPE_4_TAG_ISO_STATUS_NO_CMD 0x68, 0x00 +#define TYPE_4_TAG_ISO_RW_CHUNK_LEN (255U) +#define TYPE_4_TAG_ISO_APP_NAME_LEN (7U) +#define TYPE_4_TAG_ISO_APP_NAME 0xD2, 0x76, 0x00, 0x00, 0x85, 0x01, 0x01 +#define TYPE_4_TAG_T4T_CC_FILE_ID_LEN (2U) +#define TYPE_4_TAG_T4T_CC_FILE_ID 0xE1, 0x03 +#define TYPE_4_TAG_T4T_CC_VNO (0x20) + +// Read returns 2 byte status trailer, write sends 5 byte command header +#define TYPE_4_TAG_BUF_SIZE (TYPE_4_TAG_ISO_RW_CHUNK_LEN + 5) // Capability Container parsing structures @@ -57,6 +67,10 @@ typedef struct FURI_PACKED { Type4TagCcTlv tlv[]; } Type4TagCc; +// Internal helpers + +Type4TagError type_4_tag_process_error(Iso14443_4aError error); + // Load internal Type4Tag structures bool type_4_tag_ndef_data_load(Type4TagData* data, FlipperFormat* ff); diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_listener.c b/lib/nfc/protocols/type_4_tag/type_4_tag_listener.c new file mode 100644 index 000000000..58e94470d --- /dev/null +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_listener.c @@ -0,0 +1,88 @@ +#include "type_4_tag_listener_i.h" +#include "type_4_tag_listener_defs.h" +#include "type_4_tag_i.h" + +#define TAG "Type4TagListener" + +static void type_4_tag_listener_reset_state(Type4TagListener* instance) { + instance->state = Type4TagListenerStateIdle; +} + +static Type4TagListener* + type_4_tag_listener_alloc(Iso14443_4aListener* iso14443_4a_listener, Type4TagData* data) { + furi_assert(iso14443_4a_listener); + + Type4TagListener* instance = malloc(sizeof(Type4TagListener)); + instance->iso14443_4a_listener = iso14443_4a_listener; + instance->data = data; + + instance->tx_buffer = bit_buffer_alloc(TYPE_4_TAG_BUF_SIZE); + + instance->type_4_tag_event.data = &instance->type_4_tag_event_data; + instance->generic_event.protocol = NfcProtocolType4Tag; + instance->generic_event.instance = instance; + instance->generic_event.event_data = &instance->type_4_tag_event; + + return instance; +} + +static void type_4_tag_listener_free(Type4TagListener* instance) { + furi_assert(instance); + furi_assert(instance->data); + furi_assert(instance->tx_buffer); + + bit_buffer_free(instance->tx_buffer); + free(instance); +} + +static void type_4_tag_listener_set_callback( + Type4TagListener* instance, + NfcGenericCallback callback, + void* context) { + furi_assert(instance); + + instance->callback = callback; + instance->context = context; +} + +static const Type4TagData* type_4_tag_listener_get_data(Type4TagListener* instance) { + furi_assert(instance); + furi_assert(instance->data); + + return instance->data; +} + +static NfcCommand type_4_tag_listener_run(NfcGenericEvent event, void* context) { + furi_assert(context); + furi_assert(event.protocol == NfcProtocolIso15693_3); + furi_assert(event.event_data); + + Type4TagListener* instance = context; + Iso14443_4aListenerEvent* iso14443_4a_event = event.event_data; + BitBuffer* rx_buffer = iso14443_4a_event->data->buffer; + NfcCommand command = NfcCommandContinue; + + if(iso14443_4a_event->type == Iso14443_4aListenerEventTypeFieldOff) { + type_4_tag_listener_reset_state(instance); + command = NfcCommandSleep; + } else if(iso14443_4a_event->type == Iso14443_4aListenerEventTypeHalted) { + type_4_tag_listener_reset_state(instance); + } else if(iso14443_4a_event->type == Iso14443_4aListenerEventTypeReceivedData) { + const Type4TagError error = type_4_tag_listener_handle_apdu(instance, rx_buffer); + if(error == Type4TagErrorCustomCommand && instance->callback) { + instance->type_4_tag_event.type = Type4TagListenerEventTypeCustomCommand; + instance->type_4_tag_event.data->buffer = rx_buffer; + command = instance->callback(instance->generic_event, instance->context); + } + } + + return command; +} + +const NfcListenerBase nfc_listener_type_4_tag = { + .alloc = (NfcListenerAlloc)type_4_tag_listener_alloc, + .free = (NfcListenerFree)type_4_tag_listener_free, + .set_callback = (NfcListenerSetCallback)type_4_tag_listener_set_callback, + .get_data = (NfcListenerGetData)type_4_tag_listener_get_data, + .run = (NfcListenerRun)type_4_tag_listener_run, +}; diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_listener.h b/lib/nfc/protocols/type_4_tag/type_4_tag_listener.h new file mode 100644 index 000000000..7e0301322 --- /dev/null +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_listener.h @@ -0,0 +1,26 @@ +#pragma once + +#include "type_4_tag.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct Type4TagListener Type4TagListener; + +typedef enum { + Type4TagListenerEventTypeCustomCommand, +} Type4TagListenerEventType; + +typedef struct { + BitBuffer* buffer; +} Type4TagListenerEventData; + +typedef struct { + Type4TagListenerEventType type; + Type4TagListenerEventData* data; +} Type4TagListenerEvent; + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_listener_defs.h b/lib/nfc/protocols/type_4_tag/type_4_tag_listener_defs.h new file mode 100644 index 000000000..87206fe6b --- /dev/null +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_listener_defs.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +extern const NfcListenerBase nfc_listener_type_4_tag; diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_listener_i.c b/lib/nfc/protocols/type_4_tag/type_4_tag_listener_i.c new file mode 100644 index 000000000..b18189f1e --- /dev/null +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_listener_i.c @@ -0,0 +1,314 @@ +#include "type_4_tag_listener_i.h" +#include "type_4_tag_i.h" + +#include + +#define TAG "Type4TagListener" + +typedef Type4TagError (*Type4TagListenerApduHandler)( + Type4TagListener* instance, + uint8_t p1, + uint8_t p2, + size_t lc, + const uint8_t* data, + size_t le); + +typedef struct { + uint8_t cla_ins[2]; + Type4TagListenerApduHandler handler; +} Type4TagListenerApduCommand; + +static const uint8_t type_4_tag_success_apdu[] = {TYPE_4_TAG_ISO_STATUS_SUCCESS}; +static const uint8_t type_4_tag_offset_error_apdu[] = {TYPE_4_TAG_ISO_STATUS_OFFSET_ERR}; +static const uint8_t type_4_tag_not_found_apdu[] = {TYPE_4_TAG_ISO_STATUS_NOT_FOUND}; +static const uint8_t type_4_tag_no_support_apdu[] = {TYPE_4_TAG_ISO_STATUS_NO_SUPPORT}; +static const uint8_t type_4_tag_bad_params_apdu[] = {TYPE_4_TAG_ISO_STATUS_BAD_PARAMS}; +static const uint8_t type_4_tag_no_cmd_apdu[] = {TYPE_4_TAG_ISO_STATUS_NO_CMD}; + +static Type4TagError type_4_tag_listener_iso_select( + Type4TagListener* instance, + uint8_t p1, + uint8_t p2, + size_t lc, + const uint8_t* data, + size_t le) { + UNUSED(p2); + UNUSED(le); + + if(p1 == TYPE_4_TAG_ISO_SELECT_P1_BY_DF_NAME) { + static const uint8_t t4t_app[TYPE_4_TAG_ISO_APP_NAME_LEN] = {TYPE_4_TAG_ISO_APP_NAME}; + if(lc == sizeof(t4t_app) && memcmp(t4t_app, data, sizeof(t4t_app)) == 0) { + instance->state = Type4TagListenerStateSelectedApplication; + bit_buffer_append_bytes( + instance->tx_buffer, type_4_tag_success_apdu, sizeof(type_4_tag_success_apdu)); + return Type4TagErrorNone; + } + + instance->state = Type4TagListenerStateIdle; + bit_buffer_append_bytes( + instance->tx_buffer, type_4_tag_not_found_apdu, sizeof(type_4_tag_not_found_apdu)); + return Type4TagErrorCustomCommand; + } + + if(instance->state >= Type4TagListenerStateSelectedApplication) { + if(p1 == TYPE_4_TAG_ISO_SELECT_P1_BY_ID || p1 == TYPE_4_TAG_ISO_SELECT_P1_BY_EF_ID) { + static const uint8_t cc_id[TYPE_4_TAG_T4T_CC_FILE_ID_LEN] = { + TYPE_4_TAG_T4T_CC_FILE_ID}; + if(lc == sizeof(cc_id) && memcmp(cc_id, data, sizeof(cc_id)) == 0) { + instance->state = Type4TagListenerStateSelectedCapabilityContainer; + bit_buffer_append_bytes( + instance->tx_buffer, type_4_tag_success_apdu, sizeof(type_4_tag_success_apdu)); + return Type4TagErrorNone; + } + + uint8_t ndef_file_id_be[sizeof(instance->data->ndef_file_id)]; + bit_lib_num_to_bytes_be( + instance->data->ndef_file_id, sizeof(ndef_file_id_be), ndef_file_id_be); + if(lc == sizeof(ndef_file_id_be) && + memcmp(ndef_file_id_be, data, sizeof(ndef_file_id_be)) == 0) { + instance->state = Type4TagListenerStateSelectedNdefMessage; + bit_buffer_append_bytes( + instance->tx_buffer, type_4_tag_success_apdu, sizeof(type_4_tag_success_apdu)); + return Type4TagErrorNone; + } + } + } + + instance->state = Type4TagListenerStateIdle; + bit_buffer_append_bytes( + instance->tx_buffer, type_4_tag_not_found_apdu, sizeof(type_4_tag_not_found_apdu)); + return Type4TagErrorCustomCommand; +} + +static Type4TagError type_4_tag_listener_iso_read( + Type4TagListener* instance, + uint8_t p1, + uint8_t p2, + size_t lc, + const uint8_t* data, + size_t le) { + UNUSED(lc); + UNUSED(data); + + size_t offset; + if(p1 & TYPE_4_TAG_ISO_READ_P1_ID_MASK) { + bit_buffer_append_bytes( + instance->tx_buffer, type_4_tag_no_support_apdu, sizeof(type_4_tag_no_support_apdu)); + return Type4TagErrorCustomCommand; + } else { + offset = (p1 << 8) + p2; + } + + if(instance->state == Type4TagListenerStateSelectedCapabilityContainer) { + uint8_t cc_buf[sizeof(Type4TagCc) + sizeof(Type4TagCcTlv)]; + if(offset >= sizeof(cc_buf)) { + bit_buffer_append_bytes( + instance->tx_buffer, + type_4_tag_offset_error_apdu, + sizeof(type_4_tag_offset_error_apdu)); + return Type4TagErrorWrongFormat; + } + Type4TagCc* cc = (Type4TagCc*)cc_buf; + bit_lib_num_to_bytes_be(sizeof(cc_buf), sizeof(cc->len), (void*)&cc->len); + cc->t4t_vno = TYPE_4_TAG_T4T_CC_VNO; + bit_lib_num_to_bytes_be( + instance->data->is_tag_specific ? instance->data->chunk_max_read : + TYPE_4_TAG_ISO_RW_CHUNK_LEN, + sizeof(cc->mle), + (void*)&cc->mle); + bit_lib_num_to_bytes_be( + instance->data->is_tag_specific ? instance->data->chunk_max_write : + TYPE_4_TAG_ISO_RW_CHUNK_LEN, + sizeof(cc->mlc), + (void*)&cc->mlc); + cc->tlv[0].type = Type4TagCcTlvTypeNdefFileCtrl; + cc->tlv[0].len = sizeof(cc->tlv[0].value.ndef_file_ctrl); + bit_lib_num_to_bytes_be( + instance->data->ndef_file_id, + sizeof(cc->tlv[0].value.ndef_file_ctrl.file_id), + (void*)&cc->tlv[0].value.ndef_file_ctrl.file_id); + bit_lib_num_to_bytes_be( + instance->data->is_tag_specific ? instance->data->ndef_max_len : + TYPE_4_TAG_ISO_RW_CHUNK_LEN, + sizeof(cc->tlv[0].value.ndef_file_ctrl.max_len), + (void*)&cc->tlv[0].value.ndef_file_ctrl.max_len); + cc->tlv[0].value.ndef_file_ctrl.read_perm = + instance->data->is_tag_specific ? instance->data->ndef_read_lock : 0x00; + cc->tlv[0].value.ndef_file_ctrl.write_perm = + instance->data->is_tag_specific ? instance->data->ndef_write_lock : 0x00; + + bit_buffer_append_bytes( + instance->tx_buffer, cc_buf + offset, MIN(sizeof(cc_buf) - offset, le)); + bit_buffer_append_bytes( + instance->tx_buffer, type_4_tag_success_apdu, sizeof(type_4_tag_success_apdu)); + return Type4TagErrorNone; + } + + if(instance->state == Type4TagListenerStateSelectedNdefMessage) { + size_t ndef_file_len = + instance->data->ndef_data ? simple_array_get_count(instance->data->ndef_data) : 0; + bool included_len = false; + if(offset < sizeof(uint16_t)) { + uint8_t ndef_file_len_be[sizeof(uint16_t)]; + bit_lib_num_to_bytes_be(ndef_file_len, sizeof(ndef_file_len_be), ndef_file_len_be); + uint8_t read_len = MIN(sizeof(ndef_file_len_be) - offset, le); + bit_buffer_append_bytes(instance->tx_buffer, &ndef_file_len_be[offset], read_len); + included_len = true; + offset = sizeof(uint16_t); + le -= read_len; + } + offset -= sizeof(uint16_t); + + if(offset >= ndef_file_len) { + if(included_len) { + bit_buffer_append_bytes( + instance->tx_buffer, type_4_tag_success_apdu, sizeof(type_4_tag_success_apdu)); + return Type4TagErrorNone; + } else { + bit_buffer_append_bytes( + instance->tx_buffer, + type_4_tag_offset_error_apdu, + sizeof(type_4_tag_offset_error_apdu)); + return Type4TagErrorWrongFormat; + } + } + const uint8_t* ndef_data = simple_array_cget_data(instance->data->ndef_data); + bit_buffer_append_bytes( + instance->tx_buffer, &ndef_data[offset], MIN(ndef_file_len - offset, le)); + bit_buffer_append_bytes( + instance->tx_buffer, type_4_tag_success_apdu, sizeof(type_4_tag_success_apdu)); + return Type4TagErrorNone; + } + + bit_buffer_append_bytes( + instance->tx_buffer, type_4_tag_not_found_apdu, sizeof(type_4_tag_not_found_apdu)); + return Type4TagErrorCustomCommand; +} + +static const Type4TagListenerApduCommand type_4_tag_listener_commands[] = { + { + .cla_ins = {TYPE_4_TAG_ISO_SELECT_CMD}, + .handler = type_4_tag_listener_iso_select, + }, + { + .cla_ins = {TYPE_4_TAG_ISO_READ_CMD}, + .handler = type_4_tag_listener_iso_read, + }, +}; + +Type4TagError + type_4_tag_listener_handle_apdu(Type4TagListener* instance, const BitBuffer* rx_buffer) { + Type4TagError error = Type4TagErrorNone; + + do { + typedef struct { + uint8_t cla_ins[2]; + uint8_t p1; + uint8_t p2; + uint8_t body[]; + } Type4TagApdu; + + const size_t buf_size = bit_buffer_get_size_bytes(rx_buffer); + + if(buf_size < sizeof(Type4TagApdu)) { + error = Type4TagErrorWrongFormat; + break; + } + + const Type4TagApdu* apdu = (const Type4TagApdu*)bit_buffer_get_data(rx_buffer); + + Type4TagListenerApduHandler handler = NULL; + for(size_t i = 0; i < COUNT_OF(type_4_tag_listener_commands); i++) { + const Type4TagListenerApduCommand* command = &type_4_tag_listener_commands[i]; + if(memcmp(apdu->cla_ins, command->cla_ins, sizeof(apdu->cla_ins)) == 0) { + handler = command->handler; + break; + } + } + if(!handler) { + bit_buffer_append_bytes( + instance->tx_buffer, type_4_tag_no_cmd_apdu, sizeof(type_4_tag_no_cmd_apdu)); + error = Type4TagErrorCustomCommand; + break; + } + + size_t body_size = buf_size - offsetof(Type4TagApdu, body); + size_t lc; + const uint8_t* data = apdu->body; + size_t le; + if(body_size == 0) { + lc = 0; + data = NULL; + le = 0; + } else if(body_size == 1) { + lc = 0; + data = NULL; + le = apdu->body[0]; + if(le == 0) { + le = 256; + } + } else if(body_size == 3 && apdu->body[0] == 0) { + lc = 0; + data = NULL; + le = bit_lib_bytes_to_num_be(&apdu->body[1], sizeof(uint16_t)); + if(le == 0) { + le = 65536; + } + } else { + bool extended_lc = false; + if(data[0] == 0) { + extended_lc = true; + lc = bit_lib_bytes_to_num_be(&data[1], sizeof(uint16_t)); + data += 1 + sizeof(uint16_t); + body_size -= 1 + sizeof(uint16_t); + } else { + lc = data[0]; + data++; + body_size--; + } + if(lc == 0 || body_size < lc) { + bit_buffer_append_bytes( + instance->tx_buffer, + type_4_tag_bad_params_apdu, + sizeof(type_4_tag_bad_params_apdu)); + error = Type4TagErrorWrongFormat; + break; + } + + if(body_size == lc) { + le = 0; + } else if(!extended_lc && body_size - lc == 1) { + le = data[lc]; + if(le == 0) { + le = 256; + } + } else if(extended_lc && body_size - lc == 2) { + le = bit_lib_bytes_to_num_be(&data[lc], sizeof(uint16_t)); + if(le == 0) { + le = 65536; + } + } else { + bit_buffer_append_bytes( + instance->tx_buffer, + type_4_tag_bad_params_apdu, + sizeof(type_4_tag_bad_params_apdu)); + error = Type4TagErrorWrongFormat; + break; + } + } + + bit_buffer_reset(instance->tx_buffer); + + error = handler(instance, apdu->p1, apdu->p2, lc, data, le); + + const Iso14443_4aError iso14443_4a_error = + iso14443_4a_listener_send_block(instance->iso14443_4a_listener, instance->tx_buffer); + + // Show unknown command on screen + if(error == Type4TagErrorCustomCommand) break; + + error = type_4_tag_process_error(iso14443_4a_error); + } while(false); + + return error; +} diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_listener_i.h b/lib/nfc/protocols/type_4_tag/type_4_tag_listener_i.h new file mode 100644 index 000000000..c29eb54b0 --- /dev/null +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_listener_i.h @@ -0,0 +1,37 @@ +#pragma once + +#include "type_4_tag_listener.h" + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + Type4TagListenerStateIdle, + Type4TagListenerStateSelectedApplication, + Type4TagListenerStateSelectedCapabilityContainer, + Type4TagListenerStateSelectedNdefMessage, +} Type4TagListenerState; + +struct Type4TagListener { + Iso14443_4aListener* iso14443_4a_listener; + Type4TagData* data; + Type4TagListenerState state; + + BitBuffer* tx_buffer; + + NfcGenericEvent generic_event; + Type4TagListenerEvent type_4_tag_event; + Type4TagListenerEventData type_4_tag_event_data; + NfcGenericCallback callback; + void* context; +}; + +Type4TagError + type_4_tag_listener_handle_apdu(Type4TagListener* instance, const BitBuffer* rx_buffer); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_poller.c b/lib/nfc/protocols/type_4_tag/type_4_tag_poller.c index 3ff58ea2e..8e321312b 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag_poller.c +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_poller.c @@ -4,9 +4,6 @@ #define TAG "Type4TagPoller" -// Read returns 2 byte status trailer, write sends 5 byte command header -#define TYPE_4_TAG_BUF_SIZE (TYPE_4_TAG_ISO_RW_CHUNK_LEN + 5) - typedef NfcCommand (*Type4TagPollerReadHandler)(Type4TagPoller* instance); const Type4TagData* type_4_tag_poller_get_data(Type4TagPoller* instance) { diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c b/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c index b356604c0..2e0bc3882 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c @@ -5,19 +5,6 @@ #define TAG "Type4TagPoller" -Type4TagError type_4_tag_process_error(Iso14443_4aError error) { - switch(error) { - case Iso14443_4aErrorNone: - return Type4TagErrorNone; - case Iso14443_4aErrorNotPresent: - return Type4TagErrorNotPresent; - case Iso14443_4aErrorTimeout: - return Type4TagErrorTimeout; - default: - return Type4TagErrorProtocol; - } -} - Type4TagError type_4_tag_apdu_trx(Type4TagPoller* instance, BitBuffer* tx_buf, BitBuffer* rx_buf) { furi_check(instance); @@ -58,7 +45,7 @@ Type4TagError type_4_tag_poller_select_app(Type4TagPoller* instance) { FURI_LOG_D(TAG, "Select application"); const uint8_t type_4_tag_select_app_apdu[] = { TYPE_4_TAG_ISO_SELECT_CMD, - TYPE_4_TAG_ISO_SELECT_P1_BY_NAME, + TYPE_4_TAG_ISO_SELECT_P1_BY_DF_NAME, TYPE_4_TAG_ISO_SELECT_P2_EMPTY, TYPE_4_TAG_ISO_APP_NAME_LEN, TYPE_4_TAG_ISO_APP_NAME, diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.h b/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.h index c74135040..0301e21a3 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.h +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.h @@ -33,8 +33,6 @@ struct Type4TagPoller { void* context; }; -Type4TagError type_4_tag_process_error(Iso14443_4aError error); - const Type4TagData* type_4_tag_poller_get_data(Type4TagPoller* instance); Type4TagError type_4_tag_poller_select_app(Type4TagPoller* instance); From 2e77e878de36dac96c0893f25ff0e4f22c41cbbe Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 10 Mar 2025 11:01:46 +0000 Subject: [PATCH 14/92] Support larger NDEF messages --- lib/nfc/protocols/type_4_tag/type_4_tag_i.h | 3 ++- .../protocols/type_4_tag/type_4_tag_poller_i.c | 17 +++++++---------- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_i.h b/lib/nfc/protocols/type_4_tag/type_4_tag_i.h index 9a1add6ee..d64efcc2c 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag_i.h +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_i.h @@ -14,7 +14,8 @@ #define TYPE_4_TAG_ISO_READ_CMD 0x00, 0xB0 #define TYPE_4_TAG_ISO_READ_P1_EMPTY (0x00) #define TYPE_4_TAG_ISO_READ_P1_ID_MASK (1 << 7) -#define TYPE_4_TAG_ISO_READ_P2_BEGINNING (0x00) +#define TYPE_4_TAG_ISO_READ_P_BEGINNING (0x00) +#define TYPE_4_TAG_ISO_READ_P_OFFSET_MAX (32767U) #define TYPE_4_TAG_ISO_READ_LE_FULL (0x00) // Common APDU parameters and values diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c b/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c index 2e0bc3882..45a9da1fc 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c @@ -84,7 +84,7 @@ Type4TagError type_4_tag_poller_read_cc(Type4TagPoller* instance) { const uint8_t type_4_tag_read_cc_apdu[] = { TYPE_4_TAG_ISO_READ_CMD, TYPE_4_TAG_ISO_READ_P1_EMPTY, - TYPE_4_TAG_ISO_READ_P2_BEGINNING, + TYPE_4_TAG_ISO_READ_P_BEGINNING, TYPE_4_TAG_ISO_READ_LE_FULL, }; @@ -180,7 +180,7 @@ Type4TagError type_4_tag_poller_read_ndef(Type4TagPoller* instance) { const uint8_t type_4_tag_read_ndef_len_apdu[] = { TYPE_4_TAG_ISO_READ_CMD, TYPE_4_TAG_ISO_READ_P1_EMPTY, - TYPE_4_TAG_ISO_READ_P2_BEGINNING, + TYPE_4_TAG_ISO_READ_P_BEGINNING, sizeof(ndef_len), }; @@ -198,11 +198,8 @@ Type4TagError type_4_tag_poller_read_ndef(Type4TagPoller* instance) { FURI_LOG_D(TAG, "NDEF file is empty"); break; } - if(ndef_len > 510 - sizeof(ndef_len)) { - // Both size and offset for READ BINARY are 1 byte uint - // So the furthest we can read is 255 bytes at offset 255 - // AKA 2 * 255 byte chunks = 510 bytes -2 byte NDEF len header - // TODO: Surely there has to be another way? + uint8_t chunk_max = MIN(instance->data->chunk_max_read, TYPE_4_TAG_ISO_RW_CHUNK_LEN); + if(ndef_len > TYPE_4_TAG_ISO_READ_P_OFFSET_MAX + chunk_max - sizeof(ndef_len)) { FURI_LOG_E(TAG, "NDEF file too long: %zu bytes", ndef_len); error = Type4TagErrorNotSupported; break; @@ -212,20 +209,20 @@ Type4TagError type_4_tag_poller_read_ndef(Type4TagPoller* instance) { FURI_LOG_D(TAG, "Read NDEF"); const uint8_t type_4_tag_read_ndef_apdu_1[] = { TYPE_4_TAG_ISO_READ_CMD, - TYPE_4_TAG_ISO_READ_P1_EMPTY, }; uint16_t ndef_pos = 0; uint8_t* ndef_data = simple_array_get_data(instance->data->ndef_data); - uint8_t chunk_max = MIN(instance->data->chunk_max_read, TYPE_4_TAG_ISO_RW_CHUNK_LEN); while(ndef_len > 0) { uint8_t chunk_len = MIN(ndef_len, chunk_max); + uint8_t ndef_pos_be[sizeof(ndef_pos)]; + bit_lib_num_to_bytes_be(sizeof(ndef_len) + ndef_pos, sizeof(ndef_pos_be), ndef_pos_be); bit_buffer_append_bytes( instance->tx_buffer, type_4_tag_read_ndef_apdu_1, sizeof(type_4_tag_read_ndef_apdu_1)); - bit_buffer_append_byte(instance->tx_buffer, (uint8_t)(sizeof(ndef_len) + ndef_pos)); + bit_buffer_append_bytes(instance->tx_buffer, ndef_pos_be, sizeof(ndef_pos_be)); bit_buffer_append_byte(instance->tx_buffer, chunk_len); error = type_4_tag_apdu_trx(instance, instance->tx_buffer, instance->rx_buffer); From 99b2762cd9a9f4ff224c03995b98deaf9401c415 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 10 Mar 2025 11:35:50 +0000 Subject: [PATCH 15/92] Fix sending error responses --- .../protocols/type_4_tag/type_4_tag_listener_i.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_listener_i.c b/lib/nfc/protocols/type_4_tag/type_4_tag_listener_i.c index b18189f1e..a5557f95f 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag_listener_i.c +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_listener_i.c @@ -200,6 +200,8 @@ Type4TagError type_4_tag_listener_handle_apdu(Type4TagListener* instance, const BitBuffer* rx_buffer) { Type4TagError error = Type4TagErrorNone; + bit_buffer_reset(instance->tx_buffer); + do { typedef struct { uint8_t cla_ins[2]; @@ -297,18 +299,18 @@ Type4TagError } } - bit_buffer_reset(instance->tx_buffer); - error = handler(instance, apdu->p1, apdu->p2, lc, data, le); + } while(false); + if(bit_buffer_get_size_bytes(instance->tx_buffer) > 0) { const Iso14443_4aError iso14443_4a_error = iso14443_4a_listener_send_block(instance->iso14443_4a_listener, instance->tx_buffer); - // Show unknown command on screen - if(error == Type4TagErrorCustomCommand) break; - - error = type_4_tag_process_error(iso14443_4a_error); - } while(false); + // Keep error flag to show unknown command on screen + if(error != Type4TagErrorCustomCommand) { + error = type_4_tag_process_error(iso14443_4a_error); + } + } return error; } From e64292d47870efb3c5a3de4edd7b3249999d7f99 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Tue, 11 Mar 2025 08:58:57 +0000 Subject: [PATCH 16/92] NFC: Initial ISO14443-4 PCB listener handling --- lib/nfc/helpers/iso14443_4_layer.c | 111 +++++++++++++++++- lib/nfc/helpers/iso14443_4_layer.h | 29 ++++- lib/nfc/protocols/iso14443_4a/iso14443_4a_i.h | 3 +- .../iso14443_4a/iso14443_4a_listener.c | 51 +++++--- .../iso14443_4a/iso14443_4a_listener_i.c | 11 +- .../iso14443_4a/iso14443_4a_listener_i.h | 3 +- .../iso14443_4a/iso14443_4a_poller_i.c | 4 +- .../iso14443_4b/iso14443_4b_poller_i.c | 4 +- 8 files changed, 185 insertions(+), 31 deletions(-) diff --git a/lib/nfc/helpers/iso14443_4_layer.c b/lib/nfc/helpers/iso14443_4_layer.c index 4c5dcd6a4..3089ec59c 100644 --- a/lib/nfc/helpers/iso14443_4_layer.c +++ b/lib/nfc/helpers/iso14443_4_layer.c @@ -6,6 +6,8 @@ #define ISO14443_4_BLOCK_PCB_MASK (0x03) #define ISO14443_4_BLOCK_PCB_I (0U) +#define ISO14443_4_BLOCK_PCB_I_MASK (1U << 1) +#define ISO14443_4_BLOCK_PCB_I_ZERO_MASK (7U << 5) #define ISO14443_4_BLOCK_PCB_I_NAD_OFFSET (2) #define ISO14443_4_BLOCK_PCB_I_CID_OFFSET (3) #define ISO14443_4_BLOCK_PCB_I_CHAIN_OFFSET (4) @@ -25,8 +27,14 @@ #define ISO14443_4_BLOCK_PCB_S_CID_MASK (1U << ISO14443_4_BLOCK_PCB_R_CID_OFFSET) #define ISO14443_4_BLOCK_PCB_S_WTX_DESELECT_MASK (3U << ISO14443_4_BLOCK_PCB_S_WTX_DESELECT_OFFSET) +#define ISO14443_4_BLOCK_CID_MASK (0x0F) + #define ISO14443_4_BLOCK_PCB_BITS_ACTIVE(pcb, mask) (((pcb) & (mask)) == (mask)) +#define ISO14443_4_BLOCK_PCB_IS_I_BLOCK(pcb) \ + (ISO14443_4_BLOCK_PCB_BITS_ACTIVE(pcb, ISO14443_4_BLOCK_PCB_I_MASK) && \ + (((pcb) & ISO14443_4_BLOCK_PCB_I_ZERO_MASK) == 0)) + #define ISO14443_4_BLOCK_PCB_IS_R_BLOCK(pcb) \ ISO14443_4_BLOCK_PCB_BITS_ACTIVE(pcb, ISO14443_4_BLOCK_PCB_R_MASK) @@ -39,9 +47,16 @@ #define ISO14443_4_BLOCK_PCB_R_NACK_ACTIVE(pcb) \ ISO14443_4_BLOCK_PCB_BITS_ACTIVE(pcb, ISO14443_4_BLOCK_PCB_R_NACK_MASK) +#define ISO14443_4_LAYER_NAD_NOT_SUPPORTED ((uint8_t) - 1) +#define ISO14443_4_LAYER_NAD_NOT_SET ((uint8_t) - 2) + struct Iso14443_4Layer { uint8_t pcb; uint8_t pcb_prev; + + // Listener specific + uint8_t cid; + uint8_t nad; }; static inline void iso14443_4_layer_update_pcb(Iso14443_4Layer* instance) { @@ -65,6 +80,9 @@ void iso14443_4_layer_reset(Iso14443_4Layer* instance) { furi_assert(instance); instance->pcb_prev = 0; instance->pcb = ISO14443_4_BLOCK_PCB_I | ISO14443_4_BLOCK_PCB; + + instance->cid = ISO14443_4_LAYER_CID_NOT_SUPPORTED; + instance->nad = ISO14443_4_LAYER_NAD_NOT_SUPPORTED; } void iso14443_4_layer_set_i_block(Iso14443_4Layer* instance, bool chaining, bool CID_present) { @@ -88,7 +106,7 @@ void iso14443_4_layer_set_s_block(Iso14443_4Layer* instance, bool deselect, bool (CID_present << ISO14443_4_BLOCK_PCB_S_CID_OFFSET) | ISO14443_4_BLOCK_PCB; } -void iso14443_4_layer_encode_block( +void iso14443_4_layer_encode_command( Iso14443_4Layer* instance, const BitBuffer* input_data, BitBuffer* block_data) { @@ -105,7 +123,7 @@ static inline uint8_t iso14443_4_layer_get_response_pcb(const BitBuffer* block_d return data[0]; } -bool iso14443_4_layer_decode_block( +bool iso14443_4_layer_decode_response( Iso14443_4Layer* instance, BitBuffer* output_data, const BitBuffer* block_data) { @@ -138,3 +156,92 @@ bool iso14443_4_layer_decode_block( return ret; } + +void iso14443_4_layer_set_cid(Iso14443_4Layer* instance, uint8_t cid) { + instance->cid = cid; +} + +void iso14443_4_layer_set_nad_supported(Iso14443_4Layer* instance, bool nad) { + instance->nad = nad ? 0 : ISO14443_4_LAYER_NAD_NOT_SUPPORTED; +} + +Iso14443_4LayerStatus iso14443_4_layer_decode_command( + Iso14443_4Layer* instance, + const BitBuffer* input_data, + BitBuffer* block_data) { + uint8_t prologue_len = 0; + instance->pcb_prev = bit_buffer_get_byte(input_data, prologue_len++); + + if(ISO14443_4_BLOCK_PCB_IS_I_BLOCK(instance->pcb_prev)) { + if(instance->pcb_prev & ISO14443_4_BLOCK_PCB_I_CID_MASK) { + const uint8_t cid = bit_buffer_get_byte(input_data, prologue_len++) & + ISO14443_4_BLOCK_CID_MASK; + if(instance->cid == ISO14443_4_LAYER_CID_NOT_SUPPORTED || cid != instance->cid) { + return Iso14443_4LayerStatusIgnore; + } + } else if(instance->cid != ISO14443_4_LAYER_CID_NOT_SUPPORTED && instance->cid != 0) { + return Iso14443_4LayerStatusIgnore; + } + if(instance->pcb_prev & ISO14443_4_BLOCK_PCB_I_NAD_MASK) { + if(instance->nad == ISO14443_4_LAYER_NAD_NOT_SUPPORTED) { + return Iso14443_4LayerStatusIgnore; + } + instance->nad = bit_buffer_get_byte(input_data, prologue_len++); + // FIXME: unset NAD when chaining after first block + } + // FIXME: chaining + bit_buffer_copy_right(block_data, input_data, prologue_len); + return Iso14443_4LayerStatusOk; + + } else if(ISO14443_4_BLOCK_PCB_IS_S_BLOCK(instance->pcb_prev)) { + if(instance->pcb_prev & ISO14443_4_BLOCK_PCB_S_CID_MASK) { + const uint8_t cid = bit_buffer_get_byte(input_data, prologue_len++) & + ISO14443_4_BLOCK_CID_MASK; + if(instance->cid == ISO14443_4_LAYER_CID_NOT_SUPPORTED || cid != instance->cid) { + return Iso14443_4LayerStatusIgnore; + } + } else if(instance->cid != ISO14443_4_LAYER_CID_NOT_SUPPORTED && instance->cid != 0) { + return Iso14443_4LayerStatusIgnore; + } + if((instance->pcb_prev & ISO14443_4_BLOCK_PCB_S_WTX_DESELECT_MASK) == 0) { + // DESELECT + bit_buffer_copy(block_data, input_data); + return Iso14443_4LayerStatusSendAndHalt; + } else { + // WTX ACK or wrong value + return Iso14443_4LayerStatusIgnore; + } + + // FIXME: R blocks + } + return Iso14443_4LayerStatusIgnore; +} + +bool iso14443_4_layer_encode_response( + Iso14443_4Layer* instance, + const BitBuffer* input_data, + BitBuffer* block_data) { + if(ISO14443_4_BLOCK_PCB_IS_I_BLOCK(instance->pcb_prev)) { + instance->pcb = instance->pcb_prev; + bit_buffer_append_byte(block_data, instance->pcb); + if(instance->pcb_prev & ISO14443_4_BLOCK_PCB_I_CID_MASK) { + bit_buffer_append_byte(block_data, instance->cid); + } + if(instance->pcb_prev & ISO14443_4_BLOCK_PCB_I_NAD_MASK && + instance->nad != ISO14443_4_LAYER_NAD_NOT_SET) { + bit_buffer_append_byte(block_data, instance->nad); + instance->nad = ISO14443_4_LAYER_NAD_NOT_SET; + // FIXME: unset NAD when chaining after first block + } else { + instance->pcb &= ~ISO14443_4_BLOCK_PCB_I_NAD_MASK; + } + // FIXME: chaining + instance->pcb &= ~ISO14443_4_BLOCK_PCB_I_CHAIN_MASK; + bit_buffer_set_byte(block_data, 0, instance->pcb); + bit_buffer_append(block_data, input_data); + return true; + + // FIXME: R blocks + } + return false; +} diff --git a/lib/nfc/helpers/iso14443_4_layer.h b/lib/nfc/helpers/iso14443_4_layer.h index 67a7f37fe..344d3ccf5 100644 --- a/lib/nfc/helpers/iso14443_4_layer.h +++ b/lib/nfc/helpers/iso14443_4_layer.h @@ -18,16 +18,41 @@ void iso14443_4_layer_set_i_block(Iso14443_4Layer* instance, bool chaining, bool void iso14443_4_layer_set_r_block(Iso14443_4Layer* instance, bool acknowledged, bool CID_present); void iso14443_4_layer_set_s_block(Iso14443_4Layer* instance, bool deselect, bool CID_present); -void iso14443_4_layer_encode_block( +// Poller mode + +void iso14443_4_layer_encode_command( Iso14443_4Layer* instance, const BitBuffer* input_data, BitBuffer* block_data); -bool iso14443_4_layer_decode_block( +bool iso14443_4_layer_decode_response( Iso14443_4Layer* instance, BitBuffer* output_data, const BitBuffer* block_data); +// Listener mode + +typedef enum { + Iso14443_4LayerStatusOk, + Iso14443_4LayerStatusIgnore, + Iso14443_4LayerStatusSendAndHalt, +} Iso14443_4LayerStatus; + +Iso14443_4LayerStatus iso14443_4_layer_decode_command( + Iso14443_4Layer* instance, + const BitBuffer* input_data, + BitBuffer* block_data); + +bool iso14443_4_layer_encode_response( + Iso14443_4Layer* instance, + const BitBuffer* input_data, + BitBuffer* block_data); + +#define ISO14443_4_LAYER_CID_NOT_SUPPORTED ((uint8_t) - 1) +void iso14443_4_layer_set_cid(Iso14443_4Layer* instance, uint8_t cid); + +void iso14443_4_layer_set_nad_supported(Iso14443_4Layer* instance, bool nad); + #ifdef __cplusplus } #endif diff --git a/lib/nfc/protocols/iso14443_4a/iso14443_4a_i.h b/lib/nfc/protocols/iso14443_4a/iso14443_4a_i.h index 9b0230975..f28b26ddd 100644 --- a/lib/nfc/protocols/iso14443_4a/iso14443_4a_i.h +++ b/lib/nfc/protocols/iso14443_4a/iso14443_4a_i.h @@ -2,7 +2,8 @@ #include "iso14443_4a.h" -#define ISO14443_4A_CMD_READ_ATS (0xE0) +#define ISO14443_4A_CMD_READ_ATS (0xE0) +#define ISO14443_4A_READ_ATS_CID_MASK (0x0F) // ATS bit definitions #define ISO14443_4A_ATS_T0_TA1 (1U << 4) diff --git a/lib/nfc/protocols/iso14443_4a/iso14443_4a_listener.c b/lib/nfc/protocols/iso14443_4a/iso14443_4a_listener.c index b2e1a4e24..1d216a460 100644 --- a/lib/nfc/protocols/iso14443_4a/iso14443_4a_listener.c +++ b/lib/nfc/protocols/iso14443_4a/iso14443_4a_listener.c @@ -14,6 +14,7 @@ static Iso14443_4aListener* Iso14443_4aListener* instance = malloc(sizeof(Iso14443_4aListener)); instance->iso14443_3a_listener = iso14443_3a_listener; instance->data = data; + instance->iso14443_4_layer = iso14443_4_layer_alloc(); instance->tx_buffer = bit_buffer_alloc(ISO14443_4A_LISTENER_BUF_SIZE); @@ -30,10 +31,16 @@ static void iso14443_4a_listener_free(Iso14443_4aListener* instance) { furi_assert(instance->data); furi_assert(instance->tx_buffer); + iso14443_4_layer_free(instance->iso14443_4_layer); bit_buffer_free(instance->tx_buffer); free(instance); } +static void iso14443_4a_listener_reset(Iso14443_4aListener* instance) { + instance->state = Iso14443_4aListenerStateIdle; + iso14443_4_layer_reset(instance->iso14443_4_layer); +} + static void iso14443_4a_listener_set_callback( Iso14443_4aListener* instance, NfcGenericCallback callback, @@ -68,28 +75,44 @@ static NfcCommand iso14443_4a_listener_run(NfcGenericEvent event, void* context) if(iso14443_4a_listener_send_ats(instance, &instance->data->ats_data) == Iso14443_4aErrorNone) { instance->state = Iso14443_4aListenerStateActive; + if(iso14443_4a_supports_frame_option( + instance->data, Iso14443_4aFrameOptionCid)) { + const uint8_t cid = bit_buffer_get_byte(rx_buffer, 1) & + ISO14443_4A_READ_ATS_CID_MASK; + iso14443_4_layer_set_cid(instance->iso14443_4_layer, cid); + } + iso14443_4_layer_set_nad_supported( + instance->iso14443_4_layer, + iso14443_4a_supports_frame_option( + instance->data, Iso14443_4aFrameOptionNad)); } } - } else if(bit_buffer_get_size_bytes(rx_buffer) > 1) { - // TODO: This is a rudimentary PCB implementation! - // Just ignores its meaning and saves it for sending blocks to reader, - // there is no handling of S, R, I blocks and their different flags. - // We have iso14443_4_layer helper but it is entirely designed for poller, - // will need large rework for listener, for now this works. - instance->pcb_prev = bit_buffer_get_byte(rx_buffer, 0); - bit_buffer_copy_right(rx_buffer, rx_buffer, 1); + } else { + Iso14443_4LayerStatus status = + iso14443_4_layer_decode_command(instance->iso14443_4_layer, rx_buffer, rx_buffer); + if(status == Iso14443_4LayerStatusSendAndHalt) { + if(iso14443_3a_listener_send_standard_frame( + instance->iso14443_3a_listener, rx_buffer) == Iso14443_3aErrorNone) { + iso14443_4a_listener_reset(instance); + if(instance->callback) { + instance->iso14443_4a_event.type = Iso14443_4aListenerEventTypeHalted; + instance->callback(instance->generic_event, instance->context); + } + command = NfcCommandSleep; + } + } else if(status == Iso14443_4LayerStatusOk) { + instance->iso14443_4a_event.type = Iso14443_4aListenerEventTypeReceivedData; + instance->iso14443_4a_event.data->buffer = rx_buffer; - instance->iso14443_4a_event.type = Iso14443_4aListenerEventTypeReceivedData; - instance->iso14443_4a_event.data->buffer = rx_buffer; - - if(instance->callback) { - command = instance->callback(instance->generic_event, instance->context); + if(instance->callback) { + command = instance->callback(instance->generic_event, instance->context); + } } } } else if( iso14443_3a_event->type == Iso14443_3aListenerEventTypeHalted || iso14443_3a_event->type == Iso14443_3aListenerEventTypeFieldOff) { - instance->state = Iso14443_4aListenerStateIdle; + iso14443_4a_listener_reset(instance); instance->iso14443_4a_event.type = iso14443_3a_event->type == Iso14443_3aListenerEventTypeHalted ? diff --git a/lib/nfc/protocols/iso14443_4a/iso14443_4a_listener_i.c b/lib/nfc/protocols/iso14443_4a/iso14443_4a_listener_i.c index 20ffa3542..6ea5ae5e4 100644 --- a/lib/nfc/protocols/iso14443_4a/iso14443_4a_listener_i.c +++ b/lib/nfc/protocols/iso14443_4a/iso14443_4a_listener_i.c @@ -35,13 +35,10 @@ Iso14443_4aError iso14443_4a_listener_send_block(Iso14443_4aListener* instance, const BitBuffer* tx_buffer) { bit_buffer_reset(instance->tx_buffer); - // TODO: This is a rudimentary PCB implementation! - // Just sends back same PCB that was last received from reader, - // there is no handling of S, R, I blocks and their different flags. - // We have iso14443_4_layer helper but it is entirely designed for poller, - // will need large rework for listener, for now this works. - bit_buffer_append_byte(instance->tx_buffer, instance->pcb_prev); - bit_buffer_append(instance->tx_buffer, tx_buffer); + if(!iso14443_4_layer_encode_response( + instance->iso14443_4_layer, tx_buffer, instance->tx_buffer)) { + return Iso14443_4aErrorProtocol; + } const Iso14443_3aError error = iso14443_3a_listener_send_standard_frame( instance->iso14443_3a_listener, instance->tx_buffer); diff --git a/lib/nfc/protocols/iso14443_4a/iso14443_4a_listener_i.h b/lib/nfc/protocols/iso14443_4a/iso14443_4a_listener_i.h index 9588d6849..46233f21d 100644 --- a/lib/nfc/protocols/iso14443_4a/iso14443_4a_listener_i.h +++ b/lib/nfc/protocols/iso14443_4a/iso14443_4a_listener_i.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include "iso14443_4a_listener.h" #include "iso14443_4a_i.h" @@ -17,7 +18,7 @@ typedef enum { struct Iso14443_4aListener { Iso14443_3aListener* iso14443_3a_listener; Iso14443_4aData* data; - uint8_t pcb_prev; + Iso14443_4Layer* iso14443_4_layer; Iso14443_4aListenerState state; BitBuffer* tx_buffer; diff --git a/lib/nfc/protocols/iso14443_4a/iso14443_4a_poller_i.c b/lib/nfc/protocols/iso14443_4a/iso14443_4a_poller_i.c index 427973f4a..ce8a14f08 100644 --- a/lib/nfc/protocols/iso14443_4a/iso14443_4a_poller_i.c +++ b/lib/nfc/protocols/iso14443_4a/iso14443_4a_poller_i.c @@ -64,7 +64,7 @@ Iso14443_4aError iso14443_4a_poller_send_block( furi_check(rx_buffer); bit_buffer_reset(instance->tx_buffer); - iso14443_4_layer_encode_block(instance->iso14443_4_layer, tx_buffer, instance->tx_buffer); + iso14443_4_layer_encode_command(instance->iso14443_4_layer, tx_buffer, instance->tx_buffer); Iso14443_4aError error = Iso14443_4aErrorNone; @@ -105,7 +105,7 @@ Iso14443_4aError iso14443_4a_poller_send_block( } while(bit_buffer_starts_with_byte(instance->rx_buffer, ISO14443_4A_SWTX)); } - if(!iso14443_4_layer_decode_block( + if(!iso14443_4_layer_decode_response( instance->iso14443_4_layer, rx_buffer, instance->rx_buffer)) { error = Iso14443_4aErrorProtocol; break; diff --git a/lib/nfc/protocols/iso14443_4b/iso14443_4b_poller_i.c b/lib/nfc/protocols/iso14443_4b/iso14443_4b_poller_i.c index da82e1417..2db2da9c1 100644 --- a/lib/nfc/protocols/iso14443_4b/iso14443_4b_poller_i.c +++ b/lib/nfc/protocols/iso14443_4b/iso14443_4b_poller_i.c @@ -24,7 +24,7 @@ Iso14443_4bError iso14443_4b_poller_send_block( furi_check(rx_buffer); bit_buffer_reset(instance->tx_buffer); - iso14443_4_layer_encode_block(instance->iso14443_4_layer, tx_buffer, instance->tx_buffer); + iso14443_4_layer_encode_command(instance->iso14443_4_layer, tx_buffer, instance->tx_buffer); Iso14443_4bError error = Iso14443_4bErrorNone; @@ -36,7 +36,7 @@ Iso14443_4bError iso14443_4b_poller_send_block( error = iso14443_4b_process_error(iso14443_3b_error); break; - } else if(!iso14443_4_layer_decode_block( + } else if(!iso14443_4_layer_decode_response( instance->iso14443_4_layer, rx_buffer, instance->rx_buffer)) { error = Iso14443_4bErrorProtocol; break; From 01b113f50820b64e550be3e09d1e29918c6185da Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Fri, 14 Mar 2025 23:00:59 +0000 Subject: [PATCH 17/92] Fix chunk sizes --- lib/nfc/protocols/type_4_tag/type_4_tag_i.h | 5 ++++- lib/nfc/protocols/type_4_tag/type_4_tag_listener_i.c | 12 +++++++----- lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c | 2 +- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_i.h b/lib/nfc/protocols/type_4_tag/type_4_tag_i.h index d64efcc2c..b0e319ba9 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag_i.h +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_i.h @@ -33,8 +33,11 @@ #define TYPE_4_TAG_T4T_CC_FILE_ID 0xE1, 0x03 #define TYPE_4_TAG_T4T_CC_VNO (0x20) +// 4a layer adds 1..3 byte prefix, 3a layer adds 2 byte suffix and has 256 byte buffer +#define TYPE_4_TAG_BUF_SIZE (256U - 3U - 2U) // Read returns 2 byte status trailer, write sends 5 byte command header -#define TYPE_4_TAG_BUF_SIZE (TYPE_4_TAG_ISO_RW_CHUNK_LEN + 5) +#define TYPE_4_TAG_CHUNK_LEN MIN(TYPE_4_TAG_BUF_SIZE - 5U, TYPE_4_TAG_ISO_RW_CHUNK_LEN) +#define TYPE_4_TAG_DEFAULT_SIZE (2048U) // Capability Container parsing structures diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_listener_i.c b/lib/nfc/protocols/type_4_tag/type_4_tag_listener_i.c index a5557f95f..94e64788a 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag_listener_i.c +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_listener_i.c @@ -112,13 +112,15 @@ static Type4TagError type_4_tag_listener_iso_read( bit_lib_num_to_bytes_be(sizeof(cc_buf), sizeof(cc->len), (void*)&cc->len); cc->t4t_vno = TYPE_4_TAG_T4T_CC_VNO; bit_lib_num_to_bytes_be( - instance->data->is_tag_specific ? instance->data->chunk_max_read : - TYPE_4_TAG_ISO_RW_CHUNK_LEN, + instance->data->is_tag_specific ? + MIN(instance->data->chunk_max_read, TYPE_4_TAG_CHUNK_LEN) : + TYPE_4_TAG_CHUNK_LEN, sizeof(cc->mle), (void*)&cc->mle); bit_lib_num_to_bytes_be( - instance->data->is_tag_specific ? instance->data->chunk_max_write : - TYPE_4_TAG_ISO_RW_CHUNK_LEN, + instance->data->is_tag_specific ? + MIN(instance->data->chunk_max_write, TYPE_4_TAG_CHUNK_LEN) : + TYPE_4_TAG_CHUNK_LEN, sizeof(cc->mlc), (void*)&cc->mlc); cc->tlv[0].type = Type4TagCcTlvTypeNdefFileCtrl; @@ -129,7 +131,7 @@ static Type4TagError type_4_tag_listener_iso_read( (void*)&cc->tlv[0].value.ndef_file_ctrl.file_id); bit_lib_num_to_bytes_be( instance->data->is_tag_specific ? instance->data->ndef_max_len : - TYPE_4_TAG_ISO_RW_CHUNK_LEN, + TYPE_4_TAG_DEFAULT_SIZE, sizeof(cc->tlv[0].value.ndef_file_ctrl.max_len), (void*)&cc->tlv[0].value.ndef_file_ctrl.max_len); cc->tlv[0].value.ndef_file_ctrl.read_perm = diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c b/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c index 45a9da1fc..92caae7d7 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c @@ -198,7 +198,7 @@ Type4TagError type_4_tag_poller_read_ndef(Type4TagPoller* instance) { FURI_LOG_D(TAG, "NDEF file is empty"); break; } - uint8_t chunk_max = MIN(instance->data->chunk_max_read, TYPE_4_TAG_ISO_RW_CHUNK_LEN); + uint8_t chunk_max = MIN(instance->data->chunk_max_read, TYPE_4_TAG_CHUNK_LEN); if(ndef_len > TYPE_4_TAG_ISO_READ_P_OFFSET_MAX + chunk_max - sizeof(ndef_len)) { FURI_LOG_E(TAG, "NDEF file too long: %zu bytes", ndef_len); error = Type4TagErrorNotSupported; From 084774d9d7d61c6cdd1a4181f190be5ff27788e0 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sat, 15 Mar 2025 02:00:40 +0000 Subject: [PATCH 18/92] Fix ISO READ BINARY params definitions --- lib/nfc/protocols/type_4_tag/type_4_tag_i.h | 3 +-- lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c | 2 -- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_i.h b/lib/nfc/protocols/type_4_tag/type_4_tag_i.h index b0e319ba9..738d30935 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag_i.h +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_i.h @@ -12,9 +12,8 @@ // ISO READ BINARY command and parameters #define TYPE_4_TAG_ISO_READ_CMD 0x00, 0xB0 -#define TYPE_4_TAG_ISO_READ_P1_EMPTY (0x00) #define TYPE_4_TAG_ISO_READ_P1_ID_MASK (1 << 7) -#define TYPE_4_TAG_ISO_READ_P_BEGINNING (0x00) +#define TYPE_4_TAG_ISO_READ_P_BEGINNING 0x00, 0x00 #define TYPE_4_TAG_ISO_READ_P_OFFSET_MAX (32767U) #define TYPE_4_TAG_ISO_READ_LE_FULL (0x00) diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c b/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c index 92caae7d7..e1afdba12 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c @@ -83,7 +83,6 @@ Type4TagError type_4_tag_poller_read_cc(Type4TagPoller* instance) { FURI_LOG_D(TAG, "Read CC"); const uint8_t type_4_tag_read_cc_apdu[] = { TYPE_4_TAG_ISO_READ_CMD, - TYPE_4_TAG_ISO_READ_P1_EMPTY, TYPE_4_TAG_ISO_READ_P_BEGINNING, TYPE_4_TAG_ISO_READ_LE_FULL, }; @@ -179,7 +178,6 @@ Type4TagError type_4_tag_poller_read_ndef(Type4TagPoller* instance) { uint16_t ndef_len; const uint8_t type_4_tag_read_ndef_len_apdu[] = { TYPE_4_TAG_ISO_READ_CMD, - TYPE_4_TAG_ISO_READ_P1_EMPTY, TYPE_4_TAG_ISO_READ_P_BEGINNING, sizeof(ndef_len), }; From 799ed9fcafd8ee6396d27fb5408abf0c30031172 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sat, 15 Mar 2025 02:01:51 +0000 Subject: [PATCH 19/92] Fix emulate after save --- lib/nfc/protocols/type_4_tag/type_4_tag_i.h | 2 ++ .../type_4_tag/type_4_tag_listener_i.c | 21 ++++++++++++------- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_i.h b/lib/nfc/protocols/type_4_tag/type_4_tag_i.h index 738d30935..1afa20ec9 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag_i.h +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_i.h @@ -31,6 +31,8 @@ #define TYPE_4_TAG_T4T_CC_FILE_ID_LEN (2U) #define TYPE_4_TAG_T4T_CC_FILE_ID 0xE1, 0x03 #define TYPE_4_TAG_T4T_CC_VNO (0x20) +#define TYPE_4_TAG_T4T_DEFAULT_FILE_ID 0xE104 +#define TYPE_4_TAG_T4T_CC_RW_LOCK_NONE 0x00 // 4a layer adds 1..3 byte prefix, 3a layer adds 2 byte suffix and has 256 byte buffer #define TYPE_4_TAG_BUF_SIZE (256U - 3U - 2U) diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_listener_i.c b/lib/nfc/protocols/type_4_tag/type_4_tag_listener_i.c index 94e64788a..5bf45825b 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag_listener_i.c +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_listener_i.c @@ -63,7 +63,10 @@ static Type4TagError type_4_tag_listener_iso_select( uint8_t ndef_file_id_be[sizeof(instance->data->ndef_file_id)]; bit_lib_num_to_bytes_be( - instance->data->ndef_file_id, sizeof(ndef_file_id_be), ndef_file_id_be); + instance->data->is_tag_specific ? instance->data->ndef_file_id : + TYPE_4_TAG_T4T_DEFAULT_FILE_ID, + sizeof(ndef_file_id_be), + ndef_file_id_be); if(lc == sizeof(ndef_file_id_be) && memcmp(ndef_file_id_be, data, sizeof(ndef_file_id_be)) == 0) { instance->state = Type4TagListenerStateSelectedNdefMessage; @@ -126,7 +129,8 @@ static Type4TagError type_4_tag_listener_iso_read( cc->tlv[0].type = Type4TagCcTlvTypeNdefFileCtrl; cc->tlv[0].len = sizeof(cc->tlv[0].value.ndef_file_ctrl); bit_lib_num_to_bytes_be( - instance->data->ndef_file_id, + instance->data->is_tag_specific ? instance->data->ndef_file_id : + TYPE_4_TAG_T4T_DEFAULT_FILE_ID, sizeof(cc->tlv[0].value.ndef_file_ctrl.file_id), (void*)&cc->tlv[0].value.ndef_file_ctrl.file_id); bit_lib_num_to_bytes_be( @@ -134,10 +138,12 @@ static Type4TagError type_4_tag_listener_iso_read( TYPE_4_TAG_DEFAULT_SIZE, sizeof(cc->tlv[0].value.ndef_file_ctrl.max_len), (void*)&cc->tlv[0].value.ndef_file_ctrl.max_len); - cc->tlv[0].value.ndef_file_ctrl.read_perm = - instance->data->is_tag_specific ? instance->data->ndef_read_lock : 0x00; - cc->tlv[0].value.ndef_file_ctrl.write_perm = - instance->data->is_tag_specific ? instance->data->ndef_write_lock : 0x00; + cc->tlv[0].value.ndef_file_ctrl.read_perm = instance->data->is_tag_specific ? + instance->data->ndef_read_lock : + TYPE_4_TAG_T4T_CC_RW_LOCK_NONE; + cc->tlv[0].value.ndef_file_ctrl.write_perm = instance->data->is_tag_specific ? + instance->data->ndef_write_lock : + TYPE_4_TAG_T4T_CC_RW_LOCK_NONE; bit_buffer_append_bytes( instance->tx_buffer, cc_buf + offset, MIN(sizeof(cc_buf) - offset, le)); @@ -147,8 +153,7 @@ static Type4TagError type_4_tag_listener_iso_read( } if(instance->state == Type4TagListenerStateSelectedNdefMessage) { - size_t ndef_file_len = - instance->data->ndef_data ? simple_array_get_count(instance->data->ndef_data) : 0; + size_t ndef_file_len = simple_array_get_count(instance->data->ndef_data); bool included_len = false; if(offset < sizeof(uint16_t)) { uint8_t ndef_file_len_be[sizeof(uint16_t)]; From b05a735e8f6197075ec59de1441d65aebdab28a7 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sat, 15 Mar 2025 03:05:20 +0000 Subject: [PATCH 20/92] Allow selecting PICC level too --- lib/nfc/protocols/type_4_tag/type_4_tag_i.h | 3 +- .../type_4_tag/type_4_tag_listener_i.c | 61 ++++++++++--------- .../type_4_tag/type_4_tag_listener_i.h | 1 + .../type_4_tag/type_4_tag_poller_i.c | 2 +- 4 files changed, 35 insertions(+), 32 deletions(-) diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_i.h b/lib/nfc/protocols/type_4_tag/type_4_tag_i.h index 1afa20ec9..cde715faa 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag_i.h +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_i.h @@ -26,8 +26,9 @@ #define TYPE_4_TAG_ISO_STATUS_BAD_PARAMS 0x6A, 0x86 #define TYPE_4_TAG_ISO_STATUS_NO_CMD 0x68, 0x00 #define TYPE_4_TAG_ISO_RW_CHUNK_LEN (255U) -#define TYPE_4_TAG_ISO_APP_NAME_LEN (7U) +#define TYPE_4_TAG_ISO_NAME_LEN (7U) #define TYPE_4_TAG_ISO_APP_NAME 0xD2, 0x76, 0x00, 0x00, 0x85, 0x01, 0x01 +#define TYPE_4_TAG_ISO_PICC_NAME 0xD2, 0x76, 0x00, 0x00, 0x85, 0x01, 0x00 #define TYPE_4_TAG_T4T_CC_FILE_ID_LEN (2U) #define TYPE_4_TAG_T4T_CC_FILE_ID 0xE1, 0x03 #define TYPE_4_TAG_T4T_CC_VNO (0x20) diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_listener_i.c b/lib/nfc/protocols/type_4_tag/type_4_tag_listener_i.c index 5bf45825b..880769629 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag_listener_i.c +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_listener_i.c @@ -36,7 +36,15 @@ static Type4TagError type_4_tag_listener_iso_select( UNUSED(le); if(p1 == TYPE_4_TAG_ISO_SELECT_P1_BY_DF_NAME) { - static const uint8_t t4t_app[TYPE_4_TAG_ISO_APP_NAME_LEN] = {TYPE_4_TAG_ISO_APP_NAME}; + static const uint8_t t4t_picc[TYPE_4_TAG_ISO_NAME_LEN] = {TYPE_4_TAG_ISO_PICC_NAME}; + if(lc == sizeof(t4t_picc) && memcmp(t4t_picc, data, sizeof(t4t_picc)) == 0) { + instance->state = Type4TagListenerStateSelectedPicc; + bit_buffer_append_bytes( + instance->tx_buffer, type_4_tag_success_apdu, sizeof(type_4_tag_success_apdu)); + return Type4TagErrorNone; + } + + static const uint8_t t4t_app[TYPE_4_TAG_ISO_NAME_LEN] = {TYPE_4_TAG_ISO_APP_NAME}; if(lc == sizeof(t4t_app) && memcmp(t4t_app, data, sizeof(t4t_app)) == 0) { instance->state = Type4TagListenerStateSelectedApplication; bit_buffer_append_bytes( @@ -44,36 +52,29 @@ static Type4TagError type_4_tag_listener_iso_select( return Type4TagErrorNone; } - instance->state = Type4TagListenerStateIdle; - bit_buffer_append_bytes( - instance->tx_buffer, type_4_tag_not_found_apdu, sizeof(type_4_tag_not_found_apdu)); - return Type4TagErrorCustomCommand; - } + } else if( + instance->state >= Type4TagListenerStateSelectedApplication && + (p1 == TYPE_4_TAG_ISO_SELECT_P1_BY_ID || p1 == TYPE_4_TAG_ISO_SELECT_P1_BY_EF_ID)) { + static const uint8_t cc_id[TYPE_4_TAG_T4T_CC_FILE_ID_LEN] = {TYPE_4_TAG_T4T_CC_FILE_ID}; + if(lc == sizeof(cc_id) && memcmp(cc_id, data, sizeof(cc_id)) == 0) { + instance->state = Type4TagListenerStateSelectedCapabilityContainer; + bit_buffer_append_bytes( + instance->tx_buffer, type_4_tag_success_apdu, sizeof(type_4_tag_success_apdu)); + return Type4TagErrorNone; + } - if(instance->state >= Type4TagListenerStateSelectedApplication) { - if(p1 == TYPE_4_TAG_ISO_SELECT_P1_BY_ID || p1 == TYPE_4_TAG_ISO_SELECT_P1_BY_EF_ID) { - static const uint8_t cc_id[TYPE_4_TAG_T4T_CC_FILE_ID_LEN] = { - TYPE_4_TAG_T4T_CC_FILE_ID}; - if(lc == sizeof(cc_id) && memcmp(cc_id, data, sizeof(cc_id)) == 0) { - instance->state = Type4TagListenerStateSelectedCapabilityContainer; - bit_buffer_append_bytes( - instance->tx_buffer, type_4_tag_success_apdu, sizeof(type_4_tag_success_apdu)); - return Type4TagErrorNone; - } - - uint8_t ndef_file_id_be[sizeof(instance->data->ndef_file_id)]; - bit_lib_num_to_bytes_be( - instance->data->is_tag_specific ? instance->data->ndef_file_id : - TYPE_4_TAG_T4T_DEFAULT_FILE_ID, - sizeof(ndef_file_id_be), - ndef_file_id_be); - if(lc == sizeof(ndef_file_id_be) && - memcmp(ndef_file_id_be, data, sizeof(ndef_file_id_be)) == 0) { - instance->state = Type4TagListenerStateSelectedNdefMessage; - bit_buffer_append_bytes( - instance->tx_buffer, type_4_tag_success_apdu, sizeof(type_4_tag_success_apdu)); - return Type4TagErrorNone; - } + uint8_t ndef_file_id_be[sizeof(instance->data->ndef_file_id)]; + bit_lib_num_to_bytes_be( + instance->data->is_tag_specific ? instance->data->ndef_file_id : + TYPE_4_TAG_T4T_DEFAULT_FILE_ID, + sizeof(ndef_file_id_be), + ndef_file_id_be); + if(lc == sizeof(ndef_file_id_be) && + memcmp(ndef_file_id_be, data, sizeof(ndef_file_id_be)) == 0) { + instance->state = Type4TagListenerStateSelectedNdefMessage; + bit_buffer_append_bytes( + instance->tx_buffer, type_4_tag_success_apdu, sizeof(type_4_tag_success_apdu)); + return Type4TagErrorNone; } } diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_listener_i.h b/lib/nfc/protocols/type_4_tag/type_4_tag_listener_i.h index c29eb54b0..fd602c974 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag_listener_i.h +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_listener_i.h @@ -10,6 +10,7 @@ extern "C" { typedef enum { Type4TagListenerStateIdle, + Type4TagListenerStateSelectedPicc, Type4TagListenerStateSelectedApplication, Type4TagListenerStateSelectedCapabilityContainer, Type4TagListenerStateSelectedNdefMessage, diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c b/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c index e1afdba12..09f1febaf 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c @@ -47,7 +47,7 @@ Type4TagError type_4_tag_poller_select_app(Type4TagPoller* instance) { TYPE_4_TAG_ISO_SELECT_CMD, TYPE_4_TAG_ISO_SELECT_P1_BY_DF_NAME, TYPE_4_TAG_ISO_SELECT_P2_EMPTY, - TYPE_4_TAG_ISO_APP_NAME_LEN, + TYPE_4_TAG_ISO_NAME_LEN, TYPE_4_TAG_ISO_APP_NAME, TYPE_4_TAG_ISO_SELECT_LE_EMPTY, }; From 384f365cffe14eaa1519c1166491cfdd05b276ba Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sat, 15 Mar 2025 03:08:24 +0000 Subject: [PATCH 21/92] Refactor CC parsing and dumping --- lib/nfc/protocols/type_4_tag/type_4_tag_i.c | 84 +++++++++++++++++++ lib/nfc/protocols/type_4_tag/type_4_tag_i.h | 5 ++ .../type_4_tag/type_4_tag_listener_i.c | 36 +------- .../type_4_tag/type_4_tag_poller_i.c | 42 +--------- 4 files changed, 93 insertions(+), 74 deletions(-) diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_i.c b/lib/nfc/protocols/type_4_tag/type_4_tag_i.c index d90911212..d0b0bf22e 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag_i.c +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_i.c @@ -1,5 +1,7 @@ #include "type_4_tag_i.h" +#include + #define TAG "Type4Tag" #define TYPE_4_TAG_FFF_NDEF_DATA_SIZE_KEY "NDEF Data Size" @@ -20,6 +22,88 @@ Type4TagError type_4_tag_process_error(Iso14443_4aError error) { } } +void type_4_tag_cc_dump(const Type4TagData* data, uint8_t* buf, size_t len) { + furi_check(len >= TYPE_4_TAG_T4T_CC_MIN_SIZE); + Type4TagCc* cc = (Type4TagCc*)buf; + + bit_lib_num_to_bytes_be(TYPE_4_TAG_T4T_CC_MIN_SIZE, sizeof(cc->len), (void*)&cc->len); + cc->t4t_vno = TYPE_4_TAG_T4T_CC_VNO; + bit_lib_num_to_bytes_be( + data->is_tag_specific ? MIN(data->chunk_max_read, TYPE_4_TAG_CHUNK_LEN) : + TYPE_4_TAG_CHUNK_LEN, + sizeof(cc->mle), + (void*)&cc->mle); + bit_lib_num_to_bytes_be( + data->is_tag_specific ? MIN(data->chunk_max_write, TYPE_4_TAG_CHUNK_LEN) : + TYPE_4_TAG_CHUNK_LEN, + sizeof(cc->mlc), + (void*)&cc->mlc); + + cc->tlv[0].type = Type4TagCcTlvTypeNdefFileCtrl; + cc->tlv[0].len = sizeof(cc->tlv[0].value.ndef_file_ctrl); + + bit_lib_num_to_bytes_be( + data->is_tag_specific ? data->ndef_file_id : TYPE_4_TAG_T4T_DEFAULT_FILE_ID, + sizeof(cc->tlv[0].value.ndef_file_ctrl.file_id), + (void*)&cc->tlv[0].value.ndef_file_ctrl.file_id); + bit_lib_num_to_bytes_be( + data->is_tag_specific ? data->ndef_max_len : TYPE_4_TAG_DEFAULT_SIZE, + sizeof(cc->tlv[0].value.ndef_file_ctrl.max_len), + (void*)&cc->tlv[0].value.ndef_file_ctrl.max_len); + cc->tlv[0].value.ndef_file_ctrl.read_perm = + data->is_tag_specific ? data->ndef_read_lock : TYPE_4_TAG_T4T_CC_RW_LOCK_NONE; + cc->tlv[0].value.ndef_file_ctrl.write_perm = + data->is_tag_specific ? data->ndef_write_lock : TYPE_4_TAG_T4T_CC_RW_LOCK_NONE; +} + +Type4TagError type_4_tag_cc_parse(Type4TagData* data, const BitBuffer* buf) { + const size_t cc_len = bit_buffer_get_size_bytes(buf); + if(cc_len < TYPE_4_TAG_T4T_CC_MIN_SIZE) { + FURI_LOG_E(TAG, "Unsupported T4T version"); + return Type4TagErrorWrongFormat; + } + + const Type4TagCc* cc = (const Type4TagCc*)bit_buffer_get_data(buf); + if(cc->t4t_vno != TYPE_4_TAG_T4T_CC_VNO) { + FURI_LOG_E(TAG, "Unsupported T4T version"); + return Type4TagErrorNotSupported; + } + + const Type4TagCcTlv* tlv = cc->tlv; + const Type4TagCcTlvNdefFileCtrl* ndef_file_ctrl = NULL; + const void* end = MIN((void*)cc + cc->len, (void*)cc + cc_len); + while((void*)tlv < end) { + if(tlv->type == Type4TagCcTlvTypeNdefFileCtrl) { + ndef_file_ctrl = &tlv->value.ndef_file_ctrl; + break; + } + + if(tlv->len < 0xFF) { + tlv = (void*)&tlv->value + tlv->len; + } else { + uint16_t len = bit_lib_bytes_to_num_be((void*)&tlv->len + 1, sizeof(uint16_t)); + tlv = (void*)&tlv->value + sizeof(len) + len; + } + } + if(!ndef_file_ctrl) { + FURI_LOG_E(TAG, "No NDEF file ctrl TLV"); + return Type4TagErrorWrongFormat; + } + + data->t4t_version.value = cc->t4t_vno; + data->chunk_max_read = bit_lib_bytes_to_num_be((void*)&cc->mle, sizeof(cc->mle)); + data->chunk_max_write = bit_lib_bytes_to_num_be((void*)&cc->mlc, sizeof(cc->mlc)); + data->ndef_file_id = + bit_lib_bytes_to_num_be((void*)&ndef_file_ctrl->file_id, sizeof(ndef_file_ctrl->file_id)); + data->ndef_max_len = + bit_lib_bytes_to_num_be((void*)&ndef_file_ctrl->max_len, sizeof(ndef_file_ctrl->max_len)) - + sizeof(uint16_t); + data->ndef_read_lock = ndef_file_ctrl->read_perm; + data->ndef_write_lock = ndef_file_ctrl->write_perm; + + return Type4TagErrorNone; +} + bool type_4_tag_ndef_data_load(Type4TagData* data, FlipperFormat* ff) { uint32_t ndef_data_size; if(!flipper_format_read_uint32(ff, TYPE_4_TAG_FFF_NDEF_DATA_SIZE_KEY, &ndef_data_size, 1)) { diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_i.h b/lib/nfc/protocols/type_4_tag/type_4_tag_i.h index cde715faa..35f8f6a2b 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag_i.h +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_i.h @@ -34,6 +34,7 @@ #define TYPE_4_TAG_T4T_CC_VNO (0x20) #define TYPE_4_TAG_T4T_DEFAULT_FILE_ID 0xE104 #define TYPE_4_TAG_T4T_CC_RW_LOCK_NONE 0x00 +#define TYPE_4_TAG_T4T_CC_MIN_SIZE (sizeof(Type4TagCc) + sizeof(Type4TagCcTlv)) // 4a layer adds 1..3 byte prefix, 3a layer adds 2 byte suffix and has 256 byte buffer #define TYPE_4_TAG_BUF_SIZE (256U - 3U - 2U) @@ -77,6 +78,10 @@ typedef struct FURI_PACKED { Type4TagError type_4_tag_process_error(Iso14443_4aError error); +void type_4_tag_cc_dump(const Type4TagData* data, uint8_t* buf, size_t len); + +Type4TagError type_4_tag_cc_parse(Type4TagData* data, const BitBuffer* buf); + // Load internal Type4Tag structures bool type_4_tag_ndef_data_load(Type4TagData* data, FlipperFormat* ff); diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_listener_i.c b/lib/nfc/protocols/type_4_tag/type_4_tag_listener_i.c index 880769629..f8e799f59 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag_listener_i.c +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_listener_i.c @@ -104,7 +104,7 @@ static Type4TagError type_4_tag_listener_iso_read( } if(instance->state == Type4TagListenerStateSelectedCapabilityContainer) { - uint8_t cc_buf[sizeof(Type4TagCc) + sizeof(Type4TagCcTlv)]; + uint8_t cc_buf[TYPE_4_TAG_T4T_CC_MIN_SIZE]; if(offset >= sizeof(cc_buf)) { bit_buffer_append_bytes( instance->tx_buffer, @@ -112,39 +112,7 @@ static Type4TagError type_4_tag_listener_iso_read( sizeof(type_4_tag_offset_error_apdu)); return Type4TagErrorWrongFormat; } - Type4TagCc* cc = (Type4TagCc*)cc_buf; - bit_lib_num_to_bytes_be(sizeof(cc_buf), sizeof(cc->len), (void*)&cc->len); - cc->t4t_vno = TYPE_4_TAG_T4T_CC_VNO; - bit_lib_num_to_bytes_be( - instance->data->is_tag_specific ? - MIN(instance->data->chunk_max_read, TYPE_4_TAG_CHUNK_LEN) : - TYPE_4_TAG_CHUNK_LEN, - sizeof(cc->mle), - (void*)&cc->mle); - bit_lib_num_to_bytes_be( - instance->data->is_tag_specific ? - MIN(instance->data->chunk_max_write, TYPE_4_TAG_CHUNK_LEN) : - TYPE_4_TAG_CHUNK_LEN, - sizeof(cc->mlc), - (void*)&cc->mlc); - cc->tlv[0].type = Type4TagCcTlvTypeNdefFileCtrl; - cc->tlv[0].len = sizeof(cc->tlv[0].value.ndef_file_ctrl); - bit_lib_num_to_bytes_be( - instance->data->is_tag_specific ? instance->data->ndef_file_id : - TYPE_4_TAG_T4T_DEFAULT_FILE_ID, - sizeof(cc->tlv[0].value.ndef_file_ctrl.file_id), - (void*)&cc->tlv[0].value.ndef_file_ctrl.file_id); - bit_lib_num_to_bytes_be( - instance->data->is_tag_specific ? instance->data->ndef_max_len : - TYPE_4_TAG_DEFAULT_SIZE, - sizeof(cc->tlv[0].value.ndef_file_ctrl.max_len), - (void*)&cc->tlv[0].value.ndef_file_ctrl.max_len); - cc->tlv[0].value.ndef_file_ctrl.read_perm = instance->data->is_tag_specific ? - instance->data->ndef_read_lock : - TYPE_4_TAG_T4T_CC_RW_LOCK_NONE; - cc->tlv[0].value.ndef_file_ctrl.write_perm = instance->data->is_tag_specific ? - instance->data->ndef_write_lock : - TYPE_4_TAG_T4T_CC_RW_LOCK_NONE; + type_4_tag_cc_dump(instance->data, cc_buf, sizeof(cc_buf)); bit_buffer_append_bytes( instance->tx_buffer, cc_buf + offset, MIN(sizeof(cc_buf) - offset, le)); diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c b/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c index 09f1febaf..cf0d14861 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c @@ -93,47 +93,9 @@ Type4TagError type_4_tag_poller_read_cc(Type4TagPoller* instance) { error = type_4_tag_apdu_trx(instance, instance->tx_buffer, instance->rx_buffer); if(error != Type4TagErrorNone) break; - const Type4TagCc* cc = (const Type4TagCc*)bit_buffer_get_data(instance->rx_buffer); - if(cc->t4t_vno != TYPE_4_TAG_T4T_CC_VNO) { - FURI_LOG_E(TAG, "Unsupported T4T version"); - error = Type4TagErrorNotSupported; - break; - } - - const Type4TagCcTlv* tlv = cc->tlv; - const Type4TagCcTlvNdefFileCtrl* ndef_file_ctrl = NULL; - while((void*)tlv < (void*)cc + cc->len) { - if(tlv->type == Type4TagCcTlvTypeNdefFileCtrl) { - ndef_file_ctrl = &tlv->value.ndef_file_ctrl; - break; - } - - if(tlv->len < 0xFF) { - tlv = (void*)&tlv->value + tlv->len; - } else { - uint16_t len = bit_lib_bytes_to_num_be((void*)&tlv->len + 1, sizeof(uint16_t)); - tlv = (void*)&tlv->value + sizeof(len) + len; - } - } - if(!ndef_file_ctrl) { - FURI_LOG_E(TAG, "No NDEF file ctrl TLV"); - error = Type4TagErrorWrongFormat; - break; - } - + error = type_4_tag_cc_parse(instance->data, instance->rx_buffer); + if(error != Type4TagErrorNone) break; instance->data->is_tag_specific = true; - instance->data->t4t_version.value = cc->t4t_vno; - instance->data->chunk_max_read = bit_lib_bytes_to_num_be((void*)&cc->mle, sizeof(cc->mle)); - instance->data->chunk_max_write = - bit_lib_bytes_to_num_be((void*)&cc->mlc, sizeof(cc->mlc)); - instance->data->ndef_file_id = bit_lib_bytes_to_num_be( - (void*)&ndef_file_ctrl->file_id, sizeof(ndef_file_ctrl->file_id)); - instance->data->ndef_max_len = - bit_lib_bytes_to_num_be( - (void*)&ndef_file_ctrl->max_len, sizeof(ndef_file_ctrl->max_len)) - - sizeof(uint16_t); - instance->data->ndef_read_lock = ndef_file_ctrl->read_perm; - instance->data->ndef_write_lock = ndef_file_ctrl->write_perm; FURI_LOG_D(TAG, "Detected NDEF file ID 0x%04X", instance->data->ndef_file_id); } while(false); From 7689efa129b8569581aaa34f574b57d98d504bd7 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sat, 15 Mar 2025 05:42:57 +0000 Subject: [PATCH 22/92] Use flags for ISO14443-4 layer decode result --- lib/nfc/helpers/iso14443_4_layer.c | 55 +++++++++++-------- lib/nfc/helpers/iso14443_4_layer.h | 11 ++-- .../iso14443_4a/iso14443_4a_listener.c | 24 ++++---- 3 files changed, 50 insertions(+), 40 deletions(-) diff --git a/lib/nfc/helpers/iso14443_4_layer.c b/lib/nfc/helpers/iso14443_4_layer.c index 3089ec59c..1b4dac1dd 100644 --- a/lib/nfc/helpers/iso14443_4_layer.c +++ b/lib/nfc/helpers/iso14443_4_layer.c @@ -59,9 +59,11 @@ struct Iso14443_4Layer { uint8_t nad; }; -static inline void iso14443_4_layer_update_pcb(Iso14443_4Layer* instance) { +static inline void iso14443_4_layer_update_pcb(Iso14443_4Layer* instance, bool toggle_num) { instance->pcb_prev = instance->pcb; - instance->pcb ^= (uint8_t)0x01; + if(toggle_num) { + instance->pcb ^= (uint8_t)0x01; + } } Iso14443_4Layer* iso14443_4_layer_alloc(void) { @@ -115,7 +117,7 @@ void iso14443_4_layer_encode_command( bit_buffer_append_byte(block_data, instance->pcb); bit_buffer_append(block_data, input_data); - iso14443_4_layer_update_pcb(instance); + iso14443_4_layer_update_pcb(instance, true); } static inline uint8_t iso14443_4_layer_get_response_pcb(const BitBuffer* block_data) { @@ -137,7 +139,7 @@ bool iso14443_4_layer_decode_response( ret = (ISO14443_4_BLOCK_PCB_IS_R_BLOCK(response_pcb)) && (!ISO14443_4_BLOCK_PCB_R_NACK_ACTIVE(response_pcb)); instance->pcb &= ISO14443_4_BLOCK_PCB_MASK; - iso14443_4_layer_update_pcb(instance); + iso14443_4_layer_update_pcb(instance, true); } else if(ISO14443_4_BLOCK_PCB_IS_CHAIN_ACTIVE(instance->pcb_prev)) { const uint8_t response_pcb = iso14443_4_layer_get_response_pcb(block_data); ret = (ISO14443_4_BLOCK_PCB_IS_R_BLOCK(response_pcb)) && @@ -165,65 +167,69 @@ void iso14443_4_layer_set_nad_supported(Iso14443_4Layer* instance, bool nad) { instance->nad = nad ? 0 : ISO14443_4_LAYER_NAD_NOT_SUPPORTED; } -Iso14443_4LayerStatus iso14443_4_layer_decode_command( +Iso14443_4LayerResult iso14443_4_layer_decode_command( Iso14443_4Layer* instance, const BitBuffer* input_data, BitBuffer* block_data) { - uint8_t prologue_len = 0; - instance->pcb_prev = bit_buffer_get_byte(input_data, prologue_len++); + furi_assert(instance); - if(ISO14443_4_BLOCK_PCB_IS_I_BLOCK(instance->pcb_prev)) { - if(instance->pcb_prev & ISO14443_4_BLOCK_PCB_I_CID_MASK) { + uint8_t prologue_len = 0; + instance->pcb = bit_buffer_get_byte(input_data, prologue_len++); + + if(ISO14443_4_BLOCK_PCB_IS_I_BLOCK(instance->pcb)) { + if(instance->pcb & ISO14443_4_BLOCK_PCB_I_CID_MASK) { const uint8_t cid = bit_buffer_get_byte(input_data, prologue_len++) & ISO14443_4_BLOCK_CID_MASK; if(instance->cid == ISO14443_4_LAYER_CID_NOT_SUPPORTED || cid != instance->cid) { - return Iso14443_4LayerStatusIgnore; + return Iso14443_4LayerResultSkip; } } else if(instance->cid != ISO14443_4_LAYER_CID_NOT_SUPPORTED && instance->cid != 0) { - return Iso14443_4LayerStatusIgnore; + return Iso14443_4LayerResultSkip; } - if(instance->pcb_prev & ISO14443_4_BLOCK_PCB_I_NAD_MASK) { + if(instance->pcb & ISO14443_4_BLOCK_PCB_I_NAD_MASK) { if(instance->nad == ISO14443_4_LAYER_NAD_NOT_SUPPORTED) { - return Iso14443_4LayerStatusIgnore; + return Iso14443_4LayerResultSkip; } instance->nad = bit_buffer_get_byte(input_data, prologue_len++); // FIXME: unset NAD when chaining after first block } // FIXME: chaining bit_buffer_copy_right(block_data, input_data, prologue_len); - return Iso14443_4LayerStatusOk; + iso14443_4_layer_update_pcb(instance, false); + return Iso14443_4LayerResultData; - } else if(ISO14443_4_BLOCK_PCB_IS_S_BLOCK(instance->pcb_prev)) { - if(instance->pcb_prev & ISO14443_4_BLOCK_PCB_S_CID_MASK) { + } else if(ISO14443_4_BLOCK_PCB_IS_S_BLOCK(instance->pcb)) { + if(instance->pcb & ISO14443_4_BLOCK_PCB_S_CID_MASK) { const uint8_t cid = bit_buffer_get_byte(input_data, prologue_len++) & ISO14443_4_BLOCK_CID_MASK; if(instance->cid == ISO14443_4_LAYER_CID_NOT_SUPPORTED || cid != instance->cid) { - return Iso14443_4LayerStatusIgnore; + return Iso14443_4LayerResultSkip; } } else if(instance->cid != ISO14443_4_LAYER_CID_NOT_SUPPORTED && instance->cid != 0) { - return Iso14443_4LayerStatusIgnore; + return Iso14443_4LayerResultSkip; } - if((instance->pcb_prev & ISO14443_4_BLOCK_PCB_S_WTX_DESELECT_MASK) == 0) { + if((instance->pcb & ISO14443_4_BLOCK_PCB_S_WTX_DESELECT_MASK) == 0) { // DESELECT bit_buffer_copy(block_data, input_data); - return Iso14443_4LayerStatusSendAndHalt; + return Iso14443_4LayerResultSend | Iso14443_4LayerResultHalt; } else { // WTX ACK or wrong value - return Iso14443_4LayerStatusIgnore; + return Iso14443_4LayerResultSkip; } // FIXME: R blocks } - return Iso14443_4LayerStatusIgnore; + return Iso14443_4LayerResultSkip; } bool iso14443_4_layer_encode_response( Iso14443_4Layer* instance, const BitBuffer* input_data, BitBuffer* block_data) { + furi_assert(instance); + if(ISO14443_4_BLOCK_PCB_IS_I_BLOCK(instance->pcb_prev)) { - instance->pcb = instance->pcb_prev; - bit_buffer_append_byte(block_data, instance->pcb); + bit_buffer_append_byte(block_data, 0x00); if(instance->pcb_prev & ISO14443_4_BLOCK_PCB_I_CID_MASK) { bit_buffer_append_byte(block_data, instance->cid); } @@ -239,6 +245,7 @@ bool iso14443_4_layer_encode_response( instance->pcb &= ~ISO14443_4_BLOCK_PCB_I_CHAIN_MASK; bit_buffer_set_byte(block_data, 0, instance->pcb); bit_buffer_append(block_data, input_data); + iso14443_4_layer_update_pcb(instance, false); return true; // FIXME: R blocks diff --git a/lib/nfc/helpers/iso14443_4_layer.h b/lib/nfc/helpers/iso14443_4_layer.h index 344d3ccf5..6c5edf49e 100644 --- a/lib/nfc/helpers/iso14443_4_layer.h +++ b/lib/nfc/helpers/iso14443_4_layer.h @@ -33,12 +33,13 @@ bool iso14443_4_layer_decode_response( // Listener mode typedef enum { - Iso14443_4LayerStatusOk, - Iso14443_4LayerStatusIgnore, - Iso14443_4LayerStatusSendAndHalt, -} Iso14443_4LayerStatus; + Iso14443_4LayerResultSkip = (0), + Iso14443_4LayerResultData = (1 << 1), + Iso14443_4LayerResultSend = (1 << 2), + Iso14443_4LayerResultHalt = (1 << 3), +} Iso14443_4LayerResult; -Iso14443_4LayerStatus iso14443_4_layer_decode_command( +Iso14443_4LayerResult iso14443_4_layer_decode_command( Iso14443_4Layer* instance, const BitBuffer* input_data, BitBuffer* block_data); diff --git a/lib/nfc/protocols/iso14443_4a/iso14443_4a_listener.c b/lib/nfc/protocols/iso14443_4a/iso14443_4a_listener.c index 1d216a460..189a23dd2 100644 --- a/lib/nfc/protocols/iso14443_4a/iso14443_4a_listener.c +++ b/lib/nfc/protocols/iso14443_4a/iso14443_4a_listener.c @@ -88,19 +88,21 @@ static NfcCommand iso14443_4a_listener_run(NfcGenericEvent event, void* context) } } } else { - Iso14443_4LayerStatus status = + Iso14443_4LayerResult status = iso14443_4_layer_decode_command(instance->iso14443_4_layer, rx_buffer, rx_buffer); - if(status == Iso14443_4LayerStatusSendAndHalt) { - if(iso14443_3a_listener_send_standard_frame( - instance->iso14443_3a_listener, rx_buffer) == Iso14443_3aErrorNone) { - iso14443_4a_listener_reset(instance); - if(instance->callback) { - instance->iso14443_4a_event.type = Iso14443_4aListenerEventTypeHalted; - instance->callback(instance->generic_event, instance->context); - } - command = NfcCommandSleep; + if(status & Iso14443_4LayerResultSend) { + iso14443_3a_listener_send_standard_frame( + instance->iso14443_3a_listener, rx_buffer); + } + if(status & Iso14443_4LayerResultHalt) { + iso14443_4a_listener_reset(instance); + if(instance->callback) { + instance->iso14443_4a_event.type = Iso14443_4aListenerEventTypeHalted; + instance->callback(instance->generic_event, instance->context); } - } else if(status == Iso14443_4LayerStatusOk) { + command = NfcCommandSleep; + } + if(status & Iso14443_4LayerResultData) { instance->iso14443_4a_event.type = Iso14443_4aListenerEventTypeReceivedData; instance->iso14443_4a_event.data->buffer = rx_buffer; From e0b5aa807080e0fa351324b316a99ac154b106b1 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sat, 15 Mar 2025 05:44:31 +0000 Subject: [PATCH 23/92] For now send NACK to all R blocks --- lib/nfc/helpers/iso14443_4_layer.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/nfc/helpers/iso14443_4_layer.c b/lib/nfc/helpers/iso14443_4_layer.c index 1b4dac1dd..9e8c75e41 100644 --- a/lib/nfc/helpers/iso14443_4_layer.c +++ b/lib/nfc/helpers/iso14443_4_layer.c @@ -217,7 +217,14 @@ Iso14443_4LayerResult iso14443_4_layer_decode_command( return Iso14443_4LayerResultSkip; } + } else if(ISO14443_4_BLOCK_PCB_IS_R_BLOCK(instance->pcb)) { // FIXME: R blocks + iso14443_4_layer_update_pcb(instance, true); + instance->pcb |= ISO14443_4_BLOCK_PCB_R_NACK_MASK; + bit_buffer_reset(block_data); + bit_buffer_append_byte(block_data, instance->pcb); + iso14443_4_layer_update_pcb(instance, false); + return Iso14443_4LayerResultSend; } return Iso14443_4LayerResultSkip; } From 967eb06082b7f8cf6ca304a93a6bf5f7006dbaed Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sat, 15 Mar 2025 05:49:44 +0000 Subject: [PATCH 24/92] NFC: Handle writes to Type 4 Tag while emulating --- lib/nfc/protocols/type_4_tag/type_4_tag_i.h | 6 ++ .../type_4_tag/type_4_tag_listener_i.c | 79 +++++++++++++++++++ 2 files changed, 85 insertions(+) diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_i.h b/lib/nfc/protocols/type_4_tag/type_4_tag_i.h index 35f8f6a2b..687728fc4 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag_i.h +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_i.h @@ -17,6 +17,12 @@ #define TYPE_4_TAG_ISO_READ_P_OFFSET_MAX (32767U) #define TYPE_4_TAG_ISO_READ_LE_FULL (0x00) +// ISO UPDATE BINARY command and parameters +#define TYPE_4_TAG_ISO_WRITE_CMD 0x00, 0xD6 +#define TYPE_4_TAG_ISO_WRITE_P1_ID_MASK (1 << 7) +#define TYPE_4_TAG_ISO_WRITE_P_BEGINNING 0x00, 0x00 +#define TYPE_4_TAG_ISO_WRITE_LE_EMPTY (0x00) + // Common APDU parameters and values #define TYPE_4_TAG_ISO_STATUS_LEN (2U) #define TYPE_4_TAG_ISO_STATUS_SUCCESS 0x90, 0x00 diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_listener_i.c b/lib/nfc/protocols/type_4_tag/type_4_tag_listener_i.c index f8e799f59..940aca9e1 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag_listener_i.c +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_listener_i.c @@ -161,6 +161,81 @@ static Type4TagError type_4_tag_listener_iso_read( return Type4TagErrorCustomCommand; } +static Type4TagError type_4_tag_listener_iso_write( + Type4TagListener* instance, + uint8_t p1, + uint8_t p2, + size_t lc, + const uint8_t* data, + size_t le) { + UNUSED(le); + + size_t offset; + if(p1 & TYPE_4_TAG_ISO_WRITE_P1_ID_MASK) { + bit_buffer_append_bytes( + instance->tx_buffer, type_4_tag_no_support_apdu, sizeof(type_4_tag_no_support_apdu)); + return Type4TagErrorCustomCommand; + } else { + offset = (p1 << 8) + p2; + } + + if(instance->state == Type4TagListenerStateSelectedCapabilityContainer) { + bit_buffer_append_bytes( + instance->tx_buffer, type_4_tag_no_support_apdu, sizeof(type_4_tag_no_support_apdu)); + return Type4TagErrorNotSupported; + } + + if(instance->state == Type4TagListenerStateSelectedNdefMessage) { + if(offset + lc > (instance->data->is_tag_specific ? instance->data->ndef_max_len : + TYPE_4_TAG_DEFAULT_SIZE)) { + bit_buffer_append_bytes( + instance->tx_buffer, + type_4_tag_offset_error_apdu, + sizeof(type_4_tag_offset_error_apdu)); + return Type4TagErrorWrongFormat; + } + + const size_t ndef_file_len = simple_array_get_count(instance->data->ndef_data); + size_t ndef_file_len_new = ndef_file_len; + if(offset < sizeof(uint16_t)) { + const uint8_t write_len = sizeof(uint16_t) - offset; + ndef_file_len_new = bit_lib_bytes_to_num_be(data, write_len); + offset = sizeof(uint16_t); + data += offset; + lc -= write_len; + } + offset -= sizeof(uint16_t); + + ndef_file_len_new = MAX(ndef_file_len_new, offset + lc); + if(ndef_file_len_new != ndef_file_len) { + SimpleArray* ndef_data_temp = simple_array_alloc(&simple_array_config_uint8_t); + if(ndef_file_len_new > 0) { + simple_array_init(ndef_data_temp, ndef_file_len_new); + if(ndef_file_len > 0) { + memcpy( + simple_array_get_data(ndef_data_temp), + simple_array_get_data(instance->data->ndef_data), + MIN(ndef_file_len_new, ndef_file_len)); + } + } + simple_array_copy(instance->data->ndef_data, ndef_data_temp); + simple_array_free(ndef_data_temp); + } + + if(ndef_file_len_new > 0 && lc > 0) { + uint8_t* ndef_data = simple_array_get_data(instance->data->ndef_data); + memcpy(&ndef_data[offset], data, lc); + } + bit_buffer_append_bytes( + instance->tx_buffer, type_4_tag_success_apdu, sizeof(type_4_tag_success_apdu)); + return Type4TagErrorNone; + } + + bit_buffer_append_bytes( + instance->tx_buffer, type_4_tag_not_found_apdu, sizeof(type_4_tag_not_found_apdu)); + return Type4TagErrorCustomCommand; +} + static const Type4TagListenerApduCommand type_4_tag_listener_commands[] = { { .cla_ins = {TYPE_4_TAG_ISO_SELECT_CMD}, @@ -170,6 +245,10 @@ static const Type4TagListenerApduCommand type_4_tag_listener_commands[] = { .cla_ins = {TYPE_4_TAG_ISO_READ_CMD}, .handler = type_4_tag_listener_iso_read, }, + { + .cla_ins = {TYPE_4_TAG_ISO_WRITE_CMD}, + .handler = type_4_tag_listener_iso_write, + }, }; Type4TagError From 22a6c37e20b09978911b3725d67070911151b0da Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 17 Mar 2025 06:16:31 +0000 Subject: [PATCH 25/92] Refactor ISO APDUs in T4T poller --- lib/nfc/protocols/type_4_tag/type_4_tag.h | 2 +- lib/nfc/protocols/type_4_tag/type_4_tag_i.c | 12 +- lib/nfc/protocols/type_4_tag/type_4_tag_i.h | 10 +- .../type_4_tag/type_4_tag_listener_i.c | 25 +- .../type_4_tag/type_4_tag_poller_i.c | 228 +++++++++--------- 5 files changed, 135 insertions(+), 142 deletions(-) diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag.h b/lib/nfc/protocols/type_4_tag/type_4_tag.h index 250f34edc..220458e86 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag.h +++ b/lib/nfc/protocols/type_4_tag/type_4_tag.h @@ -13,7 +13,7 @@ typedef enum { Type4TagErrorTimeout, Type4TagErrorWrongFormat, Type4TagErrorNotSupported, - Type4TagErrorUnknown, + Type4TagErrorApduFailed, Type4TagErrorCustomCommand, } Type4TagError; diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_i.c b/lib/nfc/protocols/type_4_tag/type_4_tag_i.c index d0b0bf22e..90b5fecc2 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag_i.c +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_i.c @@ -9,6 +9,9 @@ #define TYPE_4_TAG_FFF_NDEF_DATA_PER_LINE (16U) +const uint8_t type_4_tag_iso_picc_name[TYPE_4_TAG_ISO_NAME_LEN] = {TYPE_4_TAG_ISO_PICC_NAME}; +const uint8_t type_4_tag_iso_app_name[TYPE_4_TAG_ISO_NAME_LEN] = {TYPE_4_TAG_ISO_APP_NAME}; + Type4TagError type_4_tag_process_error(Iso14443_4aError error) { switch(error) { case Iso14443_4aErrorNone: @@ -56,14 +59,13 @@ void type_4_tag_cc_dump(const Type4TagData* data, uint8_t* buf, size_t len) { data->is_tag_specific ? data->ndef_write_lock : TYPE_4_TAG_T4T_CC_RW_LOCK_NONE; } -Type4TagError type_4_tag_cc_parse(Type4TagData* data, const BitBuffer* buf) { - const size_t cc_len = bit_buffer_get_size_bytes(buf); - if(cc_len < TYPE_4_TAG_T4T_CC_MIN_SIZE) { +Type4TagError type_4_tag_cc_parse(Type4TagData* data, const uint8_t* buf, size_t len) { + if(len < TYPE_4_TAG_T4T_CC_MIN_SIZE) { FURI_LOG_E(TAG, "Unsupported T4T version"); return Type4TagErrorWrongFormat; } - const Type4TagCc* cc = (const Type4TagCc*)bit_buffer_get_data(buf); + const Type4TagCc* cc = (const Type4TagCc*)buf; if(cc->t4t_vno != TYPE_4_TAG_T4T_CC_VNO) { FURI_LOG_E(TAG, "Unsupported T4T version"); return Type4TagErrorNotSupported; @@ -71,7 +73,7 @@ Type4TagError type_4_tag_cc_parse(Type4TagData* data, const BitBuffer* buf) { const Type4TagCcTlv* tlv = cc->tlv; const Type4TagCcTlvNdefFileCtrl* ndef_file_ctrl = NULL; - const void* end = MIN((void*)cc + cc->len, (void*)cc + cc_len); + const void* end = MIN((void*)cc + cc->len, (void*)cc + len); while((void*)tlv < end) { if(tlv->type == Type4TagCcTlvTypeNdefFileCtrl) { ndef_file_ctrl = &tlv->value.ndef_file_ctrl; diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_i.h b/lib/nfc/protocols/type_4_tag/type_4_tag_i.h index 687728fc4..00571f90f 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag_i.h +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_i.h @@ -33,10 +33,9 @@ #define TYPE_4_TAG_ISO_STATUS_NO_CMD 0x68, 0x00 #define TYPE_4_TAG_ISO_RW_CHUNK_LEN (255U) #define TYPE_4_TAG_ISO_NAME_LEN (7U) -#define TYPE_4_TAG_ISO_APP_NAME 0xD2, 0x76, 0x00, 0x00, 0x85, 0x01, 0x01 #define TYPE_4_TAG_ISO_PICC_NAME 0xD2, 0x76, 0x00, 0x00, 0x85, 0x01, 0x00 -#define TYPE_4_TAG_T4T_CC_FILE_ID_LEN (2U) -#define TYPE_4_TAG_T4T_CC_FILE_ID 0xE1, 0x03 +#define TYPE_4_TAG_ISO_APP_NAME 0xD2, 0x76, 0x00, 0x00, 0x85, 0x01, 0x01 +#define TYPE_4_TAG_T4T_CC_FILE_ID 0xE103 #define TYPE_4_TAG_T4T_CC_VNO (0x20) #define TYPE_4_TAG_T4T_DEFAULT_FILE_ID 0xE104 #define TYPE_4_TAG_T4T_CC_RW_LOCK_NONE 0x00 @@ -48,6 +47,9 @@ #define TYPE_4_TAG_CHUNK_LEN MIN(TYPE_4_TAG_BUF_SIZE - 5U, TYPE_4_TAG_ISO_RW_CHUNK_LEN) #define TYPE_4_TAG_DEFAULT_SIZE (2048U) +extern const uint8_t type_4_tag_iso_picc_name[TYPE_4_TAG_ISO_NAME_LEN]; +extern const uint8_t type_4_tag_iso_app_name[TYPE_4_TAG_ISO_NAME_LEN]; + // Capability Container parsing structures typedef enum FURI_PACKED { @@ -86,7 +88,7 @@ Type4TagError type_4_tag_process_error(Iso14443_4aError error); void type_4_tag_cc_dump(const Type4TagData* data, uint8_t* buf, size_t len); -Type4TagError type_4_tag_cc_parse(Type4TagData* data, const BitBuffer* buf); +Type4TagError type_4_tag_cc_parse(Type4TagData* data, const uint8_t* buf, size_t len); // Load internal Type4Tag structures diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_listener_i.c b/lib/nfc/protocols/type_4_tag/type_4_tag_listener_i.c index 940aca9e1..264771e9b 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag_listener_i.c +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_listener_i.c @@ -36,16 +36,16 @@ static Type4TagError type_4_tag_listener_iso_select( UNUSED(le); if(p1 == TYPE_4_TAG_ISO_SELECT_P1_BY_DF_NAME) { - static const uint8_t t4t_picc[TYPE_4_TAG_ISO_NAME_LEN] = {TYPE_4_TAG_ISO_PICC_NAME}; - if(lc == sizeof(t4t_picc) && memcmp(t4t_picc, data, sizeof(t4t_picc)) == 0) { + if(lc == sizeof(type_4_tag_iso_picc_name) && + memcmp(type_4_tag_iso_picc_name, data, sizeof(type_4_tag_iso_picc_name)) == 0) { instance->state = Type4TagListenerStateSelectedPicc; bit_buffer_append_bytes( instance->tx_buffer, type_4_tag_success_apdu, sizeof(type_4_tag_success_apdu)); return Type4TagErrorNone; } - static const uint8_t t4t_app[TYPE_4_TAG_ISO_NAME_LEN] = {TYPE_4_TAG_ISO_APP_NAME}; - if(lc == sizeof(t4t_app) && memcmp(t4t_app, data, sizeof(t4t_app)) == 0) { + if(lc == sizeof(type_4_tag_iso_app_name) && + memcmp(type_4_tag_iso_app_name, data, sizeof(type_4_tag_iso_app_name)) == 0) { instance->state = Type4TagListenerStateSelectedApplication; bit_buffer_append_bytes( instance->tx_buffer, type_4_tag_success_apdu, sizeof(type_4_tag_success_apdu)); @@ -54,23 +54,18 @@ static Type4TagError type_4_tag_listener_iso_select( } else if( instance->state >= Type4TagListenerStateSelectedApplication && - (p1 == TYPE_4_TAG_ISO_SELECT_P1_BY_ID || p1 == TYPE_4_TAG_ISO_SELECT_P1_BY_EF_ID)) { - static const uint8_t cc_id[TYPE_4_TAG_T4T_CC_FILE_ID_LEN] = {TYPE_4_TAG_T4T_CC_FILE_ID}; - if(lc == sizeof(cc_id) && memcmp(cc_id, data, sizeof(cc_id)) == 0) { + (p1 == TYPE_4_TAG_ISO_SELECT_P1_BY_ID || p1 == TYPE_4_TAG_ISO_SELECT_P1_BY_EF_ID) && + lc == sizeof(uint16_t)) { + uint16_t file_id = bit_lib_bytes_to_num_be(data, sizeof(uint16_t)); + if(file_id == TYPE_4_TAG_T4T_CC_FILE_ID) { instance->state = Type4TagListenerStateSelectedCapabilityContainer; bit_buffer_append_bytes( instance->tx_buffer, type_4_tag_success_apdu, sizeof(type_4_tag_success_apdu)); return Type4TagErrorNone; } - uint8_t ndef_file_id_be[sizeof(instance->data->ndef_file_id)]; - bit_lib_num_to_bytes_be( - instance->data->is_tag_specific ? instance->data->ndef_file_id : - TYPE_4_TAG_T4T_DEFAULT_FILE_ID, - sizeof(ndef_file_id_be), - ndef_file_id_be); - if(lc == sizeof(ndef_file_id_be) && - memcmp(ndef_file_id_be, data, sizeof(ndef_file_id_be)) == 0) { + if(file_id == (instance->data->is_tag_specific ? instance->data->ndef_file_id : + TYPE_4_TAG_T4T_DEFAULT_FILE_ID)) { instance->state = Type4TagListenerStateSelectedNdefMessage; bit_buffer_append_bytes( instance->tx_buffer, type_4_tag_success_apdu, sizeof(type_4_tag_success_apdu)); diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c b/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c index cf0d14861..55ba1b059 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c @@ -35,27 +35,106 @@ Type4TagError type_4_tag_apdu_trx(Type4TagPoller* instance, BitBuffer* tx_buf, B return Type4TagErrorNone; } else { FURI_LOG_E(TAG, "APDU failed: 0x%02X%02X", status[0], status[1]); - return Type4TagErrorUnknown; + return Type4TagErrorApduFailed; } } +static Type4TagError type_5_tag_poller_iso_select_name( + Type4TagPoller* instance, + const uint8_t* df_name, + uint8_t df_name_len) { + const uint8_t type_4_tag_iso_select_name_apdu[] = { + TYPE_4_TAG_ISO_SELECT_CMD, + TYPE_4_TAG_ISO_SELECT_P1_BY_DF_NAME, + TYPE_4_TAG_ISO_SELECT_P2_EMPTY, + }; + + bit_buffer_append_bytes( + instance->tx_buffer, + type_4_tag_iso_select_name_apdu, + sizeof(type_4_tag_iso_select_name_apdu)); + bit_buffer_append_byte(instance->tx_buffer, df_name_len); + bit_buffer_append_bytes(instance->tx_buffer, df_name, df_name_len); + + return type_4_tag_apdu_trx(instance, instance->tx_buffer, instance->rx_buffer); +} + +static Type4TagError + type_5_tag_poller_iso_select_file(Type4TagPoller* instance, uint16_t file_id) { + const uint8_t type_4_tag_iso_select_file_apdu[] = { + TYPE_4_TAG_ISO_SELECT_CMD, + TYPE_4_TAG_ISO_SELECT_P1_BY_ID, + TYPE_4_TAG_ISO_SELECT_P2_EMPTY, + sizeof(file_id), + }; + uint8_t file_id_be[sizeof(file_id)]; + bit_lib_num_to_bytes_be(file_id, sizeof(file_id), file_id_be); + + bit_buffer_append_bytes( + instance->tx_buffer, + type_4_tag_iso_select_file_apdu, + sizeof(type_4_tag_iso_select_file_apdu)); + bit_buffer_append_bytes(instance->tx_buffer, file_id_be, sizeof(file_id_be)); + + return type_4_tag_apdu_trx(instance, instance->tx_buffer, instance->rx_buffer); +} + +static Type4TagError type_5_tag_poller_iso_read( + Type4TagPoller* instance, + uint16_t offset, + uint16_t length, + uint8_t* buffer) { + const uint8_t chunk_max = instance->data->is_tag_specific ? + MIN(instance->data->chunk_max_read, TYPE_4_TAG_CHUNK_LEN) : + TYPE_4_TAG_CHUNK_LEN; + if(offset + length > TYPE_4_TAG_ISO_READ_P_OFFSET_MAX + chunk_max - sizeof(length)) { + FURI_LOG_E(TAG, "File too large: %zu bytes", length); + return Type4TagErrorNotSupported; + } + + const uint8_t type_4_tag_iso_read_apdu[] = { + TYPE_4_TAG_ISO_READ_CMD, + }; + + while(length > 0) { + uint8_t chunk_len = MIN(length, chunk_max); + uint8_t offset_be[sizeof(offset)]; + bit_lib_num_to_bytes_be(offset, sizeof(offset_be), offset_be); + + bit_buffer_append_bytes( + instance->tx_buffer, type_4_tag_iso_read_apdu, sizeof(type_4_tag_iso_read_apdu)); + bit_buffer_append_bytes(instance->tx_buffer, offset_be, sizeof(offset_be)); + bit_buffer_append_byte(instance->tx_buffer, chunk_len); + + Type4TagError error = + type_4_tag_apdu_trx(instance, instance->tx_buffer, instance->rx_buffer); + if(error != Type4TagErrorNone) { + return error; + } + if(bit_buffer_get_size_bytes(instance->rx_buffer) != chunk_len) { + FURI_LOG_E( + TAG, + "Wrong chunk len: %zu != %zu", + bit_buffer_get_size_bytes(instance->rx_buffer), + chunk_len); + return Type4TagErrorWrongFormat; + } + + memcpy(buffer, bit_buffer_get_data(instance->rx_buffer), chunk_len); + buffer += chunk_len; + offset += chunk_len; + length -= chunk_len; + } + + return Type4TagErrorNone; +} + Type4TagError type_4_tag_poller_select_app(Type4TagPoller* instance) { furi_check(instance); FURI_LOG_D(TAG, "Select application"); - const uint8_t type_4_tag_select_app_apdu[] = { - TYPE_4_TAG_ISO_SELECT_CMD, - TYPE_4_TAG_ISO_SELECT_P1_BY_DF_NAME, - TYPE_4_TAG_ISO_SELECT_P2_EMPTY, - TYPE_4_TAG_ISO_NAME_LEN, - TYPE_4_TAG_ISO_APP_NAME, - TYPE_4_TAG_ISO_SELECT_LE_EMPTY, - }; - - bit_buffer_append_bytes( - instance->tx_buffer, type_4_tag_select_app_apdu, sizeof(type_4_tag_select_app_apdu)); - - return type_4_tag_apdu_trx(instance, instance->tx_buffer, instance->rx_buffer); + return type_5_tag_poller_iso_select_name( + instance, type_4_tag_iso_app_name, sizeof(type_4_tag_iso_app_name)); } Type4TagError type_4_tag_poller_read_cc(Type4TagPoller* instance) { @@ -65,35 +144,22 @@ Type4TagError type_4_tag_poller_read_cc(Type4TagPoller* instance) { do { FURI_LOG_D(TAG, "Select CC"); - const uint8_t type_4_tag_select_cc_apdu[] = { - TYPE_4_TAG_ISO_SELECT_CMD, - TYPE_4_TAG_ISO_SELECT_P1_BY_ID, - TYPE_4_TAG_ISO_SELECT_P2_EMPTY, - TYPE_4_TAG_T4T_CC_FILE_ID_LEN, - TYPE_4_TAG_T4T_CC_FILE_ID, - TYPE_4_TAG_ISO_SELECT_LE_EMPTY, - }; - - bit_buffer_append_bytes( - instance->tx_buffer, type_4_tag_select_cc_apdu, sizeof(type_4_tag_select_cc_apdu)); - - error = type_4_tag_apdu_trx(instance, instance->tx_buffer, instance->rx_buffer); + error = type_5_tag_poller_iso_select_file(instance, TYPE_4_TAG_T4T_CC_FILE_ID); if(error != Type4TagErrorNone) break; + FURI_LOG_D(TAG, "Read CC len"); + uint16_t cc_len; + uint8_t cc_len_be[sizeof(cc_len)]; + error = type_5_tag_poller_iso_read(instance, 0, sizeof(cc_len_be), cc_len_be); + if(error != Type4TagErrorNone) break; + cc_len = bit_lib_bytes_to_num_be(cc_len_be, sizeof(cc_len_be)); + FURI_LOG_D(TAG, "Read CC"); - const uint8_t type_4_tag_read_cc_apdu[] = { - TYPE_4_TAG_ISO_READ_CMD, - TYPE_4_TAG_ISO_READ_P_BEGINNING, - TYPE_4_TAG_ISO_READ_LE_FULL, - }; - - bit_buffer_append_bytes( - instance->tx_buffer, type_4_tag_read_cc_apdu, sizeof(type_4_tag_read_cc_apdu)); - - error = type_4_tag_apdu_trx(instance, instance->tx_buffer, instance->rx_buffer); + uint8_t cc_buf[cc_len]; + error = type_5_tag_poller_iso_read(instance, 0, sizeof(cc_buf), cc_buf); if(error != Type4TagErrorNone) break; - error = type_4_tag_cc_parse(instance->data, instance->rx_buffer); + error = type_4_tag_cc_parse(instance->data, cc_buf, sizeof(cc_buf)); if(error != Type4TagErrorNone) break; instance->data->is_tag_specific = true; @@ -110,97 +176,25 @@ Type4TagError type_4_tag_poller_read_ndef(Type4TagPoller* instance) { do { FURI_LOG_D(TAG, "Select NDEF"); - const uint8_t type_4_tag_select_ndef_apdu_1[] = { - TYPE_4_TAG_ISO_SELECT_CMD, - TYPE_4_TAG_ISO_SELECT_P1_BY_ID, - TYPE_4_TAG_ISO_SELECT_P2_EMPTY, - sizeof(instance->data->ndef_file_id), - }; - uint8_t ndef_file_id_be[sizeof(instance->data->ndef_file_id)]; - bit_lib_num_to_bytes_be( - instance->data->ndef_file_id, sizeof(instance->data->ndef_file_id), ndef_file_id_be); - const uint8_t type_4_tag_select_ndef_apdu_2[] = { - TYPE_4_TAG_ISO_SELECT_LE_EMPTY, - }; - - bit_buffer_append_bytes( - instance->tx_buffer, - type_4_tag_select_ndef_apdu_1, - sizeof(type_4_tag_select_ndef_apdu_1)); - bit_buffer_append_bytes(instance->tx_buffer, ndef_file_id_be, sizeof(ndef_file_id_be)); - bit_buffer_append_bytes( - instance->tx_buffer, - type_4_tag_select_ndef_apdu_2, - sizeof(type_4_tag_select_ndef_apdu_2)); - - error = type_4_tag_apdu_trx(instance, instance->tx_buffer, instance->rx_buffer); + error = type_5_tag_poller_iso_select_file(instance, instance->data->ndef_file_id); if(error != Type4TagErrorNone) break; FURI_LOG_D(TAG, "Read NDEF len"); uint16_t ndef_len; - const uint8_t type_4_tag_read_ndef_len_apdu[] = { - TYPE_4_TAG_ISO_READ_CMD, - TYPE_4_TAG_ISO_READ_P_BEGINNING, - sizeof(ndef_len), - }; - - bit_buffer_append_bytes( - instance->tx_buffer, - type_4_tag_read_ndef_len_apdu, - sizeof(type_4_tag_read_ndef_len_apdu)); - - error = type_4_tag_apdu_trx(instance, instance->tx_buffer, instance->rx_buffer); + uint8_t ndef_len_be[sizeof(ndef_len)]; + error = type_5_tag_poller_iso_read(instance, 0, sizeof(ndef_len_be), ndef_len_be); if(error != Type4TagErrorNone) break; + ndef_len = bit_lib_bytes_to_num_be(ndef_len_be, sizeof(ndef_len_be)); - ndef_len = - bit_lib_bytes_to_num_be(bit_buffer_get_data(instance->rx_buffer), sizeof(ndef_len)); if(ndef_len == 0) { FURI_LOG_D(TAG, "NDEF file is empty"); break; } - uint8_t chunk_max = MIN(instance->data->chunk_max_read, TYPE_4_TAG_CHUNK_LEN); - if(ndef_len > TYPE_4_TAG_ISO_READ_P_OFFSET_MAX + chunk_max - sizeof(ndef_len)) { - FURI_LOG_E(TAG, "NDEF file too long: %zu bytes", ndef_len); - error = Type4TagErrorNotSupported; - break; - } - simple_array_init(instance->data->ndef_data, ndef_len); FURI_LOG_D(TAG, "Read NDEF"); - const uint8_t type_4_tag_read_ndef_apdu_1[] = { - TYPE_4_TAG_ISO_READ_CMD, - }; - - uint16_t ndef_pos = 0; - uint8_t* ndef_data = simple_array_get_data(instance->data->ndef_data); - while(ndef_len > 0) { - uint8_t chunk_len = MIN(ndef_len, chunk_max); - uint8_t ndef_pos_be[sizeof(ndef_pos)]; - bit_lib_num_to_bytes_be(sizeof(ndef_len) + ndef_pos, sizeof(ndef_pos_be), ndef_pos_be); - - bit_buffer_append_bytes( - instance->tx_buffer, - type_4_tag_read_ndef_apdu_1, - sizeof(type_4_tag_read_ndef_apdu_1)); - bit_buffer_append_bytes(instance->tx_buffer, ndef_pos_be, sizeof(ndef_pos_be)); - bit_buffer_append_byte(instance->tx_buffer, chunk_len); - - error = type_4_tag_apdu_trx(instance, instance->tx_buffer, instance->rx_buffer); - if(error != Type4TagErrorNone) break; - if(bit_buffer_get_size_bytes(instance->rx_buffer) != chunk_len) { - FURI_LOG_E( - TAG, - "Wrong NDEF chunk len: %zu != %zu", - bit_buffer_get_size_bytes(instance->rx_buffer), - ndef_len); - error = Type4TagErrorWrongFormat; - break; - } - memcpy(&ndef_data[ndef_pos], bit_buffer_get_data(instance->rx_buffer), chunk_len); - - ndef_pos += chunk_len; - ndef_len -= chunk_len; - } + simple_array_init(instance->data->ndef_data, ndef_len); + uint8_t* ndef_buf = simple_array_get_data(instance->data->ndef_data); + error = type_5_tag_poller_iso_read(instance, sizeof(ndef_len), ndef_len, ndef_buf); if(error != Type4TagErrorNone) break; FURI_LOG_D( From 0df5a66b191cd30fee732a96cbe24a98cb870189 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 17 Mar 2025 06:17:25 +0000 Subject: [PATCH 26/92] Keep prev selection when new selection fails --- lib/nfc/protocols/type_4_tag/type_4_tag_listener_i.c | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_listener_i.c b/lib/nfc/protocols/type_4_tag/type_4_tag_listener_i.c index 264771e9b..ba0ee694a 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag_listener_i.c +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_listener_i.c @@ -73,7 +73,6 @@ static Type4TagError type_4_tag_listener_iso_select( } } - instance->state = Type4TagListenerStateIdle; bit_buffer_append_bytes( instance->tx_buffer, type_4_tag_not_found_apdu, sizeof(type_4_tag_not_found_apdu)); return Type4TagErrorCustomCommand; From b503020f6dafaa2ef170ba177ae8c2f1229040d2 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 17 Mar 2025 06:46:13 +0000 Subject: [PATCH 27/92] NFC: Implement Type 4 Tag writing biolerplate and structure --- .../protocol_support/type_4_tag/type_4_tag.c | 6 +- .../main/nfc/scenes/nfc_scene_config.h | 4 + .../nfc/scenes/nfc_scene_type_4_tag_write.c | 105 ++++++++++++++ .../scenes/nfc_scene_type_4_tag_write_fail.c | 72 ++++++++++ .../nfc_scene_type_4_tag_write_success.c | 46 ++++++ lib/nfc/protocols/type_4_tag/type_4_tag.h | 1 + .../protocols/type_4_tag/type_4_tag_poller.c | 135 +++++++++++++++--- .../protocols/type_4_tag/type_4_tag_poller.h | 22 +++ .../type_4_tag/type_4_tag_poller_i.c | 25 +++- .../type_4_tag/type_4_tag_poller_i.h | 18 ++- 10 files changed, 408 insertions(+), 26 deletions(-) create mode 100644 applications/main/nfc/scenes/nfc_scene_type_4_tag_write.c create mode 100644 applications/main/nfc/scenes/nfc_scene_type_4_tag_write_fail.c create mode 100644 applications/main/nfc/scenes/nfc_scene_type_4_tag_write_success.c diff --git a/applications/main/nfc/helpers/protocol_support/type_4_tag/type_4_tag.c b/applications/main/nfc/helpers/protocol_support/type_4_tag/type_4_tag.c index 043fd5f73..f0ee7baf3 100644 --- a/applications/main/nfc/helpers/protocol_support/type_4_tag/type_4_tag.c +++ b/applications/main/nfc/helpers/protocol_support/type_4_tag/type_4_tag.c @@ -129,9 +129,10 @@ static void nfc_scene_read_on_enter_type_4_tag(NfcApp* instance) { static void nfc_scene_read_and_saved_menu_on_enter_type_4_tag(NfcApp* instance) { Submenu* submenu = instance->submenu; + // FIXME: standardize this and the write scenes into protocol support helper submenu_add_item( submenu, - "Write (Not Implemented)", + "Write", SubmenuIndexWrite, nfc_protocol_support_common_submenu_callback, instance); @@ -144,8 +145,7 @@ static bool if(event.type == SceneManagerEventTypeCustom) { if(event.event == SubmenuIndexWrite) { - // TODO: Implement write - // scene_manager_next_scene(instance->scene_manager, NfcSceneType4TagWrite); + scene_manager_next_scene(instance->scene_manager, NfcSceneType4TagWrite); consumed = true; } } diff --git a/applications/main/nfc/scenes/nfc_scene_config.h b/applications/main/nfc/scenes/nfc_scene_config.h index 83c8ffeed..080294126 100644 --- a/applications/main/nfc/scenes/nfc_scene_config.h +++ b/applications/main/nfc/scenes/nfc_scene_config.h @@ -67,4 +67,8 @@ ADD_SCENE(nfc, slix_key_input, SlixKeyInput) ADD_SCENE(nfc, slix_unlock, SlixUnlock) ADD_SCENE(nfc, slix_unlock_success, SlixUnlockSuccess) +ADD_SCENE(nfc, type_4_tag_write, Type4TagWrite) +ADD_SCENE(nfc, type_4_tag_write_success, Type4TagWriteSuccess) +ADD_SCENE(nfc, type_4_tag_write_fail, Type4TagWriteFail) + ADD_SCENE(nfc, generate_info, GenerateInfo) diff --git a/applications/main/nfc/scenes/nfc_scene_type_4_tag_write.c b/applications/main/nfc/scenes/nfc_scene_type_4_tag_write.c new file mode 100644 index 000000000..cf1c93c34 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_type_4_tag_write.c @@ -0,0 +1,105 @@ +#include "../nfc_app_i.h" + +#include + +enum { + NfcSceneType4TagWriteStateCardSearch, + NfcSceneType4TagWriteStateCardFound, +}; + +NfcCommand nfc_scene_type_4_tag_write_worker_callback(NfcGenericEvent event, void* context) { + furi_assert(context); + furi_assert(event.event_data); + furi_assert(event.protocol == NfcProtocolType4Tag); + + NfcCommand command = NfcCommandContinue; + NfcApp* instance = context; + Type4TagPollerEvent* t4t_event = event.event_data; + + if(t4t_event->type == Type4TagPollerEventTypeRequestMode) { + t4t_event->data->poller_mode.mode = Type4TagPollerModeWrite; + t4t_event->data->poller_mode.data = + nfc_device_get_data(instance->nfc_device, NfcProtocolType4Tag); + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventCardDetected); + } else if(t4t_event->type == Type4TagPollerEventTypeWriteFail) { + scene_manager_set_scene_state( + instance->scene_manager, NfcSceneType4TagWriteFail, t4t_event->data->error); + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerFailure); + command = NfcCommandStop; + } else if(t4t_event->type == Type4TagPollerEventTypeWriteSuccess) { + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess); + command = NfcCommandStop; + } + return command; +} + +static void nfc_scene_type_4_tag_write_setup_view(NfcApp* instance) { + Popup* popup = instance->popup; + popup_reset(popup); + uint32_t state = scene_manager_get_scene_state(instance->scene_manager, NfcSceneType4TagWrite); + + if(state == NfcSceneType4TagWriteStateCardSearch) { + popup_set_header(instance->popup, "Writing", 95, 20, AlignCenter, AlignCenter); + popup_set_text( + instance->popup, "Apply card\nto the back", 95, 38, AlignCenter, AlignCenter); + popup_set_icon(instance->popup, 0, 8, &I_NFC_manual_60x50); + } else { + popup_set_header(popup, "Writing\nDon't move...", 52, 32, AlignLeft, AlignCenter); + popup_set_icon(popup, 12, 23, &A_Loading_24); + } + + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup); +} + +void nfc_scene_type_4_tag_write_on_enter(void* context) { + NfcApp* instance = context; + dolphin_deed(DolphinDeedNfcEmulate); + + scene_manager_set_scene_state( + instance->scene_manager, NfcSceneType4TagWrite, NfcSceneType4TagWriteStateCardSearch); + nfc_scene_type_4_tag_write_setup_view(instance); + + // Setup and start worker + instance->poller = nfc_poller_alloc(instance->nfc, NfcProtocolType4Tag); + nfc_poller_start(instance->poller, nfc_scene_type_4_tag_write_worker_callback, instance); + + nfc_blink_emulate_start(instance); +} + +bool nfc_scene_type_4_tag_write_on_event(void* context, SceneManagerEvent event) { + NfcApp* instance = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventCardDetected) { + scene_manager_set_scene_state( + instance->scene_manager, + NfcSceneType4TagWrite, + NfcSceneType4TagWriteStateCardFound); + nfc_scene_type_4_tag_write_setup_view(instance); + consumed = true; + } else if(event.event == NfcCustomEventPollerSuccess) { + scene_manager_next_scene(instance->scene_manager, NfcSceneType4TagWriteSuccess); + consumed = true; + } else if(event.event == NfcCustomEventPollerFailure) { + scene_manager_next_scene(instance->scene_manager, NfcSceneType4TagWriteFail); + consumed = true; + } + } + + return consumed; +} + +void nfc_scene_type_4_tag_write_on_exit(void* context) { + NfcApp* instance = context; + + nfc_poller_stop(instance->poller); + nfc_poller_free(instance->poller); + + scene_manager_set_scene_state( + instance->scene_manager, NfcSceneType4TagWrite, NfcSceneType4TagWriteStateCardSearch); + // Clear view + popup_reset(instance->popup); + + nfc_blink_stop(instance); +} diff --git a/applications/main/nfc/scenes/nfc_scene_type_4_tag_write_fail.c b/applications/main/nfc/scenes/nfc_scene_type_4_tag_write_fail.c new file mode 100644 index 000000000..7b4b0a9ae --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_type_4_tag_write_fail.c @@ -0,0 +1,72 @@ +#include "../nfc_app_i.h" + +#include + +void nfc_scene_type_4_tag_write_fail_widget_callback( + GuiButtonType result, + InputType type, + void* context) { + NfcApp* instance = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(instance->view_dispatcher, result); + } +} + +void nfc_scene_type_4_tag_write_fail_on_enter(void* context) { + NfcApp* instance = context; + Widget* widget = instance->widget; + + notification_message(instance->notifications, &sequence_error); + + widget_add_icon_element(widget, 83, 22, &I_WarningDolphinFlip_45x42); + widget_add_string_element( + widget, 7, 4, AlignLeft, AlignTop, FontPrimary, "Writing gone wrong!"); + Type4TagError error = + scene_manager_get_scene_state(instance->scene_manager, NfcSceneType4TagWriteFail); + widget_add_string_multiline_element( + widget, + 7, + 17, + AlignLeft, + AlignTop, + FontSecondary, + error == Type4TagErrorCardLocked ? "Card does not\nallow writing\nnew data" : + "Failed to\nwrite new data"); + + widget_add_button_element( + widget, + GuiButtonTypeLeft, + "Finish", + nfc_scene_type_4_tag_write_fail_widget_callback, + instance); + + // Setup and start worker + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewWidget); +} + +static bool nfc_scene_type_4_tag_write_fail_move_to_back_scene(const NfcApp* const instance) { + bool was_saved = scene_manager_has_previous_scene(instance->scene_manager, NfcSceneSavedMenu); + uint32_t scene_id = was_saved ? NfcSceneSavedMenu : NfcSceneReadMenu; + + return scene_manager_search_and_switch_to_previous_scene(instance->scene_manager, scene_id); +} + +bool nfc_scene_type_4_tag_write_fail_on_event(void* context, SceneManagerEvent event) { + NfcApp* instance = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeLeft) { + consumed = nfc_scene_type_4_tag_write_fail_move_to_back_scene(instance); + } + } else if(event.type == SceneManagerEventTypeBack) { + consumed = nfc_scene_type_4_tag_write_fail_move_to_back_scene(instance); + } + return consumed; +} + +void nfc_scene_type_4_tag_write_fail_on_exit(void* context) { + NfcApp* instance = context; + + widget_reset(instance->widget); +} diff --git a/applications/main/nfc/scenes/nfc_scene_type_4_tag_write_success.c b/applications/main/nfc/scenes/nfc_scene_type_4_tag_write_success.c new file mode 100644 index 000000000..125c29e71 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_type_4_tag_write_success.c @@ -0,0 +1,46 @@ +#include "../nfc_app_i.h" + +void nfc_scene_type_4_tag_write_success_popup_callback(void* context) { + NfcApp* instance = context; + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventViewExit); +} + +void nfc_scene_type_4_tag_write_success_on_enter(void* context) { + NfcApp* instance = context; + dolphin_deed(DolphinDeedNfcSave); + + notification_message(instance->notifications, &sequence_success); + + Popup* popup = instance->popup; + popup_set_icon(popup, 48, 6, &I_DolphinDone_80x58); + popup_set_header(popup, "Successfully\nwritten", 5, 22, AlignLeft, AlignBottom); + popup_set_timeout(popup, 1500); + popup_set_context(popup, instance); + popup_set_callback(popup, nfc_scene_type_4_tag_write_success_popup_callback); + popup_enable_timeout(popup); + + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup); +} + +bool nfc_scene_type_4_tag_write_success_on_event(void* context, SceneManagerEvent event) { + NfcApp* instance = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventViewExit) { + bool was_saved = + scene_manager_has_previous_scene(instance->scene_manager, NfcSceneSavedMenu); + + consumed = scene_manager_search_and_switch_to_previous_scene( + instance->scene_manager, was_saved ? NfcSceneSavedMenu : NfcSceneReadSuccess); + } + } + return consumed; +} + +void nfc_scene_type_4_tag_write_success_on_exit(void* context) { + NfcApp* instance = context; + + // Clear view + popup_reset(instance->popup); +} diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag.h b/lib/nfc/protocols/type_4_tag/type_4_tag.h index 220458e86..e68895a2f 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag.h +++ b/lib/nfc/protocols/type_4_tag/type_4_tag.h @@ -14,6 +14,7 @@ typedef enum { Type4TagErrorWrongFormat, Type4TagErrorNotSupported, Type4TagErrorApduFailed, + Type4TagErrorCardLocked, Type4TagErrorCustomCommand, } Type4TagError; diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_poller.c b/lib/nfc/protocols/type_4_tag/type_4_tag_poller.c index 8e321312b..8eef2b70d 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag_poller.c +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_poller.c @@ -45,10 +45,27 @@ static NfcCommand type_4_tag_poller_handler_idle(Type4TagPoller* instance) { instance->data->iso14443_4a_data, iso14443_4a_poller_get_data(instance->iso14443_4a_poller)); - instance->state = Type4TagPollerStateSelectApplication; + instance->state = Type4TagPollerStateRequestMode; return NfcCommandContinue; } +static NfcCommand type_4_tag_poller_handler_request_mode(Type4TagPoller* instance) { + NfcCommand command = NfcCommandContinue; + + instance->type_4_tag_event.type = Type4TagPollerEventTypeRequestMode; + instance->type_4_tag_event.data->poller_mode.mode = Type4TagPollerModeRead; + instance->type_4_tag_event.data->poller_mode.data = NULL; + + command = instance->callback(instance->general_event, instance->context); + instance->mode = instance->type_4_tag_event.data->poller_mode.mode; + if(instance->mode == Type4TagPollerModeWrite) { + type_4_tag_copy(instance->data, instance->type_4_tag_event.data->poller_mode.data); + } + + instance->state = Type4TagPollerStateSelectApplication; + return command; +} + static NfcCommand type_4_tag_poller_handler_select_app(Type4TagPoller* instance) { instance->error = type_4_tag_poller_select_app(instance); if(instance->error == Type4TagErrorNone) { @@ -56,8 +73,12 @@ static NfcCommand type_4_tag_poller_handler_select_app(Type4TagPoller* instance) instance->state = Type4TagPollerStateReadCapabilityContainer; } else { FURI_LOG_E(TAG, "Failed to select application"); - iso14443_4a_poller_halt(instance->iso14443_4a_poller); - instance->state = Type4TagPollerStateReadFailed; + if(instance->mode == Type4TagPollerModeWrite && + instance->error == Type4TagErrorApduFailed) { + instance->state = Type4TagPollerStateCreateApplication; + } else { + instance->state = Type4TagPollerStateFailed; + } } return NfcCommandContinue; @@ -67,11 +88,17 @@ static NfcCommand type_4_tag_poller_handler_read_cc(Type4TagPoller* instance) { instance->error = type_4_tag_poller_read_cc(instance); if(instance->error == Type4TagErrorNone) { FURI_LOG_D(TAG, "Read CC success"); - instance->state = Type4TagPollerStateReadNdefMessage; + instance->state = instance->mode == Type4TagPollerModeRead ? + Type4TagPollerStateReadNdefMessage : + Type4TagPollerStateWriteNdefMessage; } else { FURI_LOG_E(TAG, "Failed to read CC"); - iso14443_4a_poller_halt(instance->iso14443_4a_poller); - instance->state = Type4TagPollerStateReadFailed; + if(instance->mode == Type4TagPollerModeWrite && + instance->error == Type4TagErrorApduFailed) { + instance->state = Type4TagPollerStateCreateCapabilityContainer; + } else { + instance->state = Type4TagPollerStateFailed; + } } return NfcCommandContinue; @@ -81,41 +108,115 @@ static NfcCommand type_4_tag_poller_handler_read_ndef(Type4TagPoller* instance) instance->error = type_4_tag_poller_read_ndef(instance); if(instance->error == Type4TagErrorNone) { FURI_LOG_D(TAG, "Read NDEF success"); - instance->state = Type4TagPollerStateReadSuccess; + instance->state = Type4TagPollerStateSuccess; } else { FURI_LOG_E(TAG, "Failed to read NDEF"); - iso14443_4a_poller_halt(instance->iso14443_4a_poller); - instance->state = Type4TagPollerStateReadFailed; + instance->state = Type4TagPollerStateFailed; } return NfcCommandContinue; } -static NfcCommand type_4_tag_poller_handler_read_fail(Type4TagPoller* instance) { - FURI_LOG_D(TAG, "Read Failed"); +static NfcCommand type_4_tag_poller_handler_create_app(Type4TagPoller* instance) { + instance->error = type_4_tag_poller_create_app(instance); + if(instance->error == Type4TagErrorNone) { + FURI_LOG_D(TAG, "Create application success"); + instance->state = Type4TagPollerStateSelectApplication; + } else { + FURI_LOG_E(TAG, "Failed to create application"); + if(instance->error == Type4TagErrorApduFailed) { + instance->error = Type4TagErrorCardLocked; + } + instance->state = Type4TagPollerStateFailed; + } + + return NfcCommandContinue; +} + +static NfcCommand type_4_tag_poller_handler_create_cc(Type4TagPoller* instance) { + instance->error = type_4_tag_poller_create_cc(instance); + if(instance->error == Type4TagErrorNone) { + FURI_LOG_D(TAG, "Create CC success"); + instance->state = Type4TagPollerStateReadCapabilityContainer; + } else { + FURI_LOG_E(TAG, "Failed to create CC"); + if(instance->error == Type4TagErrorApduFailed) { + instance->error = Type4TagErrorCardLocked; + } + instance->state = Type4TagPollerStateFailed; + } + + return NfcCommandContinue; +} + +static NfcCommand type_4_tag_poller_handler_create_ndef(Type4TagPoller* instance) { + instance->error = type_4_tag_poller_create_ndef(instance); + if(instance->error == Type4TagErrorNone) { + FURI_LOG_D(TAG, "Create NDEF success"); + instance->state = Type4TagPollerStateWriteNdefMessage; + } else { + FURI_LOG_E(TAG, "Failed to create NDEF"); + if(instance->error == Type4TagErrorApduFailed) { + instance->error = Type4TagErrorCardLocked; + } + instance->state = Type4TagPollerStateFailed; + } + + return NfcCommandContinue; +} + +static NfcCommand type_4_tag_poller_handler_write_ndef(Type4TagPoller* instance) { + instance->error = type_4_tag_poller_write_ndef(instance); + if(instance->error == Type4TagErrorNone) { + FURI_LOG_D(TAG, "Write NDEF success"); + instance->state = Type4TagPollerStateSuccess; + } else { + FURI_LOG_E(TAG, "Failed to write NDEF"); + if(instance->mode == Type4TagPollerModeWrite && + instance->error == Type4TagErrorApduFailed) { + instance->state = Type4TagPollerStateCreateNdefMessage; + } else { + instance->state = Type4TagPollerStateFailed; + } + } + + return NfcCommandContinue; +} + +static NfcCommand type_4_tag_poller_handler_fail(Type4TagPoller* instance) { + FURI_LOG_D(TAG, "Operation Failed"); iso14443_4a_poller_halt(instance->iso14443_4a_poller); - instance->type_4_tag_event.type = Type4TagPollerEventTypeReadFailed; + instance->type_4_tag_event.type = instance->mode == Type4TagPollerModeRead ? + Type4TagPollerEventTypeReadFailed : + Type4TagPollerEventTypeWriteFail; instance->type_4_tag_event.data->error = instance->error; NfcCommand command = instance->callback(instance->general_event, instance->context); instance->state = Type4TagPollerStateIdle; return command; } -static NfcCommand type_4_tag_poller_handler_read_success(Type4TagPoller* instance) { - FURI_LOG_D(TAG, "Read success."); +static NfcCommand type_4_tag_poller_handler_success(Type4TagPoller* instance) { + FURI_LOG_D(TAG, "Operation succeeded"); iso14443_4a_poller_halt(instance->iso14443_4a_poller); - instance->type_4_tag_event.type = Type4TagPollerEventTypeReadSuccess; + instance->type_4_tag_event.type = instance->mode == Type4TagPollerModeRead ? + Type4TagPollerEventTypeReadSuccess : + Type4TagPollerEventTypeWriteSuccess; NfcCommand command = instance->callback(instance->general_event, instance->context); return command; } static const Type4TagPollerReadHandler type_4_tag_poller_read_handler[Type4TagPollerStateNum] = { [Type4TagPollerStateIdle] = type_4_tag_poller_handler_idle, + [Type4TagPollerStateRequestMode] = type_4_tag_poller_handler_request_mode, [Type4TagPollerStateSelectApplication] = type_4_tag_poller_handler_select_app, [Type4TagPollerStateReadCapabilityContainer] = type_4_tag_poller_handler_read_cc, [Type4TagPollerStateReadNdefMessage] = type_4_tag_poller_handler_read_ndef, - [Type4TagPollerStateReadFailed] = type_4_tag_poller_handler_read_fail, - [Type4TagPollerStateReadSuccess] = type_4_tag_poller_handler_read_success, + [Type4TagPollerStateCreateApplication] = type_4_tag_poller_handler_create_app, + [Type4TagPollerStateCreateCapabilityContainer] = type_4_tag_poller_handler_create_cc, + [Type4TagPollerStateCreateNdefMessage] = type_4_tag_poller_handler_create_ndef, + [Type4TagPollerStateWriteNdefMessage] = type_4_tag_poller_handler_write_ndef, + [Type4TagPollerStateFailed] = type_4_tag_poller_handler_fail, + [Type4TagPollerStateSuccess] = type_4_tag_poller_handler_success, }; static void type_4_tag_poller_set_callback( diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_poller.h b/lib/nfc/protocols/type_4_tag/type_4_tag_poller.h index eb5e03a14..5ac4fd0f2 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag_poller.h +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_poller.h @@ -15,15 +15,37 @@ typedef struct Type4TagPoller Type4TagPoller; * @brief Enumeration of possible Type4Tag poller event types. */ typedef enum { + Type4TagPollerEventTypeRequestMode, /**< Poller requests for operating mode. */ Type4TagPollerEventTypeReadSuccess, /**< Card was read successfully. */ Type4TagPollerEventTypeReadFailed, /**< Poller failed to read card. */ + Type4TagPollerEventTypeWriteSuccess, /**< Poller wrote card successfully. */ + Type4TagPollerEventTypeWriteFail, /**< Poller failed to write card. */ } Type4TagPollerEventType; +/** + * @brief Enumeration of possible Type4Tag poller operating modes. + */ +typedef enum { + Type4TagPollerModeRead, /**< Poller will only read card. It's a default mode. */ + Type4TagPollerModeWrite, /**< Poller will write already saved card to another presented card. */ +} Type4TagPollerMode; + +/** + * @brief Type4Tag poller request mode event data. + * + * This instance of this structure must be filled on Type4TagPollerEventTypeRequestMode event. + */ +typedef struct { + Type4TagPollerMode mode; /**< Mode to be used by poller. */ + const Type4TagData* data; /**< Data to be used by poller. */ +} Type4TagPollerEventDataRequestMode; + /** * @brief Type4Tag poller event data. */ typedef union { Type4TagError error; /**< Error code indicating card reading fail reason. */ + Type4TagPollerEventDataRequestMode poller_mode; /**< Poller mode context. */ } Type4TagPollerEventData; /** diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c b/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c index 55ba1b059..eab1d474b 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c @@ -198,11 +198,28 @@ Type4TagError type_4_tag_poller_read_ndef(Type4TagPoller* instance) { if(error != Type4TagErrorNone) break; FURI_LOG_D( - TAG, - "Read NDEF file 0x%04X of %lu bytes", - instance->data->ndef_file_id, - simple_array_get_count(instance->data->ndef_data)); + TAG, "Read %hu bytes from NDEF file 0x%04X", ndef_len, instance->data->ndef_file_id); } while(false); return error; } + +Type4TagError type_4_tag_poller_create_app(Type4TagPoller* instance) { + UNUSED(instance); + return Type4TagErrorNotSupported; +} + +Type4TagError type_4_tag_poller_create_cc(Type4TagPoller* instance) { + UNUSED(instance); + return Type4TagErrorNotSupported; +} + +Type4TagError type_4_tag_poller_create_ndef(Type4TagPoller* instance) { + UNUSED(instance); + return Type4TagErrorNotSupported; +} + +Type4TagError type_4_tag_poller_write_ndef(Type4TagPoller* instance) { + UNUSED(instance); + return Type4TagErrorNotSupported; +} diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.h b/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.h index 0301e21a3..3fbead2f8 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.h +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.h @@ -10,11 +10,16 @@ extern "C" { typedef enum { Type4TagPollerStateIdle, + Type4TagPollerStateRequestMode, Type4TagPollerStateSelectApplication, Type4TagPollerStateReadCapabilityContainer, Type4TagPollerStateReadNdefMessage, - Type4TagPollerStateReadFailed, - Type4TagPollerStateReadSuccess, + Type4TagPollerStateCreateApplication, + Type4TagPollerStateCreateCapabilityContainer, + Type4TagPollerStateCreateNdefMessage, + Type4TagPollerStateWriteNdefMessage, + Type4TagPollerStateFailed, + Type4TagPollerStateSuccess, Type4TagPollerStateNum, } Type4TagPollerState; @@ -22,6 +27,7 @@ typedef enum { struct Type4TagPoller { Iso14443_4aPoller* iso14443_4a_poller; Type4TagPollerState state; + Type4TagPollerMode mode; Type4TagError error; Type4TagData* data; BitBuffer* tx_buffer; @@ -41,6 +47,14 @@ Type4TagError type_4_tag_poller_read_cc(Type4TagPoller* instance); Type4TagError type_4_tag_poller_read_ndef(Type4TagPoller* instance); +Type4TagError type_4_tag_poller_create_app(Type4TagPoller* instance); + +Type4TagError type_4_tag_poller_create_cc(Type4TagPoller* instance); + +Type4TagError type_4_tag_poller_create_ndef(Type4TagPoller* instance); + +Type4TagError type_4_tag_poller_write_ndef(Type4TagPoller* instance); + #ifdef __cplusplus } #endif From ca3067323864575d3cfe4db360061b2afaf14c93 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Tue, 18 Mar 2025 03:01:34 +0000 Subject: [PATCH 28/92] Improve ISO SELECT naming and handling --- lib/nfc/protocols/type_4_tag/type_4_tag_i.c | 8 +-- lib/nfc/protocols/type_4_tag/type_4_tag_i.h | 49 +++++++++------- .../type_4_tag/type_4_tag_listener_i.c | 56 ++++++++++++------- .../type_4_tag/type_4_tag_poller_i.c | 16 +++--- 4 files changed, 78 insertions(+), 51 deletions(-) diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_i.c b/lib/nfc/protocols/type_4_tag/type_4_tag_i.c index 90b5fecc2..889cf635b 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag_i.c +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_i.c @@ -9,8 +9,8 @@ #define TYPE_4_TAG_FFF_NDEF_DATA_PER_LINE (16U) -const uint8_t type_4_tag_iso_picc_name[TYPE_4_TAG_ISO_NAME_LEN] = {TYPE_4_TAG_ISO_PICC_NAME}; -const uint8_t type_4_tag_iso_app_name[TYPE_4_TAG_ISO_NAME_LEN] = {TYPE_4_TAG_ISO_APP_NAME}; +const uint8_t type_4_tag_iso_mf_name[TYPE_4_TAG_ISO_NAME_LEN] = {TYPE_4_TAG_ISO_MF_NAME}; +const uint8_t type_4_tag_iso_df_name[TYPE_4_TAG_ISO_NAME_LEN] = {TYPE_4_TAG_ISO_DF_NAME}; Type4TagError type_4_tag_process_error(Iso14443_4aError error) { switch(error) { @@ -46,11 +46,11 @@ void type_4_tag_cc_dump(const Type4TagData* data, uint8_t* buf, size_t len) { cc->tlv[0].len = sizeof(cc->tlv[0].value.ndef_file_ctrl); bit_lib_num_to_bytes_be( - data->is_tag_specific ? data->ndef_file_id : TYPE_4_TAG_T4T_DEFAULT_FILE_ID, + data->is_tag_specific ? data->ndef_file_id : TYPE_4_TAG_T4T_NDEF_EF_ID, sizeof(cc->tlv[0].value.ndef_file_ctrl.file_id), (void*)&cc->tlv[0].value.ndef_file_ctrl.file_id); bit_lib_num_to_bytes_be( - data->is_tag_specific ? data->ndef_max_len : TYPE_4_TAG_DEFAULT_SIZE, + data->is_tag_specific ? data->ndef_max_len : TYPE_4_TAG_DEFAULT_NDEF_SIZE, sizeof(cc->tlv[0].value.ndef_file_ctrl.max_len), (void*)&cc->tlv[0].value.ndef_file_ctrl.max_len); cc->tlv[0].value.ndef_file_ctrl.read_perm = diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_i.h b/lib/nfc/protocols/type_4_tag/type_4_tag_i.h index 00571f90f..1af7c7f38 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag_i.h +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_i.h @@ -3,12 +3,12 @@ #include "type_4_tag.h" // ISO SELECT FILE command and parameters -#define TYPE_4_TAG_ISO_SELECT_CMD 0x00, 0xA4 -#define TYPE_4_TAG_ISO_SELECT_P1_BY_DF_NAME (0x04) -#define TYPE_4_TAG_ISO_SELECT_P1_BY_EF_ID (0x02) -#define TYPE_4_TAG_ISO_SELECT_P1_BY_ID (0x00) -#define TYPE_4_TAG_ISO_SELECT_P2_EMPTY (0x0C) -#define TYPE_4_TAG_ISO_SELECT_LE_EMPTY (0x00) +#define TYPE_4_TAG_ISO_SELECT_CMD 0x00, 0xA4 +#define TYPE_4_TAG_ISO_SELECT_P1_BY_NAME (0x04) +#define TYPE_4_TAG_ISO_SELECT_P1_BY_EF_ID (0x02) +#define TYPE_4_TAG_ISO_SELECT_P1_BY_ID (0x00) +#define TYPE_4_TAG_ISO_SELECT_P2_EMPTY (0x0C) +#define TYPE_4_TAG_ISO_SELECT_LE_EMPTY (0x00) // ISO READ BINARY command and parameters #define TYPE_4_TAG_ISO_READ_CMD 0x00, 0xB0 @@ -32,23 +32,34 @@ #define TYPE_4_TAG_ISO_STATUS_BAD_PARAMS 0x6A, 0x86 #define TYPE_4_TAG_ISO_STATUS_NO_CMD 0x68, 0x00 #define TYPE_4_TAG_ISO_RW_CHUNK_LEN (255U) -#define TYPE_4_TAG_ISO_NAME_LEN (7U) -#define TYPE_4_TAG_ISO_PICC_NAME 0xD2, 0x76, 0x00, 0x00, 0x85, 0x01, 0x00 -#define TYPE_4_TAG_ISO_APP_NAME 0xD2, 0x76, 0x00, 0x00, 0x85, 0x01, 0x01 -#define TYPE_4_TAG_T4T_CC_FILE_ID 0xE103 -#define TYPE_4_TAG_T4T_CC_VNO (0x20) -#define TYPE_4_TAG_T4T_DEFAULT_FILE_ID 0xE104 -#define TYPE_4_TAG_T4T_CC_RW_LOCK_NONE 0x00 -#define TYPE_4_TAG_T4T_CC_MIN_SIZE (sizeof(Type4TagCc) + sizeof(Type4TagCcTlv)) +// Common IDs and Names, note: +// MF = Master File (PICC/Card Level) +// DF = Dedicated File (Application) +// EF = Elementary File (File) +#define TYPE_4_TAG_ISO_NAME_LEN (7U) +#define TYPE_4_TAG_ISO_MF_NAME 0xD2, 0x76, 0x00, 0x00, 0x85, 0x01, 0x00 +#define TYPE_4_TAG_ISO_DF_NAME 0xD2, 0x76, 0x00, 0x00, 0x85, 0x01, 0x01 +#define TYPE_4_TAG_ISO_ID_LEN (2U) +#define TYPE_4_TAG_ISO_MF_ID (0x3F00) +#define TYPE_4_TAG_ISO_DF_ID (0xE110) +#define TYPE_4_TAG_T4T_CC_EF_ID (0xE103) +#define TYPE_4_TAG_T4T_NDEF_EF_ID (0xE104) + +// Capability Container parsing parameters +#define TYPE_4_TAG_T4T_CC_VNO (0x20) +#define TYPE_4_TAG_T4T_CC_RW_LOCK_NONE (0x00) +#define TYPE_4_TAG_T4T_CC_MIN_SIZE (sizeof(Type4TagCc) + sizeof(Type4TagCcTlv)) + +// Implementation-specific sizes and defaults // 4a layer adds 1..3 byte prefix, 3a layer adds 2 byte suffix and has 256 byte buffer -#define TYPE_4_TAG_BUF_SIZE (256U - 3U - 2U) +#define TYPE_4_TAG_BUF_SIZE (256U - 3U - 2U) // Read returns 2 byte status trailer, write sends 5 byte command header -#define TYPE_4_TAG_CHUNK_LEN MIN(TYPE_4_TAG_BUF_SIZE - 5U, TYPE_4_TAG_ISO_RW_CHUNK_LEN) -#define TYPE_4_TAG_DEFAULT_SIZE (2048U) +#define TYPE_4_TAG_CHUNK_LEN MIN(TYPE_4_TAG_BUF_SIZE - 5U, TYPE_4_TAG_ISO_RW_CHUNK_LEN) +#define TYPE_4_TAG_DEFAULT_NDEF_SIZE (2048U) -extern const uint8_t type_4_tag_iso_picc_name[TYPE_4_TAG_ISO_NAME_LEN]; -extern const uint8_t type_4_tag_iso_app_name[TYPE_4_TAG_ISO_NAME_LEN]; +extern const uint8_t type_4_tag_iso_mf_name[TYPE_4_TAG_ISO_NAME_LEN]; +extern const uint8_t type_4_tag_iso_df_name[TYPE_4_TAG_ISO_NAME_LEN]; // Capability Container parsing structures diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_listener_i.c b/lib/nfc/protocols/type_4_tag/type_4_tag_listener_i.c index ba0ee694a..fb0890688 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag_listener_i.c +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_listener_i.c @@ -35,17 +35,15 @@ static Type4TagError type_4_tag_listener_iso_select( UNUSED(p2); UNUSED(le); - if(p1 == TYPE_4_TAG_ISO_SELECT_P1_BY_DF_NAME) { - if(lc == sizeof(type_4_tag_iso_picc_name) && - memcmp(type_4_tag_iso_picc_name, data, sizeof(type_4_tag_iso_picc_name)) == 0) { + if(p1 == TYPE_4_TAG_ISO_SELECT_P1_BY_NAME && lc == TYPE_4_TAG_ISO_NAME_LEN) { + if(memcmp(type_4_tag_iso_mf_name, data, sizeof(type_4_tag_iso_mf_name)) == 0) { instance->state = Type4TagListenerStateSelectedPicc; bit_buffer_append_bytes( instance->tx_buffer, type_4_tag_success_apdu, sizeof(type_4_tag_success_apdu)); return Type4TagErrorNone; } - if(lc == sizeof(type_4_tag_iso_app_name) && - memcmp(type_4_tag_iso_app_name, data, sizeof(type_4_tag_iso_app_name)) == 0) { + if(memcmp(type_4_tag_iso_df_name, data, sizeof(type_4_tag_iso_df_name)) == 0) { instance->state = Type4TagListenerStateSelectedApplication; bit_buffer_append_bytes( instance->tx_buffer, type_4_tag_success_apdu, sizeof(type_4_tag_success_apdu)); @@ -53,23 +51,41 @@ static Type4TagError type_4_tag_listener_iso_select( } } else if( - instance->state >= Type4TagListenerStateSelectedApplication && (p1 == TYPE_4_TAG_ISO_SELECT_P1_BY_ID || p1 == TYPE_4_TAG_ISO_SELECT_P1_BY_EF_ID) && - lc == sizeof(uint16_t)) { - uint16_t file_id = bit_lib_bytes_to_num_be(data, sizeof(uint16_t)); - if(file_id == TYPE_4_TAG_T4T_CC_FILE_ID) { - instance->state = Type4TagListenerStateSelectedCapabilityContainer; - bit_buffer_append_bytes( - instance->tx_buffer, type_4_tag_success_apdu, sizeof(type_4_tag_success_apdu)); - return Type4TagErrorNone; + lc == TYPE_4_TAG_ISO_ID_LEN) { + uint16_t id = bit_lib_bytes_to_num_be(data, sizeof(uint16_t)); + + if(p1 == TYPE_4_TAG_ISO_SELECT_P1_BY_ID) { + if(id == TYPE_4_TAG_ISO_MF_ID) { + instance->state = Type4TagListenerStateSelectedPicc; + bit_buffer_append_bytes( + instance->tx_buffer, type_4_tag_success_apdu, sizeof(type_4_tag_success_apdu)); + return Type4TagErrorNone; + } + + if(id == TYPE_4_TAG_ISO_DF_ID) { + instance->state = Type4TagListenerStateSelectedApplication; + bit_buffer_append_bytes( + instance->tx_buffer, type_4_tag_success_apdu, sizeof(type_4_tag_success_apdu)); + return Type4TagErrorNone; + } } - if(file_id == (instance->data->is_tag_specific ? instance->data->ndef_file_id : - TYPE_4_TAG_T4T_DEFAULT_FILE_ID)) { - instance->state = Type4TagListenerStateSelectedNdefMessage; - bit_buffer_append_bytes( - instance->tx_buffer, type_4_tag_success_apdu, sizeof(type_4_tag_success_apdu)); - return Type4TagErrorNone; + if(instance->state >= Type4TagListenerStateSelectedApplication) { + if(id == TYPE_4_TAG_T4T_CC_EF_ID) { + instance->state = Type4TagListenerStateSelectedCapabilityContainer; + bit_buffer_append_bytes( + instance->tx_buffer, type_4_tag_success_apdu, sizeof(type_4_tag_success_apdu)); + return Type4TagErrorNone; + } + + if(id == (instance->data->is_tag_specific ? instance->data->ndef_file_id : + TYPE_4_TAG_T4T_NDEF_EF_ID)) { + instance->state = Type4TagListenerStateSelectedNdefMessage; + bit_buffer_append_bytes( + instance->tx_buffer, type_4_tag_success_apdu, sizeof(type_4_tag_success_apdu)); + return Type4TagErrorNone; + } } } @@ -181,7 +197,7 @@ static Type4TagError type_4_tag_listener_iso_write( if(instance->state == Type4TagListenerStateSelectedNdefMessage) { if(offset + lc > (instance->data->is_tag_specific ? instance->data->ndef_max_len : - TYPE_4_TAG_DEFAULT_SIZE)) { + TYPE_4_TAG_DEFAULT_NDEF_SIZE)) { bit_buffer_append_bytes( instance->tx_buffer, type_4_tag_offset_error_apdu, diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c b/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c index eab1d474b..bdfb093f4 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c @@ -41,11 +41,11 @@ Type4TagError type_4_tag_apdu_trx(Type4TagPoller* instance, BitBuffer* tx_buf, B static Type4TagError type_5_tag_poller_iso_select_name( Type4TagPoller* instance, - const uint8_t* df_name, - uint8_t df_name_len) { + const uint8_t* name, + uint8_t name_len) { const uint8_t type_4_tag_iso_select_name_apdu[] = { TYPE_4_TAG_ISO_SELECT_CMD, - TYPE_4_TAG_ISO_SELECT_P1_BY_DF_NAME, + TYPE_4_TAG_ISO_SELECT_P1_BY_NAME, TYPE_4_TAG_ISO_SELECT_P2_EMPTY, }; @@ -53,8 +53,8 @@ static Type4TagError type_5_tag_poller_iso_select_name( instance->tx_buffer, type_4_tag_iso_select_name_apdu, sizeof(type_4_tag_iso_select_name_apdu)); - bit_buffer_append_byte(instance->tx_buffer, df_name_len); - bit_buffer_append_bytes(instance->tx_buffer, df_name, df_name_len); + bit_buffer_append_byte(instance->tx_buffer, name_len); + bit_buffer_append_bytes(instance->tx_buffer, name, name_len); return type_4_tag_apdu_trx(instance, instance->tx_buffer, instance->rx_buffer); } @@ -63,7 +63,7 @@ static Type4TagError type_5_tag_poller_iso_select_file(Type4TagPoller* instance, uint16_t file_id) { const uint8_t type_4_tag_iso_select_file_apdu[] = { TYPE_4_TAG_ISO_SELECT_CMD, - TYPE_4_TAG_ISO_SELECT_P1_BY_ID, + TYPE_4_TAG_ISO_SELECT_P1_BY_EF_ID, TYPE_4_TAG_ISO_SELECT_P2_EMPTY, sizeof(file_id), }; @@ -134,7 +134,7 @@ Type4TagError type_4_tag_poller_select_app(Type4TagPoller* instance) { FURI_LOG_D(TAG, "Select application"); return type_5_tag_poller_iso_select_name( - instance, type_4_tag_iso_app_name, sizeof(type_4_tag_iso_app_name)); + instance, type_4_tag_iso_df_name, sizeof(type_4_tag_iso_df_name)); } Type4TagError type_4_tag_poller_read_cc(Type4TagPoller* instance) { @@ -144,7 +144,7 @@ Type4TagError type_4_tag_poller_read_cc(Type4TagPoller* instance) { do { FURI_LOG_D(TAG, "Select CC"); - error = type_5_tag_poller_iso_select_file(instance, TYPE_4_TAG_T4T_CC_FILE_ID); + error = type_5_tag_poller_iso_select_file(instance, TYPE_4_TAG_T4T_CC_EF_ID); if(error != Type4TagErrorNone) break; FURI_LOG_D(TAG, "Read CC len"); From b1346bc9bb2a7b344f3289f2edd357989fcb3d29 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Tue, 18 Mar 2025 03:03:31 +0000 Subject: [PATCH 29/92] Make APDU arrays static --- lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c b/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c index bdfb093f4..8bac81ce2 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c @@ -24,7 +24,7 @@ Type4TagError type_4_tag_apdu_trx(Type4TagPoller* instance, BitBuffer* tx_buf, B return Type4TagErrorWrongFormat; } - const uint8_t success[TYPE_4_TAG_ISO_STATUS_LEN] = {TYPE_4_TAG_ISO_STATUS_SUCCESS}; + static const uint8_t success[TYPE_4_TAG_ISO_STATUS_LEN] = {TYPE_4_TAG_ISO_STATUS_SUCCESS}; uint8_t status[TYPE_4_TAG_ISO_STATUS_LEN] = { bit_buffer_get_byte(rx_buf, response_len - 2), bit_buffer_get_byte(rx_buf, response_len - 1), @@ -43,7 +43,7 @@ static Type4TagError type_5_tag_poller_iso_select_name( Type4TagPoller* instance, const uint8_t* name, uint8_t name_len) { - const uint8_t type_4_tag_iso_select_name_apdu[] = { + static const uint8_t type_4_tag_iso_select_name_apdu[] = { TYPE_4_TAG_ISO_SELECT_CMD, TYPE_4_TAG_ISO_SELECT_P1_BY_NAME, TYPE_4_TAG_ISO_SELECT_P2_EMPTY, @@ -61,7 +61,7 @@ static Type4TagError type_5_tag_poller_iso_select_name( static Type4TagError type_5_tag_poller_iso_select_file(Type4TagPoller* instance, uint16_t file_id) { - const uint8_t type_4_tag_iso_select_file_apdu[] = { + static const uint8_t type_4_tag_iso_select_file_apdu[] = { TYPE_4_TAG_ISO_SELECT_CMD, TYPE_4_TAG_ISO_SELECT_P1_BY_EF_ID, TYPE_4_TAG_ISO_SELECT_P2_EMPTY, @@ -92,7 +92,7 @@ static Type4TagError type_5_tag_poller_iso_read( return Type4TagErrorNotSupported; } - const uint8_t type_4_tag_iso_read_apdu[] = { + static const uint8_t type_4_tag_iso_read_apdu[] = { TYPE_4_TAG_ISO_READ_CMD, }; From 106d72a332b465ab0e67577a413c039cb3db3c4e Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Tue, 18 Mar 2025 03:46:00 +0000 Subject: [PATCH 30/92] Better handling of read/write failure --- lib/nfc/protocols/type_4_tag/type_4_tag.h | 1 + lib/nfc/protocols/type_4_tag/type_4_tag_poller.c | 15 +++------------ .../protocols/type_4_tag/type_4_tag_poller_i.c | 10 ++++++++-- 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag.h b/lib/nfc/protocols/type_4_tag/type_4_tag.h index e68895a2f..653785cba 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag.h +++ b/lib/nfc/protocols/type_4_tag/type_4_tag.h @@ -14,6 +14,7 @@ typedef enum { Type4TagErrorWrongFormat, Type4TagErrorNotSupported, Type4TagErrorApduFailed, + Type4TagErrorCardUnformatted, Type4TagErrorCardLocked, Type4TagErrorCustomCommand, } Type4TagError; diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_poller.c b/lib/nfc/protocols/type_4_tag/type_4_tag_poller.c index 8eef2b70d..76a42459b 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag_poller.c +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_poller.c @@ -74,7 +74,7 @@ static NfcCommand type_4_tag_poller_handler_select_app(Type4TagPoller* instance) } else { FURI_LOG_E(TAG, "Failed to select application"); if(instance->mode == Type4TagPollerModeWrite && - instance->error == Type4TagErrorApduFailed) { + instance->error == Type4TagErrorCardUnformatted) { instance->state = Type4TagPollerStateCreateApplication; } else { instance->state = Type4TagPollerStateFailed; @@ -94,7 +94,7 @@ static NfcCommand type_4_tag_poller_handler_read_cc(Type4TagPoller* instance) { } else { FURI_LOG_E(TAG, "Failed to read CC"); if(instance->mode == Type4TagPollerModeWrite && - instance->error == Type4TagErrorApduFailed) { + instance->error == Type4TagErrorCardUnformatted) { instance->state = Type4TagPollerStateCreateCapabilityContainer; } else { instance->state = Type4TagPollerStateFailed; @@ -124,9 +124,6 @@ static NfcCommand type_4_tag_poller_handler_create_app(Type4TagPoller* instance) instance->state = Type4TagPollerStateSelectApplication; } else { FURI_LOG_E(TAG, "Failed to create application"); - if(instance->error == Type4TagErrorApduFailed) { - instance->error = Type4TagErrorCardLocked; - } instance->state = Type4TagPollerStateFailed; } @@ -140,9 +137,6 @@ static NfcCommand type_4_tag_poller_handler_create_cc(Type4TagPoller* instance) instance->state = Type4TagPollerStateReadCapabilityContainer; } else { FURI_LOG_E(TAG, "Failed to create CC"); - if(instance->error == Type4TagErrorApduFailed) { - instance->error = Type4TagErrorCardLocked; - } instance->state = Type4TagPollerStateFailed; } @@ -156,9 +150,6 @@ static NfcCommand type_4_tag_poller_handler_create_ndef(Type4TagPoller* instance instance->state = Type4TagPollerStateWriteNdefMessage; } else { FURI_LOG_E(TAG, "Failed to create NDEF"); - if(instance->error == Type4TagErrorApduFailed) { - instance->error = Type4TagErrorCardLocked; - } instance->state = Type4TagPollerStateFailed; } @@ -173,7 +164,7 @@ static NfcCommand type_4_tag_poller_handler_write_ndef(Type4TagPoller* instance) } else { FURI_LOG_E(TAG, "Failed to write NDEF"); if(instance->mode == Type4TagPollerModeWrite && - instance->error == Type4TagErrorApduFailed) { + instance->error == Type4TagErrorCardUnformatted) { instance->state = Type4TagPollerStateCreateNdefMessage; } else { instance->state = Type4TagPollerStateFailed; diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c b/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c index 8bac81ce2..ea609e622 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c @@ -56,7 +56,10 @@ static Type4TagError type_5_tag_poller_iso_select_name( bit_buffer_append_byte(instance->tx_buffer, name_len); bit_buffer_append_bytes(instance->tx_buffer, name, name_len); - return type_4_tag_apdu_trx(instance, instance->tx_buffer, instance->rx_buffer); + Type4TagError error = type_4_tag_apdu_trx(instance, instance->tx_buffer, instance->rx_buffer); + if(error == Type4TagErrorApduFailed) error = Type4TagErrorCardUnformatted; + + return error; } static Type4TagError @@ -76,7 +79,10 @@ static Type4TagError sizeof(type_4_tag_iso_select_file_apdu)); bit_buffer_append_bytes(instance->tx_buffer, file_id_be, sizeof(file_id_be)); - return type_4_tag_apdu_trx(instance, instance->tx_buffer, instance->rx_buffer); + Type4TagError error = type_4_tag_apdu_trx(instance, instance->tx_buffer, instance->rx_buffer); + if(error == Type4TagErrorApduFailed) error = Type4TagErrorCardUnformatted; + + return error; } static Type4TagError type_5_tag_poller_iso_read( From 0af703d5eee7422949ce42f043098ab9c473180d Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Tue, 18 Mar 2025 03:56:38 +0000 Subject: [PATCH 31/92] NFC: Implement Type 4 Tag writing to initialized tags --- .../type_4_tag/type_4_tag_poller_i.c | 76 ++++++++++++++++++- 1 file changed, 74 insertions(+), 2 deletions(-) diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c b/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c index ea609e622..b2895fc95 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c @@ -135,6 +135,49 @@ static Type4TagError type_5_tag_poller_iso_read( return Type4TagErrorNone; } +static Type4TagError type_5_tag_poller_iso_write( + Type4TagPoller* instance, + uint16_t offset, + uint16_t length, + uint8_t* buffer) { + const uint8_t chunk_max = instance->data->is_tag_specific ? + MIN(instance->data->chunk_max_write, TYPE_4_TAG_CHUNK_LEN) : + TYPE_4_TAG_CHUNK_LEN; + if(offset + length > TYPE_4_TAG_ISO_READ_P_OFFSET_MAX + chunk_max - sizeof(length)) { + FURI_LOG_E(TAG, "File too large: %zu bytes", length); + return Type4TagErrorNotSupported; + } + + static const uint8_t type_4_tag_iso_write_apdu[] = { + TYPE_4_TAG_ISO_WRITE_CMD, + }; + + while(length > 0) { + uint8_t chunk_len = MIN(length, chunk_max); + uint8_t offset_be[sizeof(offset)]; + bit_lib_num_to_bytes_be(offset, sizeof(offset_be), offset_be); + + bit_buffer_append_bytes( + instance->tx_buffer, type_4_tag_iso_write_apdu, sizeof(type_4_tag_iso_write_apdu)); + bit_buffer_append_bytes(instance->tx_buffer, offset_be, sizeof(offset_be)); + bit_buffer_append_byte(instance->tx_buffer, chunk_len); + bit_buffer_append_bytes(instance->tx_buffer, buffer, chunk_len); + + Type4TagError error = + type_4_tag_apdu_trx(instance, instance->tx_buffer, instance->rx_buffer); + if(error == Type4TagErrorApduFailed) error = Type4TagErrorCardLocked; + if(error != Type4TagErrorNone) { + return error; + } + + buffer += chunk_len; + offset += chunk_len; + length -= chunk_len; + } + + return Type4TagErrorNone; +} + Type4TagError type_4_tag_poller_select_app(Type4TagPoller* instance) { furi_check(instance); @@ -226,6 +269,35 @@ Type4TagError type_4_tag_poller_create_ndef(Type4TagPoller* instance) { } Type4TagError type_4_tag_poller_write_ndef(Type4TagPoller* instance) { - UNUSED(instance); - return Type4TagErrorNotSupported; + furi_check(instance); + + Type4TagError error; + + do { + FURI_LOG_D(TAG, "Select NDEF"); + error = type_5_tag_poller_iso_select_file(instance, instance->data->ndef_file_id); + if(error != Type4TagErrorNone) break; + + FURI_LOG_D(TAG, "Write NDEF len"); + uint16_t ndef_len = simple_array_get_count(instance->data->ndef_data); + uint8_t ndef_len_be[sizeof(ndef_len)]; + bit_lib_num_to_bytes_be(ndef_len, sizeof(ndef_len_be), ndef_len_be); + error = type_5_tag_poller_iso_write(instance, 0, sizeof(ndef_len_be), ndef_len_be); + if(error != Type4TagErrorNone) break; + + if(ndef_len == 0) { + FURI_LOG_D(TAG, "NDEF file is empty"); + break; + } + + FURI_LOG_D(TAG, "Write NDEF"); + uint8_t* ndef_buf = simple_array_get_data(instance->data->ndef_data); + error = type_5_tag_poller_iso_write(instance, sizeof(ndef_len), ndef_len, ndef_buf); + if(error != Type4TagErrorNone) break; + + FURI_LOG_D( + TAG, "Wrote %hu bytes from NDEF file 0x%04X", ndef_len, instance->data->ndef_file_id); + } while(false); + + return error; } From 121ce315c153411f1d3f64d04fe36fefb0047521 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Tue, 18 Mar 2025 06:28:04 +0000 Subject: [PATCH 32/92] NFC: Detect Type 4 Tag platform (eg DESFire) --- .../protocols/mf_desfire/mf_desfire_poller.c | 3 +- lib/nfc/protocols/type_4_tag/type_4_tag.c | 29 +++++++++-- lib/nfc/protocols/type_4_tag/type_4_tag.h | 10 ++++ .../protocols/type_4_tag/type_4_tag_poller.c | 16 +++++- .../type_4_tag/type_4_tag_poller_i.c | 49 +++++++++++++++++++ .../type_4_tag/type_4_tag_poller_i.h | 3 ++ 6 files changed, 104 insertions(+), 6 deletions(-) diff --git a/lib/nfc/protocols/mf_desfire/mf_desfire_poller.c b/lib/nfc/protocols/mf_desfire/mf_desfire_poller.c index 45e5a27f9..e9c5e3b6d 100644 --- a/lib/nfc/protocols/mf_desfire/mf_desfire_poller.c +++ b/lib/nfc/protocols/mf_desfire/mf_desfire_poller.c @@ -251,8 +251,7 @@ static bool mf_desfire_poller_detect(NfcGenericEvent event, void* context) { MfDesfireError error = mf_desfire_poller_read_key_version(instance, 0, &key_version); if(error != MfDesfireErrorNone) break; - MfDesfireVersion version = {}; - error = mf_desfire_poller_read_version(instance, &version); + error = mf_desfire_poller_read_version(instance, &instance->data->version); if(error != MfDesfireErrorNone) break; protocol_detected = true; diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag.c b/lib/nfc/protocols/type_4_tag/type_4_tag.c index 3bcb0ee41..09d3f5be6 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag.c +++ b/lib/nfc/protocols/type_4_tag/type_4_tag.c @@ -1,6 +1,7 @@ #include "type_4_tag_i.h" #define TYPE_4_TAG_PROTOCOL_NAME "Type 4 Tag" +#define TYPE_4_TAG_SHORT_NAME "T4T" const NfcDeviceBase nfc_device_type_4_tag = { .protocol_name = TYPE_4_TAG_PROTOCOL_NAME, @@ -21,6 +22,9 @@ const NfcDeviceBase nfc_device_type_4_tag = { Type4TagData* type_4_tag_alloc(void) { Type4TagData* data = malloc(sizeof(Type4TagData)); data->iso14443_4a_data = iso14443_4a_alloc(); + data->device_name = furi_string_alloc(); + data->platform_name_full = furi_string_alloc(); + data->platform_name_short = furi_string_alloc(); data->ndef_data = simple_array_alloc(&simple_array_config_uint8_t); return data; } @@ -30,6 +34,9 @@ void type_4_tag_free(Type4TagData* data) { type_4_tag_reset(data); simple_array_free(data->ndef_data); + furi_string_free(data->platform_name_short); + furi_string_free(data->platform_name_full); + furi_string_free(data->device_name); iso14443_4a_free(data->iso14443_4a_data); free(data); } @@ -40,6 +47,9 @@ void type_4_tag_reset(Type4TagData* data) { iso14443_4a_reset(data->iso14443_4a_data); data->is_tag_specific = false; + furi_string_reset(data->device_name); + furi_string_reset(data->platform_name_full); + furi_string_reset(data->platform_name_short); data->t4t_version.value = 0; data->chunk_max_read = 0; data->chunk_max_write = 0; @@ -60,6 +70,9 @@ void type_4_tag_copy(Type4TagData* data, const Type4TagData* other) { iso14443_4a_copy(data->iso14443_4a_data, other->iso14443_4a_data); data->is_tag_specific = other->is_tag_specific; + furi_string_set(data->device_name, other->device_name); + furi_string_set(data->platform_name_full, other->platform_name_full); + furi_string_set(data->platform_name_short, other->platform_name_short); data->t4t_version.value = other->t4t_version.value; data->chunk_max_read = other->chunk_max_read; data->chunk_max_write = other->chunk_max_write; @@ -137,9 +150,19 @@ bool type_4_tag_is_equal(const Type4TagData* data, const Type4TagData* other) { } const char* type_4_tag_get_device_name(const Type4TagData* data, NfcDeviceNameType name_type) { - UNUSED(data); - UNUSED(name_type); - return TYPE_4_TAG_PROTOCOL_NAME; + FuriString* platform_name = name_type == NfcDeviceNameTypeFull ? data->platform_name_full : + data->platform_name_short; + if(furi_string_empty(platform_name)) { + return TYPE_4_TAG_PROTOCOL_NAME; + } + furi_string_printf( + data->device_name, + "%s%c(%s)", + name_type == NfcDeviceNameTypeFull ? TYPE_4_TAG_PROTOCOL_NAME : TYPE_4_TAG_SHORT_NAME, + name_type == NfcDeviceNameTypeFull ? '\n' : ' ', + furi_string_get_cstr(platform_name)); + furi_string_replace_str(data->device_name, "Mifare", "MIFARE"); + return furi_string_get_cstr(data->device_name); } const uint8_t* type_4_tag_get_uid(const Type4TagData* data, size_t* uid_len) { diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag.h b/lib/nfc/protocols/type_4_tag/type_4_tag.h index 653785cba..9c19fd92e 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag.h +++ b/lib/nfc/protocols/type_4_tag/type_4_tag.h @@ -19,10 +19,20 @@ typedef enum { Type4TagErrorCustomCommand, } Type4TagError; +typedef enum { + Type4TagPlatformUnknown, + Type4TagPlatformNtag4xx, + Type4TagPlatformMfDesfire, +} Type4TagPlatform; + typedef struct { Iso14443_4aData* iso14443_4a_data; + FuriString* device_name; // Tag specific data bool is_tag_specific; + Type4TagPlatform platform; + FuriString* platform_name_full; + FuriString* platform_name_short; union { struct { uint8_t minor : 4; diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_poller.c b/lib/nfc/protocols/type_4_tag/type_4_tag_poller.c index 76a42459b..4a8e984fc 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag_poller.c +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_poller.c @@ -62,10 +62,23 @@ static NfcCommand type_4_tag_poller_handler_request_mode(Type4TagPoller* instanc type_4_tag_copy(instance->data, instance->type_4_tag_event.data->poller_mode.data); } - instance->state = Type4TagPollerStateSelectApplication; + instance->state = Type4TagPollerStateDetectPlatform; return command; } +static NfcCommand type_4_tag_poller_handler_detect_platform(Type4TagPoller* instance) { + instance->error = type_4_tag_poller_detect_platform(instance); + iso14443_4a_poller_halt(instance->iso14443_4a_poller); + if(instance->error == Type4TagErrorNone) { + FURI_LOG_D(TAG, "Detect platform success"); + } else { + FURI_LOG_W(TAG, "Failed to detect platform"); + } + instance->state = Type4TagPollerStateSelectApplication; + + return NfcCommandContinue; +} + static NfcCommand type_4_tag_poller_handler_select_app(Type4TagPoller* instance) { instance->error = type_4_tag_poller_select_app(instance); if(instance->error == Type4TagErrorNone) { @@ -199,6 +212,7 @@ static NfcCommand type_4_tag_poller_handler_success(Type4TagPoller* instance) { static const Type4TagPollerReadHandler type_4_tag_poller_read_handler[Type4TagPollerStateNum] = { [Type4TagPollerStateIdle] = type_4_tag_poller_handler_idle, [Type4TagPollerStateRequestMode] = type_4_tag_poller_handler_request_mode, + [Type4TagPollerStateDetectPlatform] = type_4_tag_poller_handler_detect_platform, [Type4TagPollerStateSelectApplication] = type_4_tag_poller_handler_select_app, [Type4TagPollerStateReadCapabilityContainer] = type_4_tag_poller_handler_read_cc, [Type4TagPollerStateReadNdefMessage] = type_4_tag_poller_handler_read_ndef, diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c b/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c index b2895fc95..078fbf990 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c @@ -3,6 +3,9 @@ #include +#include +#include + #define TAG "Type4TagPoller" Type4TagError type_4_tag_apdu_trx(Type4TagPoller* instance, BitBuffer* tx_buf, BitBuffer* rx_buf) { @@ -178,6 +181,52 @@ static Type4TagError type_5_tag_poller_iso_write( return Type4TagErrorNone; } +Type4TagError type_4_tag_poller_detect_platform(Type4TagPoller* instance) { + furi_check(instance); + + Iso14443_4aPollerEvent iso14443_4a_event = { + .type = Iso14443_4aPollerEventTypeReady, + .data = NULL, + }; + NfcGenericEvent event = { + .protocol = NfcProtocolIso14443_4a, + .instance = instance->iso14443_4a_poller, + .event_data = &iso14443_4a_event, + }; + + Type4TagPlatform platform = Type4TagPlatformUnknown; + NfcDevice* device = nfc_device_alloc(); + + do { + FURI_LOG_D(TAG, "Detect DESFire"); + NfcGenericInstance* mf_des = mf_desfire_poller.alloc(instance->iso14443_4a_poller); + if(mf_desfire_poller.detect(event, mf_des)) { + platform = Type4TagPlatformMfDesfire; + nfc_device_set_data(device, NfcProtocolMfDesfire, mf_desfire_poller.get_data(mf_des)); + } + mf_desfire_poller.free(mf_des); + if(platform != Type4TagPlatformUnknown) break; + + // FIXME: detect NTAG4xx + } while(false); + + if(platform != Type4TagPlatformUnknown) { + furi_string_set( + instance->data->platform_name_full, + nfc_device_get_name(device, NfcDeviceNameTypeFull)); + furi_string_set( + instance->data->platform_name_short, + nfc_device_get_name(device, NfcDeviceNameTypeShort)); + } else { + furi_string_reset(instance->data->platform_name_full); + furi_string_reset(instance->data->platform_name_short); + } + instance->data->platform = platform; + nfc_device_free(device); + + return platform != Type4TagPlatformUnknown ? Type4TagErrorNone : Type4TagErrorNotSupported; +} + Type4TagError type_4_tag_poller_select_app(Type4TagPoller* instance) { furi_check(instance); diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.h b/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.h index 3fbead2f8..0989bca60 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.h +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.h @@ -11,6 +11,7 @@ extern "C" { typedef enum { Type4TagPollerStateIdle, Type4TagPollerStateRequestMode, + Type4TagPollerStateDetectPlatform, Type4TagPollerStateSelectApplication, Type4TagPollerStateReadCapabilityContainer, Type4TagPollerStateReadNdefMessage, @@ -41,6 +42,8 @@ struct Type4TagPoller { const Type4TagData* type_4_tag_poller_get_data(Type4TagPoller* instance); +Type4TagError type_4_tag_poller_detect_platform(Type4TagPoller* instance); + Type4TagError type_4_tag_poller_select_app(Type4TagPoller* instance); Type4TagError type_4_tag_poller_read_cc(Type4TagPoller* instance); From 27c977b7c2b4aa8219d778692502a17f991d0bd9 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Wed, 19 Mar 2025 06:05:19 +0000 Subject: [PATCH 33/92] Fix writing with platform detect --- lib/nfc/protocols/type_4_tag/type_4_tag_poller.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_poller.c b/lib/nfc/protocols/type_4_tag/type_4_tag_poller.c index 4a8e984fc..5a978d08a 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag_poller.c +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_poller.c @@ -76,7 +76,7 @@ static NfcCommand type_4_tag_poller_handler_detect_platform(Type4TagPoller* inst } instance->state = Type4TagPollerStateSelectApplication; - return NfcCommandContinue; + return NfcCommandReset; } static NfcCommand type_4_tag_poller_handler_select_app(Type4TagPoller* instance) { From f7198c6105e9fbcf69a3072977c5e5ba598840fd Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Wed, 19 Mar 2025 06:37:13 +0000 Subject: [PATCH 34/92] NFC: Support creating NDEF AID on DESFire --- lib/nfc/protocols/mf_desfire/mf_desfire.h | 2 + lib/nfc/protocols/mf_desfire/mf_desfire_i.c | 35 ++++++++---- lib/nfc/protocols/mf_desfire/mf_desfire_i.h | 2 + .../protocols/mf_desfire/mf_desfire_poller.h | 21 +++++++ .../mf_desfire/mf_desfire_poller_i.c | 35 ++++++++++++ .../protocols/type_4_tag/type_4_tag_poller.c | 25 ++++++++- .../type_4_tag/type_4_tag_poller_i.c | 55 ++++++++++++++++++- targets/f7/api_symbols.csv | 1 + 8 files changed, 160 insertions(+), 16 deletions(-) diff --git a/lib/nfc/protocols/mf_desfire/mf_desfire.h b/lib/nfc/protocols/mf_desfire/mf_desfire.h index fb50008db..8eb650320 100644 --- a/lib/nfc/protocols/mf_desfire/mf_desfire.h +++ b/lib/nfc/protocols/mf_desfire/mf_desfire.h @@ -17,6 +17,8 @@ extern "C" { #define MF_DESFIRE_CMD_GET_FILE_IDS (0x6F) #define MF_DESFIRE_CMD_GET_FILE_SETTINGS (0xF5) +#define MF_DESFIRE_CMD_CREATE_APPLICATION (0xCA) + #define MF_DESFIRE_CMD_READ_DATA (0xBD) #define MF_DESFIRE_CMD_GET_VALUE (0x6C) #define MF_DESFIRE_CMD_READ_RECORDS (0xBB) diff --git a/lib/nfc/protocols/mf_desfire/mf_desfire_i.c b/lib/nfc/protocols/mf_desfire/mf_desfire_i.c index d83a91ad1..bbfb7ffa8 100644 --- a/lib/nfc/protocols/mf_desfire/mf_desfire_i.c +++ b/lib/nfc/protocols/mf_desfire/mf_desfire_i.c @@ -75,17 +75,17 @@ bool mf_desfire_free_memory_parse(MfDesfireFreeMemory* data, const BitBuffer* bu return can_parse; } -bool mf_desfire_key_settings_parse(MfDesfireKeySettings* data, const BitBuffer* buf) { - typedef struct FURI_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; +typedef struct FURI_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; +bool mf_desfire_key_settings_parse(MfDesfireKeySettings* data, const BitBuffer* buf) { const bool can_parse = bit_buffer_get_size_bytes(buf) == sizeof(MfDesfireKeySettingsLayout); if(can_parse) { @@ -105,6 +105,21 @@ bool mf_desfire_key_settings_parse(MfDesfireKeySettings* data, const BitBuffer* return can_parse; } +void mf_desfire_key_settings_dump(const MfDesfireKeySettings* data, BitBuffer* buf) { + MfDesfireKeySettingsLayout layout; + + layout.is_master_key_changeable = data->is_master_key_changeable; + layout.is_free_directory_list = data->is_free_directory_list; + layout.is_free_create_delete = data->is_free_create_delete; + layout.is_config_changeable = data->is_config_changeable; + + layout.change_key_id = data->change_key_id; + layout.max_keys = data->max_keys; + layout.flags = data->flags; + + bit_buffer_append_bytes(buf, (uint8_t*)&layout, sizeof(MfDesfireKeySettingsLayout)); +} + bool mf_desfire_key_version_parse(MfDesfireKeyVersion* data, const BitBuffer* buf) { const bool can_parse = bit_buffer_get_size_bytes(buf) == sizeof(MfDesfireKeyVersion); diff --git a/lib/nfc/protocols/mf_desfire/mf_desfire_i.h b/lib/nfc/protocols/mf_desfire/mf_desfire_i.h index 921bbb9de..e55e422fc 100644 --- a/lib/nfc/protocols/mf_desfire/mf_desfire_i.h +++ b/lib/nfc/protocols/mf_desfire/mf_desfire_i.h @@ -68,6 +68,8 @@ bool mf_desfire_free_memory_parse(MfDesfireFreeMemory* data, const BitBuffer* bu bool mf_desfire_key_settings_parse(MfDesfireKeySettings* data, const BitBuffer* buf); +void mf_desfire_key_settings_dump(const MfDesfireKeySettings* data, BitBuffer* buf); + bool mf_desfire_key_version_parse(MfDesfireKeyVersion* data, const BitBuffer* buf); bool mf_desfire_application_id_parse( diff --git a/lib/nfc/protocols/mf_desfire/mf_desfire_poller.h b/lib/nfc/protocols/mf_desfire/mf_desfire_poller.h index 707df42cd..5ac44e26a 100644 --- a/lib/nfc/protocols/mf_desfire/mf_desfire_poller.h +++ b/lib/nfc/protocols/mf_desfire/mf_desfire_poller.h @@ -187,6 +187,27 @@ MfDesfireError mf_desfire_poller_read_file_settings_multi( const SimpleArray* file_ids, SimpleArray* data); +/** + * @brief Create Application on MfDesfire card. + * + * Must ONLY be used inside the callback function. + * + * @param[in, out] instance pointer to the instance to be used in the transaction. + * @param[in] id pointer to the application id for the new application. + * @param[in] key_settings pointer to the key settings for the new application. + * @param[in] iso_df_id optional identifier for the new application. + * @param[in] iso_df_name optional name for the new application. + * @param[in] iso_df_name_len length of the optional application name. + * @return MfDesfireErrorNone on success, an error code on failure. + */ +MfDesfireError mf_desfire_poller_create_application( + MfDesfirePoller* instance, + const MfDesfireApplicationId* id, + const MfDesfireKeySettings* key_settings, + uint16_t iso_df_id, + const uint8_t* iso_df_name, + uint8_t iso_df_name_len); + /** * @brief Read file data on MfDesfire card. * diff --git a/lib/nfc/protocols/mf_desfire/mf_desfire_poller_i.c b/lib/nfc/protocols/mf_desfire/mf_desfire_poller_i.c index 6d8dfda16..507db2e53 100644 --- a/lib/nfc/protocols/mf_desfire/mf_desfire_poller_i.c +++ b/lib/nfc/protocols/mf_desfire/mf_desfire_poller_i.c @@ -1,6 +1,7 @@ #include "mf_desfire_poller_i.h" #include +#include #include "mf_desfire_i.h" @@ -329,6 +330,40 @@ MfDesfireError mf_desfire_poller_read_file_settings_multi( return error; } +MfDesfireError mf_desfire_poller_create_application( + MfDesfirePoller* instance, + const MfDesfireApplicationId* id, + const MfDesfireKeySettings* key_settings, + uint16_t iso_df_id, + const uint8_t* iso_df_name, + uint8_t iso_df_name_len) { + furi_check(instance); + + bit_buffer_reset(instance->input_buffer); + bit_buffer_append_byte(instance->input_buffer, MF_DESFIRE_CMD_CREATE_APPLICATION); + bit_buffer_append_bytes( + instance->input_buffer, (const uint8_t*)id, sizeof(MfDesfireApplicationId)); + mf_desfire_key_settings_dump(key_settings, instance->input_buffer); + + if(iso_df_name && iso_df_name_len) { + uint8_t ks2_pos = bit_buffer_get_size_bytes(instance->input_buffer) - 1; + uint8_t ks2 = bit_buffer_get_byte(instance->input_buffer, ks2_pos); + ks2 |= (1 << 5); // Mark file id present + bit_buffer_set_byte(instance->input_buffer, ks2_pos, ks2); + + uint8_t iso_file_id_le[sizeof(iso_df_id)]; + bit_lib_num_to_bytes_le(iso_df_id, sizeof(iso_file_id_le), iso_file_id_le); + bit_buffer_append_bytes(instance->input_buffer, iso_file_id_le, sizeof(iso_file_id_le)); + + bit_buffer_append_bytes(instance->input_buffer, iso_df_name, iso_df_name_len); + } + + MfDesfireError error = + mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer); + + return error; +} + static MfDesfireError mf_desfire_poller_read_file( MfDesfirePoller* instance, MfDesfireFileId id, diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_poller.c b/lib/nfc/protocols/type_4_tag/type_4_tag_poller.c index 5a978d08a..d74b1914b 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag_poller.c +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_poller.c @@ -68,7 +68,6 @@ static NfcCommand type_4_tag_poller_handler_request_mode(Type4TagPoller* instanc static NfcCommand type_4_tag_poller_handler_detect_platform(Type4TagPoller* instance) { instance->error = type_4_tag_poller_detect_platform(instance); - iso14443_4a_poller_halt(instance->iso14443_4a_poller); if(instance->error == Type4TagErrorNone) { FURI_LOG_D(TAG, "Detect platform success"); } else { @@ -76,6 +75,8 @@ static NfcCommand type_4_tag_poller_handler_detect_platform(Type4TagPoller* inst } instance->state = Type4TagPollerStateSelectApplication; + // Reset card state so platform-specific commands do not interfere + iso14443_4a_poller_halt(instance->iso14443_4a_poller); return NfcCommandReset; } @@ -89,6 +90,9 @@ static NfcCommand type_4_tag_poller_handler_select_app(Type4TagPoller* instance) if(instance->mode == Type4TagPollerModeWrite && instance->error == Type4TagErrorCardUnformatted) { instance->state = Type4TagPollerStateCreateApplication; + // Reset card state so platform-specific commands do not interfere + iso14443_4a_poller_halt(instance->iso14443_4a_poller); + return NfcCommandReset; } else { instance->state = Type4TagPollerStateFailed; } @@ -109,6 +113,9 @@ static NfcCommand type_4_tag_poller_handler_read_cc(Type4TagPoller* instance) { if(instance->mode == Type4TagPollerModeWrite && instance->error == Type4TagErrorCardUnformatted) { instance->state = Type4TagPollerStateCreateCapabilityContainer; + // Reset card state so platform-specific commands do not interfere + iso14443_4a_poller_halt(instance->iso14443_4a_poller); + return NfcCommandReset; } else { instance->state = Type4TagPollerStateFailed; } @@ -134,7 +141,10 @@ static NfcCommand type_4_tag_poller_handler_create_app(Type4TagPoller* instance) instance->error = type_4_tag_poller_create_app(instance); if(instance->error == Type4TagErrorNone) { FURI_LOG_D(TAG, "Create application success"); + // Reset card state so platform-specific commands do not interfere instance->state = Type4TagPollerStateSelectApplication; + iso14443_4a_poller_halt(instance->iso14443_4a_poller); + return NfcCommandReset; } else { FURI_LOG_E(TAG, "Failed to create application"); instance->state = Type4TagPollerStateFailed; @@ -147,7 +157,10 @@ static NfcCommand type_4_tag_poller_handler_create_cc(Type4TagPoller* instance) instance->error = type_4_tag_poller_create_cc(instance); if(instance->error == Type4TagErrorNone) { FURI_LOG_D(TAG, "Create CC success"); - instance->state = Type4TagPollerStateReadCapabilityContainer; + // Reset card state so platform-specific commands do not interfere + instance->state = Type4TagPollerStateSelectApplication; + iso14443_4a_poller_halt(instance->iso14443_4a_poller); + return NfcCommandReset; } else { FURI_LOG_E(TAG, "Failed to create CC"); instance->state = Type4TagPollerStateFailed; @@ -160,7 +173,10 @@ static NfcCommand type_4_tag_poller_handler_create_ndef(Type4TagPoller* instance instance->error = type_4_tag_poller_create_ndef(instance); if(instance->error == Type4TagErrorNone) { FURI_LOG_D(TAG, "Create NDEF success"); - instance->state = Type4TagPollerStateWriteNdefMessage; + // Reset card state so platform-specific commands do not interfere + instance->state = Type4TagPollerStateSelectApplication; + iso14443_4a_poller_halt(instance->iso14443_4a_poller); + return NfcCommandReset; } else { FURI_LOG_E(TAG, "Failed to create NDEF"); instance->state = Type4TagPollerStateFailed; @@ -179,6 +195,9 @@ static NfcCommand type_4_tag_poller_handler_write_ndef(Type4TagPoller* instance) if(instance->mode == Type4TagPollerModeWrite && instance->error == Type4TagErrorCardUnformatted) { instance->state = Type4TagPollerStateCreateNdefMessage; + // Reset card state so platform-specific commands do not interfere + iso14443_4a_poller_halt(instance->iso14443_4a_poller); + return NfcCommandReset; } else { instance->state = Type4TagPollerStateFailed; } diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c b/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c index 078fbf990..3e09e18a9 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c @@ -4,6 +4,7 @@ #include #include +#include #include #define TAG "Type4TagPoller" @@ -199,7 +200,7 @@ Type4TagError type_4_tag_poller_detect_platform(Type4TagPoller* instance) { do { FURI_LOG_D(TAG, "Detect DESFire"); - NfcGenericInstance* mf_des = mf_desfire_poller.alloc(instance->iso14443_4a_poller); + MfDesfirePoller* mf_des = mf_desfire_poller.alloc(instance->iso14443_4a_poller); if(mf_desfire_poller.detect(event, mf_des)) { platform = Type4TagPlatformMfDesfire; nfc_device_set_data(device, NfcProtocolMfDesfire, mf_desfire_poller.get_data(mf_des)); @@ -303,8 +304,56 @@ Type4TagError type_4_tag_poller_read_ndef(Type4TagPoller* instance) { } Type4TagError type_4_tag_poller_create_app(Type4TagPoller* instance) { - UNUSED(instance); - return Type4TagErrorNotSupported; + Type4TagError error = Type4TagErrorNotSupported; + + if(instance->data->platform == Type4TagPlatformMfDesfire) { + MfDesfirePoller* mf_des = mf_desfire_poller.alloc(instance->iso14443_4a_poller); + MfDesfireError mf_des_error; + + do { + // Select PICC (Card) level + MfDesfireApplicationId picc_aid = {{0x00, 0x00, 0x00}}; + mf_des_error = mf_desfire_poller_select_application(mf_des, &picc_aid); + if(mf_des_error != MfDesfireErrorNone) { + error = Type4TagErrorProtocol; + break; + } + + // Create NDEF application + MfDesfireApplicationId ndef_aid = {{0x10, 0xEE, 0xEE}}; + MfDesfireKeySettings key_settings = { + .is_master_key_changeable = true, + .is_free_directory_list = true, + .is_free_create_delete = true, + .is_config_changeable = true, + .change_key_id = 0, + .max_keys = 1, + .flags = 0, + }; + mf_des_error = mf_desfire_poller_create_application( + mf_des, + &ndef_aid, + &key_settings, + TYPE_4_TAG_ISO_DF_ID, + type_4_tag_iso_df_name, + sizeof(type_4_tag_iso_df_name)); + if(mf_des_error != MfDesfireErrorNone) { + if(mf_des_error != MfDesfireErrorNotPresent && + mf_des_error != MfDesfireErrorTimeout) { + error = Type4TagErrorCardLocked; + } else { + error = Type4TagErrorProtocol; + } + break; + } + + error = Type4TagErrorNone; + } while(false); + + mf_desfire_poller.free(mf_des); + } + + return error; } Type4TagError type_4_tag_poller_create_cc(Type4TagPoller* instance) { diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index 03df9a8c8..c1aa5627c 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -2636,6 +2636,7 @@ Function,+,mf_desfire_get_file_settings,const MfDesfireFileSettings*,"const MfDe Function,+,mf_desfire_get_uid,const uint8_t*,"const MfDesfireData*, size_t*" Function,+,mf_desfire_is_equal,_Bool,"const MfDesfireData*, const MfDesfireData*" Function,+,mf_desfire_load,_Bool,"MfDesfireData*, FlipperFormat*, uint32_t" +Function,+,mf_desfire_poller_create_application,MfDesfireError,"MfDesfirePoller*, const MfDesfireApplicationId*, const MfDesfireKeySettings*, uint16_t, const uint8_t*, uint8_t" Function,+,mf_desfire_poller_read_application,MfDesfireError,"MfDesfirePoller*, MfDesfireApplication*" Function,+,mf_desfire_poller_read_application_ids,MfDesfireError,"MfDesfirePoller*, SimpleArray*" Function,+,mf_desfire_poller_read_applications,MfDesfireError,"MfDesfirePoller*, const SimpleArray*, SimpleArray*" From 19ee0f459832174a84530cd591720905700ed1c9 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 20 Mar 2025 02:50:17 +0000 Subject: [PATCH 35/92] Rename mf_desfire_send_chunks() to mf_desfire_poller_send_chunks() --- .../protocols/mf_desfire/mf_desfire_poller.h | 7 +++- .../mf_desfire/mf_desfire_poller_i.c | 32 ++++++++++++------- targets/f7/api_symbols.csv | 2 +- 3 files changed, 27 insertions(+), 14 deletions(-) diff --git a/lib/nfc/protocols/mf_desfire/mf_desfire_poller.h b/lib/nfc/protocols/mf_desfire/mf_desfire_poller.h index 5ac44e26a..66b06499a 100644 --- a/lib/nfc/protocols/mf_desfire/mf_desfire_poller.h +++ b/lib/nfc/protocols/mf_desfire/mf_desfire_poller.h @@ -51,11 +51,16 @@ typedef struct { * @param[out] rx_buffer pointer to the buffer to be filled with received data. * @return MfDesfireErrorNone on success, an error code on failure. */ -MfDesfireError mf_desfire_send_chunks( +MfDesfireError mf_desfire_poller_send_chunks( MfDesfirePoller* instance, const BitBuffer* tx_buffer, BitBuffer* rx_buffer); +/** + * @warning deprecated, use mf_desfire_poller_send_chunks instead + */ +#define mf_desfire_send_chunks mf_desfire_poller_send_chunks + /** * @brief Read MfDesfire card version. * diff --git a/lib/nfc/protocols/mf_desfire/mf_desfire_poller_i.c b/lib/nfc/protocols/mf_desfire/mf_desfire_poller_i.c index 507db2e53..cd197ce1c 100644 --- a/lib/nfc/protocols/mf_desfire/mf_desfire_poller_i.c +++ b/lib/nfc/protocols/mf_desfire/mf_desfire_poller_i.c @@ -33,7 +33,7 @@ MfDesfireError mf_desfire_process_status_code(uint8_t status_code) { } } -MfDesfireError mf_desfire_send_chunks( +MfDesfireError mf_desfire_poller_send_chunks( MfDesfirePoller* instance, const BitBuffer* tx_buffer, BitBuffer* rx_buffer) { @@ -103,7 +103,8 @@ MfDesfireError mf_desfire_poller_read_version(MfDesfirePoller* instance, MfDesfi MfDesfireError error; do { - error = mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer); + error = mf_desfire_poller_send_chunks( + instance, instance->input_buffer, instance->result_buffer); if(error != MfDesfireErrorNone) break; @@ -125,7 +126,8 @@ MfDesfireError MfDesfireError error; do { - error = mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer); + error = mf_desfire_poller_send_chunks( + instance, instance->input_buffer, instance->result_buffer); if(error != MfDesfireErrorNone) break; @@ -147,7 +149,8 @@ MfDesfireError MfDesfireError error; do { - error = mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer); + error = mf_desfire_poller_send_chunks( + instance, instance->input_buffer, instance->result_buffer); if(error != MfDesfireErrorNone) break; @@ -171,7 +174,7 @@ MfDesfireError mf_desfire_poller_read_key_version( bit_buffer_set_byte(instance->input_buffer, 1, key_num); MfDesfireError error = - mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer); + mf_desfire_poller_send_chunks(instance, instance->input_buffer, instance->result_buffer); if(error == MfDesfireErrorNone) { if(!mf_desfire_key_version_parse(data, instance->result_buffer)) { error = MfDesfireErrorProtocol; @@ -211,7 +214,8 @@ MfDesfireError MfDesfireError error; do { - error = mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer); + error = mf_desfire_poller_send_chunks( + instance, instance->input_buffer, instance->result_buffer); if(error != MfDesfireErrorNone) break; @@ -244,7 +248,7 @@ MfDesfireError mf_desfire_poller_select_application( instance->input_buffer, (const uint8_t*)id, sizeof(MfDesfireApplicationId)); MfDesfireError error = - mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer); + mf_desfire_poller_send_chunks(instance, instance->input_buffer, instance->result_buffer); return error; } @@ -259,7 +263,8 @@ MfDesfireError mf_desfire_poller_read_file_ids(MfDesfirePoller* instance, Simple MfDesfireError error; do { - error = mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer); + error = mf_desfire_poller_send_chunks( + instance, instance->input_buffer, instance->result_buffer); if(error != MfDesfireErrorNone) break; @@ -294,7 +299,8 @@ MfDesfireError mf_desfire_poller_read_file_settings( MfDesfireError error; do { - error = mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer); + error = mf_desfire_poller_send_chunks( + instance, instance->input_buffer, instance->result_buffer); if(error != MfDesfireErrorNone) break; @@ -359,7 +365,7 @@ MfDesfireError mf_desfire_poller_create_application( } MfDesfireError error = - mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer); + mf_desfire_poller_send_chunks(instance, instance->input_buffer, instance->result_buffer); return error; } @@ -389,7 +395,8 @@ static MfDesfireError mf_desfire_poller_read_file( bit_buffer_append_bytes(instance->input_buffer, (const uint8_t*)¤t_offset, 3); bit_buffer_append_bytes(instance->input_buffer, (const uint8_t*)&bytes_to_read, 3); - error = mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer); + error = mf_desfire_poller_send_chunks( + instance, instance->input_buffer, instance->result_buffer); if(error != MfDesfireErrorNone) break; size_t bytes_received = bit_buffer_get_size_bytes(instance->result_buffer); @@ -435,7 +442,8 @@ MfDesfireError mf_desfire_poller_read_file_value( MfDesfireError error; do { - error = mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer); + error = mf_desfire_poller_send_chunks( + instance, instance->input_buffer, instance->result_buffer); if(error != MfDesfireErrorNone) break; diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index c1aa5627c..b5d8e7430 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -2653,9 +2653,9 @@ Function,+,mf_desfire_poller_read_key_version,MfDesfireError,"MfDesfirePoller*, Function,+,mf_desfire_poller_read_key_versions,MfDesfireError,"MfDesfirePoller*, SimpleArray*, uint32_t" Function,+,mf_desfire_poller_read_version,MfDesfireError,"MfDesfirePoller*, MfDesfireVersion*" Function,+,mf_desfire_poller_select_application,MfDesfireError,"MfDesfirePoller*, const MfDesfireApplicationId*" +Function,+,mf_desfire_poller_send_chunks,MfDesfireError,"MfDesfirePoller*, const BitBuffer*, BitBuffer*" Function,+,mf_desfire_reset,void,MfDesfireData* Function,+,mf_desfire_save,_Bool,"const MfDesfireData*, FlipperFormat*" -Function,+,mf_desfire_send_chunks,MfDesfireError,"MfDesfirePoller*, const BitBuffer*, BitBuffer*" Function,+,mf_desfire_set_uid,_Bool,"MfDesfireData*, const uint8_t*, size_t" Function,+,mf_desfire_verify,_Bool,"MfDesfireData*, const FuriString*" Function,+,mf_plus_alloc,MfPlusData*, From cc789115ddbe5cb1d8dc1ec90c7275c728d39049 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 20 Mar 2025 04:10:19 +0000 Subject: [PATCH 36/92] NFC: Implement DESFire ISO 7816 command wrapping --- lib/nfc/protocols/mf_desfire/mf_desfire_i.h | 8 ++ .../protocols/mf_desfire/mf_desfire_poller.h | 20 ++++ .../mf_desfire/mf_desfire_poller_i.c | 91 ++++++++++++++++--- .../mf_desfire/mf_desfire_poller_i.h | 1 + targets/f7/api_symbols.csv | 1 + 5 files changed, 106 insertions(+), 15 deletions(-) diff --git a/lib/nfc/protocols/mf_desfire/mf_desfire_i.h b/lib/nfc/protocols/mf_desfire/mf_desfire_i.h index e55e422fc..526a4e0b8 100644 --- a/lib/nfc/protocols/mf_desfire/mf_desfire_i.h +++ b/lib/nfc/protocols/mf_desfire/mf_desfire_i.h @@ -5,6 +5,14 @@ #define MF_DESFIRE_FFF_PICC_PREFIX "PICC" #define MF_DESFIRE_FFF_APP_PREFIX "Application" +// ISO 7816 command wrapping +#define MF_DESFIRE_CMD_ISO_CLA (0x90) +#define MF_DESFIRE_CMD_ISO_P1 (0x00) +#define MF_DESFIRE_CMD_ISO_P2 (0x00) +#define MF_DESFIRE_CMD_ISO_LE (0x00) +// ISO 7816 status wrapping +#define MF_DESFIRE_STATUS_ISO_SW1 (0x91) + // Successful operation #define MF_DESFIRE_STATUS_OPERATION_OK (0x00) // No changes done to backup files, CommitTransaction / AbortTransaction not necessary diff --git a/lib/nfc/protocols/mf_desfire/mf_desfire_poller.h b/lib/nfc/protocols/mf_desfire/mf_desfire_poller.h index 66b06499a..8dc0513d8 100644 --- a/lib/nfc/protocols/mf_desfire/mf_desfire_poller.h +++ b/lib/nfc/protocols/mf_desfire/mf_desfire_poller.h @@ -38,6 +38,26 @@ typedef struct { MfDesfirePollerEventData* data; /**< Pointer to event specific data. */ } MfDesfirePollerEvent; +/** + * @brief Enumeration of possible MfDesfire poller command modes. + */ +typedef enum { + MfDesfirePollerCommandModeNative, /**< Native MfDesfire commands. */ + MfDesfirePollerCommandModeIsoWrapped, /**< ISO 7816-wrapped commands. */ + + MfDesfirePollerCommandModeMAX, +} MfDesfirePollerCommandMode; + +/** + * @brief Change MfDesfire command mode used in poller mode. + * + * @param[in, out] instance pointer to the instance to affect. + * @param[in] command_mode command mode to use in further communication with the card. + */ +void mf_desfire_poller_set_command_mode( + MfDesfirePoller* instance, + MfDesfirePollerCommandMode command_mode); + /** * @brief Transmit and receive MfDesfire chunks in poller mode. * diff --git a/lib/nfc/protocols/mf_desfire/mf_desfire_poller_i.c b/lib/nfc/protocols/mf_desfire/mf_desfire_poller_i.c index cd197ce1c..57210b9c5 100644 --- a/lib/nfc/protocols/mf_desfire/mf_desfire_poller_i.c +++ b/lib/nfc/protocols/mf_desfire/mf_desfire_poller_i.c @@ -33,6 +33,16 @@ MfDesfireError mf_desfire_process_status_code(uint8_t status_code) { } } +void mf_desfire_poller_set_command_mode( + MfDesfirePoller* instance, + MfDesfirePollerCommandMode command_mode) { + furi_check(instance); + furi_check(instance->state == MfDesfirePollerStateIdle); + furi_check(command_mode < MfDesfirePollerCommandModeMAX); + + instance->command_mode = command_mode; +} + MfDesfireError mf_desfire_poller_send_chunks( MfDesfirePoller* instance, const BitBuffer* tx_buffer, @@ -45,10 +55,27 @@ MfDesfireError mf_desfire_poller_send_chunks( furi_check(rx_buffer); MfDesfireError error = MfDesfireErrorNone; + uint8_t status_code = MF_DESFIRE_STATUS_OPERATION_OK; do { + bit_buffer_reset(instance->tx_buffer); + if(instance->command_mode == MfDesfirePollerCommandModeNative) { + bit_buffer_append(instance->tx_buffer, tx_buffer); + } else if(instance->command_mode == MfDesfirePollerCommandModeIsoWrapped) { + bit_buffer_append_byte(instance->tx_buffer, MF_DESFIRE_CMD_ISO_CLA); + bit_buffer_append_byte(instance->tx_buffer, bit_buffer_get_byte(tx_buffer, 0)); + bit_buffer_append_byte(instance->tx_buffer, MF_DESFIRE_CMD_ISO_P1); + bit_buffer_append_byte(instance->tx_buffer, MF_DESFIRE_CMD_ISO_P2); + if(bit_buffer_get_size_bytes(tx_buffer) > 1) { + bit_buffer_append_byte( + instance->tx_buffer, bit_buffer_get_size_bytes(tx_buffer) - 1); + bit_buffer_append_right(instance->tx_buffer, tx_buffer, 1); + } + bit_buffer_append_byte(instance->tx_buffer, MF_DESFIRE_CMD_ISO_LE); + } + Iso14443_4aError iso14443_4a_error = iso14443_4a_poller_send_block( - instance->iso14443_4a_poller, tx_buffer, instance->rx_buffer); + instance->iso14443_4a_poller, instance->tx_buffer, instance->rx_buffer); if(iso14443_4a_error != Iso14443_4aErrorNone) { error = mf_desfire_process_error(iso14443_4a_error); @@ -56,16 +83,38 @@ MfDesfireError mf_desfire_poller_send_chunks( } bit_buffer_reset(instance->tx_buffer); - 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)); - } else { - bit_buffer_reset(rx_buffer); + if(instance->command_mode == MfDesfirePollerCommandModeNative) { + bit_buffer_append_byte(instance->tx_buffer, MF_DESFIRE_STATUS_ADDITIONAL_FRAME); + } else if(instance->command_mode == MfDesfirePollerCommandModeIsoWrapped) { + bit_buffer_append_byte(instance->tx_buffer, MF_DESFIRE_CMD_ISO_CLA); + bit_buffer_append_byte(instance->tx_buffer, MF_DESFIRE_STATUS_ADDITIONAL_FRAME); + bit_buffer_append_byte(instance->tx_buffer, MF_DESFIRE_CMD_ISO_P1); + bit_buffer_append_byte(instance->tx_buffer, MF_DESFIRE_CMD_ISO_P2); + bit_buffer_append_byte(instance->tx_buffer, MF_DESFIRE_CMD_ISO_LE); } - while( - bit_buffer_starts_with_byte(instance->rx_buffer, MF_DESFIRE_STATUS_ADDITIONAL_FRAME)) { + size_t response_len = bit_buffer_get_size_bytes(instance->rx_buffer); + status_code = MF_DESFIRE_STATUS_LENGTH_ERROR; + bit_buffer_reset(rx_buffer); + if(instance->command_mode == MfDesfirePollerCommandModeNative && + response_len >= sizeof(uint8_t)) { + status_code = bit_buffer_get_byte(instance->rx_buffer, 0); + if(response_len > sizeof(uint8_t)) { + bit_buffer_copy_right(rx_buffer, instance->rx_buffer, sizeof(uint8_t)); + } + } else if( + instance->command_mode == MfDesfirePollerCommandModeIsoWrapped && + response_len >= 2 * sizeof(uint8_t) && + bit_buffer_get_byte(instance->rx_buffer, response_len - 2) == + MF_DESFIRE_STATUS_ISO_SW1) { + status_code = bit_buffer_get_byte(instance->rx_buffer, response_len - 1); + if(response_len > 2 * sizeof(uint8_t)) { + bit_buffer_copy_left( + rx_buffer, instance->rx_buffer, response_len - 2 * sizeof(uint8_t)); + } + } + + while(status_code == 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); @@ -78,17 +127,29 @@ MfDesfireError mf_desfire_poller_send_chunks( 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 + 1) { - 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 - 1); + if(instance->command_mode == MfDesfirePollerCommandModeNative) { + status_code = rx_size < 1 ? MF_DESFIRE_STATUS_LENGTH_ERROR : + bit_buffer_get_byte(instance->rx_buffer, 0); + if(rx_size <= rx_capacity_remaining + 1) { + 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 - 1); + } + } else if(instance->command_mode == MfDesfirePollerCommandModeIsoWrapped) { + status_code = rx_size < 2 ? MF_DESFIRE_STATUS_LENGTH_ERROR : + bit_buffer_get_byte(instance->rx_buffer, rx_size - 1); + if(rx_size <= rx_capacity_remaining + 2) { + bit_buffer_set_size_bytes(instance->rx_buffer, rx_size - 2); + bit_buffer_append(rx_buffer, instance->rx_buffer); + } else { + FURI_LOG_W(TAG, "RX buffer overflow: ignoring %zu bytes", rx_size - 2); + } } } } 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); + error = mf_desfire_process_status_code(status_code); } return error; diff --git a/lib/nfc/protocols/mf_desfire/mf_desfire_poller_i.h b/lib/nfc/protocols/mf_desfire/mf_desfire_poller_i.h index 19e38bebb..135e2ec98 100644 --- a/lib/nfc/protocols/mf_desfire/mf_desfire_poller_i.h +++ b/lib/nfc/protocols/mf_desfire/mf_desfire_poller_i.h @@ -30,6 +30,7 @@ typedef enum { struct MfDesfirePoller { Iso14443_4aPoller* iso14443_4a_poller; + MfDesfirePollerCommandMode command_mode; MfDesfirePollerSessionState session_state; MfDesfirePollerState state; MfDesfireError error; diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index b5d8e7430..f39f80c26 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -2654,6 +2654,7 @@ Function,+,mf_desfire_poller_read_key_versions,MfDesfireError,"MfDesfirePoller*, Function,+,mf_desfire_poller_read_version,MfDesfireError,"MfDesfirePoller*, MfDesfireVersion*" Function,+,mf_desfire_poller_select_application,MfDesfireError,"MfDesfirePoller*, const MfDesfireApplicationId*" Function,+,mf_desfire_poller_send_chunks,MfDesfireError,"MfDesfirePoller*, const BitBuffer*, BitBuffer*" +Function,+,mf_desfire_poller_set_command_mode,void,"MfDesfirePoller*, MfDesfirePollerCommandMode" Function,+,mf_desfire_reset,void,MfDesfireData* Function,+,mf_desfire_save,_Bool,"const MfDesfireData*, FlipperFormat*" Function,+,mf_desfire_set_uid,_Bool,"MfDesfireData*, const uint8_t*, size_t" From da36d40bd73f5ed0da424a40648c2f3d4fc6f366 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 20 Mar 2025 04:11:11 +0000 Subject: [PATCH 37/92] NFC: T4T interact with DESFire without state reset --- .../protocols/type_4_tag/type_4_tag_poller.c | 26 +++---------------- .../type_4_tag/type_4_tag_poller_i.c | 2 ++ 2 files changed, 5 insertions(+), 23 deletions(-) diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_poller.c b/lib/nfc/protocols/type_4_tag/type_4_tag_poller.c index d74b1914b..1fe07d3c2 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag_poller.c +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_poller.c @@ -75,9 +75,7 @@ static NfcCommand type_4_tag_poller_handler_detect_platform(Type4TagPoller* inst } instance->state = Type4TagPollerStateSelectApplication; - // Reset card state so platform-specific commands do not interfere - iso14443_4a_poller_halt(instance->iso14443_4a_poller); - return NfcCommandReset; + return NfcCommandContinue; } static NfcCommand type_4_tag_poller_handler_select_app(Type4TagPoller* instance) { @@ -90,9 +88,6 @@ static NfcCommand type_4_tag_poller_handler_select_app(Type4TagPoller* instance) if(instance->mode == Type4TagPollerModeWrite && instance->error == Type4TagErrorCardUnformatted) { instance->state = Type4TagPollerStateCreateApplication; - // Reset card state so platform-specific commands do not interfere - iso14443_4a_poller_halt(instance->iso14443_4a_poller); - return NfcCommandReset; } else { instance->state = Type4TagPollerStateFailed; } @@ -113,9 +108,6 @@ static NfcCommand type_4_tag_poller_handler_read_cc(Type4TagPoller* instance) { if(instance->mode == Type4TagPollerModeWrite && instance->error == Type4TagErrorCardUnformatted) { instance->state = Type4TagPollerStateCreateCapabilityContainer; - // Reset card state so platform-specific commands do not interfere - iso14443_4a_poller_halt(instance->iso14443_4a_poller); - return NfcCommandReset; } else { instance->state = Type4TagPollerStateFailed; } @@ -141,10 +133,7 @@ static NfcCommand type_4_tag_poller_handler_create_app(Type4TagPoller* instance) instance->error = type_4_tag_poller_create_app(instance); if(instance->error == Type4TagErrorNone) { FURI_LOG_D(TAG, "Create application success"); - // Reset card state so platform-specific commands do not interfere instance->state = Type4TagPollerStateSelectApplication; - iso14443_4a_poller_halt(instance->iso14443_4a_poller); - return NfcCommandReset; } else { FURI_LOG_E(TAG, "Failed to create application"); instance->state = Type4TagPollerStateFailed; @@ -157,10 +146,7 @@ static NfcCommand type_4_tag_poller_handler_create_cc(Type4TagPoller* instance) instance->error = type_4_tag_poller_create_cc(instance); if(instance->error == Type4TagErrorNone) { FURI_LOG_D(TAG, "Create CC success"); - // Reset card state so platform-specific commands do not interfere - instance->state = Type4TagPollerStateSelectApplication; - iso14443_4a_poller_halt(instance->iso14443_4a_poller); - return NfcCommandReset; + instance->state = Type4TagPollerStateReadCapabilityContainer; } else { FURI_LOG_E(TAG, "Failed to create CC"); instance->state = Type4TagPollerStateFailed; @@ -173,10 +159,7 @@ static NfcCommand type_4_tag_poller_handler_create_ndef(Type4TagPoller* instance instance->error = type_4_tag_poller_create_ndef(instance); if(instance->error == Type4TagErrorNone) { FURI_LOG_D(TAG, "Create NDEF success"); - // Reset card state so platform-specific commands do not interfere - instance->state = Type4TagPollerStateSelectApplication; - iso14443_4a_poller_halt(instance->iso14443_4a_poller); - return NfcCommandReset; + instance->state = Type4TagPollerStateWriteNdefMessage; } else { FURI_LOG_E(TAG, "Failed to create NDEF"); instance->state = Type4TagPollerStateFailed; @@ -195,9 +178,6 @@ static NfcCommand type_4_tag_poller_handler_write_ndef(Type4TagPoller* instance) if(instance->mode == Type4TagPollerModeWrite && instance->error == Type4TagErrorCardUnformatted) { instance->state = Type4TagPollerStateCreateNdefMessage; - // Reset card state so platform-specific commands do not interfere - iso14443_4a_poller_halt(instance->iso14443_4a_poller); - return NfcCommandReset; } else { instance->state = Type4TagPollerStateFailed; } diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c b/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c index 3e09e18a9..0a33cbbf8 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c @@ -201,6 +201,7 @@ Type4TagError type_4_tag_poller_detect_platform(Type4TagPoller* instance) { do { FURI_LOG_D(TAG, "Detect DESFire"); MfDesfirePoller* mf_des = mf_desfire_poller.alloc(instance->iso14443_4a_poller); + mf_desfire_poller_set_command_mode(mf_des, MfDesfirePollerCommandModeIsoWrapped); if(mf_desfire_poller.detect(event, mf_des)) { platform = Type4TagPlatformMfDesfire; nfc_device_set_data(device, NfcProtocolMfDesfire, mf_desfire_poller.get_data(mf_des)); @@ -308,6 +309,7 @@ Type4TagError type_4_tag_poller_create_app(Type4TagPoller* instance) { if(instance->data->platform == Type4TagPlatformMfDesfire) { MfDesfirePoller* mf_des = mf_desfire_poller.alloc(instance->iso14443_4a_poller); + mf_desfire_poller_set_command_mode(mf_des, MfDesfirePollerCommandModeIsoWrapped); MfDesfireError mf_des_error; do { From 3cffc69cb55a180f45820317f0431f1ea9e8f518 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 20 Mar 2025 04:23:33 +0000 Subject: [PATCH 38/92] Fix some message strings --- .../protocol_support/type_4_tag/type_4_tag_render.c | 6 +++--- lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/applications/main/nfc/helpers/protocol_support/type_4_tag/type_4_tag_render.c b/applications/main/nfc/helpers/protocol_support/type_4_tag/type_4_tag_render.c index ac1f5bdbc..492bbb457 100644 --- a/applications/main/nfc/helpers/protocol_support/type_4_tag/type_4_tag_render.c +++ b/applications/main/nfc/helpers/protocol_support/type_4_tag/type_4_tag_render.c @@ -15,18 +15,18 @@ void nfc_render_type_4_tag_info( furi_string_cat(str, "\n::::::::::::::::::[Tag Specs]::::::::::::::::::\n"); furi_string_cat_printf( str, "T4T Mapping Version: %u.%u\n", data->t4t_version.major, data->t4t_version.minor); - furi_string_cat_printf(str, "NDEF File ID: 0x%04X\n", data->ndef_file_id); + furi_string_cat_printf(str, "NDEF File ID: %04X\n", data->ndef_file_id); furi_string_cat_printf(str, "Max NDEF Size: %u\n", data->ndef_max_len); furi_string_cat_printf( str, "APDU Sizes: R:%u W:%u\n", data->chunk_max_read, data->chunk_max_write); furi_string_cat_printf( str, - "Read Lock: 0x%02X%s\n", + "Read Lock: %02X%s\n", data->ndef_read_lock, data->ndef_read_lock == 0 ? " (unlocked)" : ""); furi_string_cat_printf( str, - "Write Lock: 0x%02X%s", + "Write Lock: %02X%s", data->ndef_write_lock, data->ndef_write_lock == 0 ? " (unlocked)" : ""); } diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c b/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c index 0a33cbbf8..ee5183ac4 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c @@ -38,7 +38,7 @@ Type4TagError type_4_tag_apdu_trx(Type4TagPoller* instance, BitBuffer* tx_buf, B if(memcmp(status, success, sizeof(status)) == 0) { return Type4TagErrorNone; } else { - FURI_LOG_E(TAG, "APDU failed: 0x%02X%02X", status[0], status[1]); + FURI_LOG_E(TAG, "APDU failed: %02X%02X", status[0], status[1]); return Type4TagErrorApduFailed; } } @@ -263,7 +263,7 @@ Type4TagError type_4_tag_poller_read_cc(Type4TagPoller* instance) { if(error != Type4TagErrorNone) break; instance->data->is_tag_specific = true; - FURI_LOG_D(TAG, "Detected NDEF file ID 0x%04X", instance->data->ndef_file_id); + FURI_LOG_D(TAG, "Detected NDEF file ID %04X", instance->data->ndef_file_id); } while(false); return error; @@ -298,7 +298,7 @@ Type4TagError type_4_tag_poller_read_ndef(Type4TagPoller* instance) { if(error != Type4TagErrorNone) break; FURI_LOG_D( - TAG, "Read %hu bytes from NDEF file 0x%04X", ndef_len, instance->data->ndef_file_id); + TAG, "Read %hu bytes from NDEF file %04X", ndef_len, instance->data->ndef_file_id); } while(false); return error; @@ -396,7 +396,7 @@ Type4TagError type_4_tag_poller_write_ndef(Type4TagPoller* instance) { if(error != Type4TagErrorNone) break; FURI_LOG_D( - TAG, "Wrote %hu bytes from NDEF file 0x%04X", ndef_len, instance->data->ndef_file_id); + TAG, "Wrote %hu bytes to NDEF file %04X", ndef_len, instance->data->ndef_file_id); } while(false); return error; From 3c977b746863eb1f4a671cbcd27e6423ce9e7f35 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 20 Mar 2025 04:24:53 +0000 Subject: [PATCH 39/92] Static define T4T DESFire app ids --- lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c b/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c index ee5183ac4..cb65a8137 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c @@ -9,6 +9,9 @@ #define TAG "Type4TagPoller" +static const MfDesfireApplicationId mf_des_picc_app_id = {.data = {0x00, 0x00, 0x00}}; +static const MfDesfireApplicationId mf_des_ndef_app_id = {.data = {0x10, 0xEE, 0xEE}}; + Type4TagError type_4_tag_apdu_trx(Type4TagPoller* instance, BitBuffer* tx_buf, BitBuffer* rx_buf) { furi_check(instance); @@ -314,15 +317,13 @@ Type4TagError type_4_tag_poller_create_app(Type4TagPoller* instance) { do { // Select PICC (Card) level - MfDesfireApplicationId picc_aid = {{0x00, 0x00, 0x00}}; - mf_des_error = mf_desfire_poller_select_application(mf_des, &picc_aid); + mf_des_error = mf_desfire_poller_select_application(mf_des, &mf_des_picc_app_id); if(mf_des_error != MfDesfireErrorNone) { error = Type4TagErrorProtocol; break; } // Create NDEF application - MfDesfireApplicationId ndef_aid = {{0x10, 0xEE, 0xEE}}; MfDesfireKeySettings key_settings = { .is_master_key_changeable = true, .is_free_directory_list = true, @@ -334,7 +335,7 @@ Type4TagError type_4_tag_poller_create_app(Type4TagPoller* instance) { }; mf_des_error = mf_desfire_poller_create_application( mf_des, - &ndef_aid, + &mf_des_ndef_app_id, &key_settings, TYPE_4_TAG_ISO_DF_ID, type_4_tag_iso_df_name, From e2b7cd2d2cc3b5840dacfad43abcfe20301c244f Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 20 Mar 2025 05:01:50 +0000 Subject: [PATCH 40/92] Simplify return here --- lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c b/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c index cb65a8137..ddb309bad 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c @@ -215,6 +215,7 @@ Type4TagError type_4_tag_poller_detect_platform(Type4TagPoller* instance) { // FIXME: detect NTAG4xx } while(false); + Type4TagError error; if(platform != Type4TagPlatformUnknown) { furi_string_set( instance->data->platform_name_full, @@ -222,14 +223,16 @@ Type4TagError type_4_tag_poller_detect_platform(Type4TagPoller* instance) { furi_string_set( instance->data->platform_name_short, nfc_device_get_name(device, NfcDeviceNameTypeShort)); + error = Type4TagErrorNone; } else { furi_string_reset(instance->data->platform_name_full); furi_string_reset(instance->data->platform_name_short); + error = Type4TagErrorNotSupported; } instance->data->platform = platform; nfc_device_free(device); - return platform != Type4TagPlatformUnknown ? Type4TagErrorNone : Type4TagErrorNotSupported; + return error; } Type4TagError type_4_tag_poller_select_app(Type4TagPoller* instance) { From c4f220625ca20d05bc5190f0108fc23e6cc5825b Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 20 Mar 2025 07:19:54 +0000 Subject: [PATCH 41/92] Fix some edge cases with max NDEF size --- lib/nfc/protocols/type_4_tag/type_4_tag_i.c | 3 ++- lib/nfc/protocols/type_4_tag/type_4_tag_i.h | 2 +- lib/nfc/protocols/type_4_tag/type_4_tag_listener_i.c | 5 +++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_i.c b/lib/nfc/protocols/type_4_tag/type_4_tag_i.c index 889cf635b..69e9aadf9 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag_i.c +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_i.c @@ -50,7 +50,8 @@ void type_4_tag_cc_dump(const Type4TagData* data, uint8_t* buf, size_t len) { sizeof(cc->tlv[0].value.ndef_file_ctrl.file_id), (void*)&cc->tlv[0].value.ndef_file_ctrl.file_id); bit_lib_num_to_bytes_be( - data->is_tag_specific ? data->ndef_max_len : TYPE_4_TAG_DEFAULT_NDEF_SIZE, + sizeof(uint16_t) + + (data->is_tag_specific ? data->ndef_max_len : TYPE_4_TAG_DEFAULT_NDEF_SIZE), sizeof(cc->tlv[0].value.ndef_file_ctrl.max_len), (void*)&cc->tlv[0].value.ndef_file_ctrl.max_len); cc->tlv[0].value.ndef_file_ctrl.read_perm = diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_i.h b/lib/nfc/protocols/type_4_tag/type_4_tag_i.h index 1af7c7f38..18b46679a 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag_i.h +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_i.h @@ -56,7 +56,7 @@ #define TYPE_4_TAG_BUF_SIZE (256U - 3U - 2U) // Read returns 2 byte status trailer, write sends 5 byte command header #define TYPE_4_TAG_CHUNK_LEN MIN(TYPE_4_TAG_BUF_SIZE - 5U, TYPE_4_TAG_ISO_RW_CHUNK_LEN) -#define TYPE_4_TAG_DEFAULT_NDEF_SIZE (2048U) +#define TYPE_4_TAG_DEFAULT_NDEF_SIZE (2048U - sizeof(uint16_t)) extern const uint8_t type_4_tag_iso_mf_name[TYPE_4_TAG_ISO_NAME_LEN]; extern const uint8_t type_4_tag_iso_df_name[TYPE_4_TAG_ISO_NAME_LEN]; diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_listener_i.c b/lib/nfc/protocols/type_4_tag/type_4_tag_listener_i.c index fb0890688..7da6fadc9 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag_listener_i.c +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_listener_i.c @@ -196,8 +196,9 @@ static Type4TagError type_4_tag_listener_iso_write( } if(instance->state == Type4TagListenerStateSelectedNdefMessage) { - if(offset + lc > (instance->data->is_tag_specific ? instance->data->ndef_max_len : - TYPE_4_TAG_DEFAULT_NDEF_SIZE)) { + if(offset + lc > sizeof(uint16_t) + (instance->data->is_tag_specific ? + instance->data->ndef_max_len : + TYPE_4_TAG_DEFAULT_NDEF_SIZE)) { bit_buffer_append_bytes( instance->tx_buffer, type_4_tag_offset_error_apdu, From f01972ea718fb887fc98e7a035c7c26b64f6e09d Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 20 Mar 2025 07:21:36 +0000 Subject: [PATCH 42/92] NFC: Support creating CC and NDEF files on DESFire --- lib/nfc/protocols/mf_desfire/mf_desfire.h | 7 +- .../protocols/mf_desfire/mf_desfire_poller.h | 23 +++- .../mf_desfire/mf_desfire_poller_i.c | 74 ++++++++++- .../type_4_tag/type_4_tag_poller_i.c | 125 +++++++++++++++--- targets/f7/api_symbols.csv | 1 + 5 files changed, 205 insertions(+), 25 deletions(-) diff --git a/lib/nfc/protocols/mf_desfire/mf_desfire.h b/lib/nfc/protocols/mf_desfire/mf_desfire.h index 8eb650320..3cad11473 100644 --- a/lib/nfc/protocols/mf_desfire/mf_desfire.h +++ b/lib/nfc/protocols/mf_desfire/mf_desfire.h @@ -17,7 +17,12 @@ extern "C" { #define MF_DESFIRE_CMD_GET_FILE_IDS (0x6F) #define MF_DESFIRE_CMD_GET_FILE_SETTINGS (0xF5) -#define MF_DESFIRE_CMD_CREATE_APPLICATION (0xCA) +#define MF_DESFIRE_CMD_CREATE_APPLICATION (0xCA) +#define MF_DESFIRE_CMD_CREATE_STD_DATA_FILE (0xCD) +#define MF_DESFIRE_CMD_CREATE_BACKUP_DATA_FILE (0xCB) +#define MF_DESFIRE_CMD_CREATE_VALUE_FILE (0xCC) +#define MF_DESFIRE_CMD_CREATE_LINEAR_RECORD_FILE (0xC1) +#define MF_DESFIRE_CMD_CREATE_CYCLIC_RECORD_FILE (0xC0) #define MF_DESFIRE_CMD_READ_DATA (0xBD) #define MF_DESFIRE_CMD_GET_VALUE (0x6C) diff --git a/lib/nfc/protocols/mf_desfire/mf_desfire_poller.h b/lib/nfc/protocols/mf_desfire/mf_desfire_poller.h index 8dc0513d8..683b12359 100644 --- a/lib/nfc/protocols/mf_desfire/mf_desfire_poller.h +++ b/lib/nfc/protocols/mf_desfire/mf_desfire_poller.h @@ -220,9 +220,9 @@ MfDesfireError mf_desfire_poller_read_file_settings_multi( * @param[in, out] instance pointer to the instance to be used in the transaction. * @param[in] id pointer to the application id for the new application. * @param[in] key_settings pointer to the key settings for the new application. - * @param[in] iso_df_id optional identifier for the new application. - * @param[in] iso_df_name optional name for the new application. - * @param[in] iso_df_name_len length of the optional application name. + * @param[in] iso_df_id optional iso identifier for the new application. + * @param[in] iso_df_name optional iso name for the new application. + * @param[in] iso_df_name_len length of the optional iso application name. * @return MfDesfireErrorNone on success, an error code on failure. */ MfDesfireError mf_desfire_poller_create_application( @@ -233,6 +233,23 @@ MfDesfireError mf_desfire_poller_create_application( const uint8_t* iso_df_name, uint8_t iso_df_name_len); +/** + * @brief Create File on MfDesfire card. + * + * Must ONLY be used inside the callback function. + * + * @param[in, out] instance pointer to the instance to be used in the transaction. + * @param[in] id file id for the new file. + * @param[in] data pointer to the file settings for the new file. + * @param[in] iso_ef_id optional iso identifier for the new file. + * @return MfDesfireErrorNone on success, an error code on failure. + */ +MfDesfireError mf_desfire_poller_create_file( + MfDesfirePoller* instance, + MfDesfireFileId id, + const MfDesfireFileSettings* data, + uint16_t iso_ef_id); + /** * @brief Read file data on MfDesfire card. * diff --git a/lib/nfc/protocols/mf_desfire/mf_desfire_poller_i.c b/lib/nfc/protocols/mf_desfire/mf_desfire_poller_i.c index 57210b9c5..21d88623f 100644 --- a/lib/nfc/protocols/mf_desfire/mf_desfire_poller_i.c +++ b/lib/nfc/protocols/mf_desfire/mf_desfire_poller_i.c @@ -405,6 +405,7 @@ MfDesfireError mf_desfire_poller_create_application( const uint8_t* iso_df_name, uint8_t iso_df_name_len) { furi_check(instance); + furi_check(key_settings); bit_buffer_reset(instance->input_buffer); bit_buffer_append_byte(instance->input_buffer, MF_DESFIRE_CMD_CREATE_APPLICATION); @@ -418,9 +419,9 @@ MfDesfireError mf_desfire_poller_create_application( ks2 |= (1 << 5); // Mark file id present bit_buffer_set_byte(instance->input_buffer, ks2_pos, ks2); - uint8_t iso_file_id_le[sizeof(iso_df_id)]; - bit_lib_num_to_bytes_le(iso_df_id, sizeof(iso_file_id_le), iso_file_id_le); - bit_buffer_append_bytes(instance->input_buffer, iso_file_id_le, sizeof(iso_file_id_le)); + uint8_t iso_df_id_le[sizeof(iso_df_id)]; + bit_lib_num_to_bytes_le(iso_df_id, sizeof(iso_df_id_le), iso_df_id_le); + bit_buffer_append_bytes(instance->input_buffer, iso_df_id_le, sizeof(iso_df_id_le)); bit_buffer_append_bytes(instance->input_buffer, iso_df_name, iso_df_name_len); } @@ -431,6 +432,73 @@ MfDesfireError mf_desfire_poller_create_application( return error; } +MfDesfireError mf_desfire_poller_create_file( + MfDesfirePoller* instance, + MfDesfireFileId id, + const MfDesfireFileSettings* data, + uint16_t iso_ef_id) { + furi_check(instance); + furi_check(data); + + bit_buffer_reset(instance->input_buffer); + bit_buffer_append_byte( + instance->input_buffer, + data->type == MfDesfireFileTypeStandard ? MF_DESFIRE_CMD_CREATE_STD_DATA_FILE : + data->type == MfDesfireFileTypeBackup ? MF_DESFIRE_CMD_CREATE_BACKUP_DATA_FILE : + data->type == MfDesfireFileTypeValue ? MF_DESFIRE_CMD_CREATE_VALUE_FILE : + data->type == MfDesfireFileTypeLinearRecord ? MF_DESFIRE_CMD_CREATE_LINEAR_RECORD_FILE : + data->type == MfDesfireFileTypeCyclicRecord ? MF_DESFIRE_CMD_CREATE_CYCLIC_RECORD_FILE : + 0x00); + bit_buffer_append_byte(instance->input_buffer, id); + if(iso_ef_id) { + uint8_t iso_ef_id_le[sizeof(iso_ef_id)]; + bit_lib_num_to_bytes_le(iso_ef_id, sizeof(iso_ef_id_le), iso_ef_id_le); + bit_buffer_append_bytes(instance->input_buffer, iso_ef_id_le, sizeof(iso_ef_id_le)); + } + bit_buffer_append_byte(instance->input_buffer, data->comm); + bit_buffer_append_bytes( + instance->input_buffer, + (const uint8_t*)data->access_rights, + sizeof(MfDesfireFileAccessRights) * data->access_rights_len); + + if(data->type == MfDesfireFileTypeStandard || data->type == MfDesfireFileTypeBackup) { + uint8_t data_size_le[3 * sizeof(uint8_t)]; + bit_lib_num_to_bytes_le(data->data.size, sizeof(data_size_le), data_size_le); + bit_buffer_append_bytes(instance->input_buffer, data_size_le, sizeof(data_size_le)); + + } else if(data->type == MfDesfireFileTypeValue) { + uint8_t lo_limit_le[sizeof(data->value.lo_limit)]; + bit_lib_num_to_bytes_le(data->value.lo_limit, sizeof(lo_limit_le), lo_limit_le); + bit_buffer_append_bytes(instance->input_buffer, lo_limit_le, sizeof(lo_limit_le)); + + uint8_t hi_limit_le[sizeof(data->value.hi_limit)]; + bit_lib_num_to_bytes_le(data->value.hi_limit, sizeof(hi_limit_le), hi_limit_le); + bit_buffer_append_bytes(instance->input_buffer, hi_limit_le, sizeof(hi_limit_le)); + + uint8_t value_le[sizeof(data->value.limited_credit_value)]; + bit_lib_num_to_bytes_le(data->value.limited_credit_value, sizeof(value_le), value_le); + bit_buffer_append_bytes(instance->input_buffer, value_le, sizeof(value_le)); + + bit_buffer_append_byte(instance->input_buffer, data->value.limited_credit_enabled); + + } else if( + data->type == MfDesfireFileTypeLinearRecord || + data->type == MfDesfireFileTypeCyclicRecord) { + uint8_t record_size_le[3 * sizeof(uint8_t)]; + bit_lib_num_to_bytes_le(data->record.size, sizeof(record_size_le), record_size_le); + bit_buffer_append_bytes(instance->input_buffer, record_size_le, sizeof(record_size_le)); + + uint8_t record_max_le[3 * sizeof(uint8_t)]; + bit_lib_num_to_bytes_le(data->record.max, sizeof(record_max_le), record_max_le); + bit_buffer_append_bytes(instance->input_buffer, record_max_le, sizeof(record_max_le)); + } + + MfDesfireError error = + mf_desfire_poller_send_chunks(instance, instance->input_buffer, instance->result_buffer); + + return error; +} + static MfDesfireError mf_desfire_poller_read_file( MfDesfirePoller* instance, MfDesfireFileId id, diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c b/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c index ddb309bad..e9555fcf8 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c @@ -10,7 +10,31 @@ #define TAG "Type4TagPoller" static const MfDesfireApplicationId mf_des_picc_app_id = {.data = {0x00, 0x00, 0x00}}; -static const MfDesfireApplicationId mf_des_ndef_app_id = {.data = {0x10, 0xEE, 0xEE}}; +static const MfDesfireApplicationId mf_des_t4t_app_id = {.data = {0x10, 0xEE, 0xEE}}; +static const MfDesfireKeySettings mf_des_t4t_app_key_settings = { + .is_master_key_changeable = true, + .is_free_directory_list = true, + .is_free_create_delete = true, + .is_config_changeable = true, + .change_key_id = 0, + .max_keys = 1, + .flags = 0, +}; +#define MF_DES_T4T_CC_FILE_ID (0x01) +static const MfDesfireFileSettings mf_des_t4t_cc_file = { + .type = MfDesfireFileTypeStandard, + .comm = MfDesfireFileCommunicationSettingsPlaintext, + .access_rights[0] = 0xEEEE, + .access_rights_len = 1, + .data.size = TYPE_4_TAG_T4T_CC_MIN_SIZE, +}; +#define MF_DES_T4T_NDEF_FILE_ID (0x02) +static const MfDesfireFileSettings mf_des_t4t_ndef_file_default = { + .type = MfDesfireFileTypeStandard, + .comm = MfDesfireFileCommunicationSettingsPlaintext, + .access_rights[0] = 0xEEE0, + .access_rights_len = 1, +}; Type4TagError type_4_tag_apdu_trx(Type4TagPoller* instance, BitBuffer* tx_buf, BitBuffer* rx_buf) { furi_check(instance); @@ -319,27 +343,18 @@ Type4TagError type_4_tag_poller_create_app(Type4TagPoller* instance) { MfDesfireError mf_des_error; do { - // Select PICC (Card) level + FURI_LOG_D(TAG, "Select DESFire PICC"); mf_des_error = mf_desfire_poller_select_application(mf_des, &mf_des_picc_app_id); if(mf_des_error != MfDesfireErrorNone) { error = Type4TagErrorProtocol; break; } - // Create NDEF application - MfDesfireKeySettings key_settings = { - .is_master_key_changeable = true, - .is_free_directory_list = true, - .is_free_create_delete = true, - .is_config_changeable = true, - .change_key_id = 0, - .max_keys = 1, - .flags = 0, - }; + FURI_LOG_D(TAG, "Create DESFire T4T app"); mf_des_error = mf_desfire_poller_create_application( mf_des, - &mf_des_ndef_app_id, - &key_settings, + &mf_des_t4t_app_id, + &mf_des_t4t_app_key_settings, TYPE_4_TAG_ISO_DF_ID, type_4_tag_iso_df_name, sizeof(type_4_tag_iso_df_name)); @@ -363,13 +378,87 @@ Type4TagError type_4_tag_poller_create_app(Type4TagPoller* instance) { } Type4TagError type_4_tag_poller_create_cc(Type4TagPoller* instance) { - UNUSED(instance); - return Type4TagErrorNotSupported; + Type4TagError error = Type4TagErrorNotSupported; + + if(instance->data->platform == Type4TagPlatformMfDesfire) { + MfDesfirePoller* mf_des = mf_desfire_poller.alloc(instance->iso14443_4a_poller); + mf_desfire_poller_set_command_mode(mf_des, MfDesfirePollerCommandModeIsoWrapped); + MfDesfireError mf_des_error; + + do { + FURI_LOG_D(TAG, "Create DESFire CC"); + mf_des_error = mf_desfire_poller_create_file( + mf_des, MF_DES_T4T_CC_FILE_ID, &mf_des_t4t_cc_file, TYPE_4_TAG_T4T_CC_EF_ID); + if(mf_des_error != MfDesfireErrorNone) { + if(mf_des_error != MfDesfireErrorNotPresent && + mf_des_error != MfDesfireErrorTimeout) { + error = Type4TagErrorCardLocked; + } else { + error = Type4TagErrorProtocol; + } + break; + } + + FURI_LOG_D(TAG, "Select CC"); + error = type_5_tag_poller_iso_select_file(instance, TYPE_4_TAG_T4T_CC_EF_ID); + if(error != Type4TagErrorNone) break; + + FURI_LOG_D(TAG, "Write DESFire CC"); + instance->data->t4t_version.value = TYPE_4_TAG_T4T_CC_VNO; + instance->data->chunk_max_read = 0x3A; + instance->data->chunk_max_write = 0x34; + instance->data->ndef_file_id = TYPE_4_TAG_T4T_NDEF_EF_ID; + instance->data->ndef_max_len = TYPE_4_TAG_DEFAULT_NDEF_SIZE; + instance->data->ndef_read_lock = TYPE_4_TAG_T4T_CC_RW_LOCK_NONE; + instance->data->ndef_write_lock = TYPE_4_TAG_T4T_CC_RW_LOCK_NONE; + instance->data->is_tag_specific = true; + uint8_t cc_buf[TYPE_4_TAG_T4T_CC_MIN_SIZE]; + type_4_tag_cc_dump(instance->data, cc_buf, sizeof(cc_buf)); + error = type_5_tag_poller_iso_write(instance, 0, sizeof(cc_buf), cc_buf); + if(error != Type4TagErrorNone) break; + + error = Type4TagErrorNone; + } while(false); + + mf_desfire_poller.free(mf_des); + } + + return error; } Type4TagError type_4_tag_poller_create_ndef(Type4TagPoller* instance) { - UNUSED(instance); - return Type4TagErrorNotSupported; + Type4TagError error = Type4TagErrorNotSupported; + + if(instance->data->platform == Type4TagPlatformMfDesfire) { + MfDesfirePoller* mf_des = mf_desfire_poller.alloc(instance->iso14443_4a_poller); + mf_desfire_poller_set_command_mode(mf_des, MfDesfirePollerCommandModeIsoWrapped); + MfDesfireError mf_des_error; + + do { + FURI_LOG_D(TAG, "Create DESFire NDEF"); + MfDesfireFileSettings mf_des_t4t_ndef_file = mf_des_t4t_ndef_file_default; + mf_des_t4t_ndef_file.data.size = sizeof(uint16_t) + (instance->data->is_tag_specific ? + instance->data->ndef_max_len : + TYPE_4_TAG_DEFAULT_NDEF_SIZE); + mf_des_error = mf_desfire_poller_create_file( + mf_des, MF_DES_T4T_NDEF_FILE_ID, &mf_des_t4t_ndef_file, TYPE_4_TAG_T4T_NDEF_EF_ID); + if(mf_des_error != MfDesfireErrorNone) { + if(mf_des_error != MfDesfireErrorNotPresent && + mf_des_error != MfDesfireErrorTimeout) { + error = Type4TagErrorCardLocked; + } else { + error = Type4TagErrorProtocol; + } + break; + } + + error = Type4TagErrorNone; + } while(false); + + mf_desfire_poller.free(mf_des); + } + + return error; } Type4TagError type_4_tag_poller_write_ndef(Type4TagPoller* instance) { diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index f39f80c26..17bb37266 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -2637,6 +2637,7 @@ Function,+,mf_desfire_get_uid,const uint8_t*,"const MfDesfireData*, size_t*" Function,+,mf_desfire_is_equal,_Bool,"const MfDesfireData*, const MfDesfireData*" Function,+,mf_desfire_load,_Bool,"MfDesfireData*, FlipperFormat*, uint32_t" Function,+,mf_desfire_poller_create_application,MfDesfireError,"MfDesfirePoller*, const MfDesfireApplicationId*, const MfDesfireKeySettings*, uint16_t, const uint8_t*, uint8_t" +Function,+,mf_desfire_poller_create_file,MfDesfireError,"MfDesfirePoller*, MfDesfireFileId, const MfDesfireFileSettings*, uint16_t" Function,+,mf_desfire_poller_read_application,MfDesfireError,"MfDesfirePoller*, MfDesfireApplication*" Function,+,mf_desfire_poller_read_application_ids,MfDesfireError,"MfDesfirePoller*, SimpleArray*" Function,+,mf_desfire_poller_read_applications,MfDesfireError,"MfDesfirePoller*, const SimpleArray*, SimpleArray*" From 5ed0dc20660901d3cbf767d1a9cb04543596967b Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Fri, 21 Mar 2025 05:08:29 +0000 Subject: [PATCH 43/92] GUI: Add submenu_remove_item() --- applications/services/gui/modules/submenu.c | 19 +++++++++++++++++++ applications/services/gui/modules/submenu.h | 8 ++++++++ targets/f7/api_symbols.csv | 1 + 3 files changed, 28 insertions(+) diff --git a/applications/services/gui/modules/submenu.c b/applications/services/gui/modules/submenu.c index 5b1bdccda..cdc1ecc28 100644 --- a/applications/services/gui/modules/submenu.c +++ b/applications/services/gui/modules/submenu.c @@ -235,6 +235,25 @@ void submenu_change_item_label(Submenu* submenu, uint32_t index, const char* lab true); } +void submenu_remove_item(Submenu* submenu, uint32_t index) { + furi_check(submenu); + + with_view_model( + submenu->view, + SubmenuModel * model, + { + SubmenuItemArray_it_t it; + for(SubmenuItemArray_it(it, model->items); !SubmenuItemArray_end_p(it); + SubmenuItemArray_next(it)) { + if(index == SubmenuItemArray_cref(it)->index) { + SubmenuItemArray_remove(model->items, it); + break; + } + } + }, + true); +} + void submenu_reset(Submenu* submenu) { furi_check(submenu); diff --git a/applications/services/gui/modules/submenu.h b/applications/services/gui/modules/submenu.h index e435f94a2..0408d52c0 100644 --- a/applications/services/gui/modules/submenu.h +++ b/applications/services/gui/modules/submenu.h @@ -61,6 +61,14 @@ void submenu_add_item( */ void submenu_change_item_label(Submenu* submenu, uint32_t index, const char* label); +/** Remove item from submenu + * + * @param submenu Submenu instance + * @param index menu item index, used for callback, may be + * the same with other items, first one is removed + */ +void submenu_remove_item(Submenu* submenu, uint32_t index); + /** Remove all items from submenu * * @param submenu Submenu instance diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index 17bb37266..71d065f75 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -3582,6 +3582,7 @@ Function,+,submenu_change_item_label,void,"Submenu*, uint32_t, const char*" Function,+,submenu_free,void,Submenu* Function,+,submenu_get_selected_item,uint32_t,Submenu* Function,+,submenu_get_view,View*,Submenu* +Function,+,submenu_remove_item,void,"Submenu*, uint32_t" Function,+,submenu_reset,void,Submenu* Function,+,submenu_set_header,void,"Submenu*, const char*" Function,+,submenu_set_selected_item,void,"Submenu*, uint32_t" From 1943661ea3611336f33d9942cc3b95ebcfc55564 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Fri, 21 Mar 2025 06:02:08 +0000 Subject: [PATCH 44/92] NFC: Refactor Write actions into ProtocolSupport --- .../helpers/protocol_support/felica/felica.c | 5 + .../iso14443_3a/iso14443_3a.c | 5 + .../iso14443_3b/iso14443_3b.c | 5 + .../iso14443_4a/iso14443_4a.c | 5 + .../iso14443_4b/iso14443_4b.c | 5 + .../protocol_support/iso15693_3/iso15693_3.c | 5 + .../protocol_support/mf_classic/mf_classic.c | 93 ++++++-- .../protocol_support/mf_desfire/mf_desfire.c | 5 + .../protocol_support/mf_plus/mf_plus.c | 5 + .../mf_ultralight/mf_ultralight.c | 75 +++++-- .../protocol_support/nfc_protocol_support.c | 203 ++++++++++++++++++ .../nfc_protocol_support_base.h | 9 + .../nfc_protocol_support_common.h | 2 + .../nfc_protocol_support_gui_common.h | 1 + .../nfc/helpers/protocol_support/slix/slix.c | 5 + .../helpers/protocol_support/st25tb/st25tb.c | 5 + .../protocol_support/type_4_tag/type_4_tag.c | 82 ++++--- .../main/nfc/scenes/nfc_scene_config.h | 13 +- .../nfc_scene_mf_classic_write_initial.c | 148 ------------- .../nfc_scene_mf_classic_write_initial_fail.c | 62 ------ ...c_scene_mf_classic_write_initial_success.c | 43 ---- ...cene_mf_classic_write_initial_wrong_card.c | 57 ----- .../scenes/nfc_scene_mf_ultralight_write.c | 120 ----------- .../nfc_scene_mf_ultralight_write_fail.c | 67 ------ .../nfc_scene_mf_ultralight_write_success.c | 46 ---- .../nfc_scene_mf_ultralight_wrong_card.c | 58 ----- .../nfc/scenes/nfc_scene_type_4_tag_write.c | 105 --------- .../scenes/nfc_scene_type_4_tag_write_fail.c | 72 ------- .../nfc_scene_type_4_tag_write_success.c | 46 ---- .../main/nfc/scenes/nfc_scene_write.c | 13 ++ 30 files changed, 463 insertions(+), 902 deletions(-) delete mode 100644 applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial.c delete mode 100644 applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial_fail.c delete mode 100644 applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial_success.c delete mode 100644 applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial_wrong_card.c delete mode 100644 applications/main/nfc/scenes/nfc_scene_mf_ultralight_write.c delete mode 100644 applications/main/nfc/scenes/nfc_scene_mf_ultralight_write_fail.c delete mode 100644 applications/main/nfc/scenes/nfc_scene_mf_ultralight_write_success.c delete mode 100644 applications/main/nfc/scenes/nfc_scene_mf_ultralight_wrong_card.c delete mode 100644 applications/main/nfc/scenes/nfc_scene_type_4_tag_write.c delete mode 100644 applications/main/nfc/scenes/nfc_scene_type_4_tag_write_fail.c delete mode 100644 applications/main/nfc/scenes/nfc_scene_type_4_tag_write_success.c create mode 100644 applications/main/nfc/scenes/nfc_scene_write.c diff --git a/applications/main/nfc/helpers/protocol_support/felica/felica.c b/applications/main/nfc/helpers/protocol_support/felica/felica.c index 561cd4d2e..7717396a3 100644 --- a/applications/main/nfc/helpers/protocol_support/felica/felica.c +++ b/applications/main/nfc/helpers/protocol_support/felica/felica.c @@ -213,4 +213,9 @@ const NfcProtocolSupportBase nfc_protocol_support_felica = { .on_enter = nfc_scene_emulate_on_enter_felica, .on_event = nfc_protocol_support_common_on_event_empty, }, + .scene_write = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, + }, }; diff --git a/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a.c b/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a.c index 99e211301..9d6dba5a7 100644 --- a/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a.c +++ b/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a.c @@ -144,4 +144,9 @@ const NfcProtocolSupportBase nfc_protocol_support_iso14443_3a = { .on_enter = nfc_scene_emulate_on_enter_iso14443_3a, .on_event = nfc_protocol_support_common_on_event_empty, }, + .scene_write = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, + }, }; diff --git a/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b.c b/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b.c index 43b541111..49da93d7b 100644 --- a/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b.c +++ b/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b.c @@ -111,4 +111,9 @@ const NfcProtocolSupportBase nfc_protocol_support_iso14443_3b = { .on_enter = nfc_protocol_support_common_on_enter_empty, .on_event = nfc_protocol_support_common_on_event_empty, }, + .scene_write = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, + }, }; diff --git a/applications/main/nfc/helpers/protocol_support/iso14443_4a/iso14443_4a.c b/applications/main/nfc/helpers/protocol_support/iso14443_4a/iso14443_4a.c index 17435ccd4..190c26e87 100644 --- a/applications/main/nfc/helpers/protocol_support/iso14443_4a/iso14443_4a.c +++ b/applications/main/nfc/helpers/protocol_support/iso14443_4a/iso14443_4a.c @@ -147,4 +147,9 @@ const NfcProtocolSupportBase nfc_protocol_support_iso14443_4a = { .on_enter = nfc_scene_emulate_on_enter_iso14443_4a, .on_event = nfc_protocol_support_common_on_event_empty, }, + .scene_write = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, + }, }; diff --git a/applications/main/nfc/helpers/protocol_support/iso14443_4b/iso14443_4b.c b/applications/main/nfc/helpers/protocol_support/iso14443_4b/iso14443_4b.c index 8038e0491..8fd823f21 100644 --- a/applications/main/nfc/helpers/protocol_support/iso14443_4b/iso14443_4b.c +++ b/applications/main/nfc/helpers/protocol_support/iso14443_4b/iso14443_4b.c @@ -116,4 +116,9 @@ const NfcProtocolSupportBase nfc_protocol_support_iso14443_4b = { .on_enter = nfc_protocol_support_common_on_enter_empty, .on_event = nfc_protocol_support_common_on_event_empty, }, + .scene_write = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, + }, }; diff --git a/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3.c b/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3.c index 7efd102f1..ffd6971e0 100644 --- a/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3.c +++ b/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3.c @@ -161,4 +161,9 @@ const NfcProtocolSupportBase nfc_protocol_support_iso15693_3 = { .on_enter = nfc_scene_emulate_on_enter_iso15693_3, .on_event = nfc_protocol_support_common_on_event_empty, }, + .scene_write = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, + }, }; diff --git a/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c b/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c index 6f7be7f4c..acc641c34 100644 --- a/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c +++ b/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c @@ -12,9 +12,8 @@ enum { SubmenuIndexDetectReader = SubmenuIndexCommonMax, - SubmenuIndexWrite, + SubmenuIndexDictAttack, SubmenuIndexUpdate, - SubmenuIndexDictAttack }; static void nfc_scene_info_on_enter_mf_classic(NfcApp* instance) { @@ -114,6 +113,9 @@ static void nfc_scene_read_menu_on_enter_mf_classic(NfcApp* instance) { Submenu* submenu = instance->submenu; const MfClassicData* data = nfc_device_get_data(instance->nfc_device, NfcProtocolMfClassic); + // Doesn't make sense to show "Write to Initial Card" right after reading + submenu_remove_item(submenu, SubmenuIndexCommonWrite); + if(!mf_classic_is_card_read(data)) { submenu_add_item( submenu, @@ -152,6 +154,8 @@ static void nfc_scene_saved_menu_on_enter_mf_classic(NfcApp* instance) { Submenu* submenu = instance->submenu; const MfClassicData* data = nfc_device_get_data(instance->nfc_device, NfcProtocolMfClassic); + submenu_change_item_label(submenu, SubmenuIndexCommonWrite, "Write to Initial Card"); + if(!mf_classic_is_card_read(data)) { submenu_add_item( submenu, @@ -167,12 +171,6 @@ static void nfc_scene_saved_menu_on_enter_mf_classic(NfcApp* instance) { nfc_protocol_support_common_submenu_callback, instance); } - submenu_add_item( - submenu, - "Write to Initial Card", - SubmenuIndexWrite, - nfc_protocol_support_common_submenu_callback, - instance); submenu_add_item( submenu, @@ -218,18 +216,15 @@ static bool nfc_scene_saved_menu_on_event_mf_classic(NfcApp* instance, SceneMana if(event.event == SubmenuIndexDetectReader) { scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicDetectReader); consumed = true; - } else if(event.event == SubmenuIndexWrite) { - scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicWriteInitial); - consumed = true; - } else if(event.event == SubmenuIndexUpdate) { - scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicUpdateInitial); - consumed = true; } else if(event.event == SubmenuIndexDictAttack) { if(!scene_manager_search_and_switch_to_previous_scene( instance->scene_manager, NfcSceneMfClassicDictAttack)) { scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicDictAttack); } consumed = true; + } else if(event.event == SubmenuIndexUpdate) { + scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicUpdateInitial); + consumed = true; } } @@ -249,8 +244,71 @@ static bool nfc_scene_save_name_on_event_mf_classic(NfcApp* instance, SceneManag return consumed; } +static NfcCommand + nfc_scene_write_poller_callback_mf_classic(NfcGenericEvent event, void* context) { + furi_assert(event.protocol == NfcProtocolMfClassic); + + NfcApp* instance = context; + MfClassicPollerEvent* mfc_event = event.event_data; + NfcCommand command = NfcCommandContinue; + const MfClassicData* write_data = + nfc_device_get_data(instance->nfc_device, NfcProtocolMfClassic); + + if(mfc_event->type == MfClassicPollerEventTypeCardDetected) { + furi_string_reset(instance->text_box_store); + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventCardDetected); + } else if(mfc_event->type == MfClassicPollerEventTypeCardLost) { + furi_string_set(instance->text_box_store, "Use the source\ncard only"); + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventCardLost); + } else if(mfc_event->type == MfClassicPollerEventTypeRequestMode) { + const MfClassicData* tag_data = nfc_poller_get_data(instance->poller); + if(iso14443_3a_is_equal(tag_data->iso14443_3a_data, write_data->iso14443_3a_data)) { + mfc_event->data->poller_mode.mode = MfClassicPollerModeWrite; + } else { + furi_string_set( + instance->text_box_store, "Use source card!\nTo write blanks\nuse NFC Magic app"); + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventWrongCard); + command = NfcCommandStop; + } + } else if(mfc_event->type == MfClassicPollerEventTypeRequestSectorTrailer) { + uint8_t sector = mfc_event->data->sec_tr_data.sector_num; + uint8_t sec_tr = mf_classic_get_sector_trailer_num_by_sector(sector); + if(mf_classic_is_block_read(write_data, sec_tr)) { + mfc_event->data->sec_tr_data.sector_trailer = write_data->block[sec_tr]; + mfc_event->data->sec_tr_data.sector_trailer_provided = true; + } else { + mfc_event->data->sec_tr_data.sector_trailer_provided = false; + } + } else if(mfc_event->type == MfClassicPollerEventTypeRequestWriteBlock) { + uint8_t block_num = mfc_event->data->write_block_data.block_num; + if(mf_classic_is_block_read(write_data, block_num)) { + mfc_event->data->write_block_data.write_block = write_data->block[block_num]; + mfc_event->data->write_block_data.write_block_provided = true; + } else { + mfc_event->data->write_block_data.write_block_provided = false; + } + } else if(mfc_event->type == MfClassicPollerEventTypeSuccess) { + furi_string_reset(instance->text_box_store); + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess); + command = NfcCommandStop; + } else if(mfc_event->type == MfClassicPollerEventTypeFail) { + furi_string_set(instance->text_box_store, "Not all sectors\nwere written\ncorrectly"); + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerFailure); + command = NfcCommandStop; + } + + return command; +} + +static void nfc_scene_write_on_enter_mf_classic(NfcApp* instance) { + instance->poller = nfc_poller_alloc(instance->nfc, NfcProtocolMfClassic); + nfc_poller_start(instance->poller, nfc_scene_write_poller_callback_mf_classic, instance); + furi_string_set(instance->text_box_store, "Use the source\ncard only"); +} + const NfcProtocolSupportBase nfc_protocol_support_mf_classic = { - .features = NfcProtocolFeatureEmulateFull | NfcProtocolFeatureMoreInfo, + .features = NfcProtocolFeatureEmulateFull | NfcProtocolFeatureMoreInfo | + NfcProtocolFeatureWrite, .scene_info = { @@ -292,4 +350,9 @@ const NfcProtocolSupportBase nfc_protocol_support_mf_classic = { .on_enter = nfc_scene_emulate_on_enter_mf_classic, .on_event = nfc_protocol_support_common_on_event_empty, }, + .scene_write = + { + .on_enter = nfc_scene_write_on_enter_mf_classic, + .on_event = nfc_protocol_support_common_on_event_empty, + }, }; diff --git a/applications/main/nfc/helpers/protocol_support/mf_desfire/mf_desfire.c b/applications/main/nfc/helpers/protocol_support/mf_desfire/mf_desfire.c index deba1bca2..bbca076f7 100644 --- a/applications/main/nfc/helpers/protocol_support/mf_desfire/mf_desfire.c +++ b/applications/main/nfc/helpers/protocol_support/mf_desfire/mf_desfire.c @@ -124,4 +124,9 @@ const NfcProtocolSupportBase nfc_protocol_support_mf_desfire = { .on_enter = nfc_scene_emulate_on_enter_mf_desfire, .on_event = nfc_protocol_support_common_on_event_empty, }, + .scene_write = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, + }, }; diff --git a/applications/main/nfc/helpers/protocol_support/mf_plus/mf_plus.c b/applications/main/nfc/helpers/protocol_support/mf_plus/mf_plus.c index c7b36e21e..e791eb291 100644 --- a/applications/main/nfc/helpers/protocol_support/mf_plus/mf_plus.c +++ b/applications/main/nfc/helpers/protocol_support/mf_plus/mf_plus.c @@ -120,4 +120,9 @@ const NfcProtocolSupportBase nfc_protocol_support_mf_plus = { .on_enter = nfc_scene_emulate_on_enter_mf_plus, .on_event = nfc_protocol_support_common_on_event_empty, }, + .scene_write = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, + }, }; diff --git a/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c b/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c index 3adf2a1f5..34354741d 100644 --- a/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c +++ b/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c @@ -14,7 +14,6 @@ enum { SubmenuIndexUnlock = SubmenuIndexCommonMax, SubmenuIndexUnlockByReader, SubmenuIndexUnlockByPassword, - SubmenuIndexWrite, }; enum { @@ -182,24 +181,22 @@ static void nfc_scene_read_and_saved_menu_on_enter_mf_ultralight(NfcApp* instanc const MfUltralightData* data = nfc_device_get_data(instance->nfc_device, NfcProtocolMfUltralight); + bool is_locked = !mf_ultralight_is_all_data_read(data); - if(!mf_ultralight_is_all_data_read(data)) { + if(is_locked || + (data->type != MfUltralightTypeNTAG213 && data->type != MfUltralightTypeNTAG215 && + data->type != MfUltralightTypeNTAG216 && data->type != MfUltralightTypeUL11 && + data->type != MfUltralightTypeUL21 && data->type != MfUltralightTypeOrigin)) { + submenu_remove_item(submenu, SubmenuIndexCommonWrite); + } + + if(is_locked) { submenu_add_item( submenu, "Unlock", SubmenuIndexUnlock, nfc_protocol_support_common_submenu_callback, instance); - } else if( - data->type == MfUltralightTypeNTAG213 || data->type == MfUltralightTypeNTAG215 || - data->type == MfUltralightTypeNTAG216 || data->type == MfUltralightTypeUL11 || - data->type == MfUltralightTypeUL21 || data->type == MfUltralightTypeOrigin) { - submenu_add_item( - submenu, - "Write", - SubmenuIndexWrite, - nfc_protocol_support_common_submenu_callback, - instance); } } @@ -252,9 +249,6 @@ static bool nfc_scene_read_and_saved_menu_on_event_mf_ultralight( NfcSceneMfUltralightUnlockMenu; scene_manager_next_scene(instance->scene_manager, next_scene); consumed = true; - } else if(event.event == SubmenuIndexWrite) { - scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightWrite); - consumed = true; } else if(event.event == SubmenuIndexCommonEdit) { scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid); consumed = true; @@ -263,8 +257,52 @@ static bool nfc_scene_read_and_saved_menu_on_event_mf_ultralight( return consumed; } +static NfcCommand + nfc_scene_write_poller_callback_mf_ultralight(NfcGenericEvent event, void* context) { + furi_assert(event.protocol == NfcProtocolMfUltralight); + + NfcApp* instance = context; + MfUltralightPollerEvent* mf_ultralight_event = event.event_data; + NfcCommand command = NfcCommandContinue; + + if(mf_ultralight_event->type == MfUltralightPollerEventTypeRequestMode) { + mf_ultralight_event->data->poller_mode = MfUltralightPollerModeWrite; + furi_string_reset(instance->text_box_store); + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventCardDetected); + } else if(mf_ultralight_event->type == MfUltralightPollerEventTypeAuthRequest) { + mf_ultralight_event->data->auth_context.skip_auth = true; + } else if(mf_ultralight_event->type == MfUltralightPollerEventTypeRequestWriteData) { + mf_ultralight_event->data->write_data = + nfc_device_get_data(instance->nfc_device, NfcProtocolMfUltralight); + } else if(mf_ultralight_event->type == MfUltralightPollerEventTypeCardMismatch) { + furi_string_set(instance->text_box_store, "Card of the same\ntype should be\n presented"); + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventWrongCard); + command = NfcCommandStop; + } else if(mf_ultralight_event->type == MfUltralightPollerEventTypeCardLocked) { + furi_string_set( + instance->text_box_store, "Card protected by\npassword, AUTH0\nor lock bits"); + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerFailure); + command = NfcCommandStop; + } else if(mf_ultralight_event->type == MfUltralightPollerEventTypeWriteFail) { + command = NfcCommandStop; + } else if(mf_ultralight_event->type == MfUltralightPollerEventTypeWriteSuccess) { + furi_string_reset(instance->text_box_store); + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess); + command = NfcCommandStop; + } + + return command; +} + +static void nfc_scene_write_on_enter_mf_ultralight(NfcApp* instance) { + instance->poller = nfc_poller_alloc(instance->nfc, NfcProtocolMfUltralight); + nfc_poller_start(instance->poller, nfc_scene_write_poller_callback_mf_ultralight, instance); + furi_string_set(instance->text_box_store, "Apply the initial\ncard only"); +} + const NfcProtocolSupportBase nfc_protocol_support_mf_ultralight = { - .features = NfcProtocolFeatureEmulateFull | NfcProtocolFeatureMoreInfo, + .features = NfcProtocolFeatureEmulateFull | NfcProtocolFeatureMoreInfo | + NfcProtocolFeatureWrite, .scene_info = { @@ -306,4 +344,9 @@ const NfcProtocolSupportBase nfc_protocol_support_mf_ultralight = { .on_enter = nfc_scene_emulate_on_enter_mf_ultralight, .on_event = nfc_protocol_support_common_on_event_empty, }, + .scene_write = + { + .on_enter = nfc_scene_write_on_enter_mf_ultralight, + .on_event = nfc_protocol_support_common_on_event_empty, + }, }; diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c index 0d63dc56b..8711af476 100644 --- a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c @@ -258,6 +258,15 @@ static void nfc_protocol_support_scene_read_menu_on_enter(NfcApp* instance) { instance); } + if(nfc_protocol_support_has_feature(protocol, NfcProtocolFeatureWrite)) { + submenu_add_item( + submenu, + "Write", + SubmenuIndexCommonWrite, + nfc_protocol_support_common_submenu_callback, + instance); + } + nfc_protocol_support[protocol]->scene_read_menu.on_enter(instance); submenu_add_item( @@ -291,6 +300,10 @@ static bool dolphin_deed(DolphinDeedNfcEmulate); scene_manager_next_scene(instance->scene_manager, NfcSceneEmulate); consumed = true; + } else if(event.event == SubmenuIndexCommonWrite) { + dolphin_deed(DolphinDeedNfcEmulate); + scene_manager_next_scene(instance->scene_manager, NfcSceneWrite); + consumed = true; } else { const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device); consumed = nfc_protocol_support[protocol]->scene_read_menu.on_event(instance, event); @@ -383,6 +396,15 @@ static void nfc_protocol_support_scene_saved_menu_on_enter(NfcApp* instance) { instance); } + if(nfc_protocol_support_has_feature(protocol, NfcProtocolFeatureWrite)) { + submenu_add_item( + submenu, + "Write", + SubmenuIndexCommonWrite, + nfc_protocol_support_common_submenu_callback, + instance); + } + if(nfc_protocol_support_has_feature(protocol, NfcProtocolFeatureEditUid)) { submenu_add_item( submenu, @@ -456,6 +478,12 @@ static bool dolphin_deed(is_added ? DolphinDeedNfcAddEmulate : DolphinDeedNfcEmulate); scene_manager_next_scene(instance->scene_manager, NfcSceneEmulate); consumed = true; + } else if(event.event == SubmenuIndexCommonWrite) { + const bool is_added = + scene_manager_has_previous_scene(instance->scene_manager, NfcSceneSetType); + dolphin_deed(is_added ? DolphinDeedNfcAddEmulate : DolphinDeedNfcEmulate); + scene_manager_next_scene(instance->scene_manager, NfcSceneWrite); + consumed = true; } else if(event.event == SubmenuIndexCommonEdit) { scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid); consumed = true; @@ -688,6 +716,175 @@ static void nfc_protocol_support_scene_emulate_on_exit(NfcApp* instance) { nfc_blink_stop(instance); } +// SceneWrite +/** + * @brief Current view displayed on the write scene. + * + * The emulation scene has five states, some protocols may not use all states. + * Protocol handles poller events, when scene state needs to change it should + * fill text_box_store with a short caption (when applicable) before sending + * the relevant view dispatcher event. + */ +enum { + NfcSceneWriteStateSearching, /**< Ask user to touch the card. Event: on_enter, CardLost. Needs caption. */ + NfcSceneWriteStateWriting, /**< Ask not to move while writing. Event: CardDetected. No caption. */ + NfcSceneWriteStateSuccess, /**< Card written successfully. Event: PollerSuccess. No caption. */ + NfcSceneWriteStateFailure, /**< An error is displayed. Event: PollerFailure. Needs caption. */ + NfcSceneWriteStateWrongCard, /**< Wrong card was presented. Event: WrongCard. Needs caption. */ +}; + +static void nfc_protocol_support_scene_write_popup_callback(void* context) { + NfcApp* instance = context; + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventViewExit); +} + +void nfc_protocol_support_scene_write_widget_callback( + GuiButtonType result, + InputType type, + void* context) { + NfcApp* instance = context; + if(type == InputTypeShort && result == GuiButtonTypeLeft) { + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventViewExit); + } +} + +static void nfc_protocol_support_scene_write_setup_view(NfcApp* instance) { + Popup* popup = instance->popup; + Widget* widget = instance->widget; + popup_reset(popup); + widget_reset(widget); + uint32_t state = scene_manager_get_scene_state(instance->scene_manager, NfcSceneWrite); + NfcView view = NfcViewPopup; + + if(state == NfcSceneWriteStateSearching) { + popup_set_header(popup, "Writing", 95, 20, AlignCenter, AlignCenter); + popup_set_text( + popup, + furi_string_get_cstr(instance->text_box_store), + 95, + 38, + AlignCenter, + AlignCenter); + popup_set_icon(popup, 0, 8, &I_NFC_manual_60x50); + } else if(state == NfcSceneWriteStateWriting) { + popup_set_header(popup, "Writing\nDon't move...", 52, 32, AlignLeft, AlignCenter); + popup_set_icon(popup, 12, 23, &A_Loading_24); + } else if(state == NfcSceneWriteStateSuccess) { + popup_set_header(popup, "Successfully\nwritten!", 126, 2, AlignRight, AlignTop); + popup_set_icon(popup, 0, 9, &I_DolphinSuccess_91x55); + popup_set_timeout(popup, 1500); + popup_set_context(popup, instance); + popup_set_callback(popup, nfc_protocol_support_scene_write_popup_callback); + popup_enable_timeout(popup); + } else if(state == NfcSceneWriteStateFailure) { + view = NfcViewWidget; + widget_add_string_element( + widget, 7, 4, AlignLeft, AlignTop, FontPrimary, "Writing gone wrong!"); + widget_add_string_multiline_element( + widget, + 7, + 17, + AlignLeft, + AlignTop, + FontSecondary, + furi_string_get_cstr(instance->text_box_store)); + widget_add_icon_element(widget, 83, 22, &I_WarningDolphinFlip_45x42); + widget_add_button_element( + widget, + GuiButtonTypeLeft, + "Retry", + nfc_protocol_support_scene_write_widget_callback, + instance); + } else if(state == NfcSceneWriteStateWrongCard) { + view = NfcViewWidget; + widget_add_string_element(widget, 3, 4, AlignLeft, AlignTop, FontPrimary, "Wrong card!"); + widget_add_string_multiline_element( + widget, + 4, + 17, + AlignLeft, + AlignTop, + FontSecondary, + furi_string_get_cstr(instance->text_box_store)); + widget_add_icon_element(widget, 83, 22, &I_WarningDolphinFlip_45x42); + widget_add_button_element( + widget, + GuiButtonTypeLeft, + "Retry", + nfc_protocol_support_scene_write_widget_callback, + instance); + } + + view_dispatcher_switch_to_view(instance->view_dispatcher, view); +} + +static void nfc_protocol_support_scene_write_on_enter(NfcApp* instance) { + scene_manager_set_scene_state( + instance->scene_manager, NfcSceneWrite, NfcSceneWriteStateSearching); + furi_string_reset(instance->text_box_store); + + const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device); + + // instance->poller is allocated in the respective on_enter() handler + nfc_protocol_support[protocol]->scene_write.on_enter(instance); + + nfc_protocol_support_scene_write_setup_view(instance); + nfc_blink_emulate_start(instance); +} + +static bool nfc_protocol_support_scene_write_on_event(NfcApp* instance, SceneManagerEvent event) { + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + uint32_t new_state = -1; + + if(event.event == NfcCustomEventCardDetected) { + new_state = NfcSceneWriteStateWriting; + consumed = true; + } else if(event.event == NfcCustomEventCardLost) { + new_state = NfcSceneWriteStateSearching; + consumed = true; + } else if(event.event == NfcCustomEventPollerSuccess) { + dolphin_deed(DolphinDeedNfcSave); + notification_message(instance->notifications, &sequence_success); + new_state = NfcSceneWriteStateSuccess; + consumed = true; + } else if(event.event == NfcCustomEventPollerFailure) { + notification_message(instance->notifications, &sequence_error); + new_state = NfcSceneWriteStateFailure; + consumed = true; + } else if(event.event == NfcCustomEventWrongCard) { + notification_message(instance->notifications, &sequence_error); + new_state = NfcSceneWriteStateWrongCard; + consumed = true; + } else if(event.event == NfcCustomEventViewExit) { + // Retry + nfc_protocol_support_scenes[NfcProtocolSupportSceneWrite].on_exit(instance); + nfc_protocol_support_scenes[NfcProtocolSupportSceneWrite].on_enter(instance); + consumed = true; + } + + if(new_state != (uint32_t)-1) { + scene_manager_set_scene_state(instance->scene_manager, NfcSceneWrite, new_state); + nfc_protocol_support_scene_write_setup_view(instance); + } + } + + return consumed; +} + +static void nfc_protocol_support_scene_write_on_exit(NfcApp* instance) { + nfc_poller_stop(instance->poller); + nfc_poller_free(instance->poller); + + // Clear view + popup_reset(instance->popup); + widget_reset(instance->widget); + furi_string_reset(instance->text_box_store); + + nfc_blink_stop(instance); +} + static void nfc_protocol_support_scene_rpc_on_enter(NfcApp* instance) { UNUSED(instance); } @@ -803,6 +1000,12 @@ static const NfcProtocolSupportCommonSceneBase .on_event = nfc_protocol_support_scene_emulate_on_event, .on_exit = nfc_protocol_support_scene_emulate_on_exit, }, + [NfcProtocolSupportSceneWrite] = + { + .on_enter = nfc_protocol_support_scene_write_on_enter, + .on_event = nfc_protocol_support_scene_write_on_event, + .on_exit = nfc_protocol_support_scene_write_on_exit, + }, [NfcProtocolSupportSceneRpc] = { .on_enter = nfc_protocol_support_scene_rpc_on_enter, diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_base.h b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_base.h index eec736ca2..0982bae1f 100644 --- a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_base.h +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_base.h @@ -114,4 +114,13 @@ typedef struct { * It is responsible for creating a listener and for handling its events. */ NfcProtocolSupportSceneBase scene_emulate; + + /** + * @brief Handlers for protocol-specific write scene. + * + * This scene is activated when a write operation is in progress. + * It is responsible for creating a poller, handling its events and + * displaying short captions for what is happening. + */ + NfcProtocolSupportSceneBase scene_write; } NfcProtocolSupportBase; diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_common.h b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_common.h index 6e3214106..574c23a4c 100644 --- a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_common.h +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_common.h @@ -13,6 +13,7 @@ typedef enum { NfcProtocolFeatureEmulateFull = 1UL << 1, /**< Complete emulation is supported. */ NfcProtocolFeatureEditUid = 1UL << 2, /**< UID editing is supported. */ NfcProtocolFeatureMoreInfo = 1UL << 3, /**< More information is provided. */ + NfcProtocolFeatureWrite = 1UL << 4, /**< Writing to real card is supported. */ } NfcProtocolFeature; /** @@ -30,6 +31,7 @@ typedef enum { NfcProtocolSupportSceneSavedMenu, /**< Menu for the card that was loaded from file. */ NfcProtocolSupportSceneSaveName, /**< Shown when saving or renaming a file. */ NfcProtocolSupportSceneEmulate, /**< Shown when emulating a card. */ + NfcProtocolSupportSceneWrite, /**< Shown when writing to a card. */ NfcProtocolSupportSceneRpc, /**< Shown in remote-controlled (RPC) mode. */ NfcProtocolSupportSceneCount, /**< Special value equal to total scene count. Internal use. */ diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_gui_common.h b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_gui_common.h index 3230f1a7e..63a1d9ac1 100644 --- a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_gui_common.h +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_gui_common.h @@ -15,6 +15,7 @@ enum { SubmenuIndexCommonSave, /**< Save menu option. */ SubmenuIndexCommonEmulate, /**< Emulate menu option. */ + SubmenuIndexCommonWrite, /**< Write menu option. */ SubmenuIndexCommonEdit, /**< Edit menu option. */ SubmenuIndexCommonInfo, /**< Info menu option. */ SubmenuIndexCommonRename, /**< Rename menu option. */ diff --git a/applications/main/nfc/helpers/protocol_support/slix/slix.c b/applications/main/nfc/helpers/protocol_support/slix/slix.c index 35592eaa1..e998d0ad2 100644 --- a/applications/main/nfc/helpers/protocol_support/slix/slix.c +++ b/applications/main/nfc/helpers/protocol_support/slix/slix.c @@ -157,4 +157,9 @@ const NfcProtocolSupportBase nfc_protocol_support_slix = { .on_enter = nfc_scene_emulate_on_enter_slix, .on_event = nfc_protocol_support_common_on_event_empty, }, + .scene_write = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, + }, }; diff --git a/applications/main/nfc/helpers/protocol_support/st25tb/st25tb.c b/applications/main/nfc/helpers/protocol_support/st25tb/st25tb.c index 0305d614c..a119f8cd6 100644 --- a/applications/main/nfc/helpers/protocol_support/st25tb/st25tb.c +++ b/applications/main/nfc/helpers/protocol_support/st25tb/st25tb.c @@ -108,4 +108,9 @@ const NfcProtocolSupportBase nfc_protocol_support_st25tb = { .on_enter = nfc_protocol_support_common_on_enter_empty, .on_event = nfc_protocol_support_common_on_event_empty, }, + .scene_write = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, + }, }; diff --git a/applications/main/nfc/helpers/protocol_support/type_4_tag/type_4_tag.c b/applications/main/nfc/helpers/protocol_support/type_4_tag/type_4_tag.c index f0ee7baf3..30c64d962 100644 --- a/applications/main/nfc/helpers/protocol_support/type_4_tag/type_4_tag.c +++ b/applications/main/nfc/helpers/protocol_support/type_4_tag/type_4_tag.c @@ -10,10 +10,6 @@ #include "../nfc_protocol_support_common.h" #include "../nfc_protocol_support_gui_common.h" -enum { - SubmenuIndexWrite = SubmenuIndexCommonMax, -}; - enum { NfcSceneMoreInfoStateASCII, NfcSceneMoreInfoStateRawData, @@ -126,32 +122,6 @@ static void nfc_scene_read_on_enter_type_4_tag(NfcApp* instance) { nfc_poller_start(instance->poller, nfc_scene_read_poller_callback_type_4_tag, instance); } -static void nfc_scene_read_and_saved_menu_on_enter_type_4_tag(NfcApp* instance) { - Submenu* submenu = instance->submenu; - - // FIXME: standardize this and the write scenes into protocol support helper - submenu_add_item( - submenu, - "Write", - SubmenuIndexWrite, - nfc_protocol_support_common_submenu_callback, - instance); -} - -static bool - nfc_scene_read_and_saved_menu_on_event_type_4_tag(NfcApp* instance, SceneManagerEvent event) { - bool consumed = false; - UNUSED(instance); - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SubmenuIndexWrite) { - scene_manager_next_scene(instance->scene_manager, NfcSceneType4TagWrite); - consumed = true; - } - } - return consumed; -} - static void nfc_scene_read_success_on_enter_type_4_tag(NfcApp* instance) { const NfcDevice* device = instance->nfc_device; const Type4TagData* data = nfc_device_get_data(device, NfcProtocolType4Tag); @@ -201,8 +171,45 @@ static void nfc_scene_emulate_on_enter_type_4_tag(NfcApp* instance) { instance->listener, nfc_scene_emulate_listener_callback_type_4_tag, instance); } +static NfcCommand + nfc_scene_write_poller_callback_type_4_tag(NfcGenericEvent event, void* context) { + furi_assert(event.protocol == NfcProtocolType4Tag); + + NfcApp* instance = context; + Type4TagPollerEvent* type_4_tag_event = event.event_data; + NfcCommand command = NfcCommandContinue; + + if(type_4_tag_event->type == Type4TagPollerEventTypeRequestMode) { + type_4_tag_event->data->poller_mode.mode = Type4TagPollerModeWrite; + type_4_tag_event->data->poller_mode.data = + nfc_device_get_data(instance->nfc_device, NfcProtocolType4Tag); + furi_string_reset(instance->text_box_store); + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventCardDetected); + } else if(type_4_tag_event->type == Type4TagPollerEventTypeWriteFail) { + const char* error_str = type_4_tag_event->data->error == Type4TagErrorCardLocked ? + "Card does not\nallow writing\nnew data" : + "Failed to\nwrite new data"; + furi_string_set(instance->text_box_store, error_str); + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerFailure); + command = NfcCommandStop; + } else if(type_4_tag_event->type == Type4TagPollerEventTypeWriteSuccess) { + furi_string_reset(instance->text_box_store); + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess); + command = NfcCommandStop; + } + + return command; +} + +static void nfc_scene_write_on_enter_type_4_tag(NfcApp* instance) { + instance->poller = nfc_poller_alloc(instance->nfc, NfcProtocolType4Tag); + nfc_poller_start(instance->poller, nfc_scene_write_poller_callback_type_4_tag, instance); + furi_string_set(instance->text_box_store, "Apply card\nto the back"); +} + const NfcProtocolSupportBase nfc_protocol_support_type_4_tag = { - .features = NfcProtocolFeatureEmulateFull | NfcProtocolFeatureMoreInfo, + .features = NfcProtocolFeatureEmulateFull | NfcProtocolFeatureMoreInfo | + NfcProtocolFeatureWrite, .scene_info = { @@ -221,8 +228,8 @@ const NfcProtocolSupportBase nfc_protocol_support_type_4_tag = { }, .scene_read_menu = { - .on_enter = nfc_scene_read_and_saved_menu_on_enter_type_4_tag, - .on_event = nfc_scene_read_and_saved_menu_on_event_type_4_tag, + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, }, .scene_read_success = { @@ -231,8 +238,8 @@ const NfcProtocolSupportBase nfc_protocol_support_type_4_tag = { }, .scene_saved_menu = { - .on_enter = nfc_scene_read_and_saved_menu_on_enter_type_4_tag, - .on_event = nfc_scene_read_and_saved_menu_on_event_type_4_tag, + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, }, .scene_save_name = { @@ -244,4 +251,9 @@ const NfcProtocolSupportBase nfc_protocol_support_type_4_tag = { .on_enter = nfc_scene_emulate_on_enter_type_4_tag, .on_event = nfc_protocol_support_common_on_event_empty, }, + .scene_write = + { + .on_enter = nfc_scene_write_on_enter_type_4_tag, + .on_event = nfc_protocol_support_common_on_event_empty, + }, }; diff --git a/applications/main/nfc/scenes/nfc_scene_config.h b/applications/main/nfc/scenes/nfc_scene_config.h index 080294126..9ded94373 100644 --- a/applications/main/nfc/scenes/nfc_scene_config.h +++ b/applications/main/nfc/scenes/nfc_scene_config.h @@ -18,6 +18,7 @@ ADD_SCENE(nfc, extra_actions, ExtraActions) ADD_SCENE(nfc, read_success, ReadSuccess) ADD_SCENE(nfc, read_menu, ReadMenu) ADD_SCENE(nfc, emulate, Emulate) +ADD_SCENE(nfc, write, Write) ADD_SCENE(nfc, rpc, Rpc) ADD_SCENE(nfc, debug, Debug) ADD_SCENE(nfc, field, Field) @@ -25,10 +26,6 @@ ADD_SCENE(nfc, retry_confirm, RetryConfirm) ADD_SCENE(nfc, exit_confirm, ExitConfirm) ADD_SCENE(nfc, save_confirm, SaveConfirm) -ADD_SCENE(nfc, mf_ultralight_write, MfUltralightWrite) -ADD_SCENE(nfc, mf_ultralight_write_success, MfUltralightWriteSuccess) -ADD_SCENE(nfc, mf_ultralight_write_fail, MfUltralightWriteFail) -ADD_SCENE(nfc, mf_ultralight_wrong_card, MfUltralightWrongCard) ADD_SCENE(nfc, mf_ultralight_unlock_menu, MfUltralightUnlockMenu) ADD_SCENE(nfc, mf_ultralight_unlock_warn, MfUltralightUnlockWarn) ADD_SCENE(nfc, mf_ultralight_key_input, MfUltralightKeyInput) @@ -46,10 +43,6 @@ ADD_SCENE(nfc, mf_classic_mfkey_complete, MfClassicMfkeyComplete) ADD_SCENE(nfc, mf_classic_update_initial, MfClassicUpdateInitial) ADD_SCENE(nfc, mf_classic_update_initial_success, MfClassicUpdateInitialSuccess) ADD_SCENE(nfc, mf_classic_update_initial_wrong_card, MfClassicUpdateInitialWrongCard) -ADD_SCENE(nfc, mf_classic_write_initial, MfClassicWriteInitial) -ADD_SCENE(nfc, mf_classic_write_initial_success, MfClassicWriteInitialSuccess) -ADD_SCENE(nfc, mf_classic_write_initial_fail, MfClassicWriteInitialFail) -ADD_SCENE(nfc, mf_classic_write_initial_wrong_card, MfClassicWriteInitialWrongCard) ADD_SCENE(nfc, mf_classic_keys, MfClassicKeys) ADD_SCENE(nfc, mf_classic_keys_list, MfClassicKeysList) @@ -67,8 +60,4 @@ ADD_SCENE(nfc, slix_key_input, SlixKeyInput) ADD_SCENE(nfc, slix_unlock, SlixUnlock) ADD_SCENE(nfc, slix_unlock_success, SlixUnlockSuccess) -ADD_SCENE(nfc, type_4_tag_write, Type4TagWrite) -ADD_SCENE(nfc, type_4_tag_write_success, Type4TagWriteSuccess) -ADD_SCENE(nfc, type_4_tag_write_fail, Type4TagWriteFail) - ADD_SCENE(nfc, generate_info, GenerateInfo) diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial.c deleted file mode 100644 index 12e7ba1ec..000000000 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial.c +++ /dev/null @@ -1,148 +0,0 @@ -#include "../nfc_app_i.h" - -#include - -enum { - NfcSceneMfClassicWriteInitialStateCardSearch, - NfcSceneMfClassicWriteInitialStateCardFound, -}; - -NfcCommand - nfc_scene_mf_classic_write_initial_worker_callback(NfcGenericEvent event, void* context) { - furi_assert(context); - furi_assert(event.event_data); - furi_assert(event.protocol == NfcProtocolMfClassic); - - NfcCommand command = NfcCommandContinue; - NfcApp* instance = context; - MfClassicPollerEvent* mfc_event = event.event_data; - const MfClassicData* write_data = - nfc_device_get_data(instance->nfc_device, NfcProtocolMfClassic); - - if(mfc_event->type == MfClassicPollerEventTypeCardDetected) { - view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventCardDetected); - } else if(mfc_event->type == MfClassicPollerEventTypeCardLost) { - view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventCardLost); - } else if(mfc_event->type == MfClassicPollerEventTypeRequestMode) { - const MfClassicData* tag_data = nfc_poller_get_data(instance->poller); - if(iso14443_3a_is_equal(tag_data->iso14443_3a_data, write_data->iso14443_3a_data)) { - mfc_event->data->poller_mode.mode = MfClassicPollerModeWrite; - } else { - view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventWrongCard); - command = NfcCommandStop; - } - } else if(mfc_event->type == MfClassicPollerEventTypeRequestSectorTrailer) { - uint8_t sector = mfc_event->data->sec_tr_data.sector_num; - uint8_t sec_tr = mf_classic_get_sector_trailer_num_by_sector(sector); - if(mf_classic_is_block_read(write_data, sec_tr)) { - mfc_event->data->sec_tr_data.sector_trailer = write_data->block[sec_tr]; - mfc_event->data->sec_tr_data.sector_trailer_provided = true; - } else { - mfc_event->data->sec_tr_data.sector_trailer_provided = false; - } - } else if(mfc_event->type == MfClassicPollerEventTypeRequestWriteBlock) { - uint8_t block_num = mfc_event->data->write_block_data.block_num; - if(mf_classic_is_block_read(write_data, block_num)) { - mfc_event->data->write_block_data.write_block = write_data->block[block_num]; - mfc_event->data->write_block_data.write_block_provided = true; - } else { - mfc_event->data->write_block_data.write_block_provided = false; - } - } else if(mfc_event->type == MfClassicPollerEventTypeSuccess) { - view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess); - command = NfcCommandStop; - } else if(mfc_event->type == MfClassicPollerEventTypeFail) { - view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerFailure); - command = NfcCommandStop; - } - return command; -} - -static void nfc_scene_mf_classic_write_initial_setup_view(NfcApp* instance) { - Popup* popup = instance->popup; - popup_reset(popup); - uint32_t state = - scene_manager_get_scene_state(instance->scene_manager, NfcSceneMfClassicWriteInitial); - - if(state == NfcSceneMfClassicWriteInitialStateCardSearch) { - popup_set_header(instance->popup, "Writing", 95, 20, AlignCenter, AlignCenter); - popup_set_text( - instance->popup, "Use the source\ncard only", 95, 38, AlignCenter, AlignCenter); - popup_set_icon(instance->popup, 0, 8, &I_NFC_manual_60x50); - } else { - popup_set_header(popup, "Writing\nDon't move...", 52, 32, AlignLeft, AlignCenter); - popup_set_icon(popup, 12, 23, &A_Loading_24); - } - - view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup); -} - -void nfc_scene_mf_classic_write_initial_on_enter(void* context) { - NfcApp* instance = context; - dolphin_deed(DolphinDeedNfcEmulate); - - scene_manager_set_scene_state( - instance->scene_manager, - NfcSceneMfClassicWriteInitial, - NfcSceneMfClassicWriteInitialStateCardSearch); - nfc_scene_mf_classic_write_initial_setup_view(instance); - - // Setup and start worker - instance->poller = nfc_poller_alloc(instance->nfc, NfcProtocolMfClassic); - nfc_poller_start( - instance->poller, nfc_scene_mf_classic_write_initial_worker_callback, instance); - - nfc_blink_emulate_start(instance); -} - -bool nfc_scene_mf_classic_write_initial_on_event(void* context, SceneManagerEvent event) { - NfcApp* instance = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == NfcCustomEventCardDetected) { - scene_manager_set_scene_state( - instance->scene_manager, - NfcSceneMfClassicWriteInitial, - NfcSceneMfClassicWriteInitialStateCardFound); - nfc_scene_mf_classic_write_initial_setup_view(instance); - consumed = true; - } else if(event.event == NfcCustomEventCardLost) { - scene_manager_set_scene_state( - instance->scene_manager, - NfcSceneMfClassicWriteInitial, - NfcSceneMfClassicWriteInitialStateCardSearch); - nfc_scene_mf_classic_write_initial_setup_view(instance); - consumed = true; - } else if(event.event == NfcCustomEventWrongCard) { - scene_manager_next_scene( - instance->scene_manager, NfcSceneMfClassicWriteInitialWrongCard); - consumed = true; - } else if(event.event == NfcCustomEventPollerSuccess) { - scene_manager_next_scene( - instance->scene_manager, NfcSceneMfClassicWriteInitialSuccess); - consumed = true; - } else if(event.event == NfcCustomEventPollerFailure) { - scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicWriteInitialFail); - consumed = true; - } - } - - return consumed; -} - -void nfc_scene_mf_classic_write_initial_on_exit(void* context) { - NfcApp* instance = context; - - nfc_poller_stop(instance->poller); - nfc_poller_free(instance->poller); - - scene_manager_set_scene_state( - instance->scene_manager, - NfcSceneMfClassicWriteInitial, - NfcSceneMfClassicWriteInitialStateCardSearch); - // Clear view - popup_reset(instance->popup); - - nfc_blink_stop(instance); -} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial_fail.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial_fail.c deleted file mode 100644 index 4d4367ec8..000000000 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial_fail.c +++ /dev/null @@ -1,62 +0,0 @@ -#include "../nfc_app_i.h" - -void nfc_scene_mf_classic_write_initial_fail_widget_callback( - GuiButtonType result, - InputType type, - void* context) { - NfcApp* instance = context; - if(type == InputTypeShort) { - view_dispatcher_send_custom_event(instance->view_dispatcher, result); - } -} - -void nfc_scene_mf_classic_write_initial_fail_on_enter(void* context) { - NfcApp* instance = context; - Widget* widget = instance->widget; - - notification_message(instance->notifications, &sequence_error); - - widget_add_icon_element(widget, 83, 22, &I_WarningDolphinFlip_45x42); - widget_add_string_element( - widget, 7, 4, AlignLeft, AlignTop, FontPrimary, "Writing gone wrong!"); - widget_add_string_multiline_element( - widget, - 7, - 17, - AlignLeft, - AlignTop, - FontSecondary, - "Not all sectors\nwere written\ncorrectly."); - - widget_add_button_element( - widget, - GuiButtonTypeLeft, - "Finish", - nfc_scene_mf_classic_write_initial_fail_widget_callback, - instance); - - // Setup and start worker - view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewWidget); -} - -bool nfc_scene_mf_classic_write_initial_fail_on_event(void* context, SceneManagerEvent event) { - NfcApp* instance = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == GuiButtonTypeLeft) { - consumed = scene_manager_search_and_switch_to_previous_scene( - instance->scene_manager, NfcSceneSavedMenu); - } - } else if(event.type == SceneManagerEventTypeBack) { - consumed = scene_manager_search_and_switch_to_previous_scene( - instance->scene_manager, NfcSceneSavedMenu); - } - return consumed; -} - -void nfc_scene_mf_classic_write_initial_fail_on_exit(void* context) { - NfcApp* instance = context; - - widget_reset(instance->widget); -} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial_success.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial_success.c deleted file mode 100644 index 100c5c431..000000000 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial_success.c +++ /dev/null @@ -1,43 +0,0 @@ -#include "../nfc_app_i.h" - -void nfc_scene_mf_classic_write_initial_success_popup_callback(void* context) { - NfcApp* instance = context; - view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventViewExit); -} - -void nfc_scene_mf_classic_write_initial_success_on_enter(void* context) { - NfcApp* instance = context; - dolphin_deed(DolphinDeedNfcSave); - - notification_message(instance->notifications, &sequence_success); - - Popup* popup = instance->popup; - popup_set_header(popup, "Success!", 75, 10, AlignLeft, AlignTop); - popup_set_icon(popup, 0, 9, &I_DolphinSuccess_91x55); - popup_set_timeout(popup, 1500); - popup_set_context(popup, instance); - popup_set_callback(popup, nfc_scene_mf_classic_write_initial_success_popup_callback); - popup_enable_timeout(popup); - - view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup); -} - -bool nfc_scene_mf_classic_write_initial_success_on_event(void* context, SceneManagerEvent event) { - NfcApp* instance = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == NfcCustomEventViewExit) { - consumed = scene_manager_search_and_switch_to_previous_scene( - instance->scene_manager, NfcSceneSavedMenu); - } - } - return consumed; -} - -void nfc_scene_mf_classic_write_initial_success_on_exit(void* context) { - NfcApp* instance = context; - - // Clear view - popup_reset(instance->popup); -} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial_wrong_card.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial_wrong_card.c deleted file mode 100644 index 3d49b3cac..000000000 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial_wrong_card.c +++ /dev/null @@ -1,57 +0,0 @@ -#include "../nfc_app_i.h" - -void nfc_scene_mf_classic_write_initial_wrong_card_widget_callback( - GuiButtonType result, - InputType type, - void* context) { - NfcApp* instance = context; - if(type == InputTypeShort) { - view_dispatcher_send_custom_event(instance->view_dispatcher, result); - } -} - -void nfc_scene_mf_classic_write_initial_wrong_card_on_enter(void* context) { - NfcApp* instance = context; - Widget* widget = instance->widget; - - notification_message(instance->notifications, &sequence_error); - - widget_add_icon_element(widget, 83, 22, &I_WarningDolphinFlip_45x42); - widget_add_string_element( - widget, 3, 4, AlignLeft, AlignTop, FontPrimary, "Use The Source Card!"); - widget_add_string_multiline_element( - widget, - 4, - 17, - AlignLeft, - AlignTop, - FontSecondary, - "Go to NFC Magic\napp if you want to\nwrite blanks"); - widget_add_button_element( - widget, - GuiButtonTypeLeft, - "Retry", - nfc_scene_mf_classic_write_initial_wrong_card_widget_callback, - instance); - - // Setup and start worker - view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewWidget); -} - -bool nfc_scene_mf_classic_write_initial_wrong_card_on_event(void* context, SceneManagerEvent event) { - NfcApp* instance = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == GuiButtonTypeLeft) { - consumed = scene_manager_previous_scene(instance->scene_manager); - } - } - return consumed; -} - -void nfc_scene_mf_classic_write_initial_wrong_card_on_exit(void* context) { - NfcApp* instance = context; - - widget_reset(instance->widget); -} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_write.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_write.c deleted file mode 100644 index 157d6ce1b..000000000 --- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_write.c +++ /dev/null @@ -1,120 +0,0 @@ -#include "../nfc_app_i.h" - -#include - -enum { - NfcSceneMfUltralightWriteStateCardSearch, - NfcSceneMfUltralightWriteStateCardFound, -}; - -NfcCommand nfc_scene_mf_ultralight_write_worker_callback(NfcGenericEvent event, void* context) { - furi_assert(context); - furi_assert(event.event_data); - furi_assert(event.protocol == NfcProtocolMfUltralight); - - NfcCommand command = NfcCommandContinue; - NfcApp* instance = context; - MfUltralightPollerEvent* mfu_event = event.event_data; - - if(mfu_event->type == MfUltralightPollerEventTypeRequestMode) { - mfu_event->data->poller_mode = MfUltralightPollerModeWrite; - view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventCardDetected); - } else if(mfu_event->type == MfUltralightPollerEventTypeAuthRequest) { - mfu_event->data->auth_context.skip_auth = true; - } else if(mfu_event->type == MfUltralightPollerEventTypeRequestWriteData) { - mfu_event->data->write_data = - nfc_device_get_data(instance->nfc_device, NfcProtocolMfUltralight); - } else if(mfu_event->type == MfUltralightPollerEventTypeCardMismatch) { - view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventWrongCard); - command = NfcCommandStop; - } else if(mfu_event->type == MfUltralightPollerEventTypeCardLocked) { - view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerFailure); - command = NfcCommandStop; - } else if(mfu_event->type == MfUltralightPollerEventTypeWriteFail) { - command = NfcCommandStop; - } else if(mfu_event->type == MfUltralightPollerEventTypeWriteSuccess) { - view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess); - command = NfcCommandStop; - } - return command; -} - -static void nfc_scene_mf_ultralight_write_setup_view(NfcApp* instance) { - Popup* popup = instance->popup; - popup_reset(popup); - uint32_t state = - scene_manager_get_scene_state(instance->scene_manager, NfcSceneMfUltralightWrite); - - if(state == NfcSceneMfUltralightWriteStateCardSearch) { - popup_set_header(instance->popup, "Writing", 95, 20, AlignCenter, AlignCenter); - popup_set_text( - instance->popup, "Apply the initial\ncard only", 95, 38, AlignCenter, AlignCenter); - popup_set_icon(instance->popup, 0, 8, &I_NFC_manual_60x50); - } else { - popup_set_header(popup, "Writing\nDon't move...", 52, 32, AlignLeft, AlignCenter); - popup_set_icon(popup, 12, 23, &A_Loading_24); - } - - view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup); -} - -void nfc_scene_mf_ultralight_write_on_enter(void* context) { - NfcApp* instance = context; - dolphin_deed(DolphinDeedNfcEmulate); - - scene_manager_set_scene_state( - instance->scene_manager, - NfcSceneMfUltralightWrite, - NfcSceneMfUltralightWriteStateCardSearch); - nfc_scene_mf_ultralight_write_setup_view(instance); - - // Setup and start worker - FURI_LOG_D("WMFU", "Card searching..."); - instance->poller = nfc_poller_alloc(instance->nfc, NfcProtocolMfUltralight); - nfc_poller_start(instance->poller, nfc_scene_mf_ultralight_write_worker_callback, instance); - - nfc_blink_emulate_start(instance); -} - -bool nfc_scene_mf_ultralight_write_on_event(void* context, SceneManagerEvent event) { - NfcApp* instance = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == NfcCustomEventCardDetected) { - scene_manager_set_scene_state( - instance->scene_manager, - NfcSceneMfUltralightWrite, - NfcSceneMfUltralightWriteStateCardFound); - nfc_scene_mf_ultralight_write_setup_view(instance); - consumed = true; - } else if(event.event == NfcCustomEventWrongCard) { - scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightWrongCard); - consumed = true; - } else if(event.event == NfcCustomEventPollerSuccess) { - scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightWriteSuccess); - consumed = true; - } else if(event.event == NfcCustomEventPollerFailure) { - scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightWriteFail); - consumed = true; - } - } - - return consumed; -} - -void nfc_scene_mf_ultralight_write_on_exit(void* context) { - NfcApp* instance = context; - - nfc_poller_stop(instance->poller); - nfc_poller_free(instance->poller); - - scene_manager_set_scene_state( - instance->scene_manager, - NfcSceneMfUltralightWrite, - NfcSceneMfUltralightWriteStateCardSearch); - // Clear view - popup_reset(instance->popup); - - nfc_blink_stop(instance); -} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_write_fail.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_write_fail.c deleted file mode 100644 index fcfb5f2b0..000000000 --- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_write_fail.c +++ /dev/null @@ -1,67 +0,0 @@ -#include "../nfc_app_i.h" - -void nfc_scene_mf_ultralight_write_fail_widget_callback( - GuiButtonType result, - InputType type, - void* context) { - NfcApp* instance = context; - if(type == InputTypeShort) { - view_dispatcher_send_custom_event(instance->view_dispatcher, result); - } -} - -void nfc_scene_mf_ultralight_write_fail_on_enter(void* context) { - NfcApp* instance = context; - Widget* widget = instance->widget; - - notification_message(instance->notifications, &sequence_error); - - widget_add_icon_element(widget, 83, 22, &I_WarningDolphinFlip_45x42); - widget_add_string_element( - widget, 7, 4, AlignLeft, AlignTop, FontPrimary, "Writing gone wrong!"); - widget_add_string_multiline_element( - widget, - 7, - 17, - AlignLeft, - AlignTop, - FontSecondary, - "Card protected by\npassword, AUTH0\nor lock bits"); - - widget_add_button_element( - widget, - GuiButtonTypeLeft, - "Finish", - nfc_scene_mf_ultralight_write_fail_widget_callback, - instance); - - // Setup and start worker - view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewWidget); -} - -static bool nfc_scene_mf_ultralight_write_fail_move_to_back_scene(const NfcApp* const instance) { - bool was_saved = scene_manager_has_previous_scene(instance->scene_manager, NfcSceneSavedMenu); - uint32_t scene_id = was_saved ? NfcSceneSavedMenu : NfcSceneReadMenu; - - return scene_manager_search_and_switch_to_previous_scene(instance->scene_manager, scene_id); -} - -bool nfc_scene_mf_ultralight_write_fail_on_event(void* context, SceneManagerEvent event) { - NfcApp* instance = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == GuiButtonTypeLeft) { - consumed = nfc_scene_mf_ultralight_write_fail_move_to_back_scene(instance); - } - } else if(event.type == SceneManagerEventTypeBack) { - consumed = nfc_scene_mf_ultralight_write_fail_move_to_back_scene(instance); - } - return consumed; -} - -void nfc_scene_mf_ultralight_write_fail_on_exit(void* context) { - NfcApp* instance = context; - - widget_reset(instance->widget); -} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_write_success.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_write_success.c deleted file mode 100644 index bb34190d2..000000000 --- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_write_success.c +++ /dev/null @@ -1,46 +0,0 @@ -#include "../nfc_app_i.h" - -void nfc_scene_mf_ultralight_write_success_popup_callback(void* context) { - NfcApp* instance = context; - view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventViewExit); -} - -void nfc_scene_mf_ultralight_write_success_on_enter(void* context) { - NfcApp* instance = context; - dolphin_deed(DolphinDeedNfcSave); - - notification_message(instance->notifications, &sequence_success); - - Popup* popup = instance->popup; - popup_set_icon(popup, 48, 6, &I_DolphinDone_80x58); - popup_set_header(popup, "Successfully\nwritten", 5, 22, AlignLeft, AlignBottom); - popup_set_timeout(popup, 1500); - popup_set_context(popup, instance); - popup_set_callback(popup, nfc_scene_mf_ultralight_write_success_popup_callback); - popup_enable_timeout(popup); - - view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup); -} - -bool nfc_scene_mf_ultralight_write_success_on_event(void* context, SceneManagerEvent event) { - NfcApp* instance = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == NfcCustomEventViewExit) { - bool was_saved = - scene_manager_has_previous_scene(instance->scene_manager, NfcSceneSavedMenu); - - consumed = scene_manager_search_and_switch_to_previous_scene( - instance->scene_manager, was_saved ? NfcSceneSavedMenu : NfcSceneReadSuccess); - } - } - return consumed; -} - -void nfc_scene_mf_ultralight_write_success_on_exit(void* context) { - NfcApp* instance = context; - - // Clear view - popup_reset(instance->popup); -} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_wrong_card.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_wrong_card.c deleted file mode 100644 index bc34a45b4..000000000 --- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_wrong_card.c +++ /dev/null @@ -1,58 +0,0 @@ -#include "../nfc_app_i.h" - -void nfc_scene_mf_ultralight_wrong_card_widget_callback( - GuiButtonType result, - InputType type, - void* context) { - NfcApp* instance = context; - if(type == InputTypeShort) { - view_dispatcher_send_custom_event(instance->view_dispatcher, result); - } -} - -void nfc_scene_mf_ultralight_wrong_card_on_enter(void* context) { - NfcApp* instance = context; - Widget* widget = instance->widget; - - notification_message(instance->notifications, &sequence_error); - - widget_add_icon_element(widget, 83, 22, &I_WarningDolphinFlip_45x42); - widget_add_string_element( - widget, 3, 4, AlignLeft, AlignTop, FontPrimary, "This is wrong card"); - widget_add_string_multiline_element( - widget, - 4, - 17, - AlignLeft, - AlignTop, - FontSecondary, - "Card of the same\ntype should be\n presented"); - //"Data management\nis only possible\nwith card of same type"); - widget_add_button_element( - widget, - GuiButtonTypeLeft, - "Retry", - nfc_scene_mf_ultralight_wrong_card_widget_callback, - instance); - - // Setup and start worker - view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewWidget); -} - -bool nfc_scene_mf_ultralight_wrong_card_on_event(void* context, SceneManagerEvent event) { - NfcApp* instance = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == GuiButtonTypeLeft) { - consumed = scene_manager_previous_scene(instance->scene_manager); - } - } - return consumed; -} - -void nfc_scene_mf_ultralight_wrong_card_on_exit(void* context) { - NfcApp* instance = context; - - widget_reset(instance->widget); -} diff --git a/applications/main/nfc/scenes/nfc_scene_type_4_tag_write.c b/applications/main/nfc/scenes/nfc_scene_type_4_tag_write.c deleted file mode 100644 index cf1c93c34..000000000 --- a/applications/main/nfc/scenes/nfc_scene_type_4_tag_write.c +++ /dev/null @@ -1,105 +0,0 @@ -#include "../nfc_app_i.h" - -#include - -enum { - NfcSceneType4TagWriteStateCardSearch, - NfcSceneType4TagWriteStateCardFound, -}; - -NfcCommand nfc_scene_type_4_tag_write_worker_callback(NfcGenericEvent event, void* context) { - furi_assert(context); - furi_assert(event.event_data); - furi_assert(event.protocol == NfcProtocolType4Tag); - - NfcCommand command = NfcCommandContinue; - NfcApp* instance = context; - Type4TagPollerEvent* t4t_event = event.event_data; - - if(t4t_event->type == Type4TagPollerEventTypeRequestMode) { - t4t_event->data->poller_mode.mode = Type4TagPollerModeWrite; - t4t_event->data->poller_mode.data = - nfc_device_get_data(instance->nfc_device, NfcProtocolType4Tag); - view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventCardDetected); - } else if(t4t_event->type == Type4TagPollerEventTypeWriteFail) { - scene_manager_set_scene_state( - instance->scene_manager, NfcSceneType4TagWriteFail, t4t_event->data->error); - view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerFailure); - command = NfcCommandStop; - } else if(t4t_event->type == Type4TagPollerEventTypeWriteSuccess) { - view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess); - command = NfcCommandStop; - } - return command; -} - -static void nfc_scene_type_4_tag_write_setup_view(NfcApp* instance) { - Popup* popup = instance->popup; - popup_reset(popup); - uint32_t state = scene_manager_get_scene_state(instance->scene_manager, NfcSceneType4TagWrite); - - if(state == NfcSceneType4TagWriteStateCardSearch) { - popup_set_header(instance->popup, "Writing", 95, 20, AlignCenter, AlignCenter); - popup_set_text( - instance->popup, "Apply card\nto the back", 95, 38, AlignCenter, AlignCenter); - popup_set_icon(instance->popup, 0, 8, &I_NFC_manual_60x50); - } else { - popup_set_header(popup, "Writing\nDon't move...", 52, 32, AlignLeft, AlignCenter); - popup_set_icon(popup, 12, 23, &A_Loading_24); - } - - view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup); -} - -void nfc_scene_type_4_tag_write_on_enter(void* context) { - NfcApp* instance = context; - dolphin_deed(DolphinDeedNfcEmulate); - - scene_manager_set_scene_state( - instance->scene_manager, NfcSceneType4TagWrite, NfcSceneType4TagWriteStateCardSearch); - nfc_scene_type_4_tag_write_setup_view(instance); - - // Setup and start worker - instance->poller = nfc_poller_alloc(instance->nfc, NfcProtocolType4Tag); - nfc_poller_start(instance->poller, nfc_scene_type_4_tag_write_worker_callback, instance); - - nfc_blink_emulate_start(instance); -} - -bool nfc_scene_type_4_tag_write_on_event(void* context, SceneManagerEvent event) { - NfcApp* instance = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == NfcCustomEventCardDetected) { - scene_manager_set_scene_state( - instance->scene_manager, - NfcSceneType4TagWrite, - NfcSceneType4TagWriteStateCardFound); - nfc_scene_type_4_tag_write_setup_view(instance); - consumed = true; - } else if(event.event == NfcCustomEventPollerSuccess) { - scene_manager_next_scene(instance->scene_manager, NfcSceneType4TagWriteSuccess); - consumed = true; - } else if(event.event == NfcCustomEventPollerFailure) { - scene_manager_next_scene(instance->scene_manager, NfcSceneType4TagWriteFail); - consumed = true; - } - } - - return consumed; -} - -void nfc_scene_type_4_tag_write_on_exit(void* context) { - NfcApp* instance = context; - - nfc_poller_stop(instance->poller); - nfc_poller_free(instance->poller); - - scene_manager_set_scene_state( - instance->scene_manager, NfcSceneType4TagWrite, NfcSceneType4TagWriteStateCardSearch); - // Clear view - popup_reset(instance->popup); - - nfc_blink_stop(instance); -} diff --git a/applications/main/nfc/scenes/nfc_scene_type_4_tag_write_fail.c b/applications/main/nfc/scenes/nfc_scene_type_4_tag_write_fail.c deleted file mode 100644 index 7b4b0a9ae..000000000 --- a/applications/main/nfc/scenes/nfc_scene_type_4_tag_write_fail.c +++ /dev/null @@ -1,72 +0,0 @@ -#include "../nfc_app_i.h" - -#include - -void nfc_scene_type_4_tag_write_fail_widget_callback( - GuiButtonType result, - InputType type, - void* context) { - NfcApp* instance = context; - if(type == InputTypeShort) { - view_dispatcher_send_custom_event(instance->view_dispatcher, result); - } -} - -void nfc_scene_type_4_tag_write_fail_on_enter(void* context) { - NfcApp* instance = context; - Widget* widget = instance->widget; - - notification_message(instance->notifications, &sequence_error); - - widget_add_icon_element(widget, 83, 22, &I_WarningDolphinFlip_45x42); - widget_add_string_element( - widget, 7, 4, AlignLeft, AlignTop, FontPrimary, "Writing gone wrong!"); - Type4TagError error = - scene_manager_get_scene_state(instance->scene_manager, NfcSceneType4TagWriteFail); - widget_add_string_multiline_element( - widget, - 7, - 17, - AlignLeft, - AlignTop, - FontSecondary, - error == Type4TagErrorCardLocked ? "Card does not\nallow writing\nnew data" : - "Failed to\nwrite new data"); - - widget_add_button_element( - widget, - GuiButtonTypeLeft, - "Finish", - nfc_scene_type_4_tag_write_fail_widget_callback, - instance); - - // Setup and start worker - view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewWidget); -} - -static bool nfc_scene_type_4_tag_write_fail_move_to_back_scene(const NfcApp* const instance) { - bool was_saved = scene_manager_has_previous_scene(instance->scene_manager, NfcSceneSavedMenu); - uint32_t scene_id = was_saved ? NfcSceneSavedMenu : NfcSceneReadMenu; - - return scene_manager_search_and_switch_to_previous_scene(instance->scene_manager, scene_id); -} - -bool nfc_scene_type_4_tag_write_fail_on_event(void* context, SceneManagerEvent event) { - NfcApp* instance = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == GuiButtonTypeLeft) { - consumed = nfc_scene_type_4_tag_write_fail_move_to_back_scene(instance); - } - } else if(event.type == SceneManagerEventTypeBack) { - consumed = nfc_scene_type_4_tag_write_fail_move_to_back_scene(instance); - } - return consumed; -} - -void nfc_scene_type_4_tag_write_fail_on_exit(void* context) { - NfcApp* instance = context; - - widget_reset(instance->widget); -} diff --git a/applications/main/nfc/scenes/nfc_scene_type_4_tag_write_success.c b/applications/main/nfc/scenes/nfc_scene_type_4_tag_write_success.c deleted file mode 100644 index 125c29e71..000000000 --- a/applications/main/nfc/scenes/nfc_scene_type_4_tag_write_success.c +++ /dev/null @@ -1,46 +0,0 @@ -#include "../nfc_app_i.h" - -void nfc_scene_type_4_tag_write_success_popup_callback(void* context) { - NfcApp* instance = context; - view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventViewExit); -} - -void nfc_scene_type_4_tag_write_success_on_enter(void* context) { - NfcApp* instance = context; - dolphin_deed(DolphinDeedNfcSave); - - notification_message(instance->notifications, &sequence_success); - - Popup* popup = instance->popup; - popup_set_icon(popup, 48, 6, &I_DolphinDone_80x58); - popup_set_header(popup, "Successfully\nwritten", 5, 22, AlignLeft, AlignBottom); - popup_set_timeout(popup, 1500); - popup_set_context(popup, instance); - popup_set_callback(popup, nfc_scene_type_4_tag_write_success_popup_callback); - popup_enable_timeout(popup); - - view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup); -} - -bool nfc_scene_type_4_tag_write_success_on_event(void* context, SceneManagerEvent event) { - NfcApp* instance = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == NfcCustomEventViewExit) { - bool was_saved = - scene_manager_has_previous_scene(instance->scene_manager, NfcSceneSavedMenu); - - consumed = scene_manager_search_and_switch_to_previous_scene( - instance->scene_manager, was_saved ? NfcSceneSavedMenu : NfcSceneReadSuccess); - } - } - return consumed; -} - -void nfc_scene_type_4_tag_write_success_on_exit(void* context) { - NfcApp* instance = context; - - // Clear view - popup_reset(instance->popup); -} diff --git a/applications/main/nfc/scenes/nfc_scene_write.c b/applications/main/nfc/scenes/nfc_scene_write.c new file mode 100644 index 000000000..a5f8fbb13 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_write.c @@ -0,0 +1,13 @@ +#include "../helpers/protocol_support/nfc_protocol_support.h" + +void nfc_scene_write_on_enter(void* context) { + nfc_protocol_support_on_enter(NfcProtocolSupportSceneWrite, context); +} + +bool nfc_scene_write_on_event(void* context, SceneManagerEvent event) { + return nfc_protocol_support_on_event(NfcProtocolSupportSceneWrite, context, event); +} + +void nfc_scene_write_on_exit(void* context) { + nfc_protocol_support_on_exit(NfcProtocolSupportSceneWrite, context); +} From 2a3112743f51e732fd1cd8910db6d8d945e18a9b Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Fri, 21 Mar 2025 06:03:12 +0000 Subject: [PATCH 45/92] NFC: Pressing back on MFC Update doesn't retry --- .../scenes/nfc_scene_mf_classic_update_initial_wrong_card.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_update_initial_wrong_card.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_update_initial_wrong_card.c index f3aefb781..ac9e7a192 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_update_initial_wrong_card.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_update_initial_wrong_card.c @@ -47,6 +47,9 @@ bool nfc_scene_mf_classic_update_initial_wrong_card_on_event( if(event.event == GuiButtonTypeLeft) { consumed = scene_manager_previous_scene(instance->scene_manager); } + } else if(event.type == SceneManagerEventTypeBack) { + consumed = scene_manager_search_and_switch_to_previous_scene( + instance->scene_manager, NfcSceneSavedMenu); } return consumed; } From 3bd92f8cf166c43d164ec434b5c87f266771dc4a Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Fri, 21 Mar 2025 06:26:55 +0000 Subject: [PATCH 46/92] NFC: Fix duplicated ProtocolSupport handling, fix some inconsistent naming --- .../helpers/protocol_support/felica/felica.c | 11 +-------- .../iso14443_3a/iso14443_3a.c | 24 +++++++------------ .../iso14443_3b/iso14443_3b.c | 15 +----------- .../iso14443_3b/iso14443_3b_i.h | 7 ------ .../iso14443_4a/iso14443_4a.c | 24 +++++++------------ .../iso14443_4b/iso14443_4b.c | 24 +++---------------- .../protocol_support/iso15693_3/iso15693_3.c | 24 +++++++------------ .../protocol_support/mf_classic/mf_classic.c | 3 --- .../mf_ultralight/mf_ultralight.c | 3 --- .../protocol_support/nfc_protocol_support.c | 3 +++ .../nfc_protocol_support_gui_common.c | 4 ++-- .../nfc/helpers/protocol_support/slix/slix.c | 24 +++++++------------ .../helpers/protocol_support/st25tb/st25tb.c | 11 +-------- .../protocol_support/type_4_tag/type_4_tag.c | 15 ++++++------ 14 files changed, 50 insertions(+), 142 deletions(-) delete mode 100644 applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b_i.h diff --git a/applications/main/nfc/helpers/protocol_support/felica/felica.c b/applications/main/nfc/helpers/protocol_support/felica/felica.c index 7717396a3..7f12064a4 100644 --- a/applications/main/nfc/helpers/protocol_support/felica/felica.c +++ b/applications/main/nfc/helpers/protocol_support/felica/felica.c @@ -133,15 +133,6 @@ static void nfc_scene_read_success_on_enter_felica(NfcApp* instance) { furi_string_free(temp_str); } -static bool nfc_scene_saved_menu_on_event_felica(NfcApp* instance, SceneManagerEvent event) { - if(event.type == SceneManagerEventTypeCustom && event.event == SubmenuIndexCommonEdit) { - scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid); - return true; - } - - return false; -} - static void nfc_scene_emulate_on_enter_felica(NfcApp* instance) { const FelicaData* data = nfc_device_get_data(instance->nfc_device, NfcProtocolFelica); instance->listener = nfc_listener_alloc(instance->nfc, NfcProtocolFelica, data); @@ -201,7 +192,7 @@ const NfcProtocolSupportBase nfc_protocol_support_felica = { .scene_saved_menu = { .on_enter = nfc_protocol_support_common_on_enter_empty, - .on_event = nfc_scene_saved_menu_on_event_felica, + .on_event = nfc_protocol_support_common_on_event_empty, }, .scene_save_name = { diff --git a/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a.c b/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a.c index 9d6dba5a7..068c6a1d7 100644 --- a/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a.c +++ b/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a.c @@ -67,21 +67,22 @@ static NfcCommand furi_assert(event.protocol == NfcProtocolIso14443_3a); furi_assert(event.event_data); - NfcApp* nfc = context; + NfcApp* instance = context; Iso14443_3aListenerEvent* iso14443_3a_event = event.event_data; if(iso14443_3a_event->type == Iso14443_3aListenerEventTypeReceivedStandardFrame) { - if(furi_string_size(nfc->text_box_store) < NFC_LOG_SIZE_MAX) { - furi_string_cat_printf(nfc->text_box_store, "R:"); + if(furi_string_size(instance->text_box_store) < NFC_LOG_SIZE_MAX) { + furi_string_cat_printf(instance->text_box_store, "R:"); for(size_t i = 0; i < bit_buffer_get_size_bytes(iso14443_3a_event->data->buffer); i++) { furi_string_cat_printf( - nfc->text_box_store, + instance->text_box_store, " %02X", bit_buffer_get_byte(iso14443_3a_event->data->buffer, i)); } - furi_string_push_back(nfc->text_box_store, '\n'); - view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventListenerUpdate); + furi_string_push_back(instance->text_box_store, '\n'); + view_dispatcher_send_custom_event( + instance->view_dispatcher, NfcCustomEventListenerUpdate); } } @@ -97,15 +98,6 @@ static void nfc_scene_emulate_on_enter_iso14443_3a(NfcApp* instance) { instance->listener, nfc_scene_emulate_listener_callback_iso14443_3a, instance); } -static bool nfc_scene_read_menu_on_event_iso14443_3a(NfcApp* instance, SceneManagerEvent event) { - if(event.type == SceneManagerEventTypeCustom && event.event == SubmenuIndexCommonEmulate) { - scene_manager_next_scene(instance->scene_manager, NfcSceneEmulate); - return true; - } - - return false; -} - const NfcProtocolSupportBase nfc_protocol_support_iso14443_3a = { .features = NfcProtocolFeatureEmulateUid | NfcProtocolFeatureEditUid, @@ -122,7 +114,7 @@ const NfcProtocolSupportBase nfc_protocol_support_iso14443_3a = { .scene_read_menu = { .on_enter = nfc_protocol_support_common_on_enter_empty, - .on_event = nfc_scene_read_menu_on_event_iso14443_3a, + .on_event = nfc_protocol_support_common_on_event_empty, }, .scene_read_success = { diff --git a/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b.c b/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b.c index 49da93d7b..ccafc1c9d 100644 --- a/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b.c +++ b/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b.c @@ -60,19 +60,6 @@ static void nfc_scene_read_success_on_enter_iso14443_3b(NfcApp* instance) { furi_string_free(temp_str); } -bool nfc_scene_saved_menu_on_event_iso14443_3b_common(NfcApp* instance, SceneManagerEvent event) { - if(event.type == SceneManagerEventTypeCustom && event.event == SubmenuIndexCommonEdit) { - scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid); - return true; - } - - return false; -} - -static bool nfc_scene_saved_menu_on_event_iso14443_3b(NfcApp* instance, SceneManagerEvent event) { - return nfc_scene_saved_menu_on_event_iso14443_3b_common(instance, event); -} - const NfcProtocolSupportBase nfc_protocol_support_iso14443_3b = { .features = NfcProtocolFeatureNone, @@ -99,7 +86,7 @@ const NfcProtocolSupportBase nfc_protocol_support_iso14443_3b = { .scene_saved_menu = { .on_enter = nfc_protocol_support_common_on_enter_empty, - .on_event = nfc_scene_saved_menu_on_event_iso14443_3b, + .on_event = nfc_protocol_support_common_on_event_empty, }, .scene_save_name = { diff --git a/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b_i.h b/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b_i.h deleted file mode 100644 index 6c7c2a0bc..000000000 --- a/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b_i.h +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once - -#include - -#include "iso14443_3b.h" - -bool nfc_scene_saved_menu_on_event_iso14443_3b_common(NfcApp* instance, SceneManagerEvent event); diff --git a/applications/main/nfc/helpers/protocol_support/iso14443_4a/iso14443_4a.c b/applications/main/nfc/helpers/protocol_support/iso14443_4a/iso14443_4a.c index 190c26e87..5eec0a1ad 100644 --- a/applications/main/nfc/helpers/protocol_support/iso14443_4a/iso14443_4a.c +++ b/applications/main/nfc/helpers/protocol_support/iso14443_4a/iso14443_4a.c @@ -70,21 +70,22 @@ NfcCommand nfc_scene_emulate_listener_callback_iso14443_4a(NfcGenericEvent event furi_assert(event.protocol == NfcProtocolIso14443_4a); furi_assert(event.event_data); - NfcApp* nfc = context; + NfcApp* instance = context; Iso14443_4aListenerEvent* iso14443_4a_event = event.event_data; if(iso14443_4a_event->type == Iso14443_4aListenerEventTypeReceivedData) { - if(furi_string_size(nfc->text_box_store) < NFC_LOG_SIZE_MAX) { - furi_string_cat_printf(nfc->text_box_store, "R:"); + if(furi_string_size(instance->text_box_store) < NFC_LOG_SIZE_MAX) { + furi_string_cat_printf(instance->text_box_store, "R:"); for(size_t i = 0; i < bit_buffer_get_size_bytes(iso14443_4a_event->data->buffer); i++) { furi_string_cat_printf( - nfc->text_box_store, + instance->text_box_store, " %02X", bit_buffer_get_byte(iso14443_4a_event->data->buffer, i)); } - furi_string_push_back(nfc->text_box_store, '\n'); - view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventListenerUpdate); + furi_string_push_back(instance->text_box_store, '\n'); + view_dispatcher_send_custom_event( + instance->view_dispatcher, NfcCustomEventListenerUpdate); } } @@ -100,15 +101,6 @@ static void nfc_scene_emulate_on_enter_iso14443_4a(NfcApp* instance) { instance->listener, nfc_scene_emulate_listener_callback_iso14443_4a, instance); } -static bool nfc_scene_read_menu_on_event_iso14443_4a(NfcApp* instance, SceneManagerEvent event) { - if(event.type == SceneManagerEventTypeCustom && event.event == SubmenuIndexCommonEmulate) { - scene_manager_next_scene(instance->scene_manager, NfcSceneEmulate); - return true; - } - - return false; -} - const NfcProtocolSupportBase nfc_protocol_support_iso14443_4a = { .features = NfcProtocolFeatureEmulateUid | NfcProtocolFeatureEditUid, @@ -125,7 +117,7 @@ const NfcProtocolSupportBase nfc_protocol_support_iso14443_4a = { .scene_read_menu = { .on_enter = nfc_protocol_support_common_on_enter_empty, - .on_event = nfc_scene_read_menu_on_event_iso14443_4a, + .on_event = nfc_protocol_support_common_on_event_empty, }, .scene_read_success = { diff --git a/applications/main/nfc/helpers/protocol_support/iso14443_4b/iso14443_4b.c b/applications/main/nfc/helpers/protocol_support/iso14443_4b/iso14443_4b.c index 8fd823f21..7bfea69fa 100644 --- a/applications/main/nfc/helpers/protocol_support/iso14443_4b/iso14443_4b.c +++ b/applications/main/nfc/helpers/protocol_support/iso14443_4b/iso14443_4b.c @@ -7,7 +7,6 @@ #include "../nfc_protocol_support_common.h" #include "../nfc_protocol_support_gui_common.h" -#include "../iso14443_3b/iso14443_3b_i.h" static void nfc_scene_info_on_enter_iso14443_4b(NfcApp* instance) { const NfcDevice* device = instance->nfc_device; @@ -61,23 +60,6 @@ static void nfc_scene_read_success_on_enter_iso14443_4b(NfcApp* instance) { furi_string_free(temp_str); } -static void nfc_scene_saved_menu_on_enter_iso14443_4b(NfcApp* instance) { - UNUSED(instance); -} - -static bool nfc_scene_read_menu_on_event_iso14443_4b(NfcApp* instance, SceneManagerEvent event) { - if(event.type == SceneManagerEventTypeCustom && event.event == SubmenuIndexCommonEmulate) { - scene_manager_next_scene(instance->scene_manager, NfcSceneEmulate); - return true; - } - - return false; -} - -static bool nfc_scene_saved_menu_on_event_iso14443_4b(NfcApp* instance, SceneManagerEvent event) { - return nfc_scene_saved_menu_on_event_iso14443_3b_common(instance, event); -} - const NfcProtocolSupportBase nfc_protocol_support_iso14443_4b = { .features = NfcProtocolFeatureNone, @@ -94,7 +76,7 @@ const NfcProtocolSupportBase nfc_protocol_support_iso14443_4b = { .scene_read_menu = { .on_enter = nfc_protocol_support_common_on_enter_empty, - .on_event = nfc_scene_read_menu_on_event_iso14443_4b, + .on_event = nfc_protocol_support_common_on_event_empty, }, .scene_read_success = { @@ -103,8 +85,8 @@ const NfcProtocolSupportBase nfc_protocol_support_iso14443_4b = { }, .scene_saved_menu = { - .on_enter = nfc_scene_saved_menu_on_enter_iso14443_4b, - .on_event = nfc_scene_saved_menu_on_event_iso14443_4b, + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, }, .scene_save_name = { diff --git a/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3.c b/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3.c index ffd6971e0..63aff8332 100644 --- a/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3.c +++ b/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3.c @@ -80,20 +80,21 @@ static NfcCommand furi_assert(event.protocol == NfcProtocolIso15693_3); furi_assert(event.event_data); - NfcApp* nfc = context; + NfcApp* instance = context; Iso15693_3ListenerEvent* iso15693_3_event = event.event_data; if(iso15693_3_event->type == Iso15693_3ListenerEventTypeCustomCommand) { - if(furi_string_size(nfc->text_box_store) < NFC_LOG_SIZE_MAX) { - furi_string_cat_printf(nfc->text_box_store, "R:"); + if(furi_string_size(instance->text_box_store) < NFC_LOG_SIZE_MAX) { + furi_string_cat_printf(instance->text_box_store, "R:"); for(size_t i = 0; i < bit_buffer_get_size_bytes(iso15693_3_event->data->buffer); i++) { furi_string_cat_printf( - nfc->text_box_store, + instance->text_box_store, " %02X", bit_buffer_get_byte(iso15693_3_event->data->buffer, i)); } - furi_string_push_back(nfc->text_box_store, '\n'); - view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventListenerUpdate); + furi_string_push_back(instance->text_box_store, '\n'); + view_dispatcher_send_custom_event( + instance->view_dispatcher, NfcCustomEventListenerUpdate); } } @@ -108,15 +109,6 @@ static void nfc_scene_emulate_on_enter_iso15693_3(NfcApp* instance) { instance->listener, nfc_scene_emulate_listener_callback_iso15693_3, instance); } -static bool nfc_scene_saved_menu_on_event_iso15693_3(NfcApp* instance, SceneManagerEvent event) { - if(event.type == SceneManagerEventTypeCustom && event.event == SubmenuIndexCommonEdit) { - scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid); - return true; - } - - return false; -} - const NfcProtocolSupportBase nfc_protocol_support_iso15693_3 = { .features = NfcProtocolFeatureEmulateFull | NfcProtocolFeatureEditUid | NfcProtocolFeatureMoreInfo, @@ -149,7 +141,7 @@ const NfcProtocolSupportBase nfc_protocol_support_iso15693_3 = { .scene_saved_menu = { .on_enter = nfc_protocol_support_common_on_enter_empty, - .on_event = nfc_scene_saved_menu_on_event_iso15693_3, + .on_event = nfc_protocol_support_common_on_event_empty, }, .scene_save_name = { diff --git a/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c b/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c index acc641c34..4b639f981 100644 --- a/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c +++ b/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c @@ -200,9 +200,6 @@ static bool nfc_scene_read_menu_on_event_mf_classic(NfcApp* instance, SceneManag scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicDictAttack); } consumed = true; - } else if(event.event == SubmenuIndexCommonEdit) { - scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid); - consumed = true; } } diff --git a/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c b/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c index 34354741d..0875f77b3 100644 --- a/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c +++ b/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c @@ -249,9 +249,6 @@ static bool nfc_scene_read_and_saved_menu_on_event_mf_ultralight( NfcSceneMfUltralightUnlockMenu; scene_manager_next_scene(instance->scene_manager, next_scene); consumed = true; - } else if(event.event == SubmenuIndexCommonEdit) { - scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid); - consumed = true; } } return consumed; diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c index 8711af476..5c4352342 100644 --- a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c @@ -304,6 +304,9 @@ static bool dolphin_deed(DolphinDeedNfcEmulate); scene_manager_next_scene(instance->scene_manager, NfcSceneWrite); consumed = true; + } else if(event.event == SubmenuIndexCommonEdit) { + scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid); + consumed = true; } else { const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device); consumed = nfc_protocol_support[protocol]->scene_read_menu.on_event(instance, event); diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_gui_common.c b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_gui_common.c index 8c38f8475..ba309244a 100644 --- a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_gui_common.c +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_gui_common.c @@ -26,9 +26,9 @@ void nfc_protocol_support_common_byte_input_done_callback(void* context) { } void nfc_protocol_support_common_text_input_done_callback(void* context) { - NfcApp* nfc = context; + NfcApp* instance = context; - view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventTextInputDone); + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventTextInputDone); } void nfc_protocol_support_common_on_enter_empty(NfcApp* instance) { diff --git a/applications/main/nfc/helpers/protocol_support/slix/slix.c b/applications/main/nfc/helpers/protocol_support/slix/slix.c index e998d0ad2..4d9a2061c 100644 --- a/applications/main/nfc/helpers/protocol_support/slix/slix.c +++ b/applications/main/nfc/helpers/protocol_support/slix/slix.c @@ -78,20 +78,21 @@ static NfcCommand nfc_scene_emulate_listener_callback_slix(NfcGenericEvent event furi_assert(event.protocol == NfcProtocolSlix); furi_assert(event.event_data); - NfcApp* nfc = context; + NfcApp* instance = context; SlixListenerEvent* slix_event = event.event_data; if(slix_event->type == SlixListenerEventTypeCustomCommand) { - if(furi_string_size(nfc->text_box_store) < NFC_LOG_SIZE_MAX) { - furi_string_cat_printf(nfc->text_box_store, "R:"); + if(furi_string_size(instance->text_box_store) < NFC_LOG_SIZE_MAX) { + furi_string_cat_printf(instance->text_box_store, "R:"); for(size_t i = 0; i < bit_buffer_get_size_bytes(slix_event->data->buffer); i++) { furi_string_cat_printf( - nfc->text_box_store, + instance->text_box_store, " %02X", bit_buffer_get_byte(slix_event->data->buffer, i)); } - furi_string_push_back(nfc->text_box_store, '\n'); - view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventListenerUpdate); + furi_string_push_back(instance->text_box_store, '\n'); + view_dispatcher_send_custom_event( + instance->view_dispatcher, NfcCustomEventListenerUpdate); } } @@ -105,15 +106,6 @@ static void nfc_scene_emulate_on_enter_slix(NfcApp* instance) { nfc_listener_start(instance->listener, nfc_scene_emulate_listener_callback_slix, instance); } -static bool nfc_scene_saved_menu_on_event_slix(NfcApp* instance, SceneManagerEvent event) { - if(event.type == SceneManagerEventTypeCustom && event.event == SubmenuIndexCommonEdit) { - scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid); - return true; - } - - return false; -} - const NfcProtocolSupportBase nfc_protocol_support_slix = { .features = NfcProtocolFeatureEmulateFull | NfcProtocolFeatureMoreInfo, @@ -145,7 +137,7 @@ const NfcProtocolSupportBase nfc_protocol_support_slix = { .scene_saved_menu = { .on_enter = nfc_protocol_support_common_on_enter_empty, - .on_event = nfc_scene_saved_menu_on_event_slix, + .on_event = nfc_protocol_support_common_on_event_empty, }, .scene_save_name = { diff --git a/applications/main/nfc/helpers/protocol_support/st25tb/st25tb.c b/applications/main/nfc/helpers/protocol_support/st25tb/st25tb.c index a119f8cd6..42b547fcb 100644 --- a/applications/main/nfc/helpers/protocol_support/st25tb/st25tb.c +++ b/applications/main/nfc/helpers/protocol_support/st25tb/st25tb.c @@ -61,15 +61,6 @@ static void nfc_scene_read_success_on_enter_st25tb(NfcApp* instance) { furi_string_free(temp_str); } -static bool nfc_scene_saved_menu_on_event_st25tb(NfcApp* instance, SceneManagerEvent event) { - if(event.type == SceneManagerEventTypeCustom && event.event == SubmenuIndexCommonEdit) { - scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid); - return true; - } - - return false; -} - const NfcProtocolSupportBase nfc_protocol_support_st25tb = { .features = NfcProtocolFeatureNone, @@ -96,7 +87,7 @@ const NfcProtocolSupportBase nfc_protocol_support_st25tb = { .scene_saved_menu = { .on_enter = nfc_protocol_support_common_on_enter_empty, - .on_event = nfc_scene_saved_menu_on_event_st25tb, + .on_event = nfc_protocol_support_common_on_event_empty, }, .scene_save_name = { diff --git a/applications/main/nfc/helpers/protocol_support/type_4_tag/type_4_tag.c b/applications/main/nfc/helpers/protocol_support/type_4_tag/type_4_tag.c index 30c64d962..3e4ab3212 100644 --- a/applications/main/nfc/helpers/protocol_support/type_4_tag/type_4_tag.c +++ b/applications/main/nfc/helpers/protocol_support/type_4_tag/type_4_tag.c @@ -139,24 +139,23 @@ static void nfc_scene_read_success_on_enter_type_4_tag(NfcApp* instance) { static NfcCommand nfc_scene_emulate_listener_callback_type_4_tag(NfcGenericEvent event, void* context) { - furi_assert(context); furi_assert(event.protocol == NfcProtocolType4Tag); - furi_assert(event.event_data); - NfcApp* nfc = context; + NfcApp* instance = context; Type4TagListenerEvent* type_4_tag_event = event.event_data; if(type_4_tag_event->type == Type4TagListenerEventTypeCustomCommand) { - if(furi_string_size(nfc->text_box_store) < NFC_LOG_SIZE_MAX) { - furi_string_cat_printf(nfc->text_box_store, "R:"); + if(furi_string_size(instance->text_box_store) < NFC_LOG_SIZE_MAX) { + furi_string_cat_printf(instance->text_box_store, "R:"); for(size_t i = 0; i < bit_buffer_get_size_bytes(type_4_tag_event->data->buffer); i++) { furi_string_cat_printf( - nfc->text_box_store, + instance->text_box_store, " %02X", bit_buffer_get_byte(type_4_tag_event->data->buffer, i)); } - furi_string_push_back(nfc->text_box_store, '\n'); - view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventListenerUpdate); + furi_string_push_back(instance->text_box_store, '\n'); + view_dispatcher_send_custom_event( + instance->view_dispatcher, NfcCustomEventListenerUpdate); } } From 4c1273378cdcb591c01e3fe5951bfd51d6be03b8 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Fri, 21 Mar 2025 06:34:49 +0000 Subject: [PATCH 47/92] Fix writing retrying after success --- applications/main/nfc/helpers/nfc_custom_event.h | 1 + .../nfc/helpers/protocol_support/nfc_protocol_support.c | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/applications/main/nfc/helpers/nfc_custom_event.h b/applications/main/nfc/helpers/nfc_custom_event.h index 16fbc4749..1f10a2dea 100644 --- a/applications/main/nfc/helpers/nfc_custom_event.h +++ b/applications/main/nfc/helpers/nfc_custom_event.h @@ -13,6 +13,7 @@ typedef enum { NfcCustomEventCardLost, NfcCustomEventViewExit, + NfcCustomEventRetry, NfcCustomEventWorkerExit, NfcCustomEventWorkerUpdate, NfcCustomEventWrongCard, diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c index 5c4352342..f28218c6f 100644 --- a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c @@ -747,7 +747,7 @@ void nfc_protocol_support_scene_write_widget_callback( void* context) { NfcApp* instance = context; if(type == InputTypeShort && result == GuiButtonTypeLeft) { - view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventViewExit); + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventRetry); } } @@ -861,7 +861,9 @@ static bool nfc_protocol_support_scene_write_on_event(NfcApp* instance, SceneMan new_state = NfcSceneWriteStateWrongCard; consumed = true; } else if(event.event == NfcCustomEventViewExit) { - // Retry + scene_manager_previous_scene(instance->scene_manager); + consumed = true; + } else if(event.event == NfcCustomEventRetry) { nfc_protocol_support_scenes[NfcProtocolSupportSceneWrite].on_exit(instance); nfc_protocol_support_scenes[NfcProtocolSupportSceneWrite].on_enter(instance); consumed = true; From ff340ea2568f393af37d74e38a24d8944291d640 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sat, 22 Mar 2025 09:17:54 +0000 Subject: [PATCH 48/92] NFC: Implement basic NTAG4xx detection --- .../nfc_protocol_support_defs.c | 2 + .../protocol_support/ntag4xx/ntag4xx.c | 138 +++++++++++++ .../protocol_support/ntag4xx/ntag4xx.h | 5 + .../protocol_support/ntag4xx/ntag4xx_render.c | 110 ++++++++++ .../protocol_support/ntag4xx/ntag4xx_render.h | 14 ++ lib/nfc/SConscript | 2 + lib/nfc/protocols/nfc_device_defs.c | 2 + lib/nfc/protocols/nfc_listener_defs.c | 1 + lib/nfc/protocols/nfc_poller_defs.c | 2 + lib/nfc/protocols/nfc_protocol.c | 13 +- lib/nfc/protocols/nfc_protocol.h | 1 + lib/nfc/protocols/ntag4xx/ntag4xx.c | 192 ++++++++++++++++++ lib/nfc/protocols/ntag4xx/ntag4xx.h | 116 +++++++++++ lib/nfc/protocols/ntag4xx/ntag4xx_i.c | 58 ++++++ lib/nfc/protocols/ntag4xx/ntag4xx_i.h | 59 ++++++ lib/nfc/protocols/ntag4xx/ntag4xx_poller.c | 165 +++++++++++++++ lib/nfc/protocols/ntag4xx/ntag4xx_poller.h | 43 ++++ .../protocols/ntag4xx/ntag4xx_poller_defs.h | 5 + lib/nfc/protocols/ntag4xx/ntag4xx_poller_i.c | 113 +++++++++++ lib/nfc/protocols/ntag4xx/ntag4xx_poller_i.h | 40 ++++ targets/f7/api_symbols.csv | 16 ++ 21 files changed, 1094 insertions(+), 3 deletions(-) create mode 100644 applications/main/nfc/helpers/protocol_support/ntag4xx/ntag4xx.c create mode 100644 applications/main/nfc/helpers/protocol_support/ntag4xx/ntag4xx.h create mode 100644 applications/main/nfc/helpers/protocol_support/ntag4xx/ntag4xx_render.c create mode 100644 applications/main/nfc/helpers/protocol_support/ntag4xx/ntag4xx_render.h create mode 100644 lib/nfc/protocols/ntag4xx/ntag4xx.c create mode 100644 lib/nfc/protocols/ntag4xx/ntag4xx.h create mode 100644 lib/nfc/protocols/ntag4xx/ntag4xx_i.c create mode 100644 lib/nfc/protocols/ntag4xx/ntag4xx_i.h create mode 100644 lib/nfc/protocols/ntag4xx/ntag4xx_poller.c create mode 100644 lib/nfc/protocols/ntag4xx/ntag4xx_poller.h create mode 100644 lib/nfc/protocols/ntag4xx/ntag4xx_poller_defs.h create mode 100644 lib/nfc/protocols/ntag4xx/ntag4xx_poller_i.c create mode 100644 lib/nfc/protocols/ntag4xx/ntag4xx_poller_i.h diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_defs.c b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_defs.c index dd724b13a..ac019bc7d 100644 --- a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_defs.c +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_defs.c @@ -21,6 +21,7 @@ #include "mf_desfire/mf_desfire.h" #include "slix/slix.h" #include "st25tb/st25tb.h" +#include "ntag4xx/ntag4xx.h" #include "type_4_tag/type_4_tag.h" /** @@ -44,6 +45,7 @@ const NfcProtocolSupportBase* nfc_protocol_support[NfcProtocolNum] = { [NfcProtocolMfDesfire] = &nfc_protocol_support_mf_desfire, [NfcProtocolSlix] = &nfc_protocol_support_slix, [NfcProtocolSt25tb] = &nfc_protocol_support_st25tb, + [NfcProtocolNtag4xx] = &nfc_protocol_support_ntag4xx, [NfcProtocolType4Tag] = &nfc_protocol_support_type_4_tag, /* Add new protocol support implementations here */ }; diff --git a/applications/main/nfc/helpers/protocol_support/ntag4xx/ntag4xx.c b/applications/main/nfc/helpers/protocol_support/ntag4xx/ntag4xx.c new file mode 100644 index 000000000..851b4ad97 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/ntag4xx/ntag4xx.c @@ -0,0 +1,138 @@ +#include "ntag4xx.h" +#include "ntag4xx_render.h" + +#include + +#include "nfc/nfc_app_i.h" + +#include "../nfc_protocol_support_common.h" +#include "../nfc_protocol_support_gui_common.h" +#include "../iso14443_4a/iso14443_4a_i.h" + +static void nfc_scene_info_on_enter_ntag4xx(NfcApp* instance) { + const NfcDevice* device = instance->nfc_device; + const Ntag4xxData* data = nfc_device_get_data(device, NfcProtocolNtag4xx); + + FuriString* temp_str = furi_string_alloc(); + nfc_append_filename_string_when_present(instance, temp_str); + furi_string_cat_printf( + temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); + nfc_render_ntag4xx_info(data, NfcProtocolFormatTypeFull, temp_str); + + widget_add_text_scroll_element( + instance->widget, 0, 0, 128, 64, furi_string_get_cstr(temp_str)); + + furi_string_free(temp_str); +} + +static void nfc_scene_more_info_on_enter_ntag4xx(NfcApp* instance) { + const NfcDevice* device = instance->nfc_device; + const Ntag4xxData* data = nfc_device_get_data(device, NfcProtocolNtag4xx); + + furi_string_reset(instance->text_box_store); + nfc_render_ntag4xx_data(data, instance->text_box_store); + + text_box_set_font(instance->text_box, TextBoxFontHex); + text_box_set_text(instance->text_box, furi_string_get_cstr(instance->text_box_store)); + + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewTextBox); +} + +static NfcCommand nfc_scene_read_poller_callback_ntag4xx(NfcGenericEvent event, void* context) { + furi_assert(event.protocol == NfcProtocolNtag4xx); + + NfcCommand command = NfcCommandContinue; + + NfcApp* instance = context; + const Ntag4xxPollerEvent* ntag4xx_event = event.event_data; + + if(ntag4xx_event->type == Ntag4xxPollerEventTypeReadSuccess) { + nfc_device_set_data( + instance->nfc_device, NfcProtocolNtag4xx, nfc_poller_get_data(instance->poller)); + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess); + command = NfcCommandStop; + } else if(ntag4xx_event->type == Ntag4xxPollerEventTypeReadFailed) { + command = NfcCommandReset; + } + + return command; +} + +static void nfc_scene_read_on_enter_ntag4xx(NfcApp* instance) { + nfc_poller_start(instance->poller, nfc_scene_read_poller_callback_ntag4xx, instance); +} + +static void nfc_scene_read_success_on_enter_ntag4xx(NfcApp* instance) { + const NfcDevice* device = instance->nfc_device; + const Ntag4xxData* data = nfc_device_get_data(device, NfcProtocolNtag4xx); + + FuriString* temp_str = furi_string_alloc(); + furi_string_cat_printf( + temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); + nfc_render_ntag4xx_info(data, NfcProtocolFormatTypeShort, temp_str); + + widget_add_text_scroll_element( + instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str)); + + furi_string_free(temp_str); +} + +static void nfc_scene_emulate_on_enter_ntag4xx(NfcApp* instance) { + const Iso14443_4aData* iso14443_4a_data = + nfc_device_get_data(instance->nfc_device, NfcProtocolIso14443_4a); + + instance->listener = + nfc_listener_alloc(instance->nfc, NfcProtocolIso14443_4a, iso14443_4a_data); + nfc_listener_start( + instance->listener, nfc_scene_emulate_listener_callback_iso14443_4a, instance); +} + +const NfcProtocolSupportBase nfc_protocol_support_ntag4xx = { + .features = NfcProtocolFeatureEmulateUid | NfcProtocolFeatureMoreInfo, + + .scene_info = + { + .on_enter = nfc_scene_info_on_enter_ntag4xx, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_more_info = + { + .on_enter = nfc_scene_more_info_on_enter_ntag4xx, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_read = + { + .on_enter = nfc_scene_read_on_enter_ntag4xx, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_read_menu = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_read_success = + { + .on_enter = nfc_scene_read_success_on_enter_ntag4xx, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_saved_menu = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_save_name = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_emulate = + { + .on_enter = nfc_scene_emulate_on_enter_ntag4xx, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_write = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, + }, +}; diff --git a/applications/main/nfc/helpers/protocol_support/ntag4xx/ntag4xx.h b/applications/main/nfc/helpers/protocol_support/ntag4xx/ntag4xx.h new file mode 100644 index 000000000..09a8388fa --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/ntag4xx/ntag4xx.h @@ -0,0 +1,5 @@ +#pragma once + +#include "../nfc_protocol_support_base.h" + +extern const NfcProtocolSupportBase nfc_protocol_support_ntag4xx; diff --git a/applications/main/nfc/helpers/protocol_support/ntag4xx/ntag4xx_render.c b/applications/main/nfc/helpers/protocol_support/ntag4xx/ntag4xx_render.c new file mode 100644 index 000000000..0cb587726 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/ntag4xx/ntag4xx_render.c @@ -0,0 +1,110 @@ +#include "ntag4xx_render.h" + +#include "../iso14443_4a/iso14443_4a_render.h" + +void nfc_render_ntag4xx_info( + const Ntag4xxData* data, + NfcProtocolFormatType format_type, + FuriString* str) { + nfc_render_iso14443_4a_brief(ntag4xx_get_base_data(data), str); + + const Ntag4xxType type = ntag4xx_get_type_from_version(&data->version); + if(type >= Ntag4xxTypeUnknown) { + furi_string_cat(str, "Memory Size: unknown"); + } else { + size_t size_cc = 32; + size_t size_ndef = 0; + size_t size_proprietary = 0; + bool has_tagtamper = false; + switch(type) { + case Ntag4xxType413DNA: + size_ndef = 128; + size_proprietary = 0; + break; + case Ntag4xxType424DNATT: + has_tagtamper = true; + /* fall-through */ + case Ntag4xxType424DNA: + size_ndef = 256; + size_proprietary = 128; + break; + case Ntag4xxType426QDNATT: + has_tagtamper = true; + /* fall-through */ + case Ntag4xxType426QDNA: + size_ndef = 768; + size_proprietary = 128; + break; + default: + break; + } + furi_string_cat_printf( + str, "\nMemory Size: %zu bytes\n", size_cc + size_ndef + size_proprietary); + furi_string_cat_printf(str, "Usable NDEF Size: %zu bytes\n", size_ndef - sizeof(uint16_t)); + furi_string_cat_printf(str, "Capability Cont.: %zu bytes\n", size_cc); + if(size_proprietary) { + furi_string_cat_printf(str, "Proprietary File: %zu bytes\n", size_proprietary); + } + furi_string_cat_printf(str, "TagTamper: %ssupported", has_tagtamper ? "" : "not "); + } + + if(format_type != NfcProtocolFormatTypeFull) return; + + furi_string_cat(str, "\n\e#ISO14443-4 data"); + nfc_render_iso14443_4a_extra(ntag4xx_get_base_data(data), str); +} + +void nfc_render_ntag4xx_data(const Ntag4xxData* data, FuriString* str) { + nfc_render_ntag4xx_version(&data->version, str); +} + +void nfc_render_ntag4xx_version(const Ntag4xxVersion* data, FuriString* str) { + furi_string_cat_printf( + str, + "%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", + data->uid[0], + data->uid[1], + data->uid[2], + data->uid[3], + data->uid[4], + data->uid[5], + data->uid[6]); + furi_string_cat_printf( + str, + "hw %02x type %02x sub %02x\n" + " maj %02x min %02x\n" + " size %02x proto %02x\n", + data->hw_vendor, + data->hw_type, + data->hw_subtype, + data->hw_major, + data->hw_minor, + data->hw_storage, + data->hw_proto); + furi_string_cat_printf( + str, + "sw %02x type %02x sub %02x\n" + " maj %02x min %02x\n" + " size %02x proto %02x\n", + data->sw_vendor, + data->sw_type, + data->sw_subtype, + data->sw_major, + data->sw_minor, + data->sw_storage, + data->sw_proto); + furi_string_cat_printf( + str, + "batch %02x:%02x:%02x:%02x:%01x\n" + "week %d year %d\n" + "fab key %02x id %02x\n", + data->batch[0], + data->batch[1], + data->batch[2], + data->batch[3], + data->batch_extra, + data->prod_week, + data->prod_year, + (data->fab_key_4b << 1) | (data->fab_key_1b), + data->optional.fab_key_id); +} diff --git a/applications/main/nfc/helpers/protocol_support/ntag4xx/ntag4xx_render.h b/applications/main/nfc/helpers/protocol_support/ntag4xx/ntag4xx_render.h new file mode 100644 index 000000000..ca81cf4c4 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/ntag4xx/ntag4xx_render.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +#include "../nfc_protocol_support_render_common.h" + +void nfc_render_ntag4xx_info( + const Ntag4xxData* data, + NfcProtocolFormatType format_type, + FuriString* str); + +void nfc_render_ntag4xx_data(const Ntag4xxData* data, FuriString* str); + +void nfc_render_ntag4xx_version(const Ntag4xxVersion* data, FuriString* str); diff --git a/lib/nfc/SConscript b/lib/nfc/SConscript index f0ca22aa7..ad9057d03 100644 --- a/lib/nfc/SConscript +++ b/lib/nfc/SConscript @@ -27,6 +27,7 @@ env.Append( File("protocols/mf_desfire/mf_desfire.h"), File("protocols/slix/slix.h"), File("protocols/st25tb/st25tb.h"), + File("protocols/ntag4xx/ntag4xx.h"), File("protocols/type_4_tag/type_4_tag.h"), # Pollers File("protocols/iso14443_3a/iso14443_3a_poller.h"), @@ -41,6 +42,7 @@ env.Append( File("protocols/mf_desfire/mf_desfire_poller.h"), File("protocols/slix/slix_poller.h"), File("protocols/st25tb/st25tb_poller.h"), + File("protocols/ntag4xx/ntag4xx_poller.h"), File("protocols/type_4_tag/type_4_tag_poller.h"), # Listeners File("protocols/iso14443_3a/iso14443_3a_listener.h"), diff --git a/lib/nfc/protocols/nfc_device_defs.c b/lib/nfc/protocols/nfc_device_defs.c index df7ebde40..1963faeb0 100644 --- a/lib/nfc/protocols/nfc_device_defs.c +++ b/lib/nfc/protocols/nfc_device_defs.c @@ -24,6 +24,7 @@ #include #include #include +#include #include /** @@ -45,6 +46,7 @@ const NfcDeviceBase* nfc_devices[NfcProtocolNum] = { [NfcProtocolMfDesfire] = &nfc_device_mf_desfire, [NfcProtocolSlix] = &nfc_device_slix, [NfcProtocolSt25tb] = &nfc_device_st25tb, + [NfcProtocolNtag4xx] = &nfc_device_ntag4xx, [NfcProtocolType4Tag] = &nfc_device_type_4_tag, /* Add new protocols here */ }; diff --git a/lib/nfc/protocols/nfc_listener_defs.c b/lib/nfc/protocols/nfc_listener_defs.c index a4f9e225a..b0c24023f 100644 --- a/lib/nfc/protocols/nfc_listener_defs.c +++ b/lib/nfc/protocols/nfc_listener_defs.c @@ -22,5 +22,6 @@ const NfcListenerBase* nfc_listeners_api[NfcProtocolNum] = { [NfcProtocolMfDesfire] = NULL, [NfcProtocolSlix] = &nfc_listener_slix, [NfcProtocolSt25tb] = NULL, + [NfcProtocolNtag4xx] = NULL, [NfcProtocolType4Tag] = &nfc_listener_type_4_tag, }; diff --git a/lib/nfc/protocols/nfc_poller_defs.c b/lib/nfc/protocols/nfc_poller_defs.c index d11b74eb6..cbbe145db 100644 --- a/lib/nfc/protocols/nfc_poller_defs.c +++ b/lib/nfc/protocols/nfc_poller_defs.c @@ -12,6 +12,7 @@ #include #include #include +#include #include const NfcPollerBase* nfc_pollers_api[NfcProtocolNum] = { @@ -27,6 +28,7 @@ const NfcPollerBase* nfc_pollers_api[NfcProtocolNum] = { [NfcProtocolMfDesfire] = &mf_desfire_poller, [NfcProtocolSlix] = &nfc_poller_slix, [NfcProtocolSt25tb] = &nfc_poller_st25tb, + [NfcProtocolNtag4xx] = &ntag4xx_poller, [NfcProtocolType4Tag] = &type_4_tag_poller, /* Add new pollers here */ }; diff --git a/lib/nfc/protocols/nfc_protocol.c b/lib/nfc/protocols/nfc_protocol.c index 7fd7ffb6d..34fa843b5 100644 --- a/lib/nfc/protocols/nfc_protocol.c +++ b/lib/nfc/protocols/nfc_protocol.c @@ -22,9 +22,9 @@ * | | | * ISO14443-4A Mf Ultralight Mf Classic * | - * +-----+------+----------+ - * | | | - * Mf Desfire Type 4 Tag Mf Plus + * +-----+------+----------+----------+ + * | | | | + * Mf Desfire Type 4 Tag Mf Plus NTAG4xx * ``` * * When implementing a new protocol, its place in the tree must be determined first. @@ -64,6 +64,7 @@ static const NfcProtocol nfc_protocol_iso14443_3b_children_protocol[] = { static const NfcProtocol nfc_protocol_iso14443_4a_children_protocol[] = { NfcProtocolMfPlus, NfcProtocolMfDesfire, + NfcProtocolNtag4xx, NfcProtocolType4Tag, }; @@ -156,6 +157,12 @@ static const NfcProtocolTreeNode nfc_protocol_nodes[NfcProtocolNum] = { .children_num = 0, .children_protocol = NULL, }, + [NfcProtocolNtag4xx] = + { + .parent_protocol = NfcProtocolIso14443_4a, + .children_num = 0, + .children_protocol = NULL, + }, [NfcProtocolType4Tag] = { .parent_protocol = NfcProtocolIso14443_4a, diff --git a/lib/nfc/protocols/nfc_protocol.h b/lib/nfc/protocols/nfc_protocol.h index db03c74e7..03caa3979 100644 --- a/lib/nfc/protocols/nfc_protocol.h +++ b/lib/nfc/protocols/nfc_protocol.h @@ -188,6 +188,7 @@ typedef enum { NfcProtocolMfDesfire, NfcProtocolSlix, NfcProtocolSt25tb, + NfcProtocolNtag4xx, NfcProtocolType4Tag, /* Add new protocols here */ diff --git a/lib/nfc/protocols/ntag4xx/ntag4xx.c b/lib/nfc/protocols/ntag4xx/ntag4xx.c new file mode 100644 index 000000000..7b0991903 --- /dev/null +++ b/lib/nfc/protocols/ntag4xx/ntag4xx.c @@ -0,0 +1,192 @@ +#include "ntag4xx_i.h" + +#include + +#define NTAG4XX_PROTOCOL_NAME "NTAG4xx" + +#define NTAG4XX_HW_MAJOR_TYPE_413_DNA (0x10) +#define NTAG4XX_HW_MAJOR_TYPE_424_DNA (0x30) + +#define NTAG4XX_HW_SUBTYPE_TAGTAMPER_FLAG (0x08) + +static const char* ntag4xx_type_strings[] = { + [Ntag4xxType413DNA] = "NTAG413 DNA", + [Ntag4xxType424DNA] = "NTAG424 DNA", + [Ntag4xxType424DNATT] = "NTAG424 DNA TagTamper", + [Ntag4xxType426QDNA] = "NTAG426Q DNA", + [Ntag4xxType426QDNATT] = "NTAG426Q DNA TagTamper", + [Ntag4xxTypeUnknown] = "UNK", +}; + +const NfcDeviceBase nfc_device_ntag4xx = { + .protocol_name = NTAG4XX_PROTOCOL_NAME, + .alloc = (NfcDeviceAlloc)ntag4xx_alloc, + .free = (NfcDeviceFree)ntag4xx_free, + .reset = (NfcDeviceReset)ntag4xx_reset, + .copy = (NfcDeviceCopy)ntag4xx_copy, + .verify = (NfcDeviceVerify)ntag4xx_verify, + .load = (NfcDeviceLoad)ntag4xx_load, + .save = (NfcDeviceSave)ntag4xx_save, + .is_equal = (NfcDeviceEqual)ntag4xx_is_equal, + .get_name = (NfcDeviceGetName)ntag4xx_get_device_name, + .get_uid = (NfcDeviceGetUid)ntag4xx_get_uid, + .set_uid = (NfcDeviceSetUid)ntag4xx_set_uid, + .get_base_data = (NfcDeviceGetBaseData)ntag4xx_get_base_data, +}; + +Ntag4xxData* ntag4xx_alloc(void) { + Ntag4xxData* data = malloc(sizeof(Ntag4xxData)); + data->iso14443_4a_data = iso14443_4a_alloc(); + data->device_name = furi_string_alloc(); + return data; +} + +void ntag4xx_free(Ntag4xxData* data) { + furi_check(data); + + ntag4xx_reset(data); + iso14443_4a_free(data->iso14443_4a_data); + furi_string_free(data->device_name); + free(data); +} + +void ntag4xx_reset(Ntag4xxData* data) { + furi_check(data); + + iso14443_4a_reset(data->iso14443_4a_data); + + memset(&data->version, 0, sizeof(Ntag4xxVersion)); +} + +void ntag4xx_copy(Ntag4xxData* data, const Ntag4xxData* other) { + furi_check(data); + furi_check(other); + + ntag4xx_reset(data); + + iso14443_4a_copy(data->iso14443_4a_data, other->iso14443_4a_data); + + data->version = other->version; +} + +bool ntag4xx_verify(Ntag4xxData* data, const FuriString* device_type) { + UNUSED(data); + UNUSED(device_type); + + return false; +} + +bool ntag4xx_load(Ntag4xxData* data, FlipperFormat* ff, uint32_t version) { + furi_check(data); + furi_check(ff); + + FuriString* prefix = furi_string_alloc(); + + bool success = false; + + do { + if(!iso14443_4a_load(data->iso14443_4a_data, ff, version)) break; + + if(!ntag4xx_version_load(&data->version, ff)) break; + + success = true; + } while(false); + + furi_string_free(prefix); + return success; +} + +bool ntag4xx_save(const Ntag4xxData* data, FlipperFormat* ff) { + furi_check(data); + furi_check(ff); + + 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, NTAG4XX_PROTOCOL_NAME " specific data")) break; + if(!ntag4xx_version_save(&data->version, ff)) break; + + success = true; + } while(false); + + furi_string_free(prefix); + return success; +} + +bool ntag4xx_is_equal(const Ntag4xxData* data, const Ntag4xxData* other) { + furi_check(data); + furi_check(other); + + return iso14443_4a_is_equal(data->iso14443_4a_data, other->iso14443_4a_data) && + memcmp(&data->version, &other->version, sizeof(Ntag4xxVersion)) == 0; +} + +Ntag4xxType ntag4xx_get_type_from_version(const Ntag4xxVersion* const version) { + Ntag4xxType type = Ntag4xxTypeUnknown; + + switch(version->hw_major) { + case NTAG4XX_HW_MAJOR_TYPE_413_DNA: + type = Ntag4xxType413DNA; + break; + case NTAG4XX_HW_MAJOR_TYPE_424_DNA: + if(version->hw_subtype & NTAG4XX_HW_SUBTYPE_TAGTAMPER_FLAG) { + type = Ntag4xxType424DNATT; + } else { + type = Ntag4xxType424DNA; + } + break; + // TODO: there is no info online or in other implementations (NXP TagInfo, NFC Tools, Proxmark3) + // about what the HWMajorVersion is supposed to be for NTAG426Q DNA, and they don't seem to be for sale + // case NTAG4XX_HW_MAJOR_TYPE_426Q_DNA: + // if(version->hw_subtype & NTAG4XX_HW_SUBTYPE_TAGTAMPER_FLAG) { + // type = Ntag4xxType426QDNATT; + // } else { + // type = Ntag4xxType426QDNA; + // } + // break; + default: + break; + } + + return type; +} + +const char* ntag4xx_get_device_name(const Ntag4xxData* data, NfcDeviceNameType name_type) { + furi_check(data); + + const Ntag4xxType type = ntag4xx_get_type_from_version(&data->version); + + if(type == Ntag4xxTypeUnknown) { + furi_string_printf(data->device_name, "Unknown %s", NTAG4XX_PROTOCOL_NAME); + } else { + furi_string_printf(data->device_name, "%s", ntag4xx_type_strings[type]); + if(name_type == NfcDeviceNameTypeShort) { + furi_string_replace(data->device_name, "TagTamper", "TT"); + } + } + + return furi_string_get_cstr(data->device_name); +} + +const uint8_t* ntag4xx_get_uid(const Ntag4xxData* data, size_t* uid_len) { + furi_check(data); + furi_check(uid_len); + + return iso14443_4a_get_uid(data->iso14443_4a_data, uid_len); +} + +bool ntag4xx_set_uid(Ntag4xxData* data, const uint8_t* uid, size_t uid_len) { + furi_check(data); + + return iso14443_4a_set_uid(data->iso14443_4a_data, uid, uid_len); +} + +Iso14443_4aData* ntag4xx_get_base_data(const Ntag4xxData* data) { + furi_check(data); + + return data->iso14443_4a_data; +} diff --git a/lib/nfc/protocols/ntag4xx/ntag4xx.h b/lib/nfc/protocols/ntag4xx/ntag4xx.h new file mode 100644 index 000000000..85dd1e52d --- /dev/null +++ b/lib/nfc/protocols/ntag4xx/ntag4xx.h @@ -0,0 +1,116 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define NTAG4XX_UID_SIZE (7) +#define NTAG4XX_BATCH_SIZE (4) +#define NTAG4XX_BATCH_EXTRA_BITS 4 +#define NTAG4XX_FAB_KEY_SIZE_BITS_4 4 +#define NTAG4XX_FAB_KEY_SIZE_BITS_1 1 +#define NTAG4XX_PROD_WEEK_SIZE_BITS 7 + +#define NTAG4XX_CMD_GET_VERSION (0x60) + +typedef enum { + Ntag4xxErrorNone, + Ntag4xxErrorNotPresent, + Ntag4xxErrorProtocol, + Ntag4xxErrorTimeout, + Ntag4xxErrorAuthentication, + Ntag4xxErrorCommandNotSupported, +} Ntag4xxError; + +typedef enum { + Ntag4xxType413DNA, + Ntag4xxType424DNA, + Ntag4xxType424DNATT, + Ntag4xxType426QDNA, + Ntag4xxType426QDNATT, + + Ntag4xxTypeUnknown, + Ntag4xxTypeNum, +} Ntag4xxType; + +#pragma pack(push, 1) +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[NTAG4XX_UID_SIZE]; + // [36b batch][5b fab key][7b prod week] + // 5b fab key is split 4b in last byte of batch and 1b in prod week + // Due to endianness, they appear swapped in the struct definition + uint8_t batch[NTAG4XX_BATCH_SIZE]; + struct { + uint8_t fab_key_4b : NTAG4XX_FAB_KEY_SIZE_BITS_4; + uint8_t batch_extra : NTAG4XX_BATCH_EXTRA_BITS; + }; + struct { + uint8_t prod_week : NTAG4XX_PROD_WEEK_SIZE_BITS; + uint8_t fab_key_1b : NTAG4XX_FAB_KEY_SIZE_BITS_1; + }; + uint8_t prod_year; + struct { + uint8_t fab_key_id; + } optional; +} Ntag4xxVersion; +#pragma pack(pop) + +typedef struct { + Iso14443_4aData* iso14443_4a_data; + Ntag4xxVersion version; + FuriString* device_name; +} Ntag4xxData; + +extern const NfcDeviceBase nfc_device_ntag4xx; + +// Virtual methods + +Ntag4xxData* ntag4xx_alloc(void); + +void ntag4xx_free(Ntag4xxData* data); + +void ntag4xx_reset(Ntag4xxData* data); + +void ntag4xx_copy(Ntag4xxData* data, const Ntag4xxData* other); + +bool ntag4xx_verify(Ntag4xxData* data, const FuriString* device_type); + +bool ntag4xx_load(Ntag4xxData* data, FlipperFormat* ff, uint32_t version); + +bool ntag4xx_save(const Ntag4xxData* data, FlipperFormat* ff); + +bool ntag4xx_is_equal(const Ntag4xxData* data, const Ntag4xxData* other); + +const char* ntag4xx_get_device_name(const Ntag4xxData* data, NfcDeviceNameType name_type); + +const uint8_t* ntag4xx_get_uid(const Ntag4xxData* data, size_t* uid_len); + +bool ntag4xx_set_uid(Ntag4xxData* data, const uint8_t* uid, size_t uid_len); + +Iso14443_4aData* ntag4xx_get_base_data(const Ntag4xxData* data); + +// Helpers + +Ntag4xxType ntag4xx_get_type_from_version(const Ntag4xxVersion* const version); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/ntag4xx/ntag4xx_i.c b/lib/nfc/protocols/ntag4xx/ntag4xx_i.c new file mode 100644 index 000000000..6a0c7114a --- /dev/null +++ b/lib/nfc/protocols/ntag4xx/ntag4xx_i.c @@ -0,0 +1,58 @@ +#include "ntag4xx_i.h" + +#define TAG "Ntag4xx" + +#define NTAG4XX_FFF_VERSION_KEY \ + NTAG4XX_FFF_PICC_PREFIX " " \ + "Version" + +Ntag4xxError ntag4xx_process_error(Iso14443_4aError error) { + switch(error) { + case Iso14443_4aErrorNone: + return Ntag4xxErrorNone; + case Iso14443_4aErrorNotPresent: + return Ntag4xxErrorNotPresent; + case Iso14443_4aErrorTimeout: + return Ntag4xxErrorTimeout; + default: + return Ntag4xxErrorProtocol; + } +} + +Ntag4xxError ntag4xx_process_status_code(uint8_t status_code) { + switch(status_code) { + case NTAG4XX_STATUS_OPERATION_OK: + return Ntag4xxErrorNone; + case NTAG4XX_STATUS_AUTHENTICATION_ERROR: + return Ntag4xxErrorAuthentication; + case NTAG4XX_STATUS_ILLEGAL_COMMAND_CODE: + return Ntag4xxErrorCommandNotSupported; + default: + return Ntag4xxErrorProtocol; + } +} + +bool ntag4xx_version_parse(Ntag4xxVersion* data, const BitBuffer* buf) { + const size_t buf_size = bit_buffer_get_size_bytes(buf); + const bool can_parse = buf_size == sizeof(Ntag4xxVersion) || + buf_size == sizeof(Ntag4xxVersion) - sizeof(data->optional); + + if(can_parse) { + bit_buffer_write_bytes(buf, data, sizeof(Ntag4xxVersion)); + if(buf_size < sizeof(Ntag4xxVersion)) { + memset(&data->optional, 0, sizeof(data->optional)); + } + } + + return can_parse && data->hw_type == 0x04; +} + +bool ntag4xx_version_load(Ntag4xxVersion* data, FlipperFormat* ff) { + return flipper_format_read_hex( + ff, NTAG4XX_FFF_VERSION_KEY, (uint8_t*)data, sizeof(Ntag4xxVersion)); +} + +bool ntag4xx_version_save(const Ntag4xxVersion* data, FlipperFormat* ff) { + return flipper_format_write_hex( + ff, NTAG4XX_FFF_VERSION_KEY, (const uint8_t*)data, sizeof(Ntag4xxVersion)); +} diff --git a/lib/nfc/protocols/ntag4xx/ntag4xx_i.h b/lib/nfc/protocols/ntag4xx/ntag4xx_i.h new file mode 100644 index 000000000..db6dabd4b --- /dev/null +++ b/lib/nfc/protocols/ntag4xx/ntag4xx_i.h @@ -0,0 +1,59 @@ +#pragma once + +#include "ntag4xx.h" + +#define NTAG4XX_FFF_PICC_PREFIX "PICC" + +// ISO 7816 command wrapping +#define NTAG4XX_CMD_ISO_CLA (0x90) +#define NTAG4XX_CMD_ISO_P1 (0x00) +#define NTAG4XX_CMD_ISO_P2 (0x00) +#define NTAG4XX_CMD_ISO_LE (0x00) +// ISO 7816 status wrapping +#define NTAG4XX_STATUS_ISO_SW1 (0x91) + +// Successful operation +#define NTAG4XX_STATUS_OPERATION_OK (0x00) +// Command code not supported +#define NTAG4XX_STATUS_ILLEGAL_COMMAND_CODE (0x1C) +// CRC or MAC does not match data Padding bytes not valid +#define NTAG4XX_STATUS_INTEGRITY_ERROR (0x1E) +// Invalid key number specified +#define NTAG4XX_STATUS_NO_SUCH_KEY (0x40) +// Length of command string invalid +#define NTAG4XX_STATUS_LENGTH_ERROR (0x7E) +// Current configuration / status does not allow the requested command +#define NTAG4XX_STATUS_PERMISSION_DENIED (0x9D) +// Value of the parameter(s) invalid +#define NTAG4XX_STATUS_PARAMETER_ERROR (0x9E) +// Currently not allowed to authenticate. Keep trying until full delay is spent +#define NTAG4XX_STATUS_AUTHENTICATION_DELAY (0xAD) +// Current authentication status does not allow the requested command +#define NTAG4XX_STATUS_AUTHENTICATION_ERROR (0xAE) +// Additional data frame is expected to be sent +#define NTAG4XX_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 NTAG4XX_STATUS_BOUNDARY_ERROR (0xBE) +// Previous Command was not fully completed. Not all Frames were requested or provided by the PCD +#define NTAG4XX_STATUS_COMMAND_ABORTED (0xCA) +// Specified file number does not exist +#define NTAG4XX_STATUS_FILE_NOT_FOUND (0xF0) + +// Internal helpers + +Ntag4xxError ntag4xx_process_error(Iso14443_4aError error); + +Ntag4xxError ntag4xx_process_status_code(uint8_t status_code); + +// Parse internal Ntag4xx structures + +bool ntag4xx_version_parse(Ntag4xxVersion* data, const BitBuffer* buf); + +// Load internal Ntag4xx structures + +bool ntag4xx_version_load(Ntag4xxVersion* data, FlipperFormat* ff); + +// Save internal Ntag4xx structures + +bool ntag4xx_version_save(const Ntag4xxVersion* data, FlipperFormat* ff); diff --git a/lib/nfc/protocols/ntag4xx/ntag4xx_poller.c b/lib/nfc/protocols/ntag4xx/ntag4xx_poller.c new file mode 100644 index 000000000..38e6f19d4 --- /dev/null +++ b/lib/nfc/protocols/ntag4xx/ntag4xx_poller.c @@ -0,0 +1,165 @@ +#include "ntag4xx_poller_i.h" + +#include + +#include + +#define TAG "Ntag4xxPoller" + +#define NTAG4XX_BUF_SIZE (64U) +#define NTAG4XX_RESULT_BUF_SIZE (512U) + +typedef NfcCommand (*Ntag4xxPollerReadHandler)(Ntag4xxPoller* instance); + +static const Ntag4xxData* ntag4xx_poller_get_data(Ntag4xxPoller* instance) { + furi_assert(instance); + + return instance->data; +} + +static Ntag4xxPoller* ntag4xx_poller_alloc(Iso14443_4aPoller* iso14443_4a_poller) { + Ntag4xxPoller* instance = malloc(sizeof(Ntag4xxPoller)); + instance->iso14443_4a_poller = iso14443_4a_poller; + instance->data = ntag4xx_alloc(); + instance->tx_buffer = bit_buffer_alloc(NTAG4XX_BUF_SIZE); + instance->rx_buffer = bit_buffer_alloc(NTAG4XX_BUF_SIZE); + instance->input_buffer = bit_buffer_alloc(NTAG4XX_BUF_SIZE); + instance->result_buffer = bit_buffer_alloc(NTAG4XX_RESULT_BUF_SIZE); + + instance->ntag4xx_event.data = &instance->ntag4xx_event_data; + + instance->general_event.protocol = NfcProtocolNtag4xx; + instance->general_event.event_data = &instance->ntag4xx_event; + instance->general_event.instance = instance; + + return instance; +} + +static void ntag4xx_poller_free(Ntag4xxPoller* instance) { + furi_assert(instance); + + ntag4xx_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 ntag4xx_poller_handler_idle(Ntag4xxPoller* 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 = Ntag4xxPollerStateReadVersion; + return NfcCommandContinue; +} + +static NfcCommand ntag4xx_poller_handler_read_version(Ntag4xxPoller* instance) { + instance->error = ntag4xx_poller_read_version(instance, &instance->data->version); + if(instance->error == Ntag4xxErrorNone) { + FURI_LOG_D(TAG, "Read version success"); + instance->state = Ntag4xxPollerStateReadSuccess; + } else { + FURI_LOG_E(TAG, "Failed to read version"); + iso14443_4a_poller_halt(instance->iso14443_4a_poller); + instance->state = Ntag4xxPollerStateReadFailed; + } + + return NfcCommandContinue; +} + +static NfcCommand ntag4xx_poller_handler_read_failed(Ntag4xxPoller* instance) { + FURI_LOG_D(TAG, "Read Failed"); + iso14443_4a_poller_halt(instance->iso14443_4a_poller); + instance->ntag4xx_event.type = Ntag4xxPollerEventTypeReadFailed; + instance->ntag4xx_event.data->error = instance->error; + NfcCommand command = instance->callback(instance->general_event, instance->context); + instance->state = Ntag4xxPollerStateIdle; + return command; +} + +static NfcCommand ntag4xx_poller_handler_read_success(Ntag4xxPoller* instance) { + FURI_LOG_D(TAG, "Read success"); + iso14443_4a_poller_halt(instance->iso14443_4a_poller); + instance->ntag4xx_event.type = Ntag4xxPollerEventTypeReadSuccess; + NfcCommand command = instance->callback(instance->general_event, instance->context); + return command; +} + +static const Ntag4xxPollerReadHandler ntag4xx_poller_read_handler[Ntag4xxPollerStateNum] = { + [Ntag4xxPollerStateIdle] = ntag4xx_poller_handler_idle, + [Ntag4xxPollerStateReadVersion] = ntag4xx_poller_handler_read_version, + [Ntag4xxPollerStateReadFailed] = ntag4xx_poller_handler_read_failed, + [Ntag4xxPollerStateReadSuccess] = ntag4xx_poller_handler_read_success, +}; + +static void ntag4xx_poller_set_callback( + Ntag4xxPoller* instance, + NfcGenericCallback callback, + void* context) { + furi_assert(instance); + furi_assert(callback); + + instance->callback = callback; + instance->context = context; +} + +static NfcCommand ntag4xx_poller_run(NfcGenericEvent event, void* context) { + furi_assert(event.protocol == NfcProtocolIso14443_4a); + + Ntag4xxPoller* 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 = ntag4xx_poller_read_handler[instance->state](instance); + } else if(iso14443_4a_event->type == Iso14443_4aPollerEventTypeError) { + instance->ntag4xx_event.type = Ntag4xxPollerEventTypeReadFailed; + command = instance->callback(instance->general_event, instance->context); + } + + return command; +} + +static bool ntag4xx_poller_detect(NfcGenericEvent event, void* context) { + furi_assert(event.protocol == NfcProtocolIso14443_4a); + + Ntag4xxPoller* 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) { + do { + Ntag4xxError error = ntag4xx_poller_read_version(instance, &instance->data->version); + if(error != Ntag4xxErrorNone) break; + + protocol_detected = true; + } while(false); + } + + return protocol_detected; +} + +const NfcPollerBase ntag4xx_poller = { + .alloc = (NfcPollerAlloc)ntag4xx_poller_alloc, + .free = (NfcPollerFree)ntag4xx_poller_free, + .set_callback = (NfcPollerSetCallback)ntag4xx_poller_set_callback, + .run = (NfcPollerRun)ntag4xx_poller_run, + .detect = (NfcPollerDetect)ntag4xx_poller_detect, + .get_data = (NfcPollerGetData)ntag4xx_poller_get_data, +}; diff --git a/lib/nfc/protocols/ntag4xx/ntag4xx_poller.h b/lib/nfc/protocols/ntag4xx/ntag4xx_poller.h new file mode 100644 index 000000000..ce7adc785 --- /dev/null +++ b/lib/nfc/protocols/ntag4xx/ntag4xx_poller.h @@ -0,0 +1,43 @@ +#pragma once + +#include "ntag4xx.h" + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Ntag4xxPoller opaque type definition. + */ +typedef struct Ntag4xxPoller Ntag4xxPoller; + +/** + * @brief Enumeration of possible Ntag4xx poller event types. + */ +typedef enum { + Ntag4xxPollerEventTypeReadSuccess, /**< Card was read successfully. */ + Ntag4xxPollerEventTypeReadFailed, /**< Poller failed to read card. */ +} Ntag4xxPollerEventType; + +/** + * @brief Ntag4xx poller event data. + */ +typedef union { + Ntag4xxError error; /**< Error code indicating card reading fail reason. */ +} Ntag4xxPollerEventData; + +/** + * @brief Ntag4xx poller event structure. + * + * Upon emission of an event, an instance of this struct will be passed to the callback. + */ +typedef struct { + Ntag4xxPollerEventType type; /**< Type of emmitted event. */ + Ntag4xxPollerEventData* data; /**< Pointer to event specific data. */ +} Ntag4xxPollerEvent; + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/ntag4xx/ntag4xx_poller_defs.h b/lib/nfc/protocols/ntag4xx/ntag4xx_poller_defs.h new file mode 100644 index 000000000..ac0cdce9b --- /dev/null +++ b/lib/nfc/protocols/ntag4xx/ntag4xx_poller_defs.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +extern const NfcPollerBase ntag4xx_poller; diff --git a/lib/nfc/protocols/ntag4xx/ntag4xx_poller_i.c b/lib/nfc/protocols/ntag4xx/ntag4xx_poller_i.c new file mode 100644 index 000000000..9fc53e0a5 --- /dev/null +++ b/lib/nfc/protocols/ntag4xx/ntag4xx_poller_i.c @@ -0,0 +1,113 @@ +#include "ntag4xx_poller_i.h" + +#include + +#include "ntag4xx_i.h" + +#define TAG "Ntag4xxPoller" + +Ntag4xxError ntag4xx_poller_send_chunks( + Ntag4xxPoller* instance, + const BitBuffer* tx_buffer, + BitBuffer* rx_buffer) { + furi_check(instance); + furi_check(instance->iso14443_4a_poller); + furi_check(instance->tx_buffer); + furi_check(instance->rx_buffer); + furi_check(tx_buffer); + furi_check(rx_buffer); + + Ntag4xxError error = Ntag4xxErrorNone; + uint8_t status_code = NTAG4XX_STATUS_OPERATION_OK; + + do { + bit_buffer_reset(instance->tx_buffer); + bit_buffer_append_byte(instance->tx_buffer, NTAG4XX_CMD_ISO_CLA); + bit_buffer_append_byte(instance->tx_buffer, bit_buffer_get_byte(tx_buffer, 0)); + bit_buffer_append_byte(instance->tx_buffer, NTAG4XX_CMD_ISO_P1); + bit_buffer_append_byte(instance->tx_buffer, NTAG4XX_CMD_ISO_P2); + if(bit_buffer_get_size_bytes(tx_buffer) > 1) { + bit_buffer_append_byte(instance->tx_buffer, bit_buffer_get_size_bytes(tx_buffer) - 1); + bit_buffer_append_right(instance->tx_buffer, tx_buffer, 1); + } + bit_buffer_append_byte(instance->tx_buffer, NTAG4XX_CMD_ISO_LE); + + 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 = ntag4xx_process_error(iso14443_4a_error); + break; + } + + bit_buffer_reset(instance->tx_buffer); + bit_buffer_append_byte(instance->tx_buffer, NTAG4XX_CMD_ISO_CLA); + bit_buffer_append_byte(instance->tx_buffer, NTAG4XX_STATUS_ADDITIONAL_FRAME); + bit_buffer_append_byte(instance->tx_buffer, NTAG4XX_CMD_ISO_P1); + bit_buffer_append_byte(instance->tx_buffer, NTAG4XX_CMD_ISO_P2); + bit_buffer_append_byte(instance->tx_buffer, NTAG4XX_CMD_ISO_LE); + + size_t response_len = bit_buffer_get_size_bytes(instance->rx_buffer); + status_code = NTAG4XX_STATUS_LENGTH_ERROR; + bit_buffer_reset(rx_buffer); + if(response_len >= 2 * sizeof(uint8_t) && + bit_buffer_get_byte(instance->rx_buffer, response_len - 2) == NTAG4XX_STATUS_ISO_SW1) { + status_code = bit_buffer_get_byte(instance->rx_buffer, response_len - 1); + if(response_len > 2 * sizeof(uint8_t)) { + bit_buffer_copy_left( + rx_buffer, instance->rx_buffer, response_len - 2 * sizeof(uint8_t)); + } + } + + while(status_code == NTAG4XX_STATUS_ADDITIONAL_FRAME) { + 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 = ntag4xx_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); + + status_code = rx_size < 2 ? NTAG4XX_STATUS_LENGTH_ERROR : + bit_buffer_get_byte(instance->rx_buffer, rx_size - 1); + if(rx_size <= rx_capacity_remaining + 2) { + bit_buffer_set_size_bytes(instance->rx_buffer, rx_size - 2); + bit_buffer_append(rx_buffer, instance->rx_buffer); + } else { + FURI_LOG_W(TAG, "RX buffer overflow: ignoring %zu bytes", rx_size - 2); + } + } + } while(false); + + if(error == Ntag4xxErrorNone) { + error = ntag4xx_process_status_code(status_code); + } + + return error; +} + +Ntag4xxError ntag4xx_poller_read_version(Ntag4xxPoller* instance, Ntag4xxVersion* data) { + furi_check(instance); + + bit_buffer_reset(instance->input_buffer); + bit_buffer_append_byte(instance->input_buffer, NTAG4XX_CMD_GET_VERSION); + + Ntag4xxError error; + + do { + error = + ntag4xx_poller_send_chunks(instance, instance->input_buffer, instance->result_buffer); + + if(error != Ntag4xxErrorNone) break; + + if(!ntag4xx_version_parse(data, instance->result_buffer)) { + error = Ntag4xxErrorProtocol; + } + } while(false); + + return error; +} diff --git a/lib/nfc/protocols/ntag4xx/ntag4xx_poller_i.h b/lib/nfc/protocols/ntag4xx/ntag4xx_poller_i.h new file mode 100644 index 000000000..b77e694ef --- /dev/null +++ b/lib/nfc/protocols/ntag4xx/ntag4xx_poller_i.h @@ -0,0 +1,40 @@ +#pragma once + +#include "ntag4xx_poller.h" + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + Ntag4xxPollerStateIdle, + Ntag4xxPollerStateReadVersion, + Ntag4xxPollerStateReadFailed, + Ntag4xxPollerStateReadSuccess, + + Ntag4xxPollerStateNum, +} Ntag4xxPollerState; + +struct Ntag4xxPoller { + Iso14443_4aPoller* iso14443_4a_poller; + Ntag4xxPollerState state; + Ntag4xxError error; + Ntag4xxData* data; + BitBuffer* tx_buffer; + BitBuffer* rx_buffer; + BitBuffer* input_buffer; + BitBuffer* result_buffer; + Ntag4xxPollerEventData ntag4xx_event_data; + Ntag4xxPollerEvent ntag4xx_event; + NfcGenericEvent general_event; + NfcGenericCallback callback; + void* context; +}; + +Ntag4xxError ntag4xx_poller_read_version(Ntag4xxPoller* instance, Ntag4xxVersion* data); + +#ifdef __cplusplus +} +#endif diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index 71d065f75..385b687dc 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -167,6 +167,8 @@ Header,+,lib/nfc/protocols/mf_ultralight/mf_ultralight.h,, Header,+,lib/nfc/protocols/mf_ultralight/mf_ultralight_listener.h,, Header,+,lib/nfc/protocols/mf_ultralight/mf_ultralight_poller.h,, Header,+,lib/nfc/protocols/mf_ultralight/mf_ultralight_poller_sync.h,, +Header,+,lib/nfc/protocols/ntag4xx/ntag4xx.h,, +Header,+,lib/nfc/protocols/ntag4xx/ntag4xx_poller.h,, Header,+,lib/nfc/protocols/slix/slix.h,, Header,+,lib/nfc/protocols/slix/slix_listener.h,, Header,+,lib/nfc/protocols/slix/slix_poller.h,, @@ -2905,6 +2907,19 @@ Function,+,notification_internal_message_block,void,"NotificationApp*, const Not Function,+,notification_message,void,"NotificationApp*, const NotificationSequence*" Function,+,notification_message_block,void,"NotificationApp*, const NotificationSequence*" Function,-,nrand48,long,unsigned short[3] +Function,+,ntag4xx_alloc,Ntag4xxData*, +Function,+,ntag4xx_copy,void,"Ntag4xxData*, const Ntag4xxData*" +Function,+,ntag4xx_free,void,Ntag4xxData* +Function,+,ntag4xx_get_base_data,Iso14443_4aData*,const Ntag4xxData* +Function,+,ntag4xx_get_device_name,const char*,"const Ntag4xxData*, NfcDeviceNameType" +Function,+,ntag4xx_get_type_from_version,Ntag4xxType,const Ntag4xxVersion* +Function,+,ntag4xx_get_uid,const uint8_t*,"const Ntag4xxData*, size_t*" +Function,+,ntag4xx_is_equal,_Bool,"const Ntag4xxData*, const Ntag4xxData*" +Function,+,ntag4xx_load,_Bool,"Ntag4xxData*, FlipperFormat*, uint32_t" +Function,+,ntag4xx_reset,void,Ntag4xxData* +Function,+,ntag4xx_save,_Bool,"const Ntag4xxData*, FlipperFormat*" +Function,+,ntag4xx_set_uid,_Bool,"Ntag4xxData*, const uint8_t*, size_t" +Function,+,ntag4xx_verify,_Bool,"Ntag4xxData*, const FuriString*" Function,+,number_input_alloc,NumberInput*, Function,+,number_input_free,void,NumberInput* Function,+,number_input_get_view,View*,NumberInput* @@ -4045,6 +4060,7 @@ Variable,-,nfc_device_mf_classic,const NfcDeviceBase, Variable,-,nfc_device_mf_desfire,const NfcDeviceBase, Variable,-,nfc_device_mf_plus,const NfcDeviceBase, Variable,-,nfc_device_mf_ultralight,const NfcDeviceBase, +Variable,-,nfc_device_ntag4xx,const NfcDeviceBase, Variable,-,nfc_device_st25tb,const NfcDeviceBase, Variable,-,nfc_device_type_4_tag,const NfcDeviceBase, Variable,+,sequence_audiovisual_alert,const NotificationSequence, From 11af323f4fc3993a562aee5b14e939a6d5d2b250 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sat, 22 Mar 2025 09:25:12 +0000 Subject: [PATCH 49/92] Small cleanup --- lib/nfc/protocols/type_4_tag/type_4_tag_poller.c | 6 +++--- lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.h | 2 -- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_poller.c b/lib/nfc/protocols/type_4_tag/type_4_tag_poller.c index 1fe07d3c2..0291d2df9 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag_poller.c +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_poller.c @@ -6,7 +6,7 @@ typedef NfcCommand (*Type4TagPollerReadHandler)(Type4TagPoller* instance); -const Type4TagData* type_4_tag_poller_get_data(Type4TagPoller* instance) { +static const Type4TagData* type_4_tag_poller_get_data(Type4TagPoller* instance) { furi_assert(instance); return instance->data; @@ -186,7 +186,7 @@ static NfcCommand type_4_tag_poller_handler_write_ndef(Type4TagPoller* instance) return NfcCommandContinue; } -static NfcCommand type_4_tag_poller_handler_fail(Type4TagPoller* instance) { +static NfcCommand type_4_tag_poller_handler_failed(Type4TagPoller* instance) { FURI_LOG_D(TAG, "Operation Failed"); iso14443_4a_poller_halt(instance->iso14443_4a_poller); instance->type_4_tag_event.type = instance->mode == Type4TagPollerModeRead ? @@ -219,7 +219,7 @@ static const Type4TagPollerReadHandler type_4_tag_poller_read_handler[Type4TagPo [Type4TagPollerStateCreateCapabilityContainer] = type_4_tag_poller_handler_create_cc, [Type4TagPollerStateCreateNdefMessage] = type_4_tag_poller_handler_create_ndef, [Type4TagPollerStateWriteNdefMessage] = type_4_tag_poller_handler_write_ndef, - [Type4TagPollerStateFailed] = type_4_tag_poller_handler_fail, + [Type4TagPollerStateFailed] = type_4_tag_poller_handler_failed, [Type4TagPollerStateSuccess] = type_4_tag_poller_handler_success, }; diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.h b/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.h index 0989bca60..1f9142e6e 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.h +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.h @@ -40,8 +40,6 @@ struct Type4TagPoller { void* context; }; -const Type4TagData* type_4_tag_poller_get_data(Type4TagPoller* instance); - Type4TagError type_4_tag_poller_detect_platform(Type4TagPoller* instance); Type4TagError type_4_tag_poller_select_app(Type4TagPoller* instance); From 49127b199bec272e294d9f5eba2c4916b0f2564c Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sat, 22 Mar 2025 09:30:26 +0000 Subject: [PATCH 50/92] Check HWMajorVersion like NXP commands \o/ See: https://www.nxp.com/docs/en/application-note/AN10833.pdf --- lib/nfc/protocols/mf_desfire/mf_desfire_i.c | 2 +- lib/nfc/protocols/mf_plus/mf_plus_i.c | 2 +- lib/nfc/protocols/ntag4xx/ntag4xx_i.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/nfc/protocols/mf_desfire/mf_desfire_i.c b/lib/nfc/protocols/mf_desfire/mf_desfire_i.c index bbfb7ffa8..28284f212 100644 --- a/lib/nfc/protocols/mf_desfire/mf_desfire_i.c +++ b/lib/nfc/protocols/mf_desfire/mf_desfire_i.c @@ -54,7 +54,7 @@ bool mf_desfire_version_parse(MfDesfireVersion* data, const BitBuffer* buf) { bit_buffer_write_bytes(buf, data, sizeof(MfDesfireVersion)); } - return can_parse; + return can_parse && (data->hw_type & 0x0F) == 0x01; } bool mf_desfire_free_memory_parse(MfDesfireFreeMemory* data, const BitBuffer* buf) { diff --git a/lib/nfc/protocols/mf_plus/mf_plus_i.c b/lib/nfc/protocols/mf_plus/mf_plus_i.c index bd32956d6..8f06a644a 100644 --- a/lib/nfc/protocols/mf_plus/mf_plus_i.c +++ b/lib/nfc/protocols/mf_plus/mf_plus_i.c @@ -27,7 +27,7 @@ MfPlusError mf_plus_get_type_from_version( MfPlusError error = MfPlusErrorProtocol; - if(mf_plus_data->version.hw_type == 0x02 || mf_plus_data->version.hw_type == 0x82) { + if((mf_plus_data->version.hw_type & 0x0F) == 0x02) { error = MfPlusErrorNone; // Mifare Plus EV1/EV2 diff --git a/lib/nfc/protocols/ntag4xx/ntag4xx_i.c b/lib/nfc/protocols/ntag4xx/ntag4xx_i.c index 6a0c7114a..40f22fc41 100644 --- a/lib/nfc/protocols/ntag4xx/ntag4xx_i.c +++ b/lib/nfc/protocols/ntag4xx/ntag4xx_i.c @@ -44,7 +44,7 @@ bool ntag4xx_version_parse(Ntag4xxVersion* data, const BitBuffer* buf) { } } - return can_parse && data->hw_type == 0x04; + return can_parse && (data->hw_type & 0x0F) == 0x04; } bool ntag4xx_version_load(Ntag4xxVersion* data, FlipperFormat* ff) { From 1f8cfcc66930d5074b816988400525939329242d Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sat, 22 Mar 2025 09:38:43 +0000 Subject: [PATCH 51/92] NFC: Detect NTAG4xx in Type 4 Tag poller --- .../type_4_tag/type_4_tag_poller_i.c | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c b/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c index e9555fcf8..e9bbfffc0 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c @@ -4,6 +4,8 @@ #include #include +#include +#include #include #include @@ -226,17 +228,25 @@ Type4TagError type_4_tag_poller_detect_platform(Type4TagPoller* instance) { NfcDevice* device = nfc_device_alloc(); do { - FURI_LOG_D(TAG, "Detect DESFire"); - MfDesfirePoller* mf_des = mf_desfire_poller.alloc(instance->iso14443_4a_poller); - mf_desfire_poller_set_command_mode(mf_des, MfDesfirePollerCommandModeIsoWrapped); - if(mf_desfire_poller.detect(event, mf_des)) { - platform = Type4TagPlatformMfDesfire; - nfc_device_set_data(device, NfcProtocolMfDesfire, mf_desfire_poller.get_data(mf_des)); + FURI_LOG_D(TAG, "Detect NTAG4xx"); + Ntag4xxPoller* ntag4xx = ntag4xx_poller.alloc(instance->iso14443_4a_poller); + if(ntag4xx_poller.detect(event, ntag4xx)) { + platform = Type4TagPlatformNtag4xx; + nfc_device_set_data(device, NfcProtocolNtag4xx, ntag4xx_poller.get_data(ntag4xx)); } - mf_desfire_poller.free(mf_des); + ntag4xx_poller.free(ntag4xx); if(platform != Type4TagPlatformUnknown) break; - // FIXME: detect NTAG4xx + FURI_LOG_D(TAG, "Detect DESFire"); + MfDesfirePoller* mf_desfire = mf_desfire_poller.alloc(instance->iso14443_4a_poller); + mf_desfire_poller_set_command_mode(mf_desfire, MfDesfirePollerCommandModeIsoWrapped); + if(mf_desfire_poller.detect(event, mf_desfire)) { + platform = Type4TagPlatformMfDesfire; + nfc_device_set_data( + device, NfcProtocolMfDesfire, mf_desfire_poller.get_data(mf_desfire)); + } + mf_desfire_poller.free(mf_desfire); + if(platform != Type4TagPlatformUnknown) break; } while(false); Type4TagError error; From 118e7453685fe34097be2940363442e723f5a280 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sat, 22 Mar 2025 11:36:34 +0000 Subject: [PATCH 52/92] NFC: Refactor common NXP Native command handling --- lib/nfc/helpers/nxp_native_command.c | 115 ++++++++++++++++ lib/nfc/helpers/nxp_native_command.h | 92 +++++++++++++ lib/nfc/helpers/nxp_native_command_mode.h | 11 ++ lib/nfc/protocols/mf_desfire/mf_desfire_i.h | 56 +------- .../protocols/mf_desfire/mf_desfire_poller.h | 15 +-- .../mf_desfire/mf_desfire_poller_i.c | 123 +++--------------- .../mf_desfire/mf_desfire_poller_i.h | 2 +- lib/nfc/protocols/mf_plus/mf_plus_i.c | 14 +- lib/nfc/protocols/mf_plus/mf_plus_i.h | 5 +- lib/nfc/protocols/mf_plus/mf_plus_poller_i.c | 38 +++--- lib/nfc/protocols/ntag4xx/ntag4xx.h | 2 - lib/nfc/protocols/ntag4xx/ntag4xx_i.c | 6 +- lib/nfc/protocols/ntag4xx/ntag4xx_i.h | 38 +----- lib/nfc/protocols/ntag4xx/ntag4xx_poller_i.c | 85 ++---------- .../type_4_tag/type_4_tag_poller_i.c | 8 +- targets/f7/api_symbols.csv | 2 +- 16 files changed, 287 insertions(+), 325 deletions(-) create mode 100644 lib/nfc/helpers/nxp_native_command.c create mode 100644 lib/nfc/helpers/nxp_native_command.h create mode 100644 lib/nfc/helpers/nxp_native_command_mode.h diff --git a/lib/nfc/helpers/nxp_native_command.c b/lib/nfc/helpers/nxp_native_command.c new file mode 100644 index 000000000..d5d535891 --- /dev/null +++ b/lib/nfc/helpers/nxp_native_command.c @@ -0,0 +1,115 @@ +#include "nxp_native_command.h" + +#include + +#define TAG "NxpNativeCommand" + +Iso14443_4aError nxp_native_command_iso14443_4a_poller( + Iso14443_4aPoller* iso14443_4a_poller, + NxpNativeCommandStatus* status_code, + const BitBuffer* input_buffer, + BitBuffer* result_buffer, + NxpNativeCommandMode command_mode, + BitBuffer* tx_buffer, + BitBuffer* rx_buffer) { + furi_check(iso14443_4a_poller); + furi_check(tx_buffer); + furi_check(rx_buffer); + furi_check(input_buffer); + furi_check(result_buffer); + furi_check(command_mode < NxpNativeCommandModeMAX); + + Iso14443_4aError error = Iso14443_4aErrorNone; + *status_code = NXP_NATIVE_COMMAND_STATUS_OPERATION_OK; + + do { + bit_buffer_reset(tx_buffer); + if(command_mode == NxpNativeCommandModePlain) { + bit_buffer_append(tx_buffer, input_buffer); + } else if(command_mode == NxpNativeCommandModeIsoWrapped) { + bit_buffer_append_byte(tx_buffer, NXP_NATIVE_COMMAND_ISO_CLA); + bit_buffer_append_byte(tx_buffer, bit_buffer_get_byte(input_buffer, 0)); + bit_buffer_append_byte(tx_buffer, NXP_NATIVE_COMMAND_ISO_P1); + bit_buffer_append_byte(tx_buffer, NXP_NATIVE_COMMAND_ISO_P2); + if(bit_buffer_get_size_bytes(input_buffer) > 1) { + bit_buffer_append_byte(tx_buffer, bit_buffer_get_size_bytes(input_buffer) - 1); + bit_buffer_append_right(tx_buffer, input_buffer, 1); + } + bit_buffer_append_byte(tx_buffer, NXP_NATIVE_COMMAND_ISO_LE); + } + + bit_buffer_reset(rx_buffer); + error = iso14443_4a_poller_send_block(iso14443_4a_poller, tx_buffer, rx_buffer); + + if(error != Iso14443_4aErrorNone) { + break; + } + + bit_buffer_reset(tx_buffer); + if(command_mode == NxpNativeCommandModePlain) { + bit_buffer_append_byte(tx_buffer, NXP_NATIVE_COMMAND_STATUS_ADDITIONAL_FRAME); + } else if(command_mode == NxpNativeCommandModeIsoWrapped) { + bit_buffer_append_byte(tx_buffer, NXP_NATIVE_COMMAND_ISO_CLA); + bit_buffer_append_byte(tx_buffer, NXP_NATIVE_COMMAND_STATUS_ADDITIONAL_FRAME); + bit_buffer_append_byte(tx_buffer, NXP_NATIVE_COMMAND_ISO_P1); + bit_buffer_append_byte(tx_buffer, NXP_NATIVE_COMMAND_ISO_P2); + bit_buffer_append_byte(tx_buffer, NXP_NATIVE_COMMAND_ISO_LE); + } + + size_t response_len = bit_buffer_get_size_bytes(rx_buffer); + *status_code = NXP_NATIVE_COMMAND_STATUS_LENGTH_ERROR; + bit_buffer_reset(result_buffer); + if(command_mode == NxpNativeCommandModePlain && response_len >= sizeof(uint8_t)) { + *status_code = bit_buffer_get_byte(rx_buffer, 0); + if(response_len > sizeof(uint8_t)) { + bit_buffer_copy_right(result_buffer, rx_buffer, sizeof(uint8_t)); + } + } else if( + command_mode == NxpNativeCommandModeIsoWrapped && + response_len >= 2 * sizeof(uint8_t) && + bit_buffer_get_byte(rx_buffer, response_len - 2) == NXP_NATIVE_COMMAND_ISO_SW1) { + *status_code = bit_buffer_get_byte(rx_buffer, response_len - 1); + if(response_len > 2 * sizeof(uint8_t)) { + bit_buffer_copy_left(result_buffer, rx_buffer, response_len - 2 * sizeof(uint8_t)); + } + } + + while(*status_code == NXP_NATIVE_COMMAND_STATUS_ADDITIONAL_FRAME) { + bit_buffer_reset(rx_buffer); + error = iso14443_4a_poller_send_block(iso14443_4a_poller, tx_buffer, rx_buffer); + + if(error != Iso14443_4aErrorNone) { + break; + } + + const size_t rx_size = bit_buffer_get_size_bytes(rx_buffer); + const size_t rx_capacity_remaining = bit_buffer_get_capacity_bytes(result_buffer) - + bit_buffer_get_size_bytes(result_buffer); + + if(command_mode == NxpNativeCommandModePlain) { + *status_code = rx_size >= 1 ? bit_buffer_get_byte(rx_buffer, 0) : + NXP_NATIVE_COMMAND_STATUS_LENGTH_ERROR; + if(rx_size <= rx_capacity_remaining + 1) { + bit_buffer_append_right(result_buffer, rx_buffer, sizeof(uint8_t)); + } else { + FURI_LOG_W(TAG, "RX buffer overflow: ignoring %zu bytes", rx_size - 1); + } + } else if(command_mode == NxpNativeCommandModeIsoWrapped) { + if(rx_size >= 2 && + bit_buffer_get_byte(rx_buffer, rx_size - 2) == NXP_NATIVE_COMMAND_ISO_SW1) { + *status_code = bit_buffer_get_byte(rx_buffer, rx_size - 1); + } else { + *status_code = NXP_NATIVE_COMMAND_STATUS_LENGTH_ERROR; + } + if(rx_size <= rx_capacity_remaining + 2) { + bit_buffer_set_size_bytes(rx_buffer, rx_size - 2); + bit_buffer_append(result_buffer, rx_buffer); + } else { + FURI_LOG_W(TAG, "RX buffer overflow: ignoring %zu bytes", rx_size - 2); + } + } + } + } while(false); + + return error; +} diff --git a/lib/nfc/helpers/nxp_native_command.h b/lib/nfc/helpers/nxp_native_command.h new file mode 100644 index 000000000..61677a3f6 --- /dev/null +++ b/lib/nfc/helpers/nxp_native_command.h @@ -0,0 +1,92 @@ +#pragma once + +#include "nxp_native_command_mode.h" + +#include + +// ISO 7816 command wrapping +#define NXP_NATIVE_COMMAND_ISO_CLA (0x90) +#define NXP_NATIVE_COMMAND_ISO_P1 (0x00) +#define NXP_NATIVE_COMMAND_ISO_P2 (0x00) +#define NXP_NATIVE_COMMAND_ISO_LE (0x00) +// ISO 7816 status wrapping +#define NXP_NATIVE_COMMAND_ISO_SW1 (0x91) + +// Successful operation +#define NXP_NATIVE_COMMAND_STATUS_OPERATION_OK (0x00) +// No changes done to backup files, CommitTransaction / AbortTransaction not necessary +#define NXP_NATIVE_COMMAND_STATUS_NO_CHANGES (0x0C) +// Insufficient NV-Memory to complete command +#define NXP_NATIVE_COMMAND_STATUS_OUT_OF_EEPROM_ERROR (0x0E) +// Command code not supported +#define NXP_NATIVE_COMMAND_STATUS_ILLEGAL_COMMAND_CODE (0x1C) +// CRC or MAC does not match data Padding bytes not valid +#define NXP_NATIVE_COMMAND_STATUS_INTEGRITY_ERROR (0x1E) +// Invalid key number specified +#define NXP_NATIVE_COMMAND_STATUS_NO_SUCH_KEY (0x40) +// Length of command string invalid +#define NXP_NATIVE_COMMAND_STATUS_LENGTH_ERROR (0x7E) +// Current configuration / status does not allow the requested command +#define NXP_NATIVE_COMMAND_STATUS_PERMISSION_DENIED (0x9D) +// Value of the parameter(s) invalid +#define NXP_NATIVE_COMMAND_STATUS_PARAMETER_ERROR (0x9E) +// Requested AID not present on PICC +#define NXP_NATIVE_COMMAND_STATUS_APPLICATION_NOT_FOUND (0xA0) +// Unrecoverable error within application, application will be disabled +#define NXP_NATIVE_COMMAND_STATUS_APPL_INTEGRITY_ERROR (0xA1) +// Currently not allowed to authenticate. Keep trying until full delay is spent +#define NXP_NATIVE_COMMAND_STATUS_STATUS_AUTHENTICATION_DELAY (0xAD) +// Current authentication status does not allow the requested command +#define NXP_NATIVE_COMMAND_STATUS_AUTHENTICATION_ERROR (0xAE) +// Additional data frame is expected to be sent +#define NXP_NATIVE_COMMAND_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 NXP_NATIVE_COMMAND_STATUS_BOUNDARY_ERROR (0xBE) +// Unrecoverable error within PICC, PICC will be disabled +#define NXP_NATIVE_COMMAND_STATUS_PICC_INTEGRITY_ERROR (0xC1) +// Previous Command was not fully completed. Not all Frames were requested or provided by the PCD +#define NXP_NATIVE_COMMAND_STATUS_COMMAND_ABORTED (0xCA) +// PICC was disabled by an unrecoverable error +#define NXP_NATIVE_COMMAND_STATUS_PICC_DISABLED_ERROR (0xCD) +// Number of Applications limited to 28, no additional CreateApplication possible +#define NXP_NATIVE_COMMAND_STATUS_COUNT_ERROR (0xCE) +// Creation of file/application failed because file/application with same number already exists +#define NXP_NATIVE_COMMAND_STATUS_DUBLICATE_ERROR (0xDE) +// Could not complete NV-write operation due to loss of power, internal backup/rollback mechanism activated +#define NXP_NATIVE_COMMAND_STATUS_EEPROM_ERROR (0xEE) +// Specified file number does not exist +#define NXP_NATIVE_COMMAND_STATUS_FILE_NOT_FOUND (0xF0) +// Unrecoverable error within file, file will be disabled +#define NXP_NATIVE_COMMAND_STATUS_FILE_INTEGRITY_ERROR (0xF1) + +typedef uint8_t NxpNativeCommandStatus; + +/** + * @brief Transmit and receive NXP Native Command chunks in poller mode. + * + * Must ONLY be used inside the callback function. + * + * The result_buffer will be filled with any data received as a response to data + * sent from input_buffer, with a timeout defined by the fwt parameter. + * + * The tx_buffer and rx_buffer are used as working areas to handle individual + * command chunks and responses. + * + * @param[in, out] iso14443_4a_poller pointer to the instance to be used in the transaction. + * @param[out] status_code pointer to a status variable to hold the result of the operation. + * @param[in] input_buffer pointer to the buffer containing the data to be transmitted. + * @param[out] result_buffer pointer to the buffer to be filled with received data. + * @param[in] command_mode what command formatting mode to use for the transaction. + * @param[in, out] tx_buffer pointer to the buffer for command construction. + * @param[in, out] rx_buffer pointer to the buffer for response handling. + * @return Iso14443_4aErrorNone and STATUS_OPERATION_OK on success, an error code on failure. + */ +Iso14443_4aError nxp_native_command_iso14443_4a_poller( + Iso14443_4aPoller* iso14443_4a_poller, + NxpNativeCommandStatus* status_code, + const BitBuffer* input_buffer, + BitBuffer* result_buffer, + NxpNativeCommandMode command_mode, + BitBuffer* tx_buffer, + BitBuffer* rx_buffer); diff --git a/lib/nfc/helpers/nxp_native_command_mode.h b/lib/nfc/helpers/nxp_native_command_mode.h new file mode 100644 index 000000000..7684b4d48 --- /dev/null +++ b/lib/nfc/helpers/nxp_native_command_mode.h @@ -0,0 +1,11 @@ +#pragma once + +/** + * @brief Enumeration of possible command modes. + */ +typedef enum { + NxpNativeCommandModePlain, /**< Plain native commands. */ + NxpNativeCommandModeIsoWrapped, /**< ISO 7816-wrapped commands. */ + + NxpNativeCommandModeMAX, +} NxpNativeCommandMode; diff --git a/lib/nfc/protocols/mf_desfire/mf_desfire_i.h b/lib/nfc/protocols/mf_desfire/mf_desfire_i.h index 526a4e0b8..ac572ae58 100644 --- a/lib/nfc/protocols/mf_desfire/mf_desfire_i.h +++ b/lib/nfc/protocols/mf_desfire/mf_desfire_i.h @@ -2,63 +2,11 @@ #include "mf_desfire.h" +#include + #define MF_DESFIRE_FFF_PICC_PREFIX "PICC" #define MF_DESFIRE_FFF_APP_PREFIX "Application" -// ISO 7816 command wrapping -#define MF_DESFIRE_CMD_ISO_CLA (0x90) -#define MF_DESFIRE_CMD_ISO_P1 (0x00) -#define MF_DESFIRE_CMD_ISO_P2 (0x00) -#define MF_DESFIRE_CMD_ISO_LE (0x00) -// ISO 7816 status wrapping -#define MF_DESFIRE_STATUS_ISO_SW1 (0x91) - -// 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; diff --git a/lib/nfc/protocols/mf_desfire/mf_desfire_poller.h b/lib/nfc/protocols/mf_desfire/mf_desfire_poller.h index 683b12359..4010938e5 100644 --- a/lib/nfc/protocols/mf_desfire/mf_desfire_poller.h +++ b/lib/nfc/protocols/mf_desfire/mf_desfire_poller.h @@ -3,6 +3,7 @@ #include "mf_desfire.h" #include +#include #ifdef __cplusplus extern "C" { @@ -39,24 +40,14 @@ typedef struct { } MfDesfirePollerEvent; /** - * @brief Enumeration of possible MfDesfire poller command modes. - */ -typedef enum { - MfDesfirePollerCommandModeNative, /**< Native MfDesfire commands. */ - MfDesfirePollerCommandModeIsoWrapped, /**< ISO 7816-wrapped commands. */ - - MfDesfirePollerCommandModeMAX, -} MfDesfirePollerCommandMode; - -/** - * @brief Change MfDesfire command mode used in poller mode. + * @brief Change command mode used in poller mode. * * @param[in, out] instance pointer to the instance to affect. * @param[in] command_mode command mode to use in further communication with the card. */ void mf_desfire_poller_set_command_mode( MfDesfirePoller* instance, - MfDesfirePollerCommandMode command_mode); + NxpNativeCommandMode command_mode); /** * @brief Transmit and receive MfDesfire chunks in poller mode. diff --git a/lib/nfc/protocols/mf_desfire/mf_desfire_poller_i.c b/lib/nfc/protocols/mf_desfire/mf_desfire_poller_i.c index 21d88623f..b8aad6e0d 100644 --- a/lib/nfc/protocols/mf_desfire/mf_desfire_poller_i.c +++ b/lib/nfc/protocols/mf_desfire/mf_desfire_poller_i.c @@ -22,11 +22,11 @@ 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: + case NXP_NATIVE_COMMAND_STATUS_OPERATION_OK: return MfDesfireErrorNone; - case MF_DESFIRE_STATUS_AUTHENTICATION_ERROR: + case NXP_NATIVE_COMMAND_STATUS_AUTHENTICATION_ERROR: return MfDesfireErrorAuthentication; - case MF_DESFIRE_STATUS_ILLEGAL_COMMAND_CODE: + case NXP_NATIVE_COMMAND_STATUS_ILLEGAL_COMMAND_CODE: return MfDesfireErrorCommandNotSupported; default: return MfDesfireErrorProtocol; @@ -35,10 +35,10 @@ MfDesfireError mf_desfire_process_status_code(uint8_t status_code) { void mf_desfire_poller_set_command_mode( MfDesfirePoller* instance, - MfDesfirePollerCommandMode command_mode) { + NxpNativeCommandMode command_mode) { furi_check(instance); furi_check(instance->state == MfDesfirePollerStateIdle); - furi_check(command_mode < MfDesfirePollerCommandModeMAX); + furi_check(command_mode < NxpNativeCommandModeMAX); instance->command_mode = command_mode; } @@ -48,111 +48,22 @@ MfDesfireError mf_desfire_poller_send_chunks( const BitBuffer* tx_buffer, BitBuffer* rx_buffer) { furi_check(instance); - furi_check(instance->iso14443_4a_poller); - furi_check(instance->tx_buffer); - furi_check(instance->rx_buffer); - furi_check(tx_buffer); - furi_check(rx_buffer); - MfDesfireError error = MfDesfireErrorNone; - uint8_t status_code = MF_DESFIRE_STATUS_OPERATION_OK; + NxpNativeCommandStatus status_code = NXP_NATIVE_COMMAND_STATUS_OPERATION_OK; + Iso14443_4aError iso14443_4a_error = nxp_native_command_iso14443_4a_poller( + instance->iso14443_4a_poller, + &status_code, + tx_buffer, + rx_buffer, + instance->command_mode, + instance->tx_buffer, + instance->rx_buffer); - do { - bit_buffer_reset(instance->tx_buffer); - if(instance->command_mode == MfDesfirePollerCommandModeNative) { - bit_buffer_append(instance->tx_buffer, tx_buffer); - } else if(instance->command_mode == MfDesfirePollerCommandModeIsoWrapped) { - bit_buffer_append_byte(instance->tx_buffer, MF_DESFIRE_CMD_ISO_CLA); - bit_buffer_append_byte(instance->tx_buffer, bit_buffer_get_byte(tx_buffer, 0)); - bit_buffer_append_byte(instance->tx_buffer, MF_DESFIRE_CMD_ISO_P1); - bit_buffer_append_byte(instance->tx_buffer, MF_DESFIRE_CMD_ISO_P2); - if(bit_buffer_get_size_bytes(tx_buffer) > 1) { - bit_buffer_append_byte( - instance->tx_buffer, bit_buffer_get_size_bytes(tx_buffer) - 1); - bit_buffer_append_right(instance->tx_buffer, tx_buffer, 1); - } - bit_buffer_append_byte(instance->tx_buffer, MF_DESFIRE_CMD_ISO_LE); - } - - 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_reset(instance->tx_buffer); - if(instance->command_mode == MfDesfirePollerCommandModeNative) { - bit_buffer_append_byte(instance->tx_buffer, MF_DESFIRE_STATUS_ADDITIONAL_FRAME); - } else if(instance->command_mode == MfDesfirePollerCommandModeIsoWrapped) { - bit_buffer_append_byte(instance->tx_buffer, MF_DESFIRE_CMD_ISO_CLA); - bit_buffer_append_byte(instance->tx_buffer, MF_DESFIRE_STATUS_ADDITIONAL_FRAME); - bit_buffer_append_byte(instance->tx_buffer, MF_DESFIRE_CMD_ISO_P1); - bit_buffer_append_byte(instance->tx_buffer, MF_DESFIRE_CMD_ISO_P2); - bit_buffer_append_byte(instance->tx_buffer, MF_DESFIRE_CMD_ISO_LE); - } - - size_t response_len = bit_buffer_get_size_bytes(instance->rx_buffer); - status_code = MF_DESFIRE_STATUS_LENGTH_ERROR; - bit_buffer_reset(rx_buffer); - if(instance->command_mode == MfDesfirePollerCommandModeNative && - response_len >= sizeof(uint8_t)) { - status_code = bit_buffer_get_byte(instance->rx_buffer, 0); - if(response_len > sizeof(uint8_t)) { - bit_buffer_copy_right(rx_buffer, instance->rx_buffer, sizeof(uint8_t)); - } - } else if( - instance->command_mode == MfDesfirePollerCommandModeIsoWrapped && - response_len >= 2 * sizeof(uint8_t) && - bit_buffer_get_byte(instance->rx_buffer, response_len - 2) == - MF_DESFIRE_STATUS_ISO_SW1) { - status_code = bit_buffer_get_byte(instance->rx_buffer, response_len - 1); - if(response_len > 2 * sizeof(uint8_t)) { - bit_buffer_copy_left( - rx_buffer, instance->rx_buffer, response_len - 2 * sizeof(uint8_t)); - } - } - - while(status_code == 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); - - 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(instance->command_mode == MfDesfirePollerCommandModeNative) { - status_code = rx_size < 1 ? MF_DESFIRE_STATUS_LENGTH_ERROR : - bit_buffer_get_byte(instance->rx_buffer, 0); - if(rx_size <= rx_capacity_remaining + 1) { - 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 - 1); - } - } else if(instance->command_mode == MfDesfirePollerCommandModeIsoWrapped) { - status_code = rx_size < 2 ? MF_DESFIRE_STATUS_LENGTH_ERROR : - bit_buffer_get_byte(instance->rx_buffer, rx_size - 1); - if(rx_size <= rx_capacity_remaining + 2) { - bit_buffer_set_size_bytes(instance->rx_buffer, rx_size - 2); - bit_buffer_append(rx_buffer, instance->rx_buffer); - } else { - FURI_LOG_W(TAG, "RX buffer overflow: ignoring %zu bytes", rx_size - 2); - } - } - } - } while(false); - - if(error == MfDesfireErrorNone) { - error = mf_desfire_process_status_code(status_code); + if(iso14443_4a_error != Iso14443_4aErrorNone) { + return mf_desfire_process_error(iso14443_4a_error); } - return error; + return mf_desfire_process_status_code(status_code); } MfDesfireError mf_desfire_poller_read_version(MfDesfirePoller* instance, MfDesfireVersion* data) { diff --git a/lib/nfc/protocols/mf_desfire/mf_desfire_poller_i.h b/lib/nfc/protocols/mf_desfire/mf_desfire_poller_i.h index 135e2ec98..179fd44b5 100644 --- a/lib/nfc/protocols/mf_desfire/mf_desfire_poller_i.h +++ b/lib/nfc/protocols/mf_desfire/mf_desfire_poller_i.h @@ -30,7 +30,7 @@ typedef enum { struct MfDesfirePoller { Iso14443_4aPoller* iso14443_4a_poller; - MfDesfirePollerCommandMode command_mode; + NxpNativeCommandMode command_mode; MfDesfirePollerSessionState session_state; MfDesfirePollerState state; MfDesfireError error; diff --git a/lib/nfc/protocols/mf_plus/mf_plus_i.c b/lib/nfc/protocols/mf_plus/mf_plus_i.c index 8f06a644a..3d533eb58 100644 --- a/lib/nfc/protocols/mf_plus/mf_plus_i.c +++ b/lib/nfc/protocols/mf_plus/mf_plus_i.c @@ -238,22 +238,10 @@ MfPlusError } MfPlusError mf_plus_version_parse(MfPlusVersion* data, const BitBuffer* buf) { - bool can_parse = bit_buffer_get_size_bytes(buf) == sizeof(MfPlusVersion); + const bool can_parse = bit_buffer_get_size_bytes(buf) == sizeof(MfPlusVersion); if(can_parse) { bit_buffer_write_bytes(buf, data, sizeof(MfPlusVersion)); - } else if( - bit_buffer_get_size_bytes(buf) == 8 && - bit_buffer_get_byte(buf, 0) == MF_PLUS_STATUS_ADDITIONAL_FRAME) { - // HACK(-nofl): There are supposed to be three parts to the GetVersion command, - // with the second and third parts fetched by sending the AdditionalFrame - // command. I don't know whether the entire MIFARE Plus line uses status as - // the first byte, so let's just assume we only have the first part of - // the response if it's size 8 and starts with the AF status. The second - // part of the response is the same size and status byte, but so far - // we're only reading one response. - can_parse = true; - bit_buffer_write_bytes_mid(buf, data, 1, bit_buffer_get_size_bytes(buf) - 1); } return can_parse ? MfPlusErrorNone : MfPlusErrorProtocol; diff --git a/lib/nfc/protocols/mf_plus/mf_plus_i.h b/lib/nfc/protocols/mf_plus/mf_plus_i.h index 302f5a178..cadc435b9 100644 --- a/lib/nfc/protocols/mf_plus/mf_plus_i.h +++ b/lib/nfc/protocols/mf_plus/mf_plus_i.h @@ -2,10 +2,9 @@ #include "mf_plus.h" -#define MF_PLUS_FFF_PICC_PREFIX "PICC" +#include -#define MF_PLUS_STATUS_OPERATION_OK (0x90) -#define MF_PLUS_STATUS_ADDITIONAL_FRAME (0xAF) +#define MF_PLUS_FFF_PICC_PREFIX "PICC" MfPlusError mf_plus_get_type_from_version( const Iso14443_4aData* iso14443_4a_data, diff --git a/lib/nfc/protocols/mf_plus/mf_plus_poller_i.c b/lib/nfc/protocols/mf_plus/mf_plus_poller_i.c index cab906f1d..b2e4231ff 100644 --- a/lib/nfc/protocols/mf_plus/mf_plus_poller_i.c +++ b/lib/nfc/protocols/mf_plus/mf_plus_poller_i.c @@ -19,28 +19,36 @@ MfPlusError mf_plus_process_error(Iso14443_4aError error) { } } -MfPlusError mf_plus_poller_send_chunk( +MfPlusError mf_plus_process_status_code(uint8_t status_code) { + switch(status_code) { + case NXP_NATIVE_COMMAND_STATUS_OPERATION_OK: + return MfPlusErrorNone; + default: + return MfPlusErrorProtocol; + } +} + +MfPlusError mf_plus_poller_send_chunks( MfPlusPoller* 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); - Iso14443_4aError iso14443_4a_error = iso14443_4a_poller_send_block( - instance->iso14443_4a_poller, tx_buffer, instance->rx_buffer); - MfPlusError error = mf_plus_process_error(iso14443_4a_error); + NxpNativeCommandStatus status_code = NXP_NATIVE_COMMAND_STATUS_OPERATION_OK; + Iso14443_4aError iso14443_4a_error = nxp_native_command_iso14443_4a_poller( + instance->iso14443_4a_poller, + &status_code, + tx_buffer, + rx_buffer, + NxpNativeCommandModePlain, + instance->tx_buffer, + instance->rx_buffer); - if(error == MfPlusErrorNone) { - bit_buffer_copy(rx_buffer, instance->rx_buffer); + if(iso14443_4a_error != Iso14443_4aErrorNone) { + return mf_plus_process_error(iso14443_4a_error); } - bit_buffer_reset(instance->tx_buffer); - - return error; + return mf_plus_process_status_code(status_code); } MfPlusError mf_plus_poller_read_version(MfPlusPoller* instance, MfPlusVersion* data) { @@ -50,7 +58,7 @@ MfPlusError mf_plus_poller_read_version(MfPlusPoller* instance, MfPlusVersion* d bit_buffer_append_byte(instance->input_buffer, MF_PLUS_CMD_GET_VERSION); MfPlusError error = - mf_plus_poller_send_chunk(instance, instance->input_buffer, instance->result_buffer); + mf_plus_poller_send_chunks(instance, instance->input_buffer, instance->result_buffer); if(error == MfPlusErrorNone) { error = mf_plus_version_parse(data, instance->result_buffer); } diff --git a/lib/nfc/protocols/ntag4xx/ntag4xx.h b/lib/nfc/protocols/ntag4xx/ntag4xx.h index 85dd1e52d..56e5fbd65 100644 --- a/lib/nfc/protocols/ntag4xx/ntag4xx.h +++ b/lib/nfc/protocols/ntag4xx/ntag4xx.h @@ -20,8 +20,6 @@ typedef enum { Ntag4xxErrorNotPresent, Ntag4xxErrorProtocol, Ntag4xxErrorTimeout, - Ntag4xxErrorAuthentication, - Ntag4xxErrorCommandNotSupported, } Ntag4xxError; typedef enum { diff --git a/lib/nfc/protocols/ntag4xx/ntag4xx_i.c b/lib/nfc/protocols/ntag4xx/ntag4xx_i.c index 40f22fc41..b4ee3a9eb 100644 --- a/lib/nfc/protocols/ntag4xx/ntag4xx_i.c +++ b/lib/nfc/protocols/ntag4xx/ntag4xx_i.c @@ -21,12 +21,8 @@ Ntag4xxError ntag4xx_process_error(Iso14443_4aError error) { Ntag4xxError ntag4xx_process_status_code(uint8_t status_code) { switch(status_code) { - case NTAG4XX_STATUS_OPERATION_OK: + case NXP_NATIVE_COMMAND_STATUS_OPERATION_OK: return Ntag4xxErrorNone; - case NTAG4XX_STATUS_AUTHENTICATION_ERROR: - return Ntag4xxErrorAuthentication; - case NTAG4XX_STATUS_ILLEGAL_COMMAND_CODE: - return Ntag4xxErrorCommandNotSupported; default: return Ntag4xxErrorProtocol; } diff --git a/lib/nfc/protocols/ntag4xx/ntag4xx_i.h b/lib/nfc/protocols/ntag4xx/ntag4xx_i.h index db6dabd4b..5a71cf05a 100644 --- a/lib/nfc/protocols/ntag4xx/ntag4xx_i.h +++ b/lib/nfc/protocols/ntag4xx/ntag4xx_i.h @@ -2,44 +2,10 @@ #include "ntag4xx.h" +#include + #define NTAG4XX_FFF_PICC_PREFIX "PICC" -// ISO 7816 command wrapping -#define NTAG4XX_CMD_ISO_CLA (0x90) -#define NTAG4XX_CMD_ISO_P1 (0x00) -#define NTAG4XX_CMD_ISO_P2 (0x00) -#define NTAG4XX_CMD_ISO_LE (0x00) -// ISO 7816 status wrapping -#define NTAG4XX_STATUS_ISO_SW1 (0x91) - -// Successful operation -#define NTAG4XX_STATUS_OPERATION_OK (0x00) -// Command code not supported -#define NTAG4XX_STATUS_ILLEGAL_COMMAND_CODE (0x1C) -// CRC or MAC does not match data Padding bytes not valid -#define NTAG4XX_STATUS_INTEGRITY_ERROR (0x1E) -// Invalid key number specified -#define NTAG4XX_STATUS_NO_SUCH_KEY (0x40) -// Length of command string invalid -#define NTAG4XX_STATUS_LENGTH_ERROR (0x7E) -// Current configuration / status does not allow the requested command -#define NTAG4XX_STATUS_PERMISSION_DENIED (0x9D) -// Value of the parameter(s) invalid -#define NTAG4XX_STATUS_PARAMETER_ERROR (0x9E) -// Currently not allowed to authenticate. Keep trying until full delay is spent -#define NTAG4XX_STATUS_AUTHENTICATION_DELAY (0xAD) -// Current authentication status does not allow the requested command -#define NTAG4XX_STATUS_AUTHENTICATION_ERROR (0xAE) -// Additional data frame is expected to be sent -#define NTAG4XX_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 NTAG4XX_STATUS_BOUNDARY_ERROR (0xBE) -// Previous Command was not fully completed. Not all Frames were requested or provided by the PCD -#define NTAG4XX_STATUS_COMMAND_ABORTED (0xCA) -// Specified file number does not exist -#define NTAG4XX_STATUS_FILE_NOT_FOUND (0xF0) - // Internal helpers Ntag4xxError ntag4xx_process_error(Iso14443_4aError error); diff --git a/lib/nfc/protocols/ntag4xx/ntag4xx_poller_i.c b/lib/nfc/protocols/ntag4xx/ntag4xx_poller_i.c index 9fc53e0a5..5b8b7191d 100644 --- a/lib/nfc/protocols/ntag4xx/ntag4xx_poller_i.c +++ b/lib/nfc/protocols/ntag4xx/ntag4xx_poller_i.c @@ -11,83 +11,22 @@ Ntag4xxError ntag4xx_poller_send_chunks( const BitBuffer* tx_buffer, BitBuffer* rx_buffer) { furi_check(instance); - furi_check(instance->iso14443_4a_poller); - furi_check(instance->tx_buffer); - furi_check(instance->rx_buffer); - furi_check(tx_buffer); - furi_check(rx_buffer); - Ntag4xxError error = Ntag4xxErrorNone; - uint8_t status_code = NTAG4XX_STATUS_OPERATION_OK; + NxpNativeCommandStatus status_code = NXP_NATIVE_COMMAND_STATUS_OPERATION_OK; + Iso14443_4aError iso14443_4a_error = nxp_native_command_iso14443_4a_poller( + instance->iso14443_4a_poller, + &status_code, + tx_buffer, + rx_buffer, + NxpNativeCommandModeIsoWrapped, + instance->tx_buffer, + instance->rx_buffer); - do { - bit_buffer_reset(instance->tx_buffer); - bit_buffer_append_byte(instance->tx_buffer, NTAG4XX_CMD_ISO_CLA); - bit_buffer_append_byte(instance->tx_buffer, bit_buffer_get_byte(tx_buffer, 0)); - bit_buffer_append_byte(instance->tx_buffer, NTAG4XX_CMD_ISO_P1); - bit_buffer_append_byte(instance->tx_buffer, NTAG4XX_CMD_ISO_P2); - if(bit_buffer_get_size_bytes(tx_buffer) > 1) { - bit_buffer_append_byte(instance->tx_buffer, bit_buffer_get_size_bytes(tx_buffer) - 1); - bit_buffer_append_right(instance->tx_buffer, tx_buffer, 1); - } - bit_buffer_append_byte(instance->tx_buffer, NTAG4XX_CMD_ISO_LE); - - 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 = ntag4xx_process_error(iso14443_4a_error); - break; - } - - bit_buffer_reset(instance->tx_buffer); - bit_buffer_append_byte(instance->tx_buffer, NTAG4XX_CMD_ISO_CLA); - bit_buffer_append_byte(instance->tx_buffer, NTAG4XX_STATUS_ADDITIONAL_FRAME); - bit_buffer_append_byte(instance->tx_buffer, NTAG4XX_CMD_ISO_P1); - bit_buffer_append_byte(instance->tx_buffer, NTAG4XX_CMD_ISO_P2); - bit_buffer_append_byte(instance->tx_buffer, NTAG4XX_CMD_ISO_LE); - - size_t response_len = bit_buffer_get_size_bytes(instance->rx_buffer); - status_code = NTAG4XX_STATUS_LENGTH_ERROR; - bit_buffer_reset(rx_buffer); - if(response_len >= 2 * sizeof(uint8_t) && - bit_buffer_get_byte(instance->rx_buffer, response_len - 2) == NTAG4XX_STATUS_ISO_SW1) { - status_code = bit_buffer_get_byte(instance->rx_buffer, response_len - 1); - if(response_len > 2 * sizeof(uint8_t)) { - bit_buffer_copy_left( - rx_buffer, instance->rx_buffer, response_len - 2 * sizeof(uint8_t)); - } - } - - while(status_code == NTAG4XX_STATUS_ADDITIONAL_FRAME) { - 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 = ntag4xx_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); - - status_code = rx_size < 2 ? NTAG4XX_STATUS_LENGTH_ERROR : - bit_buffer_get_byte(instance->rx_buffer, rx_size - 1); - if(rx_size <= rx_capacity_remaining + 2) { - bit_buffer_set_size_bytes(instance->rx_buffer, rx_size - 2); - bit_buffer_append(rx_buffer, instance->rx_buffer); - } else { - FURI_LOG_W(TAG, "RX buffer overflow: ignoring %zu bytes", rx_size - 2); - } - } - } while(false); - - if(error == Ntag4xxErrorNone) { - error = ntag4xx_process_status_code(status_code); + if(iso14443_4a_error != Iso14443_4aErrorNone) { + return ntag4xx_process_error(iso14443_4a_error); } - return error; + return ntag4xx_process_status_code(status_code); } Ntag4xxError ntag4xx_poller_read_version(Ntag4xxPoller* instance, Ntag4xxVersion* data) { diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c b/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c index e9bbfffc0..4722a169d 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c @@ -239,7 +239,7 @@ Type4TagError type_4_tag_poller_detect_platform(Type4TagPoller* instance) { FURI_LOG_D(TAG, "Detect DESFire"); MfDesfirePoller* mf_desfire = mf_desfire_poller.alloc(instance->iso14443_4a_poller); - mf_desfire_poller_set_command_mode(mf_desfire, MfDesfirePollerCommandModeIsoWrapped); + mf_desfire_poller_set_command_mode(mf_desfire, NxpNativeCommandModeIsoWrapped); if(mf_desfire_poller.detect(event, mf_desfire)) { platform = Type4TagPlatformMfDesfire; nfc_device_set_data( @@ -349,7 +349,7 @@ Type4TagError type_4_tag_poller_create_app(Type4TagPoller* instance) { if(instance->data->platform == Type4TagPlatformMfDesfire) { MfDesfirePoller* mf_des = mf_desfire_poller.alloc(instance->iso14443_4a_poller); - mf_desfire_poller_set_command_mode(mf_des, MfDesfirePollerCommandModeIsoWrapped); + mf_desfire_poller_set_command_mode(mf_des, NxpNativeCommandModeIsoWrapped); MfDesfireError mf_des_error; do { @@ -392,7 +392,7 @@ Type4TagError type_4_tag_poller_create_cc(Type4TagPoller* instance) { if(instance->data->platform == Type4TagPlatformMfDesfire) { MfDesfirePoller* mf_des = mf_desfire_poller.alloc(instance->iso14443_4a_poller); - mf_desfire_poller_set_command_mode(mf_des, MfDesfirePollerCommandModeIsoWrapped); + mf_desfire_poller_set_command_mode(mf_des, NxpNativeCommandModeIsoWrapped); MfDesfireError mf_des_error; do { @@ -441,7 +441,7 @@ Type4TagError type_4_tag_poller_create_ndef(Type4TagPoller* instance) { if(instance->data->platform == Type4TagPlatformMfDesfire) { MfDesfirePoller* mf_des = mf_desfire_poller.alloc(instance->iso14443_4a_poller); - mf_desfire_poller_set_command_mode(mf_des, MfDesfirePollerCommandModeIsoWrapped); + mf_desfire_poller_set_command_mode(mf_des, NxpNativeCommandModeIsoWrapped); MfDesfireError mf_des_error; do { diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index 385b687dc..a221aa708 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -2657,7 +2657,7 @@ Function,+,mf_desfire_poller_read_key_versions,MfDesfireError,"MfDesfirePoller*, Function,+,mf_desfire_poller_read_version,MfDesfireError,"MfDesfirePoller*, MfDesfireVersion*" Function,+,mf_desfire_poller_select_application,MfDesfireError,"MfDesfirePoller*, const MfDesfireApplicationId*" Function,+,mf_desfire_poller_send_chunks,MfDesfireError,"MfDesfirePoller*, const BitBuffer*, BitBuffer*" -Function,+,mf_desfire_poller_set_command_mode,void,"MfDesfirePoller*, MfDesfirePollerCommandMode" +Function,+,mf_desfire_poller_set_command_mode,void,"MfDesfirePoller*, NxpNativeCommandMode" Function,+,mf_desfire_reset,void,MfDesfireData* Function,+,mf_desfire_save,_Bool,"const MfDesfireData*, FlipperFormat*" Function,+,mf_desfire_set_uid,_Bool,"MfDesfireData*, const uint8_t*, size_t" From d2f4d0c216b0fdc533c191040008a134522d7fbb Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sat, 22 Mar 2025 11:37:34 +0000 Subject: [PATCH 53/92] NFC: Show version info for MIFARE Plus (was implemented but not shown) --- .../helpers/protocol_support/mf_plus/mf_plus.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/applications/main/nfc/helpers/protocol_support/mf_plus/mf_plus.c b/applications/main/nfc/helpers/protocol_support/mf_plus/mf_plus.c index e791eb291..bc8ffe2f6 100644 --- a/applications/main/nfc/helpers/protocol_support/mf_plus/mf_plus.c +++ b/applications/main/nfc/helpers/protocol_support/mf_plus/mf_plus.c @@ -25,6 +25,20 @@ static void nfc_scene_info_on_enter_mf_plus(NfcApp* instance) { furi_string_free(temp_str); } + +static void nfc_scene_more_info_on_enter_mf_plus(NfcApp* instance) { + const NfcDevice* device = instance->nfc_device; + const MfPlusData* data = nfc_device_get_data(device, NfcProtocolMfPlus); + + furi_string_reset(instance->text_box_store); + nfc_render_mf_plus_data(data, instance->text_box_store); + + text_box_set_font(instance->text_box, TextBoxFontHex); + text_box_set_text(instance->text_box, furi_string_get_cstr(instance->text_box_store)); + + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewTextBox); +} + static NfcCommand nfc_scene_read_poller_callback_mf_plus(NfcGenericEvent event, void* context) { furi_assert(context); furi_assert(event.protocol == NfcProtocolMfPlus); @@ -78,7 +92,7 @@ static void nfc_scene_emulate_on_enter_mf_plus(NfcApp* instance) { } const NfcProtocolSupportBase nfc_protocol_support_mf_plus = { - .features = NfcProtocolFeatureEmulateUid, + .features = NfcProtocolFeatureEmulateUid | NfcProtocolFeatureMoreInfo, .scene_info = { @@ -87,7 +101,7 @@ const NfcProtocolSupportBase nfc_protocol_support_mf_plus = { }, .scene_more_info = { - .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_enter = nfc_scene_more_info_on_enter_mf_plus, .on_event = nfc_protocol_support_common_on_event_empty, }, .scene_read = From 2f521d5e000777e0c6b4f7d56fc4d2724581921c Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 23 Mar 2025 03:10:42 +0000 Subject: [PATCH 54/92] NFC: Only show MFP GetVersion info when available --- .../protocol_support/mf_plus/mf_plus_render.c | 16 +++++++++++++++- lib/nfc/protocols/mf_plus/mf_plus_i.c | 2 ++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/applications/main/nfc/helpers/protocol_support/mf_plus/mf_plus_render.c b/applications/main/nfc/helpers/protocol_support/mf_plus/mf_plus_render.c index 8640fa16d..2311004ad 100644 --- a/applications/main/nfc/helpers/protocol_support/mf_plus/mf_plus_render.c +++ b/applications/main/nfc/helpers/protocol_support/mf_plus/mf_plus_render.c @@ -15,7 +15,21 @@ void nfc_render_mf_plus_info( } void nfc_render_mf_plus_data(const MfPlusData* data, FuriString* str) { - nfc_render_mf_plus_version(&data->version, str); + MfPlusVersion empty_version = {0}; + if(memcmp(&data->version, &empty_version, sizeof(MfPlusVersion)) == 0) { + const char* device_name = mf_plus_get_device_name(data, NfcDeviceNameTypeFull); + if(data->type == MfPlusTypeUnknown || data->size == MfPlusSizeUnknown || + data->security_level == MfPlusSecurityLevelUnknown) { + furi_string_cat_printf(str, "This %s", device_name); + furi_string_replace(str, " Unknown", ""); + } else { + furi_string_cat(str, device_name); + } + furi_string_replace(str, "Mifare", "MIFARE"); + furi_string_cat(str, " does not support the GetVersion command, extra info unavailable\n"); + } else { + nfc_render_mf_plus_version(&data->version, str); + } } void nfc_render_mf_plus_version(const MfPlusVersion* data, FuriString* str) { diff --git a/lib/nfc/protocols/mf_plus/mf_plus_i.c b/lib/nfc/protocols/mf_plus/mf_plus_i.c index 3d533eb58..42f16079c 100644 --- a/lib/nfc/protocols/mf_plus/mf_plus_i.c +++ b/lib/nfc/protocols/mf_plus/mf_plus_i.c @@ -242,6 +242,8 @@ MfPlusError mf_plus_version_parse(MfPlusVersion* data, const BitBuffer* buf) { if(can_parse) { bit_buffer_write_bytes(buf, data, sizeof(MfPlusVersion)); + } else { + memset(data, 0, sizeof(MfPlusVersion)); } return can_parse ? MfPlusErrorNone : MfPlusErrorProtocol; From 5194cf7f04e11281b6339226d746a93d2ca4867c Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Tue, 25 Mar 2025 03:27:49 +0000 Subject: [PATCH 55/92] NFC: Fix detecting MIFARE Plus SE 1K SL1/3 See: https://www.nxp.com/docs/en/application-note/AN10833.pdf --- lib/nfc/protocols/mf_plus/mf_plus_i.c | 64 +++++++++++---------------- 1 file changed, 26 insertions(+), 38 deletions(-) diff --git a/lib/nfc/protocols/mf_plus/mf_plus_i.c b/lib/nfc/protocols/mf_plus/mf_plus_i.c index 42f16079c..b66cf5ea2 100644 --- a/lib/nfc/protocols/mf_plus/mf_plus_i.c +++ b/lib/nfc/protocols/mf_plus/mf_plus_i.c @@ -15,8 +15,8 @@ const uint8_t mf_plus_ats_t1_tk_values[][MF_PLUS_T1_TK_VALUE_LEN] = { {0xC1, 0x05, 0x2F, 0x2F, 0x00, 0x35, 0xC7}, // Mifare Plus S {0xC1, 0x05, 0x2F, 0x2F, 0x01, 0xBC, 0xD6}, // Mifare Plus X - {0xC1, 0x05, 0x2F, 0x2F, 0x00, 0xF6, 0xD1}, // Mifare Plus SE - {0xC1, 0x05, 0x2F, 0x2F, 0x01, 0xF6, 0xD1}, // Mifare Plus SE + {0xC1, 0x05, 0x21, 0x30, 0x00, 0xF6, 0xD1}, // Mifare Plus SE + {0xC1, 0x05, 0x21, 0x30, 0x10, 0xF6, 0xD1}, // Mifare Plus SE }; MfPlusError mf_plus_get_type_from_version( @@ -85,16 +85,15 @@ MfPlusError MfPlusError error = MfPlusErrorProtocol; - if(simple_array_get_count(iso4_data->ats_data.t1_tk) != MF_PLUS_T1_TK_VALUE_LEN) { + const size_t historical_bytes_len = simple_array_get_count(iso4_data->ats_data.t1_tk); + if(historical_bytes_len != MF_PLUS_T1_TK_VALUE_LEN) { return MfPlusErrorProtocol; } + const uint8_t* historical_bytes = simple_array_cget_data(iso4_data->ats_data.t1_tk); switch(iso4_data->iso14443_3a_data->sak) { case 0x08: - if(memcmp( - simple_array_get_data(iso4_data->ats_data.t1_tk), - mf_plus_ats_t1_tk_values[0], - simple_array_get_count(iso4_data->ats_data.t1_tk)) == 0) { + if(memcmp(historical_bytes, mf_plus_ats_t1_tk_values[0], historical_bytes_len) == 0) { // Mifare Plus S 2K SL1 mf_plus_data->type = MfPlusTypeS; mf_plus_data->size = MfPlusSize2K; @@ -102,11 +101,7 @@ MfPlusError FURI_LOG_D(TAG, "Mifare Plus S 2K SL1"); error = MfPlusErrorNone; - } else if( - memcmp( - simple_array_get_data(iso4_data->ats_data.t1_tk), - mf_plus_ats_t1_tk_values[1], - simple_array_get_count(iso4_data->ats_data.t1_tk)) == 0) { + } else if(memcmp(historical_bytes, mf_plus_ats_t1_tk_values[1], historical_bytes_len) == 0) { // Mifare Plus X 2K SL1 mf_plus_data->type = MfPlusTypeX; mf_plus_data->size = MfPlusSize2K; @@ -115,14 +110,8 @@ MfPlusError FURI_LOG_D(TAG, "Mifare Plus X 2K SL1"); error = MfPlusErrorNone; } else if( - memcmp( - simple_array_get_data(iso4_data->ats_data.t1_tk), - mf_plus_ats_t1_tk_values[2], - simple_array_get_count(iso4_data->ats_data.t1_tk)) == 0 || - memcmp( - simple_array_get_data(iso4_data->ats_data.t1_tk), - mf_plus_ats_t1_tk_values[3], - simple_array_get_count(iso4_data->ats_data.t1_tk)) == 0) { + memcmp(historical_bytes, mf_plus_ats_t1_tk_values[2], historical_bytes_len) == 0 || + memcmp(historical_bytes, mf_plus_ats_t1_tk_values[3], historical_bytes_len) == 0) { // Mifare Plus SE 1K SL1 mf_plus_data->type = MfPlusTypeSE; mf_plus_data->size = MfPlusSize1K; @@ -154,10 +143,7 @@ MfPlusError break; case 0x18: - if(memcmp( - simple_array_get_data(iso4_data->ats_data.t1_tk), - mf_plus_ats_t1_tk_values[0], - simple_array_get_count(iso4_data->ats_data.t1_tk)) == 0) { + if(memcmp(historical_bytes, mf_plus_ats_t1_tk_values[0], historical_bytes_len) == 0) { // Mifare Plus S 4K SL1 mf_plus_data->type = MfPlusTypeS; mf_plus_data->size = MfPlusSize4K; @@ -165,11 +151,7 @@ MfPlusError FURI_LOG_D(TAG, "Mifare Plus S 4K SL1"); error = MfPlusErrorNone; - } else if( - memcmp( - simple_array_get_data(iso4_data->ats_data.t1_tk), - mf_plus_ats_t1_tk_values[1], - simple_array_get_count(iso4_data->ats_data.t1_tk)) == 0) { + } else if(memcmp(historical_bytes, mf_plus_ats_t1_tk_values[1], historical_bytes_len) == 0) { // Mifare Plus X 4K SL1 mf_plus_data->type = MfPlusTypeX; mf_plus_data->size = MfPlusSize4K; @@ -183,10 +165,7 @@ MfPlusError break; case 0x20: - if(memcmp( - simple_array_get_data(iso4_data->ats_data.t1_tk), - mf_plus_ats_t1_tk_values[0], - simple_array_get_count(iso4_data->ats_data.t1_tk)) == 0) { + if(memcmp(historical_bytes, mf_plus_ats_t1_tk_values[0], historical_bytes_len) == 0) { // Mifare Plus S 2/4K SL3 FURI_LOG_D(TAG, "Mifare Plus S SL3"); mf_plus_data->type = MfPlusTypeS; @@ -207,21 +186,20 @@ MfPlusError } else { FURI_LOG_D(TAG, "Sak 20 but no known Mifare Plus type (S)"); } - } else if( - memcmp( - simple_array_get_data(iso4_data->ats_data.t1_tk), - mf_plus_ats_t1_tk_values[1], - simple_array_get_count(iso4_data->ats_data.t1_tk)) == 0) { + } else if(memcmp(historical_bytes, mf_plus_ats_t1_tk_values[1], historical_bytes_len) == 0) { + // Mifare Plus X 2/4K SL3 mf_plus_data->type = MfPlusTypeX; mf_plus_data->security_level = MfPlusSecurityLevel3; FURI_LOG_D(TAG, "Mifare Plus X SL3"); if((iso4_data->iso14443_3a_data->atqa[0] & 0x0F) == 0x04) { + // Mifare Plus X 2K SL3 mf_plus_data->size = MfPlusSize2K; FURI_LOG_D(TAG, "Mifare Plus X 2K SL3"); error = MfPlusErrorNone; } else if((iso4_data->iso14443_3a_data->atqa[0] & 0x0F) == 0x02) { + // Mifare Plus X 4K SL3 mf_plus_data->size = MfPlusSize4K; FURI_LOG_D(TAG, "Mifare Plus X 4K SL3"); @@ -229,6 +207,16 @@ MfPlusError } else { FURI_LOG_D(TAG, "Sak 20 but no known Mifare Plus type (X)"); } + } else if( + memcmp(historical_bytes, mf_plus_ats_t1_tk_values[2], historical_bytes_len) == 0 || + memcmp(historical_bytes, mf_plus_ats_t1_tk_values[3], historical_bytes_len) == 0) { + // Mifare Plus SE 1K SL3 + mf_plus_data->type = MfPlusTypeSE; + mf_plus_data->size = MfPlusSize1K; + mf_plus_data->security_level = MfPlusSecurityLevel3; + + FURI_LOG_D(TAG, "Mifare Plus SE 1K SL3"); + error = MfPlusErrorNone; } else { FURI_LOG_D(TAG, "Sak 20 but no known Mifare Plus type"); } From 0b04d7d82d16fcc6fca85d4ebbfafe94f19fb56e Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Tue, 25 Mar 2025 06:48:27 +0000 Subject: [PATCH 56/92] Expose dESFire NDEF size --- lib/nfc/protocols/type_4_tag/type_4_tag.h | 2 ++ lib/nfc/protocols/type_4_tag/type_4_tag_i.h | 2 +- lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag.h b/lib/nfc/protocols/type_4_tag/type_4_tag.h index 9c19fd92e..199953bcd 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag.h +++ b/lib/nfc/protocols/type_4_tag/type_4_tag.h @@ -6,6 +6,8 @@ extern "C" { #endif +#define TYPE_4_TAG_MF_DESFIRE_NDEF_SIZE (2048U - sizeof(uint16_t)) + typedef enum { Type4TagErrorNone, Type4TagErrorNotPresent, diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_i.h b/lib/nfc/protocols/type_4_tag/type_4_tag_i.h index 18b46679a..f47d75236 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag_i.h +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_i.h @@ -56,7 +56,7 @@ #define TYPE_4_TAG_BUF_SIZE (256U - 3U - 2U) // Read returns 2 byte status trailer, write sends 5 byte command header #define TYPE_4_TAG_CHUNK_LEN MIN(TYPE_4_TAG_BUF_SIZE - 5U, TYPE_4_TAG_ISO_RW_CHUNK_LEN) -#define TYPE_4_TAG_DEFAULT_NDEF_SIZE (2048U - sizeof(uint16_t)) +#define TYPE_4_TAG_DEFAULT_NDEF_SIZE TYPE_4_TAG_MF_DESFIRE_NDEF_SIZE extern const uint8_t type_4_tag_iso_mf_name[TYPE_4_TAG_ISO_NAME_LEN]; extern const uint8_t type_4_tag_iso_df_name[TYPE_4_TAG_ISO_NAME_LEN]; diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c b/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c index 4722a169d..25a9f8aaa 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c @@ -418,7 +418,7 @@ Type4TagError type_4_tag_poller_create_cc(Type4TagPoller* instance) { instance->data->chunk_max_read = 0x3A; instance->data->chunk_max_write = 0x34; instance->data->ndef_file_id = TYPE_4_TAG_T4T_NDEF_EF_ID; - instance->data->ndef_max_len = TYPE_4_TAG_DEFAULT_NDEF_SIZE; + instance->data->ndef_max_len = TYPE_4_TAG_MF_DESFIRE_NDEF_SIZE; instance->data->ndef_read_lock = TYPE_4_TAG_T4T_CC_RW_LOCK_NONE; instance->data->ndef_write_lock = TYPE_4_TAG_T4T_CC_RW_LOCK_NONE; instance->data->is_tag_specific = true; From 6fb122e466b02478b782d40387042dfb79cf902d Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Wed, 26 Mar 2025 12:15:32 +0000 Subject: [PATCH 57/92] Stop poller and blinking on write success/fail --- .../protocol_support/nfc_protocol_support.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c index f28218c6f..d2386d87d 100644 --- a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c @@ -840,6 +840,7 @@ static bool nfc_protocol_support_scene_write_on_event(NfcApp* instance, SceneMan if(event.type == SceneManagerEventTypeCustom) { uint32_t new_state = -1; + bool stop_poller = false; if(event.event == NfcCustomEventCardDetected) { new_state = NfcSceneWriteStateWriting; @@ -851,14 +852,17 @@ static bool nfc_protocol_support_scene_write_on_event(NfcApp* instance, SceneMan dolphin_deed(DolphinDeedNfcSave); notification_message(instance->notifications, &sequence_success); new_state = NfcSceneWriteStateSuccess; + stop_poller = true; consumed = true; } else if(event.event == NfcCustomEventPollerFailure) { notification_message(instance->notifications, &sequence_error); new_state = NfcSceneWriteStateFailure; + stop_poller = true; consumed = true; } else if(event.event == NfcCustomEventWrongCard) { notification_message(instance->notifications, &sequence_error); new_state = NfcSceneWriteStateWrongCard; + stop_poller = true; consumed = true; } else if(event.event == NfcCustomEventViewExit) { scene_manager_previous_scene(instance->scene_manager); @@ -869,6 +873,14 @@ static bool nfc_protocol_support_scene_write_on_event(NfcApp* instance, SceneMan consumed = true; } + if(stop_poller) { + if(instance->poller) { + nfc_poller_stop(instance->poller); + nfc_poller_free(instance->poller); + instance->poller = NULL; + } + nfc_blink_stop(instance); + } if(new_state != (uint32_t)-1) { scene_manager_set_scene_state(instance->scene_manager, NfcSceneWrite, new_state); nfc_protocol_support_scene_write_setup_view(instance); @@ -879,8 +891,10 @@ static bool nfc_protocol_support_scene_write_on_event(NfcApp* instance, SceneMan } static void nfc_protocol_support_scene_write_on_exit(NfcApp* instance) { - nfc_poller_stop(instance->poller); - nfc_poller_free(instance->poller); + if(instance->poller) { + nfc_poller_stop(instance->poller); + nfc_poller_free(instance->poller); + } // Clear view popup_reset(instance->popup); From 768508aafe69bd54c30e6e91a8768f64f708487d Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 27 Mar 2025 03:28:42 +0000 Subject: [PATCH 58/92] GUI: Add submenu_remove_item() --- applications/services/gui/modules/submenu.c | 19 +++++++++++++++++++ applications/services/gui/modules/submenu.h | 8 ++++++++ targets/f18/api_symbols.csv | 3 ++- targets/f7/api_symbols.csv | 3 ++- 4 files changed, 31 insertions(+), 2 deletions(-) diff --git a/applications/services/gui/modules/submenu.c b/applications/services/gui/modules/submenu.c index 5b1bdccda..cdc1ecc28 100644 --- a/applications/services/gui/modules/submenu.c +++ b/applications/services/gui/modules/submenu.c @@ -235,6 +235,25 @@ void submenu_change_item_label(Submenu* submenu, uint32_t index, const char* lab true); } +void submenu_remove_item(Submenu* submenu, uint32_t index) { + furi_check(submenu); + + with_view_model( + submenu->view, + SubmenuModel * model, + { + SubmenuItemArray_it_t it; + for(SubmenuItemArray_it(it, model->items); !SubmenuItemArray_end_p(it); + SubmenuItemArray_next(it)) { + if(index == SubmenuItemArray_cref(it)->index) { + SubmenuItemArray_remove(model->items, it); + break; + } + } + }, + true); +} + void submenu_reset(Submenu* submenu) { furi_check(submenu); diff --git a/applications/services/gui/modules/submenu.h b/applications/services/gui/modules/submenu.h index e435f94a2..0408d52c0 100644 --- a/applications/services/gui/modules/submenu.h +++ b/applications/services/gui/modules/submenu.h @@ -61,6 +61,14 @@ void submenu_add_item( */ void submenu_change_item_label(Submenu* submenu, uint32_t index, const char* label); +/** Remove item from submenu + * + * @param submenu Submenu instance + * @param index menu item index, used for callback, may be + * the same with other items, first one is removed + */ +void submenu_remove_item(Submenu* submenu, uint32_t index); + /** Remove all items from submenu * * @param submenu Submenu instance diff --git a/targets/f18/api_symbols.csv b/targets/f18/api_symbols.csv index 2074fa131..f9cb8b5d4 100644 --- a/targets/f18/api_symbols.csv +++ b/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,82.1,, +Version,+,82.2,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/bt/bt_service/bt_keys_storage.h,, Header,+,applications/services/cli/cli.h,, @@ -2712,6 +2712,7 @@ Function,+,submenu_change_item_label,void,"Submenu*, uint32_t, const char*" Function,+,submenu_free,void,Submenu* Function,+,submenu_get_selected_item,uint32_t,Submenu* Function,+,submenu_get_view,View*,Submenu* +Function,+,submenu_remove_item,void,"Submenu*, uint32_t" Function,+,submenu_reset,void,Submenu* Function,+,submenu_set_header,void,"Submenu*, const char*" Function,+,submenu_set_selected_item,void,"Submenu*, uint32_t" diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index 1168a6eea..a8ce8c145 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,82.1,, +Version,+,82.2,, Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/bt/bt_service/bt_keys_storage.h,, @@ -3561,6 +3561,7 @@ Function,+,submenu_change_item_label,void,"Submenu*, uint32_t, const char*" Function,+,submenu_free,void,Submenu* Function,+,submenu_get_selected_item,uint32_t,Submenu* Function,+,submenu_get_view,View*,Submenu* +Function,+,submenu_remove_item,void,"Submenu*, uint32_t" Function,+,submenu_reset,void,Submenu* Function,+,submenu_set_header,void,"Submenu*, const char*" Function,+,submenu_set_selected_item,void,"Submenu*, uint32_t" From e9830fad2b85e68d11f6544e1521c9e3bf556a80 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 27 Mar 2025 03:34:17 +0000 Subject: [PATCH 59/92] NFC: Refactor write scenes into protocol support --- .../main/nfc/helpers/nfc_custom_event.h | 1 + .../helpers/protocol_support/felica/felica.c | 5 + .../iso14443_3a/iso14443_3a.c | 5 + .../iso14443_3b/iso14443_3b.c | 5 + .../iso14443_4a/iso14443_4a.c | 5 + .../iso14443_4b/iso14443_4b.c | 5 + .../protocol_support/iso15693_3/iso15693_3.c | 5 + .../protocol_support/mf_classic/mf_classic.c | 93 ++++++-- .../protocol_support/mf_desfire/mf_desfire.c | 5 + .../protocol_support/mf_plus/mf_plus.c | 5 + .../mf_ultralight/mf_ultralight.c | 75 ++++-- .../protocol_support/nfc_protocol_support.c | 219 ++++++++++++++++++ .../nfc_protocol_support_base.h | 9 + .../nfc_protocol_support_common.h | 2 + .../nfc_protocol_support_gui_common.h | 1 + .../nfc/helpers/protocol_support/slix/slix.c | 5 + .../helpers/protocol_support/st25tb/st25tb.c | 5 + .../main/nfc/scenes/nfc_scene_config.h | 9 +- ...ene_mf_classic_update_initial_wrong_card.c | 3 + .../nfc_scene_mf_classic_write_initial.c | 148 ------------ .../nfc_scene_mf_classic_write_initial_fail.c | 62 ----- ...c_scene_mf_classic_write_initial_success.c | 43 ---- ...cene_mf_classic_write_initial_wrong_card.c | 57 ----- .../scenes/nfc_scene_mf_ultralight_write.c | 120 ---------- .../nfc_scene_mf_ultralight_write_fail.c | 67 ------ .../nfc_scene_mf_ultralight_write_success.c | 46 ---- .../nfc_scene_mf_ultralight_wrong_card.c | 58 ----- .../main/nfc/scenes/nfc_scene_write.c | 13 ++ 28 files changed, 436 insertions(+), 640 deletions(-) delete mode 100644 applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial.c delete mode 100644 applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial_fail.c delete mode 100644 applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial_success.c delete mode 100644 applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial_wrong_card.c delete mode 100644 applications/main/nfc/scenes/nfc_scene_mf_ultralight_write.c delete mode 100644 applications/main/nfc/scenes/nfc_scene_mf_ultralight_write_fail.c delete mode 100644 applications/main/nfc/scenes/nfc_scene_mf_ultralight_write_success.c delete mode 100644 applications/main/nfc/scenes/nfc_scene_mf_ultralight_wrong_card.c create mode 100644 applications/main/nfc/scenes/nfc_scene_write.c diff --git a/applications/main/nfc/helpers/nfc_custom_event.h b/applications/main/nfc/helpers/nfc_custom_event.h index 16fbc4749..1f10a2dea 100644 --- a/applications/main/nfc/helpers/nfc_custom_event.h +++ b/applications/main/nfc/helpers/nfc_custom_event.h @@ -13,6 +13,7 @@ typedef enum { NfcCustomEventCardLost, NfcCustomEventViewExit, + NfcCustomEventRetry, NfcCustomEventWorkerExit, NfcCustomEventWorkerUpdate, NfcCustomEventWrongCard, diff --git a/applications/main/nfc/helpers/protocol_support/felica/felica.c b/applications/main/nfc/helpers/protocol_support/felica/felica.c index 561cd4d2e..7717396a3 100644 --- a/applications/main/nfc/helpers/protocol_support/felica/felica.c +++ b/applications/main/nfc/helpers/protocol_support/felica/felica.c @@ -213,4 +213,9 @@ const NfcProtocolSupportBase nfc_protocol_support_felica = { .on_enter = nfc_scene_emulate_on_enter_felica, .on_event = nfc_protocol_support_common_on_event_empty, }, + .scene_write = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, + }, }; diff --git a/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a.c b/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a.c index 99e211301..9d6dba5a7 100644 --- a/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a.c +++ b/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a.c @@ -144,4 +144,9 @@ const NfcProtocolSupportBase nfc_protocol_support_iso14443_3a = { .on_enter = nfc_scene_emulate_on_enter_iso14443_3a, .on_event = nfc_protocol_support_common_on_event_empty, }, + .scene_write = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, + }, }; diff --git a/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b.c b/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b.c index 43b541111..49da93d7b 100644 --- a/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b.c +++ b/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b.c @@ -111,4 +111,9 @@ const NfcProtocolSupportBase nfc_protocol_support_iso14443_3b = { .on_enter = nfc_protocol_support_common_on_enter_empty, .on_event = nfc_protocol_support_common_on_event_empty, }, + .scene_write = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, + }, }; diff --git a/applications/main/nfc/helpers/protocol_support/iso14443_4a/iso14443_4a.c b/applications/main/nfc/helpers/protocol_support/iso14443_4a/iso14443_4a.c index 17435ccd4..190c26e87 100644 --- a/applications/main/nfc/helpers/protocol_support/iso14443_4a/iso14443_4a.c +++ b/applications/main/nfc/helpers/protocol_support/iso14443_4a/iso14443_4a.c @@ -147,4 +147,9 @@ const NfcProtocolSupportBase nfc_protocol_support_iso14443_4a = { .on_enter = nfc_scene_emulate_on_enter_iso14443_4a, .on_event = nfc_protocol_support_common_on_event_empty, }, + .scene_write = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, + }, }; diff --git a/applications/main/nfc/helpers/protocol_support/iso14443_4b/iso14443_4b.c b/applications/main/nfc/helpers/protocol_support/iso14443_4b/iso14443_4b.c index 8038e0491..8fd823f21 100644 --- a/applications/main/nfc/helpers/protocol_support/iso14443_4b/iso14443_4b.c +++ b/applications/main/nfc/helpers/protocol_support/iso14443_4b/iso14443_4b.c @@ -116,4 +116,9 @@ const NfcProtocolSupportBase nfc_protocol_support_iso14443_4b = { .on_enter = nfc_protocol_support_common_on_enter_empty, .on_event = nfc_protocol_support_common_on_event_empty, }, + .scene_write = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, + }, }; diff --git a/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3.c b/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3.c index 7efd102f1..ffd6971e0 100644 --- a/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3.c +++ b/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3.c @@ -161,4 +161,9 @@ const NfcProtocolSupportBase nfc_protocol_support_iso15693_3 = { .on_enter = nfc_scene_emulate_on_enter_iso15693_3, .on_event = nfc_protocol_support_common_on_event_empty, }, + .scene_write = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, + }, }; diff --git a/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c b/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c index 6f7be7f4c..acc641c34 100644 --- a/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c +++ b/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c @@ -12,9 +12,8 @@ enum { SubmenuIndexDetectReader = SubmenuIndexCommonMax, - SubmenuIndexWrite, + SubmenuIndexDictAttack, SubmenuIndexUpdate, - SubmenuIndexDictAttack }; static void nfc_scene_info_on_enter_mf_classic(NfcApp* instance) { @@ -114,6 +113,9 @@ static void nfc_scene_read_menu_on_enter_mf_classic(NfcApp* instance) { Submenu* submenu = instance->submenu; const MfClassicData* data = nfc_device_get_data(instance->nfc_device, NfcProtocolMfClassic); + // Doesn't make sense to show "Write to Initial Card" right after reading + submenu_remove_item(submenu, SubmenuIndexCommonWrite); + if(!mf_classic_is_card_read(data)) { submenu_add_item( submenu, @@ -152,6 +154,8 @@ static void nfc_scene_saved_menu_on_enter_mf_classic(NfcApp* instance) { Submenu* submenu = instance->submenu; const MfClassicData* data = nfc_device_get_data(instance->nfc_device, NfcProtocolMfClassic); + submenu_change_item_label(submenu, SubmenuIndexCommonWrite, "Write to Initial Card"); + if(!mf_classic_is_card_read(data)) { submenu_add_item( submenu, @@ -167,12 +171,6 @@ static void nfc_scene_saved_menu_on_enter_mf_classic(NfcApp* instance) { nfc_protocol_support_common_submenu_callback, instance); } - submenu_add_item( - submenu, - "Write to Initial Card", - SubmenuIndexWrite, - nfc_protocol_support_common_submenu_callback, - instance); submenu_add_item( submenu, @@ -218,18 +216,15 @@ static bool nfc_scene_saved_menu_on_event_mf_classic(NfcApp* instance, SceneMana if(event.event == SubmenuIndexDetectReader) { scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicDetectReader); consumed = true; - } else if(event.event == SubmenuIndexWrite) { - scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicWriteInitial); - consumed = true; - } else if(event.event == SubmenuIndexUpdate) { - scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicUpdateInitial); - consumed = true; } else if(event.event == SubmenuIndexDictAttack) { if(!scene_manager_search_and_switch_to_previous_scene( instance->scene_manager, NfcSceneMfClassicDictAttack)) { scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicDictAttack); } consumed = true; + } else if(event.event == SubmenuIndexUpdate) { + scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicUpdateInitial); + consumed = true; } } @@ -249,8 +244,71 @@ static bool nfc_scene_save_name_on_event_mf_classic(NfcApp* instance, SceneManag return consumed; } +static NfcCommand + nfc_scene_write_poller_callback_mf_classic(NfcGenericEvent event, void* context) { + furi_assert(event.protocol == NfcProtocolMfClassic); + + NfcApp* instance = context; + MfClassicPollerEvent* mfc_event = event.event_data; + NfcCommand command = NfcCommandContinue; + const MfClassicData* write_data = + nfc_device_get_data(instance->nfc_device, NfcProtocolMfClassic); + + if(mfc_event->type == MfClassicPollerEventTypeCardDetected) { + furi_string_reset(instance->text_box_store); + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventCardDetected); + } else if(mfc_event->type == MfClassicPollerEventTypeCardLost) { + furi_string_set(instance->text_box_store, "Use the source\ncard only"); + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventCardLost); + } else if(mfc_event->type == MfClassicPollerEventTypeRequestMode) { + const MfClassicData* tag_data = nfc_poller_get_data(instance->poller); + if(iso14443_3a_is_equal(tag_data->iso14443_3a_data, write_data->iso14443_3a_data)) { + mfc_event->data->poller_mode.mode = MfClassicPollerModeWrite; + } else { + furi_string_set( + instance->text_box_store, "Use source card!\nTo write blanks\nuse NFC Magic app"); + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventWrongCard); + command = NfcCommandStop; + } + } else if(mfc_event->type == MfClassicPollerEventTypeRequestSectorTrailer) { + uint8_t sector = mfc_event->data->sec_tr_data.sector_num; + uint8_t sec_tr = mf_classic_get_sector_trailer_num_by_sector(sector); + if(mf_classic_is_block_read(write_data, sec_tr)) { + mfc_event->data->sec_tr_data.sector_trailer = write_data->block[sec_tr]; + mfc_event->data->sec_tr_data.sector_trailer_provided = true; + } else { + mfc_event->data->sec_tr_data.sector_trailer_provided = false; + } + } else if(mfc_event->type == MfClassicPollerEventTypeRequestWriteBlock) { + uint8_t block_num = mfc_event->data->write_block_data.block_num; + if(mf_classic_is_block_read(write_data, block_num)) { + mfc_event->data->write_block_data.write_block = write_data->block[block_num]; + mfc_event->data->write_block_data.write_block_provided = true; + } else { + mfc_event->data->write_block_data.write_block_provided = false; + } + } else if(mfc_event->type == MfClassicPollerEventTypeSuccess) { + furi_string_reset(instance->text_box_store); + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess); + command = NfcCommandStop; + } else if(mfc_event->type == MfClassicPollerEventTypeFail) { + furi_string_set(instance->text_box_store, "Not all sectors\nwere written\ncorrectly"); + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerFailure); + command = NfcCommandStop; + } + + return command; +} + +static void nfc_scene_write_on_enter_mf_classic(NfcApp* instance) { + instance->poller = nfc_poller_alloc(instance->nfc, NfcProtocolMfClassic); + nfc_poller_start(instance->poller, nfc_scene_write_poller_callback_mf_classic, instance); + furi_string_set(instance->text_box_store, "Use the source\ncard only"); +} + const NfcProtocolSupportBase nfc_protocol_support_mf_classic = { - .features = NfcProtocolFeatureEmulateFull | NfcProtocolFeatureMoreInfo, + .features = NfcProtocolFeatureEmulateFull | NfcProtocolFeatureMoreInfo | + NfcProtocolFeatureWrite, .scene_info = { @@ -292,4 +350,9 @@ const NfcProtocolSupportBase nfc_protocol_support_mf_classic = { .on_enter = nfc_scene_emulate_on_enter_mf_classic, .on_event = nfc_protocol_support_common_on_event_empty, }, + .scene_write = + { + .on_enter = nfc_scene_write_on_enter_mf_classic, + .on_event = nfc_protocol_support_common_on_event_empty, + }, }; diff --git a/applications/main/nfc/helpers/protocol_support/mf_desfire/mf_desfire.c b/applications/main/nfc/helpers/protocol_support/mf_desfire/mf_desfire.c index deba1bca2..bbca076f7 100644 --- a/applications/main/nfc/helpers/protocol_support/mf_desfire/mf_desfire.c +++ b/applications/main/nfc/helpers/protocol_support/mf_desfire/mf_desfire.c @@ -124,4 +124,9 @@ const NfcProtocolSupportBase nfc_protocol_support_mf_desfire = { .on_enter = nfc_scene_emulate_on_enter_mf_desfire, .on_event = nfc_protocol_support_common_on_event_empty, }, + .scene_write = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, + }, }; diff --git a/applications/main/nfc/helpers/protocol_support/mf_plus/mf_plus.c b/applications/main/nfc/helpers/protocol_support/mf_plus/mf_plus.c index c7b36e21e..e791eb291 100644 --- a/applications/main/nfc/helpers/protocol_support/mf_plus/mf_plus.c +++ b/applications/main/nfc/helpers/protocol_support/mf_plus/mf_plus.c @@ -120,4 +120,9 @@ const NfcProtocolSupportBase nfc_protocol_support_mf_plus = { .on_enter = nfc_scene_emulate_on_enter_mf_plus, .on_event = nfc_protocol_support_common_on_event_empty, }, + .scene_write = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, + }, }; diff --git a/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c b/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c index 3adf2a1f5..34354741d 100644 --- a/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c +++ b/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c @@ -14,7 +14,6 @@ enum { SubmenuIndexUnlock = SubmenuIndexCommonMax, SubmenuIndexUnlockByReader, SubmenuIndexUnlockByPassword, - SubmenuIndexWrite, }; enum { @@ -182,24 +181,22 @@ static void nfc_scene_read_and_saved_menu_on_enter_mf_ultralight(NfcApp* instanc const MfUltralightData* data = nfc_device_get_data(instance->nfc_device, NfcProtocolMfUltralight); + bool is_locked = !mf_ultralight_is_all_data_read(data); - if(!mf_ultralight_is_all_data_read(data)) { + if(is_locked || + (data->type != MfUltralightTypeNTAG213 && data->type != MfUltralightTypeNTAG215 && + data->type != MfUltralightTypeNTAG216 && data->type != MfUltralightTypeUL11 && + data->type != MfUltralightTypeUL21 && data->type != MfUltralightTypeOrigin)) { + submenu_remove_item(submenu, SubmenuIndexCommonWrite); + } + + if(is_locked) { submenu_add_item( submenu, "Unlock", SubmenuIndexUnlock, nfc_protocol_support_common_submenu_callback, instance); - } else if( - data->type == MfUltralightTypeNTAG213 || data->type == MfUltralightTypeNTAG215 || - data->type == MfUltralightTypeNTAG216 || data->type == MfUltralightTypeUL11 || - data->type == MfUltralightTypeUL21 || data->type == MfUltralightTypeOrigin) { - submenu_add_item( - submenu, - "Write", - SubmenuIndexWrite, - nfc_protocol_support_common_submenu_callback, - instance); } } @@ -252,9 +249,6 @@ static bool nfc_scene_read_and_saved_menu_on_event_mf_ultralight( NfcSceneMfUltralightUnlockMenu; scene_manager_next_scene(instance->scene_manager, next_scene); consumed = true; - } else if(event.event == SubmenuIndexWrite) { - scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightWrite); - consumed = true; } else if(event.event == SubmenuIndexCommonEdit) { scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid); consumed = true; @@ -263,8 +257,52 @@ static bool nfc_scene_read_and_saved_menu_on_event_mf_ultralight( return consumed; } +static NfcCommand + nfc_scene_write_poller_callback_mf_ultralight(NfcGenericEvent event, void* context) { + furi_assert(event.protocol == NfcProtocolMfUltralight); + + NfcApp* instance = context; + MfUltralightPollerEvent* mf_ultralight_event = event.event_data; + NfcCommand command = NfcCommandContinue; + + if(mf_ultralight_event->type == MfUltralightPollerEventTypeRequestMode) { + mf_ultralight_event->data->poller_mode = MfUltralightPollerModeWrite; + furi_string_reset(instance->text_box_store); + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventCardDetected); + } else if(mf_ultralight_event->type == MfUltralightPollerEventTypeAuthRequest) { + mf_ultralight_event->data->auth_context.skip_auth = true; + } else if(mf_ultralight_event->type == MfUltralightPollerEventTypeRequestWriteData) { + mf_ultralight_event->data->write_data = + nfc_device_get_data(instance->nfc_device, NfcProtocolMfUltralight); + } else if(mf_ultralight_event->type == MfUltralightPollerEventTypeCardMismatch) { + furi_string_set(instance->text_box_store, "Card of the same\ntype should be\n presented"); + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventWrongCard); + command = NfcCommandStop; + } else if(mf_ultralight_event->type == MfUltralightPollerEventTypeCardLocked) { + furi_string_set( + instance->text_box_store, "Card protected by\npassword, AUTH0\nor lock bits"); + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerFailure); + command = NfcCommandStop; + } else if(mf_ultralight_event->type == MfUltralightPollerEventTypeWriteFail) { + command = NfcCommandStop; + } else if(mf_ultralight_event->type == MfUltralightPollerEventTypeWriteSuccess) { + furi_string_reset(instance->text_box_store); + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess); + command = NfcCommandStop; + } + + return command; +} + +static void nfc_scene_write_on_enter_mf_ultralight(NfcApp* instance) { + instance->poller = nfc_poller_alloc(instance->nfc, NfcProtocolMfUltralight); + nfc_poller_start(instance->poller, nfc_scene_write_poller_callback_mf_ultralight, instance); + furi_string_set(instance->text_box_store, "Apply the initial\ncard only"); +} + const NfcProtocolSupportBase nfc_protocol_support_mf_ultralight = { - .features = NfcProtocolFeatureEmulateFull | NfcProtocolFeatureMoreInfo, + .features = NfcProtocolFeatureEmulateFull | NfcProtocolFeatureMoreInfo | + NfcProtocolFeatureWrite, .scene_info = { @@ -306,4 +344,9 @@ const NfcProtocolSupportBase nfc_protocol_support_mf_ultralight = { .on_enter = nfc_scene_emulate_on_enter_mf_ultralight, .on_event = nfc_protocol_support_common_on_event_empty, }, + .scene_write = + { + .on_enter = nfc_scene_write_on_enter_mf_ultralight, + .on_event = nfc_protocol_support_common_on_event_empty, + }, }; diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c index 0d63dc56b..0c5a9d3bc 100644 --- a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c @@ -258,6 +258,15 @@ static void nfc_protocol_support_scene_read_menu_on_enter(NfcApp* instance) { instance); } + if(nfc_protocol_support_has_feature(protocol, NfcProtocolFeatureWrite)) { + submenu_add_item( + submenu, + "Write", + SubmenuIndexCommonWrite, + nfc_protocol_support_common_submenu_callback, + instance); + } + nfc_protocol_support[protocol]->scene_read_menu.on_enter(instance); submenu_add_item( @@ -291,6 +300,10 @@ static bool dolphin_deed(DolphinDeedNfcEmulate); scene_manager_next_scene(instance->scene_manager, NfcSceneEmulate); consumed = true; + } else if(event.event == SubmenuIndexCommonWrite) { + dolphin_deed(DolphinDeedNfcEmulate); + scene_manager_next_scene(instance->scene_manager, NfcSceneWrite); + consumed = true; } else { const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device); consumed = nfc_protocol_support[protocol]->scene_read_menu.on_event(instance, event); @@ -383,6 +396,15 @@ static void nfc_protocol_support_scene_saved_menu_on_enter(NfcApp* instance) { instance); } + if(nfc_protocol_support_has_feature(protocol, NfcProtocolFeatureWrite)) { + submenu_add_item( + submenu, + "Write", + SubmenuIndexCommonWrite, + nfc_protocol_support_common_submenu_callback, + instance); + } + if(nfc_protocol_support_has_feature(protocol, NfcProtocolFeatureEditUid)) { submenu_add_item( submenu, @@ -456,6 +478,12 @@ static bool dolphin_deed(is_added ? DolphinDeedNfcAddEmulate : DolphinDeedNfcEmulate); scene_manager_next_scene(instance->scene_manager, NfcSceneEmulate); consumed = true; + } else if(event.event == SubmenuIndexCommonWrite) { + const bool is_added = + scene_manager_has_previous_scene(instance->scene_manager, NfcSceneSetType); + dolphin_deed(is_added ? DolphinDeedNfcAddEmulate : DolphinDeedNfcEmulate); + scene_manager_next_scene(instance->scene_manager, NfcSceneWrite); + consumed = true; } else if(event.event == SubmenuIndexCommonEdit) { scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid); consumed = true; @@ -688,6 +716,191 @@ static void nfc_protocol_support_scene_emulate_on_exit(NfcApp* instance) { nfc_blink_stop(instance); } +// SceneWrite +/** + * @brief Current view displayed on the write scene. + * + * The emulation scene has five states, some protocols may not use all states. + * Protocol handles poller events, when scene state needs to change it should + * fill text_box_store with a short caption (when applicable) before sending + * the relevant view dispatcher event. + */ +enum { + NfcSceneWriteStateSearching, /**< Ask user to touch the card. Event: on_enter, CardLost. Needs caption. */ + NfcSceneWriteStateWriting, /**< Ask not to move while writing. Event: CardDetected. No caption. */ + NfcSceneWriteStateSuccess, /**< Card written successfully. Event: PollerSuccess. No caption. */ + NfcSceneWriteStateFailure, /**< An error is displayed. Event: PollerFailure. Needs caption. */ + NfcSceneWriteStateWrongCard, /**< Wrong card was presented. Event: WrongCard. Needs caption. */ +}; + +static void nfc_protocol_support_scene_write_popup_callback(void* context) { + NfcApp* instance = context; + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventViewExit); +} + +void nfc_protocol_support_scene_write_widget_callback( + GuiButtonType result, + InputType type, + void* context) { + NfcApp* instance = context; + if(type == InputTypeShort && result == GuiButtonTypeLeft) { + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventRetry); + } +} + +static void nfc_protocol_support_scene_write_setup_view(NfcApp* instance) { + Popup* popup = instance->popup; + Widget* widget = instance->widget; + popup_reset(popup); + widget_reset(widget); + uint32_t state = scene_manager_get_scene_state(instance->scene_manager, NfcSceneWrite); + NfcView view = NfcViewPopup; + + if(state == NfcSceneWriteStateSearching) { + popup_set_header(popup, "Writing", 95, 20, AlignCenter, AlignCenter); + popup_set_text( + popup, + furi_string_get_cstr(instance->text_box_store), + 95, + 38, + AlignCenter, + AlignCenter); + popup_set_icon(popup, 0, 8, &I_NFC_manual_60x50); + } else if(state == NfcSceneWriteStateWriting) { + popup_set_header(popup, "Writing\nDon't move...", 52, 32, AlignLeft, AlignCenter); + popup_set_icon(popup, 12, 23, &A_Loading_24); + } else if(state == NfcSceneWriteStateSuccess) { + popup_set_header(popup, "Successfully\nwritten!", 126, 2, AlignRight, AlignTop); + popup_set_icon(popup, 0, 9, &I_DolphinSuccess_91x55); + popup_set_timeout(popup, 1500); + popup_set_context(popup, instance); + popup_set_callback(popup, nfc_protocol_support_scene_write_popup_callback); + popup_enable_timeout(popup); + } else if(state == NfcSceneWriteStateFailure) { + view = NfcViewWidget; + widget_add_string_element( + widget, 7, 4, AlignLeft, AlignTop, FontPrimary, "Writing gone wrong!"); + widget_add_string_multiline_element( + widget, + 7, + 17, + AlignLeft, + AlignTop, + FontSecondary, + furi_string_get_cstr(instance->text_box_store)); + widget_add_icon_element(widget, 83, 22, &I_WarningDolphinFlip_45x42); + widget_add_button_element( + widget, + GuiButtonTypeLeft, + "Retry", + nfc_protocol_support_scene_write_widget_callback, + instance); + } else if(state == NfcSceneWriteStateWrongCard) { + view = NfcViewWidget; + widget_add_string_element(widget, 3, 4, AlignLeft, AlignTop, FontPrimary, "Wrong card!"); + widget_add_string_multiline_element( + widget, + 4, + 17, + AlignLeft, + AlignTop, + FontSecondary, + furi_string_get_cstr(instance->text_box_store)); + widget_add_icon_element(widget, 83, 22, &I_WarningDolphinFlip_45x42); + widget_add_button_element( + widget, + GuiButtonTypeLeft, + "Retry", + nfc_protocol_support_scene_write_widget_callback, + instance); + } + + view_dispatcher_switch_to_view(instance->view_dispatcher, view); +} + +static void nfc_protocol_support_scene_write_on_enter(NfcApp* instance) { + scene_manager_set_scene_state( + instance->scene_manager, NfcSceneWrite, NfcSceneWriteStateSearching); + furi_string_reset(instance->text_box_store); + + const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device); + + // instance->poller is allocated in the respective on_enter() handler + nfc_protocol_support[protocol]->scene_write.on_enter(instance); + + nfc_protocol_support_scene_write_setup_view(instance); + nfc_blink_emulate_start(instance); +} + +static bool nfc_protocol_support_scene_write_on_event(NfcApp* instance, SceneManagerEvent event) { + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + uint32_t new_state = -1; + bool stop_poller = false; + + if(event.event == NfcCustomEventCardDetected) { + new_state = NfcSceneWriteStateWriting; + consumed = true; + } else if(event.event == NfcCustomEventCardLost) { + new_state = NfcSceneWriteStateSearching; + consumed = true; + } else if(event.event == NfcCustomEventPollerSuccess) { + dolphin_deed(DolphinDeedNfcSave); + notification_message(instance->notifications, &sequence_success); + new_state = NfcSceneWriteStateSuccess; + stop_poller = true; + consumed = true; + } else if(event.event == NfcCustomEventPollerFailure) { + notification_message(instance->notifications, &sequence_error); + new_state = NfcSceneWriteStateFailure; + stop_poller = true; + consumed = true; + } else if(event.event == NfcCustomEventWrongCard) { + notification_message(instance->notifications, &sequence_error); + new_state = NfcSceneWriteStateWrongCard; + stop_poller = true; + consumed = true; + } else if(event.event == NfcCustomEventViewExit) { + scene_manager_previous_scene(instance->scene_manager); + consumed = true; + } else if(event.event == NfcCustomEventRetry) { + nfc_protocol_support_scenes[NfcProtocolSupportSceneWrite].on_exit(instance); + nfc_protocol_support_scenes[NfcProtocolSupportSceneWrite].on_enter(instance); + consumed = true; + } + + if(stop_poller) { + if(instance->poller) { + nfc_poller_stop(instance->poller); + nfc_poller_free(instance->poller); + instance->poller = NULL; + } + nfc_blink_stop(instance); + } + if(new_state != (uint32_t)-1) { + scene_manager_set_scene_state(instance->scene_manager, NfcSceneWrite, new_state); + nfc_protocol_support_scene_write_setup_view(instance); + } + } + + return consumed; +} + +static void nfc_protocol_support_scene_write_on_exit(NfcApp* instance) { + if(instance->poller) { + nfc_poller_stop(instance->poller); + nfc_poller_free(instance->poller); + } + + // Clear view + popup_reset(instance->popup); + widget_reset(instance->widget); + furi_string_reset(instance->text_box_store); + + nfc_blink_stop(instance); +} + static void nfc_protocol_support_scene_rpc_on_enter(NfcApp* instance) { UNUSED(instance); } @@ -803,6 +1016,12 @@ static const NfcProtocolSupportCommonSceneBase .on_event = nfc_protocol_support_scene_emulate_on_event, .on_exit = nfc_protocol_support_scene_emulate_on_exit, }, + [NfcProtocolSupportSceneWrite] = + { + .on_enter = nfc_protocol_support_scene_write_on_enter, + .on_event = nfc_protocol_support_scene_write_on_event, + .on_exit = nfc_protocol_support_scene_write_on_exit, + }, [NfcProtocolSupportSceneRpc] = { .on_enter = nfc_protocol_support_scene_rpc_on_enter, diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_base.h b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_base.h index eec736ca2..0982bae1f 100644 --- a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_base.h +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_base.h @@ -114,4 +114,13 @@ typedef struct { * It is responsible for creating a listener and for handling its events. */ NfcProtocolSupportSceneBase scene_emulate; + + /** + * @brief Handlers for protocol-specific write scene. + * + * This scene is activated when a write operation is in progress. + * It is responsible for creating a poller, handling its events and + * displaying short captions for what is happening. + */ + NfcProtocolSupportSceneBase scene_write; } NfcProtocolSupportBase; diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_common.h b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_common.h index 6e3214106..574c23a4c 100644 --- a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_common.h +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_common.h @@ -13,6 +13,7 @@ typedef enum { NfcProtocolFeatureEmulateFull = 1UL << 1, /**< Complete emulation is supported. */ NfcProtocolFeatureEditUid = 1UL << 2, /**< UID editing is supported. */ NfcProtocolFeatureMoreInfo = 1UL << 3, /**< More information is provided. */ + NfcProtocolFeatureWrite = 1UL << 4, /**< Writing to real card is supported. */ } NfcProtocolFeature; /** @@ -30,6 +31,7 @@ typedef enum { NfcProtocolSupportSceneSavedMenu, /**< Menu for the card that was loaded from file. */ NfcProtocolSupportSceneSaveName, /**< Shown when saving or renaming a file. */ NfcProtocolSupportSceneEmulate, /**< Shown when emulating a card. */ + NfcProtocolSupportSceneWrite, /**< Shown when writing to a card. */ NfcProtocolSupportSceneRpc, /**< Shown in remote-controlled (RPC) mode. */ NfcProtocolSupportSceneCount, /**< Special value equal to total scene count. Internal use. */ diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_gui_common.h b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_gui_common.h index 3230f1a7e..63a1d9ac1 100644 --- a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_gui_common.h +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_gui_common.h @@ -15,6 +15,7 @@ enum { SubmenuIndexCommonSave, /**< Save menu option. */ SubmenuIndexCommonEmulate, /**< Emulate menu option. */ + SubmenuIndexCommonWrite, /**< Write menu option. */ SubmenuIndexCommonEdit, /**< Edit menu option. */ SubmenuIndexCommonInfo, /**< Info menu option. */ SubmenuIndexCommonRename, /**< Rename menu option. */ diff --git a/applications/main/nfc/helpers/protocol_support/slix/slix.c b/applications/main/nfc/helpers/protocol_support/slix/slix.c index 35592eaa1..e998d0ad2 100644 --- a/applications/main/nfc/helpers/protocol_support/slix/slix.c +++ b/applications/main/nfc/helpers/protocol_support/slix/slix.c @@ -157,4 +157,9 @@ const NfcProtocolSupportBase nfc_protocol_support_slix = { .on_enter = nfc_scene_emulate_on_enter_slix, .on_event = nfc_protocol_support_common_on_event_empty, }, + .scene_write = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, + }, }; diff --git a/applications/main/nfc/helpers/protocol_support/st25tb/st25tb.c b/applications/main/nfc/helpers/protocol_support/st25tb/st25tb.c index 0305d614c..a119f8cd6 100644 --- a/applications/main/nfc/helpers/protocol_support/st25tb/st25tb.c +++ b/applications/main/nfc/helpers/protocol_support/st25tb/st25tb.c @@ -108,4 +108,9 @@ const NfcProtocolSupportBase nfc_protocol_support_st25tb = { .on_enter = nfc_protocol_support_common_on_enter_empty, .on_event = nfc_protocol_support_common_on_event_empty, }, + .scene_write = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, + }, }; diff --git a/applications/main/nfc/scenes/nfc_scene_config.h b/applications/main/nfc/scenes/nfc_scene_config.h index 83c8ffeed..9ded94373 100644 --- a/applications/main/nfc/scenes/nfc_scene_config.h +++ b/applications/main/nfc/scenes/nfc_scene_config.h @@ -18,6 +18,7 @@ ADD_SCENE(nfc, extra_actions, ExtraActions) ADD_SCENE(nfc, read_success, ReadSuccess) ADD_SCENE(nfc, read_menu, ReadMenu) ADD_SCENE(nfc, emulate, Emulate) +ADD_SCENE(nfc, write, Write) ADD_SCENE(nfc, rpc, Rpc) ADD_SCENE(nfc, debug, Debug) ADD_SCENE(nfc, field, Field) @@ -25,10 +26,6 @@ ADD_SCENE(nfc, retry_confirm, RetryConfirm) ADD_SCENE(nfc, exit_confirm, ExitConfirm) ADD_SCENE(nfc, save_confirm, SaveConfirm) -ADD_SCENE(nfc, mf_ultralight_write, MfUltralightWrite) -ADD_SCENE(nfc, mf_ultralight_write_success, MfUltralightWriteSuccess) -ADD_SCENE(nfc, mf_ultralight_write_fail, MfUltralightWriteFail) -ADD_SCENE(nfc, mf_ultralight_wrong_card, MfUltralightWrongCard) ADD_SCENE(nfc, mf_ultralight_unlock_menu, MfUltralightUnlockMenu) ADD_SCENE(nfc, mf_ultralight_unlock_warn, MfUltralightUnlockWarn) ADD_SCENE(nfc, mf_ultralight_key_input, MfUltralightKeyInput) @@ -46,10 +43,6 @@ ADD_SCENE(nfc, mf_classic_mfkey_complete, MfClassicMfkeyComplete) ADD_SCENE(nfc, mf_classic_update_initial, MfClassicUpdateInitial) ADD_SCENE(nfc, mf_classic_update_initial_success, MfClassicUpdateInitialSuccess) ADD_SCENE(nfc, mf_classic_update_initial_wrong_card, MfClassicUpdateInitialWrongCard) -ADD_SCENE(nfc, mf_classic_write_initial, MfClassicWriteInitial) -ADD_SCENE(nfc, mf_classic_write_initial_success, MfClassicWriteInitialSuccess) -ADD_SCENE(nfc, mf_classic_write_initial_fail, MfClassicWriteInitialFail) -ADD_SCENE(nfc, mf_classic_write_initial_wrong_card, MfClassicWriteInitialWrongCard) ADD_SCENE(nfc, mf_classic_keys, MfClassicKeys) ADD_SCENE(nfc, mf_classic_keys_list, MfClassicKeysList) diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_update_initial_wrong_card.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_update_initial_wrong_card.c index f3aefb781..ac9e7a192 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_update_initial_wrong_card.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_update_initial_wrong_card.c @@ -47,6 +47,9 @@ bool nfc_scene_mf_classic_update_initial_wrong_card_on_event( if(event.event == GuiButtonTypeLeft) { consumed = scene_manager_previous_scene(instance->scene_manager); } + } else if(event.type == SceneManagerEventTypeBack) { + consumed = scene_manager_search_and_switch_to_previous_scene( + instance->scene_manager, NfcSceneSavedMenu); } return consumed; } diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial.c deleted file mode 100644 index 12e7ba1ec..000000000 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial.c +++ /dev/null @@ -1,148 +0,0 @@ -#include "../nfc_app_i.h" - -#include - -enum { - NfcSceneMfClassicWriteInitialStateCardSearch, - NfcSceneMfClassicWriteInitialStateCardFound, -}; - -NfcCommand - nfc_scene_mf_classic_write_initial_worker_callback(NfcGenericEvent event, void* context) { - furi_assert(context); - furi_assert(event.event_data); - furi_assert(event.protocol == NfcProtocolMfClassic); - - NfcCommand command = NfcCommandContinue; - NfcApp* instance = context; - MfClassicPollerEvent* mfc_event = event.event_data; - const MfClassicData* write_data = - nfc_device_get_data(instance->nfc_device, NfcProtocolMfClassic); - - if(mfc_event->type == MfClassicPollerEventTypeCardDetected) { - view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventCardDetected); - } else if(mfc_event->type == MfClassicPollerEventTypeCardLost) { - view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventCardLost); - } else if(mfc_event->type == MfClassicPollerEventTypeRequestMode) { - const MfClassicData* tag_data = nfc_poller_get_data(instance->poller); - if(iso14443_3a_is_equal(tag_data->iso14443_3a_data, write_data->iso14443_3a_data)) { - mfc_event->data->poller_mode.mode = MfClassicPollerModeWrite; - } else { - view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventWrongCard); - command = NfcCommandStop; - } - } else if(mfc_event->type == MfClassicPollerEventTypeRequestSectorTrailer) { - uint8_t sector = mfc_event->data->sec_tr_data.sector_num; - uint8_t sec_tr = mf_classic_get_sector_trailer_num_by_sector(sector); - if(mf_classic_is_block_read(write_data, sec_tr)) { - mfc_event->data->sec_tr_data.sector_trailer = write_data->block[sec_tr]; - mfc_event->data->sec_tr_data.sector_trailer_provided = true; - } else { - mfc_event->data->sec_tr_data.sector_trailer_provided = false; - } - } else if(mfc_event->type == MfClassicPollerEventTypeRequestWriteBlock) { - uint8_t block_num = mfc_event->data->write_block_data.block_num; - if(mf_classic_is_block_read(write_data, block_num)) { - mfc_event->data->write_block_data.write_block = write_data->block[block_num]; - mfc_event->data->write_block_data.write_block_provided = true; - } else { - mfc_event->data->write_block_data.write_block_provided = false; - } - } else if(mfc_event->type == MfClassicPollerEventTypeSuccess) { - view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess); - command = NfcCommandStop; - } else if(mfc_event->type == MfClassicPollerEventTypeFail) { - view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerFailure); - command = NfcCommandStop; - } - return command; -} - -static void nfc_scene_mf_classic_write_initial_setup_view(NfcApp* instance) { - Popup* popup = instance->popup; - popup_reset(popup); - uint32_t state = - scene_manager_get_scene_state(instance->scene_manager, NfcSceneMfClassicWriteInitial); - - if(state == NfcSceneMfClassicWriteInitialStateCardSearch) { - popup_set_header(instance->popup, "Writing", 95, 20, AlignCenter, AlignCenter); - popup_set_text( - instance->popup, "Use the source\ncard only", 95, 38, AlignCenter, AlignCenter); - popup_set_icon(instance->popup, 0, 8, &I_NFC_manual_60x50); - } else { - popup_set_header(popup, "Writing\nDon't move...", 52, 32, AlignLeft, AlignCenter); - popup_set_icon(popup, 12, 23, &A_Loading_24); - } - - view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup); -} - -void nfc_scene_mf_classic_write_initial_on_enter(void* context) { - NfcApp* instance = context; - dolphin_deed(DolphinDeedNfcEmulate); - - scene_manager_set_scene_state( - instance->scene_manager, - NfcSceneMfClassicWriteInitial, - NfcSceneMfClassicWriteInitialStateCardSearch); - nfc_scene_mf_classic_write_initial_setup_view(instance); - - // Setup and start worker - instance->poller = nfc_poller_alloc(instance->nfc, NfcProtocolMfClassic); - nfc_poller_start( - instance->poller, nfc_scene_mf_classic_write_initial_worker_callback, instance); - - nfc_blink_emulate_start(instance); -} - -bool nfc_scene_mf_classic_write_initial_on_event(void* context, SceneManagerEvent event) { - NfcApp* instance = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == NfcCustomEventCardDetected) { - scene_manager_set_scene_state( - instance->scene_manager, - NfcSceneMfClassicWriteInitial, - NfcSceneMfClassicWriteInitialStateCardFound); - nfc_scene_mf_classic_write_initial_setup_view(instance); - consumed = true; - } else if(event.event == NfcCustomEventCardLost) { - scene_manager_set_scene_state( - instance->scene_manager, - NfcSceneMfClassicWriteInitial, - NfcSceneMfClassicWriteInitialStateCardSearch); - nfc_scene_mf_classic_write_initial_setup_view(instance); - consumed = true; - } else if(event.event == NfcCustomEventWrongCard) { - scene_manager_next_scene( - instance->scene_manager, NfcSceneMfClassicWriteInitialWrongCard); - consumed = true; - } else if(event.event == NfcCustomEventPollerSuccess) { - scene_manager_next_scene( - instance->scene_manager, NfcSceneMfClassicWriteInitialSuccess); - consumed = true; - } else if(event.event == NfcCustomEventPollerFailure) { - scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicWriteInitialFail); - consumed = true; - } - } - - return consumed; -} - -void nfc_scene_mf_classic_write_initial_on_exit(void* context) { - NfcApp* instance = context; - - nfc_poller_stop(instance->poller); - nfc_poller_free(instance->poller); - - scene_manager_set_scene_state( - instance->scene_manager, - NfcSceneMfClassicWriteInitial, - NfcSceneMfClassicWriteInitialStateCardSearch); - // Clear view - popup_reset(instance->popup); - - nfc_blink_stop(instance); -} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial_fail.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial_fail.c deleted file mode 100644 index 4d4367ec8..000000000 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial_fail.c +++ /dev/null @@ -1,62 +0,0 @@ -#include "../nfc_app_i.h" - -void nfc_scene_mf_classic_write_initial_fail_widget_callback( - GuiButtonType result, - InputType type, - void* context) { - NfcApp* instance = context; - if(type == InputTypeShort) { - view_dispatcher_send_custom_event(instance->view_dispatcher, result); - } -} - -void nfc_scene_mf_classic_write_initial_fail_on_enter(void* context) { - NfcApp* instance = context; - Widget* widget = instance->widget; - - notification_message(instance->notifications, &sequence_error); - - widget_add_icon_element(widget, 83, 22, &I_WarningDolphinFlip_45x42); - widget_add_string_element( - widget, 7, 4, AlignLeft, AlignTop, FontPrimary, "Writing gone wrong!"); - widget_add_string_multiline_element( - widget, - 7, - 17, - AlignLeft, - AlignTop, - FontSecondary, - "Not all sectors\nwere written\ncorrectly."); - - widget_add_button_element( - widget, - GuiButtonTypeLeft, - "Finish", - nfc_scene_mf_classic_write_initial_fail_widget_callback, - instance); - - // Setup and start worker - view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewWidget); -} - -bool nfc_scene_mf_classic_write_initial_fail_on_event(void* context, SceneManagerEvent event) { - NfcApp* instance = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == GuiButtonTypeLeft) { - consumed = scene_manager_search_and_switch_to_previous_scene( - instance->scene_manager, NfcSceneSavedMenu); - } - } else if(event.type == SceneManagerEventTypeBack) { - consumed = scene_manager_search_and_switch_to_previous_scene( - instance->scene_manager, NfcSceneSavedMenu); - } - return consumed; -} - -void nfc_scene_mf_classic_write_initial_fail_on_exit(void* context) { - NfcApp* instance = context; - - widget_reset(instance->widget); -} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial_success.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial_success.c deleted file mode 100644 index 100c5c431..000000000 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial_success.c +++ /dev/null @@ -1,43 +0,0 @@ -#include "../nfc_app_i.h" - -void nfc_scene_mf_classic_write_initial_success_popup_callback(void* context) { - NfcApp* instance = context; - view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventViewExit); -} - -void nfc_scene_mf_classic_write_initial_success_on_enter(void* context) { - NfcApp* instance = context; - dolphin_deed(DolphinDeedNfcSave); - - notification_message(instance->notifications, &sequence_success); - - Popup* popup = instance->popup; - popup_set_header(popup, "Success!", 75, 10, AlignLeft, AlignTop); - popup_set_icon(popup, 0, 9, &I_DolphinSuccess_91x55); - popup_set_timeout(popup, 1500); - popup_set_context(popup, instance); - popup_set_callback(popup, nfc_scene_mf_classic_write_initial_success_popup_callback); - popup_enable_timeout(popup); - - view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup); -} - -bool nfc_scene_mf_classic_write_initial_success_on_event(void* context, SceneManagerEvent event) { - NfcApp* instance = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == NfcCustomEventViewExit) { - consumed = scene_manager_search_and_switch_to_previous_scene( - instance->scene_manager, NfcSceneSavedMenu); - } - } - return consumed; -} - -void nfc_scene_mf_classic_write_initial_success_on_exit(void* context) { - NfcApp* instance = context; - - // Clear view - popup_reset(instance->popup); -} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial_wrong_card.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial_wrong_card.c deleted file mode 100644 index 3d49b3cac..000000000 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial_wrong_card.c +++ /dev/null @@ -1,57 +0,0 @@ -#include "../nfc_app_i.h" - -void nfc_scene_mf_classic_write_initial_wrong_card_widget_callback( - GuiButtonType result, - InputType type, - void* context) { - NfcApp* instance = context; - if(type == InputTypeShort) { - view_dispatcher_send_custom_event(instance->view_dispatcher, result); - } -} - -void nfc_scene_mf_classic_write_initial_wrong_card_on_enter(void* context) { - NfcApp* instance = context; - Widget* widget = instance->widget; - - notification_message(instance->notifications, &sequence_error); - - widget_add_icon_element(widget, 83, 22, &I_WarningDolphinFlip_45x42); - widget_add_string_element( - widget, 3, 4, AlignLeft, AlignTop, FontPrimary, "Use The Source Card!"); - widget_add_string_multiline_element( - widget, - 4, - 17, - AlignLeft, - AlignTop, - FontSecondary, - "Go to NFC Magic\napp if you want to\nwrite blanks"); - widget_add_button_element( - widget, - GuiButtonTypeLeft, - "Retry", - nfc_scene_mf_classic_write_initial_wrong_card_widget_callback, - instance); - - // Setup and start worker - view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewWidget); -} - -bool nfc_scene_mf_classic_write_initial_wrong_card_on_event(void* context, SceneManagerEvent event) { - NfcApp* instance = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == GuiButtonTypeLeft) { - consumed = scene_manager_previous_scene(instance->scene_manager); - } - } - return consumed; -} - -void nfc_scene_mf_classic_write_initial_wrong_card_on_exit(void* context) { - NfcApp* instance = context; - - widget_reset(instance->widget); -} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_write.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_write.c deleted file mode 100644 index 157d6ce1b..000000000 --- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_write.c +++ /dev/null @@ -1,120 +0,0 @@ -#include "../nfc_app_i.h" - -#include - -enum { - NfcSceneMfUltralightWriteStateCardSearch, - NfcSceneMfUltralightWriteStateCardFound, -}; - -NfcCommand nfc_scene_mf_ultralight_write_worker_callback(NfcGenericEvent event, void* context) { - furi_assert(context); - furi_assert(event.event_data); - furi_assert(event.protocol == NfcProtocolMfUltralight); - - NfcCommand command = NfcCommandContinue; - NfcApp* instance = context; - MfUltralightPollerEvent* mfu_event = event.event_data; - - if(mfu_event->type == MfUltralightPollerEventTypeRequestMode) { - mfu_event->data->poller_mode = MfUltralightPollerModeWrite; - view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventCardDetected); - } else if(mfu_event->type == MfUltralightPollerEventTypeAuthRequest) { - mfu_event->data->auth_context.skip_auth = true; - } else if(mfu_event->type == MfUltralightPollerEventTypeRequestWriteData) { - mfu_event->data->write_data = - nfc_device_get_data(instance->nfc_device, NfcProtocolMfUltralight); - } else if(mfu_event->type == MfUltralightPollerEventTypeCardMismatch) { - view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventWrongCard); - command = NfcCommandStop; - } else if(mfu_event->type == MfUltralightPollerEventTypeCardLocked) { - view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerFailure); - command = NfcCommandStop; - } else if(mfu_event->type == MfUltralightPollerEventTypeWriteFail) { - command = NfcCommandStop; - } else if(mfu_event->type == MfUltralightPollerEventTypeWriteSuccess) { - view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess); - command = NfcCommandStop; - } - return command; -} - -static void nfc_scene_mf_ultralight_write_setup_view(NfcApp* instance) { - Popup* popup = instance->popup; - popup_reset(popup); - uint32_t state = - scene_manager_get_scene_state(instance->scene_manager, NfcSceneMfUltralightWrite); - - if(state == NfcSceneMfUltralightWriteStateCardSearch) { - popup_set_header(instance->popup, "Writing", 95, 20, AlignCenter, AlignCenter); - popup_set_text( - instance->popup, "Apply the initial\ncard only", 95, 38, AlignCenter, AlignCenter); - popup_set_icon(instance->popup, 0, 8, &I_NFC_manual_60x50); - } else { - popup_set_header(popup, "Writing\nDon't move...", 52, 32, AlignLeft, AlignCenter); - popup_set_icon(popup, 12, 23, &A_Loading_24); - } - - view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup); -} - -void nfc_scene_mf_ultralight_write_on_enter(void* context) { - NfcApp* instance = context; - dolphin_deed(DolphinDeedNfcEmulate); - - scene_manager_set_scene_state( - instance->scene_manager, - NfcSceneMfUltralightWrite, - NfcSceneMfUltralightWriteStateCardSearch); - nfc_scene_mf_ultralight_write_setup_view(instance); - - // Setup and start worker - FURI_LOG_D("WMFU", "Card searching..."); - instance->poller = nfc_poller_alloc(instance->nfc, NfcProtocolMfUltralight); - nfc_poller_start(instance->poller, nfc_scene_mf_ultralight_write_worker_callback, instance); - - nfc_blink_emulate_start(instance); -} - -bool nfc_scene_mf_ultralight_write_on_event(void* context, SceneManagerEvent event) { - NfcApp* instance = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == NfcCustomEventCardDetected) { - scene_manager_set_scene_state( - instance->scene_manager, - NfcSceneMfUltralightWrite, - NfcSceneMfUltralightWriteStateCardFound); - nfc_scene_mf_ultralight_write_setup_view(instance); - consumed = true; - } else if(event.event == NfcCustomEventWrongCard) { - scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightWrongCard); - consumed = true; - } else if(event.event == NfcCustomEventPollerSuccess) { - scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightWriteSuccess); - consumed = true; - } else if(event.event == NfcCustomEventPollerFailure) { - scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightWriteFail); - consumed = true; - } - } - - return consumed; -} - -void nfc_scene_mf_ultralight_write_on_exit(void* context) { - NfcApp* instance = context; - - nfc_poller_stop(instance->poller); - nfc_poller_free(instance->poller); - - scene_manager_set_scene_state( - instance->scene_manager, - NfcSceneMfUltralightWrite, - NfcSceneMfUltralightWriteStateCardSearch); - // Clear view - popup_reset(instance->popup); - - nfc_blink_stop(instance); -} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_write_fail.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_write_fail.c deleted file mode 100644 index fcfb5f2b0..000000000 --- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_write_fail.c +++ /dev/null @@ -1,67 +0,0 @@ -#include "../nfc_app_i.h" - -void nfc_scene_mf_ultralight_write_fail_widget_callback( - GuiButtonType result, - InputType type, - void* context) { - NfcApp* instance = context; - if(type == InputTypeShort) { - view_dispatcher_send_custom_event(instance->view_dispatcher, result); - } -} - -void nfc_scene_mf_ultralight_write_fail_on_enter(void* context) { - NfcApp* instance = context; - Widget* widget = instance->widget; - - notification_message(instance->notifications, &sequence_error); - - widget_add_icon_element(widget, 83, 22, &I_WarningDolphinFlip_45x42); - widget_add_string_element( - widget, 7, 4, AlignLeft, AlignTop, FontPrimary, "Writing gone wrong!"); - widget_add_string_multiline_element( - widget, - 7, - 17, - AlignLeft, - AlignTop, - FontSecondary, - "Card protected by\npassword, AUTH0\nor lock bits"); - - widget_add_button_element( - widget, - GuiButtonTypeLeft, - "Finish", - nfc_scene_mf_ultralight_write_fail_widget_callback, - instance); - - // Setup and start worker - view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewWidget); -} - -static bool nfc_scene_mf_ultralight_write_fail_move_to_back_scene(const NfcApp* const instance) { - bool was_saved = scene_manager_has_previous_scene(instance->scene_manager, NfcSceneSavedMenu); - uint32_t scene_id = was_saved ? NfcSceneSavedMenu : NfcSceneReadMenu; - - return scene_manager_search_and_switch_to_previous_scene(instance->scene_manager, scene_id); -} - -bool nfc_scene_mf_ultralight_write_fail_on_event(void* context, SceneManagerEvent event) { - NfcApp* instance = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == GuiButtonTypeLeft) { - consumed = nfc_scene_mf_ultralight_write_fail_move_to_back_scene(instance); - } - } else if(event.type == SceneManagerEventTypeBack) { - consumed = nfc_scene_mf_ultralight_write_fail_move_to_back_scene(instance); - } - return consumed; -} - -void nfc_scene_mf_ultralight_write_fail_on_exit(void* context) { - NfcApp* instance = context; - - widget_reset(instance->widget); -} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_write_success.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_write_success.c deleted file mode 100644 index bb34190d2..000000000 --- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_write_success.c +++ /dev/null @@ -1,46 +0,0 @@ -#include "../nfc_app_i.h" - -void nfc_scene_mf_ultralight_write_success_popup_callback(void* context) { - NfcApp* instance = context; - view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventViewExit); -} - -void nfc_scene_mf_ultralight_write_success_on_enter(void* context) { - NfcApp* instance = context; - dolphin_deed(DolphinDeedNfcSave); - - notification_message(instance->notifications, &sequence_success); - - Popup* popup = instance->popup; - popup_set_icon(popup, 48, 6, &I_DolphinDone_80x58); - popup_set_header(popup, "Successfully\nwritten", 5, 22, AlignLeft, AlignBottom); - popup_set_timeout(popup, 1500); - popup_set_context(popup, instance); - popup_set_callback(popup, nfc_scene_mf_ultralight_write_success_popup_callback); - popup_enable_timeout(popup); - - view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup); -} - -bool nfc_scene_mf_ultralight_write_success_on_event(void* context, SceneManagerEvent event) { - NfcApp* instance = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == NfcCustomEventViewExit) { - bool was_saved = - scene_manager_has_previous_scene(instance->scene_manager, NfcSceneSavedMenu); - - consumed = scene_manager_search_and_switch_to_previous_scene( - instance->scene_manager, was_saved ? NfcSceneSavedMenu : NfcSceneReadSuccess); - } - } - return consumed; -} - -void nfc_scene_mf_ultralight_write_success_on_exit(void* context) { - NfcApp* instance = context; - - // Clear view - popup_reset(instance->popup); -} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_wrong_card.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_wrong_card.c deleted file mode 100644 index bc34a45b4..000000000 --- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_wrong_card.c +++ /dev/null @@ -1,58 +0,0 @@ -#include "../nfc_app_i.h" - -void nfc_scene_mf_ultralight_wrong_card_widget_callback( - GuiButtonType result, - InputType type, - void* context) { - NfcApp* instance = context; - if(type == InputTypeShort) { - view_dispatcher_send_custom_event(instance->view_dispatcher, result); - } -} - -void nfc_scene_mf_ultralight_wrong_card_on_enter(void* context) { - NfcApp* instance = context; - Widget* widget = instance->widget; - - notification_message(instance->notifications, &sequence_error); - - widget_add_icon_element(widget, 83, 22, &I_WarningDolphinFlip_45x42); - widget_add_string_element( - widget, 3, 4, AlignLeft, AlignTop, FontPrimary, "This is wrong card"); - widget_add_string_multiline_element( - widget, - 4, - 17, - AlignLeft, - AlignTop, - FontSecondary, - "Card of the same\ntype should be\n presented"); - //"Data management\nis only possible\nwith card of same type"); - widget_add_button_element( - widget, - GuiButtonTypeLeft, - "Retry", - nfc_scene_mf_ultralight_wrong_card_widget_callback, - instance); - - // Setup and start worker - view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewWidget); -} - -bool nfc_scene_mf_ultralight_wrong_card_on_event(void* context, SceneManagerEvent event) { - NfcApp* instance = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == GuiButtonTypeLeft) { - consumed = scene_manager_previous_scene(instance->scene_manager); - } - } - return consumed; -} - -void nfc_scene_mf_ultralight_wrong_card_on_exit(void* context) { - NfcApp* instance = context; - - widget_reset(instance->widget); -} diff --git a/applications/main/nfc/scenes/nfc_scene_write.c b/applications/main/nfc/scenes/nfc_scene_write.c new file mode 100644 index 000000000..a5f8fbb13 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_write.c @@ -0,0 +1,13 @@ +#include "../helpers/protocol_support/nfc_protocol_support.h" + +void nfc_scene_write_on_enter(void* context) { + nfc_protocol_support_on_enter(NfcProtocolSupportSceneWrite, context); +} + +bool nfc_scene_write_on_event(void* context, SceneManagerEvent event) { + return nfc_protocol_support_on_event(NfcProtocolSupportSceneWrite, context, event); +} + +void nfc_scene_write_on_exit(void* context) { + nfc_protocol_support_on_exit(NfcProtocolSupportSceneWrite, context); +} From 2db0d9b7b3ec2c1e75ca7144d8d983d86a7067fb Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 27 Mar 2025 03:41:28 +0000 Subject: [PATCH 60/92] NFC: Cleanup duplicate event handling --- .../helpers/protocol_support/felica/felica.c | 11 +-------- .../iso14443_3a/iso14443_3a.c | 24 +++++++------------ .../iso14443_3b/iso14443_3b.c | 15 +----------- .../iso14443_3b/iso14443_3b_i.h | 7 ------ .../iso14443_4a/iso14443_4a.c | 24 +++++++------------ .../iso14443_4b/iso14443_4b.c | 24 +++---------------- .../protocol_support/iso15693_3/iso15693_3.c | 24 +++++++------------ .../protocol_support/mf_classic/mf_classic.c | 3 --- .../mf_ultralight/mf_ultralight.c | 3 --- .../protocol_support/nfc_protocol_support.c | 3 +++ .../nfc_protocol_support_gui_common.c | 4 ++-- .../nfc/helpers/protocol_support/slix/slix.c | 24 +++++++------------ .../helpers/protocol_support/st25tb/st25tb.c | 11 +-------- 13 files changed, 43 insertions(+), 134 deletions(-) delete mode 100644 applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b_i.h diff --git a/applications/main/nfc/helpers/protocol_support/felica/felica.c b/applications/main/nfc/helpers/protocol_support/felica/felica.c index 7717396a3..7f12064a4 100644 --- a/applications/main/nfc/helpers/protocol_support/felica/felica.c +++ b/applications/main/nfc/helpers/protocol_support/felica/felica.c @@ -133,15 +133,6 @@ static void nfc_scene_read_success_on_enter_felica(NfcApp* instance) { furi_string_free(temp_str); } -static bool nfc_scene_saved_menu_on_event_felica(NfcApp* instance, SceneManagerEvent event) { - if(event.type == SceneManagerEventTypeCustom && event.event == SubmenuIndexCommonEdit) { - scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid); - return true; - } - - return false; -} - static void nfc_scene_emulate_on_enter_felica(NfcApp* instance) { const FelicaData* data = nfc_device_get_data(instance->nfc_device, NfcProtocolFelica); instance->listener = nfc_listener_alloc(instance->nfc, NfcProtocolFelica, data); @@ -201,7 +192,7 @@ const NfcProtocolSupportBase nfc_protocol_support_felica = { .scene_saved_menu = { .on_enter = nfc_protocol_support_common_on_enter_empty, - .on_event = nfc_scene_saved_menu_on_event_felica, + .on_event = nfc_protocol_support_common_on_event_empty, }, .scene_save_name = { diff --git a/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a.c b/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a.c index 9d6dba5a7..068c6a1d7 100644 --- a/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a.c +++ b/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a.c @@ -67,21 +67,22 @@ static NfcCommand furi_assert(event.protocol == NfcProtocolIso14443_3a); furi_assert(event.event_data); - NfcApp* nfc = context; + NfcApp* instance = context; Iso14443_3aListenerEvent* iso14443_3a_event = event.event_data; if(iso14443_3a_event->type == Iso14443_3aListenerEventTypeReceivedStandardFrame) { - if(furi_string_size(nfc->text_box_store) < NFC_LOG_SIZE_MAX) { - furi_string_cat_printf(nfc->text_box_store, "R:"); + if(furi_string_size(instance->text_box_store) < NFC_LOG_SIZE_MAX) { + furi_string_cat_printf(instance->text_box_store, "R:"); for(size_t i = 0; i < bit_buffer_get_size_bytes(iso14443_3a_event->data->buffer); i++) { furi_string_cat_printf( - nfc->text_box_store, + instance->text_box_store, " %02X", bit_buffer_get_byte(iso14443_3a_event->data->buffer, i)); } - furi_string_push_back(nfc->text_box_store, '\n'); - view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventListenerUpdate); + furi_string_push_back(instance->text_box_store, '\n'); + view_dispatcher_send_custom_event( + instance->view_dispatcher, NfcCustomEventListenerUpdate); } } @@ -97,15 +98,6 @@ static void nfc_scene_emulate_on_enter_iso14443_3a(NfcApp* instance) { instance->listener, nfc_scene_emulate_listener_callback_iso14443_3a, instance); } -static bool nfc_scene_read_menu_on_event_iso14443_3a(NfcApp* instance, SceneManagerEvent event) { - if(event.type == SceneManagerEventTypeCustom && event.event == SubmenuIndexCommonEmulate) { - scene_manager_next_scene(instance->scene_manager, NfcSceneEmulate); - return true; - } - - return false; -} - const NfcProtocolSupportBase nfc_protocol_support_iso14443_3a = { .features = NfcProtocolFeatureEmulateUid | NfcProtocolFeatureEditUid, @@ -122,7 +114,7 @@ const NfcProtocolSupportBase nfc_protocol_support_iso14443_3a = { .scene_read_menu = { .on_enter = nfc_protocol_support_common_on_enter_empty, - .on_event = nfc_scene_read_menu_on_event_iso14443_3a, + .on_event = nfc_protocol_support_common_on_event_empty, }, .scene_read_success = { diff --git a/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b.c b/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b.c index 49da93d7b..ccafc1c9d 100644 --- a/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b.c +++ b/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b.c @@ -60,19 +60,6 @@ static void nfc_scene_read_success_on_enter_iso14443_3b(NfcApp* instance) { furi_string_free(temp_str); } -bool nfc_scene_saved_menu_on_event_iso14443_3b_common(NfcApp* instance, SceneManagerEvent event) { - if(event.type == SceneManagerEventTypeCustom && event.event == SubmenuIndexCommonEdit) { - scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid); - return true; - } - - return false; -} - -static bool nfc_scene_saved_menu_on_event_iso14443_3b(NfcApp* instance, SceneManagerEvent event) { - return nfc_scene_saved_menu_on_event_iso14443_3b_common(instance, event); -} - const NfcProtocolSupportBase nfc_protocol_support_iso14443_3b = { .features = NfcProtocolFeatureNone, @@ -99,7 +86,7 @@ const NfcProtocolSupportBase nfc_protocol_support_iso14443_3b = { .scene_saved_menu = { .on_enter = nfc_protocol_support_common_on_enter_empty, - .on_event = nfc_scene_saved_menu_on_event_iso14443_3b, + .on_event = nfc_protocol_support_common_on_event_empty, }, .scene_save_name = { diff --git a/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b_i.h b/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b_i.h deleted file mode 100644 index 6c7c2a0bc..000000000 --- a/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b_i.h +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once - -#include - -#include "iso14443_3b.h" - -bool nfc_scene_saved_menu_on_event_iso14443_3b_common(NfcApp* instance, SceneManagerEvent event); diff --git a/applications/main/nfc/helpers/protocol_support/iso14443_4a/iso14443_4a.c b/applications/main/nfc/helpers/protocol_support/iso14443_4a/iso14443_4a.c index 190c26e87..5eec0a1ad 100644 --- a/applications/main/nfc/helpers/protocol_support/iso14443_4a/iso14443_4a.c +++ b/applications/main/nfc/helpers/protocol_support/iso14443_4a/iso14443_4a.c @@ -70,21 +70,22 @@ NfcCommand nfc_scene_emulate_listener_callback_iso14443_4a(NfcGenericEvent event furi_assert(event.protocol == NfcProtocolIso14443_4a); furi_assert(event.event_data); - NfcApp* nfc = context; + NfcApp* instance = context; Iso14443_4aListenerEvent* iso14443_4a_event = event.event_data; if(iso14443_4a_event->type == Iso14443_4aListenerEventTypeReceivedData) { - if(furi_string_size(nfc->text_box_store) < NFC_LOG_SIZE_MAX) { - furi_string_cat_printf(nfc->text_box_store, "R:"); + if(furi_string_size(instance->text_box_store) < NFC_LOG_SIZE_MAX) { + furi_string_cat_printf(instance->text_box_store, "R:"); for(size_t i = 0; i < bit_buffer_get_size_bytes(iso14443_4a_event->data->buffer); i++) { furi_string_cat_printf( - nfc->text_box_store, + instance->text_box_store, " %02X", bit_buffer_get_byte(iso14443_4a_event->data->buffer, i)); } - furi_string_push_back(nfc->text_box_store, '\n'); - view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventListenerUpdate); + furi_string_push_back(instance->text_box_store, '\n'); + view_dispatcher_send_custom_event( + instance->view_dispatcher, NfcCustomEventListenerUpdate); } } @@ -100,15 +101,6 @@ static void nfc_scene_emulate_on_enter_iso14443_4a(NfcApp* instance) { instance->listener, nfc_scene_emulate_listener_callback_iso14443_4a, instance); } -static bool nfc_scene_read_menu_on_event_iso14443_4a(NfcApp* instance, SceneManagerEvent event) { - if(event.type == SceneManagerEventTypeCustom && event.event == SubmenuIndexCommonEmulate) { - scene_manager_next_scene(instance->scene_manager, NfcSceneEmulate); - return true; - } - - return false; -} - const NfcProtocolSupportBase nfc_protocol_support_iso14443_4a = { .features = NfcProtocolFeatureEmulateUid | NfcProtocolFeatureEditUid, @@ -125,7 +117,7 @@ const NfcProtocolSupportBase nfc_protocol_support_iso14443_4a = { .scene_read_menu = { .on_enter = nfc_protocol_support_common_on_enter_empty, - .on_event = nfc_scene_read_menu_on_event_iso14443_4a, + .on_event = nfc_protocol_support_common_on_event_empty, }, .scene_read_success = { diff --git a/applications/main/nfc/helpers/protocol_support/iso14443_4b/iso14443_4b.c b/applications/main/nfc/helpers/protocol_support/iso14443_4b/iso14443_4b.c index 8fd823f21..7bfea69fa 100644 --- a/applications/main/nfc/helpers/protocol_support/iso14443_4b/iso14443_4b.c +++ b/applications/main/nfc/helpers/protocol_support/iso14443_4b/iso14443_4b.c @@ -7,7 +7,6 @@ #include "../nfc_protocol_support_common.h" #include "../nfc_protocol_support_gui_common.h" -#include "../iso14443_3b/iso14443_3b_i.h" static void nfc_scene_info_on_enter_iso14443_4b(NfcApp* instance) { const NfcDevice* device = instance->nfc_device; @@ -61,23 +60,6 @@ static void nfc_scene_read_success_on_enter_iso14443_4b(NfcApp* instance) { furi_string_free(temp_str); } -static void nfc_scene_saved_menu_on_enter_iso14443_4b(NfcApp* instance) { - UNUSED(instance); -} - -static bool nfc_scene_read_menu_on_event_iso14443_4b(NfcApp* instance, SceneManagerEvent event) { - if(event.type == SceneManagerEventTypeCustom && event.event == SubmenuIndexCommonEmulate) { - scene_manager_next_scene(instance->scene_manager, NfcSceneEmulate); - return true; - } - - return false; -} - -static bool nfc_scene_saved_menu_on_event_iso14443_4b(NfcApp* instance, SceneManagerEvent event) { - return nfc_scene_saved_menu_on_event_iso14443_3b_common(instance, event); -} - const NfcProtocolSupportBase nfc_protocol_support_iso14443_4b = { .features = NfcProtocolFeatureNone, @@ -94,7 +76,7 @@ const NfcProtocolSupportBase nfc_protocol_support_iso14443_4b = { .scene_read_menu = { .on_enter = nfc_protocol_support_common_on_enter_empty, - .on_event = nfc_scene_read_menu_on_event_iso14443_4b, + .on_event = nfc_protocol_support_common_on_event_empty, }, .scene_read_success = { @@ -103,8 +85,8 @@ const NfcProtocolSupportBase nfc_protocol_support_iso14443_4b = { }, .scene_saved_menu = { - .on_enter = nfc_scene_saved_menu_on_enter_iso14443_4b, - .on_event = nfc_scene_saved_menu_on_event_iso14443_4b, + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, }, .scene_save_name = { diff --git a/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3.c b/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3.c index ffd6971e0..63aff8332 100644 --- a/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3.c +++ b/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3.c @@ -80,20 +80,21 @@ static NfcCommand furi_assert(event.protocol == NfcProtocolIso15693_3); furi_assert(event.event_data); - NfcApp* nfc = context; + NfcApp* instance = context; Iso15693_3ListenerEvent* iso15693_3_event = event.event_data; if(iso15693_3_event->type == Iso15693_3ListenerEventTypeCustomCommand) { - if(furi_string_size(nfc->text_box_store) < NFC_LOG_SIZE_MAX) { - furi_string_cat_printf(nfc->text_box_store, "R:"); + if(furi_string_size(instance->text_box_store) < NFC_LOG_SIZE_MAX) { + furi_string_cat_printf(instance->text_box_store, "R:"); for(size_t i = 0; i < bit_buffer_get_size_bytes(iso15693_3_event->data->buffer); i++) { furi_string_cat_printf( - nfc->text_box_store, + instance->text_box_store, " %02X", bit_buffer_get_byte(iso15693_3_event->data->buffer, i)); } - furi_string_push_back(nfc->text_box_store, '\n'); - view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventListenerUpdate); + furi_string_push_back(instance->text_box_store, '\n'); + view_dispatcher_send_custom_event( + instance->view_dispatcher, NfcCustomEventListenerUpdate); } } @@ -108,15 +109,6 @@ static void nfc_scene_emulate_on_enter_iso15693_3(NfcApp* instance) { instance->listener, nfc_scene_emulate_listener_callback_iso15693_3, instance); } -static bool nfc_scene_saved_menu_on_event_iso15693_3(NfcApp* instance, SceneManagerEvent event) { - if(event.type == SceneManagerEventTypeCustom && event.event == SubmenuIndexCommonEdit) { - scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid); - return true; - } - - return false; -} - const NfcProtocolSupportBase nfc_protocol_support_iso15693_3 = { .features = NfcProtocolFeatureEmulateFull | NfcProtocolFeatureEditUid | NfcProtocolFeatureMoreInfo, @@ -149,7 +141,7 @@ const NfcProtocolSupportBase nfc_protocol_support_iso15693_3 = { .scene_saved_menu = { .on_enter = nfc_protocol_support_common_on_enter_empty, - .on_event = nfc_scene_saved_menu_on_event_iso15693_3, + .on_event = nfc_protocol_support_common_on_event_empty, }, .scene_save_name = { diff --git a/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c b/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c index acc641c34..4b639f981 100644 --- a/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c +++ b/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c @@ -200,9 +200,6 @@ static bool nfc_scene_read_menu_on_event_mf_classic(NfcApp* instance, SceneManag scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicDictAttack); } consumed = true; - } else if(event.event == SubmenuIndexCommonEdit) { - scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid); - consumed = true; } } diff --git a/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c b/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c index 34354741d..0875f77b3 100644 --- a/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c +++ b/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c @@ -249,9 +249,6 @@ static bool nfc_scene_read_and_saved_menu_on_event_mf_ultralight( NfcSceneMfUltralightUnlockMenu; scene_manager_next_scene(instance->scene_manager, next_scene); consumed = true; - } else if(event.event == SubmenuIndexCommonEdit) { - scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid); - consumed = true; } } return consumed; diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c index 0c5a9d3bc..d2386d87d 100644 --- a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c @@ -304,6 +304,9 @@ static bool dolphin_deed(DolphinDeedNfcEmulate); scene_manager_next_scene(instance->scene_manager, NfcSceneWrite); consumed = true; + } else if(event.event == SubmenuIndexCommonEdit) { + scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid); + consumed = true; } else { const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device); consumed = nfc_protocol_support[protocol]->scene_read_menu.on_event(instance, event); diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_gui_common.c b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_gui_common.c index 8c38f8475..ba309244a 100644 --- a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_gui_common.c +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_gui_common.c @@ -26,9 +26,9 @@ void nfc_protocol_support_common_byte_input_done_callback(void* context) { } void nfc_protocol_support_common_text_input_done_callback(void* context) { - NfcApp* nfc = context; + NfcApp* instance = context; - view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventTextInputDone); + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventTextInputDone); } void nfc_protocol_support_common_on_enter_empty(NfcApp* instance) { diff --git a/applications/main/nfc/helpers/protocol_support/slix/slix.c b/applications/main/nfc/helpers/protocol_support/slix/slix.c index e998d0ad2..4d9a2061c 100644 --- a/applications/main/nfc/helpers/protocol_support/slix/slix.c +++ b/applications/main/nfc/helpers/protocol_support/slix/slix.c @@ -78,20 +78,21 @@ static NfcCommand nfc_scene_emulate_listener_callback_slix(NfcGenericEvent event furi_assert(event.protocol == NfcProtocolSlix); furi_assert(event.event_data); - NfcApp* nfc = context; + NfcApp* instance = context; SlixListenerEvent* slix_event = event.event_data; if(slix_event->type == SlixListenerEventTypeCustomCommand) { - if(furi_string_size(nfc->text_box_store) < NFC_LOG_SIZE_MAX) { - furi_string_cat_printf(nfc->text_box_store, "R:"); + if(furi_string_size(instance->text_box_store) < NFC_LOG_SIZE_MAX) { + furi_string_cat_printf(instance->text_box_store, "R:"); for(size_t i = 0; i < bit_buffer_get_size_bytes(slix_event->data->buffer); i++) { furi_string_cat_printf( - nfc->text_box_store, + instance->text_box_store, " %02X", bit_buffer_get_byte(slix_event->data->buffer, i)); } - furi_string_push_back(nfc->text_box_store, '\n'); - view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventListenerUpdate); + furi_string_push_back(instance->text_box_store, '\n'); + view_dispatcher_send_custom_event( + instance->view_dispatcher, NfcCustomEventListenerUpdate); } } @@ -105,15 +106,6 @@ static void nfc_scene_emulate_on_enter_slix(NfcApp* instance) { nfc_listener_start(instance->listener, nfc_scene_emulate_listener_callback_slix, instance); } -static bool nfc_scene_saved_menu_on_event_slix(NfcApp* instance, SceneManagerEvent event) { - if(event.type == SceneManagerEventTypeCustom && event.event == SubmenuIndexCommonEdit) { - scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid); - return true; - } - - return false; -} - const NfcProtocolSupportBase nfc_protocol_support_slix = { .features = NfcProtocolFeatureEmulateFull | NfcProtocolFeatureMoreInfo, @@ -145,7 +137,7 @@ const NfcProtocolSupportBase nfc_protocol_support_slix = { .scene_saved_menu = { .on_enter = nfc_protocol_support_common_on_enter_empty, - .on_event = nfc_scene_saved_menu_on_event_slix, + .on_event = nfc_protocol_support_common_on_event_empty, }, .scene_save_name = { diff --git a/applications/main/nfc/helpers/protocol_support/st25tb/st25tb.c b/applications/main/nfc/helpers/protocol_support/st25tb/st25tb.c index a119f8cd6..42b547fcb 100644 --- a/applications/main/nfc/helpers/protocol_support/st25tb/st25tb.c +++ b/applications/main/nfc/helpers/protocol_support/st25tb/st25tb.c @@ -61,15 +61,6 @@ static void nfc_scene_read_success_on_enter_st25tb(NfcApp* instance) { furi_string_free(temp_str); } -static bool nfc_scene_saved_menu_on_event_st25tb(NfcApp* instance, SceneManagerEvent event) { - if(event.type == SceneManagerEventTypeCustom && event.event == SubmenuIndexCommonEdit) { - scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid); - return true; - } - - return false; -} - const NfcProtocolSupportBase nfc_protocol_support_st25tb = { .features = NfcProtocolFeatureNone, @@ -96,7 +87,7 @@ const NfcProtocolSupportBase nfc_protocol_support_st25tb = { .scene_saved_menu = { .on_enter = nfc_protocol_support_common_on_enter_empty, - .on_event = nfc_scene_saved_menu_on_event_st25tb, + .on_event = nfc_protocol_support_common_on_event_empty, }, .scene_save_name = { From 0b42027973bac6e74e3ed55891c54f6f7676f7f3 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 27 Mar 2025 04:40:03 +0000 Subject: [PATCH 61/92] NFC: Refactor DESFire poller to NxpNativeCommand helper --- lib/nfc/helpers/nxp_native_command.c | 115 ++++++++++++++++++ lib/nfc/helpers/nxp_native_command.h | 92 ++++++++++++++ lib/nfc/helpers/nxp_native_command_mode.h | 11 ++ lib/nfc/protocols/mf_desfire/mf_desfire_i.h | 48 +------- .../mf_desfire/mf_desfire_poller_i.c | 68 +++-------- 5 files changed, 235 insertions(+), 99 deletions(-) create mode 100644 lib/nfc/helpers/nxp_native_command.c create mode 100644 lib/nfc/helpers/nxp_native_command.h create mode 100644 lib/nfc/helpers/nxp_native_command_mode.h diff --git a/lib/nfc/helpers/nxp_native_command.c b/lib/nfc/helpers/nxp_native_command.c new file mode 100644 index 000000000..d5d535891 --- /dev/null +++ b/lib/nfc/helpers/nxp_native_command.c @@ -0,0 +1,115 @@ +#include "nxp_native_command.h" + +#include + +#define TAG "NxpNativeCommand" + +Iso14443_4aError nxp_native_command_iso14443_4a_poller( + Iso14443_4aPoller* iso14443_4a_poller, + NxpNativeCommandStatus* status_code, + const BitBuffer* input_buffer, + BitBuffer* result_buffer, + NxpNativeCommandMode command_mode, + BitBuffer* tx_buffer, + BitBuffer* rx_buffer) { + furi_check(iso14443_4a_poller); + furi_check(tx_buffer); + furi_check(rx_buffer); + furi_check(input_buffer); + furi_check(result_buffer); + furi_check(command_mode < NxpNativeCommandModeMAX); + + Iso14443_4aError error = Iso14443_4aErrorNone; + *status_code = NXP_NATIVE_COMMAND_STATUS_OPERATION_OK; + + do { + bit_buffer_reset(tx_buffer); + if(command_mode == NxpNativeCommandModePlain) { + bit_buffer_append(tx_buffer, input_buffer); + } else if(command_mode == NxpNativeCommandModeIsoWrapped) { + bit_buffer_append_byte(tx_buffer, NXP_NATIVE_COMMAND_ISO_CLA); + bit_buffer_append_byte(tx_buffer, bit_buffer_get_byte(input_buffer, 0)); + bit_buffer_append_byte(tx_buffer, NXP_NATIVE_COMMAND_ISO_P1); + bit_buffer_append_byte(tx_buffer, NXP_NATIVE_COMMAND_ISO_P2); + if(bit_buffer_get_size_bytes(input_buffer) > 1) { + bit_buffer_append_byte(tx_buffer, bit_buffer_get_size_bytes(input_buffer) - 1); + bit_buffer_append_right(tx_buffer, input_buffer, 1); + } + bit_buffer_append_byte(tx_buffer, NXP_NATIVE_COMMAND_ISO_LE); + } + + bit_buffer_reset(rx_buffer); + error = iso14443_4a_poller_send_block(iso14443_4a_poller, tx_buffer, rx_buffer); + + if(error != Iso14443_4aErrorNone) { + break; + } + + bit_buffer_reset(tx_buffer); + if(command_mode == NxpNativeCommandModePlain) { + bit_buffer_append_byte(tx_buffer, NXP_NATIVE_COMMAND_STATUS_ADDITIONAL_FRAME); + } else if(command_mode == NxpNativeCommandModeIsoWrapped) { + bit_buffer_append_byte(tx_buffer, NXP_NATIVE_COMMAND_ISO_CLA); + bit_buffer_append_byte(tx_buffer, NXP_NATIVE_COMMAND_STATUS_ADDITIONAL_FRAME); + bit_buffer_append_byte(tx_buffer, NXP_NATIVE_COMMAND_ISO_P1); + bit_buffer_append_byte(tx_buffer, NXP_NATIVE_COMMAND_ISO_P2); + bit_buffer_append_byte(tx_buffer, NXP_NATIVE_COMMAND_ISO_LE); + } + + size_t response_len = bit_buffer_get_size_bytes(rx_buffer); + *status_code = NXP_NATIVE_COMMAND_STATUS_LENGTH_ERROR; + bit_buffer_reset(result_buffer); + if(command_mode == NxpNativeCommandModePlain && response_len >= sizeof(uint8_t)) { + *status_code = bit_buffer_get_byte(rx_buffer, 0); + if(response_len > sizeof(uint8_t)) { + bit_buffer_copy_right(result_buffer, rx_buffer, sizeof(uint8_t)); + } + } else if( + command_mode == NxpNativeCommandModeIsoWrapped && + response_len >= 2 * sizeof(uint8_t) && + bit_buffer_get_byte(rx_buffer, response_len - 2) == NXP_NATIVE_COMMAND_ISO_SW1) { + *status_code = bit_buffer_get_byte(rx_buffer, response_len - 1); + if(response_len > 2 * sizeof(uint8_t)) { + bit_buffer_copy_left(result_buffer, rx_buffer, response_len - 2 * sizeof(uint8_t)); + } + } + + while(*status_code == NXP_NATIVE_COMMAND_STATUS_ADDITIONAL_FRAME) { + bit_buffer_reset(rx_buffer); + error = iso14443_4a_poller_send_block(iso14443_4a_poller, tx_buffer, rx_buffer); + + if(error != Iso14443_4aErrorNone) { + break; + } + + const size_t rx_size = bit_buffer_get_size_bytes(rx_buffer); + const size_t rx_capacity_remaining = bit_buffer_get_capacity_bytes(result_buffer) - + bit_buffer_get_size_bytes(result_buffer); + + if(command_mode == NxpNativeCommandModePlain) { + *status_code = rx_size >= 1 ? bit_buffer_get_byte(rx_buffer, 0) : + NXP_NATIVE_COMMAND_STATUS_LENGTH_ERROR; + if(rx_size <= rx_capacity_remaining + 1) { + bit_buffer_append_right(result_buffer, rx_buffer, sizeof(uint8_t)); + } else { + FURI_LOG_W(TAG, "RX buffer overflow: ignoring %zu bytes", rx_size - 1); + } + } else if(command_mode == NxpNativeCommandModeIsoWrapped) { + if(rx_size >= 2 && + bit_buffer_get_byte(rx_buffer, rx_size - 2) == NXP_NATIVE_COMMAND_ISO_SW1) { + *status_code = bit_buffer_get_byte(rx_buffer, rx_size - 1); + } else { + *status_code = NXP_NATIVE_COMMAND_STATUS_LENGTH_ERROR; + } + if(rx_size <= rx_capacity_remaining + 2) { + bit_buffer_set_size_bytes(rx_buffer, rx_size - 2); + bit_buffer_append(result_buffer, rx_buffer); + } else { + FURI_LOG_W(TAG, "RX buffer overflow: ignoring %zu bytes", rx_size - 2); + } + } + } + } while(false); + + return error; +} diff --git a/lib/nfc/helpers/nxp_native_command.h b/lib/nfc/helpers/nxp_native_command.h new file mode 100644 index 000000000..61677a3f6 --- /dev/null +++ b/lib/nfc/helpers/nxp_native_command.h @@ -0,0 +1,92 @@ +#pragma once + +#include "nxp_native_command_mode.h" + +#include + +// ISO 7816 command wrapping +#define NXP_NATIVE_COMMAND_ISO_CLA (0x90) +#define NXP_NATIVE_COMMAND_ISO_P1 (0x00) +#define NXP_NATIVE_COMMAND_ISO_P2 (0x00) +#define NXP_NATIVE_COMMAND_ISO_LE (0x00) +// ISO 7816 status wrapping +#define NXP_NATIVE_COMMAND_ISO_SW1 (0x91) + +// Successful operation +#define NXP_NATIVE_COMMAND_STATUS_OPERATION_OK (0x00) +// No changes done to backup files, CommitTransaction / AbortTransaction not necessary +#define NXP_NATIVE_COMMAND_STATUS_NO_CHANGES (0x0C) +// Insufficient NV-Memory to complete command +#define NXP_NATIVE_COMMAND_STATUS_OUT_OF_EEPROM_ERROR (0x0E) +// Command code not supported +#define NXP_NATIVE_COMMAND_STATUS_ILLEGAL_COMMAND_CODE (0x1C) +// CRC or MAC does not match data Padding bytes not valid +#define NXP_NATIVE_COMMAND_STATUS_INTEGRITY_ERROR (0x1E) +// Invalid key number specified +#define NXP_NATIVE_COMMAND_STATUS_NO_SUCH_KEY (0x40) +// Length of command string invalid +#define NXP_NATIVE_COMMAND_STATUS_LENGTH_ERROR (0x7E) +// Current configuration / status does not allow the requested command +#define NXP_NATIVE_COMMAND_STATUS_PERMISSION_DENIED (0x9D) +// Value of the parameter(s) invalid +#define NXP_NATIVE_COMMAND_STATUS_PARAMETER_ERROR (0x9E) +// Requested AID not present on PICC +#define NXP_NATIVE_COMMAND_STATUS_APPLICATION_NOT_FOUND (0xA0) +// Unrecoverable error within application, application will be disabled +#define NXP_NATIVE_COMMAND_STATUS_APPL_INTEGRITY_ERROR (0xA1) +// Currently not allowed to authenticate. Keep trying until full delay is spent +#define NXP_NATIVE_COMMAND_STATUS_STATUS_AUTHENTICATION_DELAY (0xAD) +// Current authentication status does not allow the requested command +#define NXP_NATIVE_COMMAND_STATUS_AUTHENTICATION_ERROR (0xAE) +// Additional data frame is expected to be sent +#define NXP_NATIVE_COMMAND_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 NXP_NATIVE_COMMAND_STATUS_BOUNDARY_ERROR (0xBE) +// Unrecoverable error within PICC, PICC will be disabled +#define NXP_NATIVE_COMMAND_STATUS_PICC_INTEGRITY_ERROR (0xC1) +// Previous Command was not fully completed. Not all Frames were requested or provided by the PCD +#define NXP_NATIVE_COMMAND_STATUS_COMMAND_ABORTED (0xCA) +// PICC was disabled by an unrecoverable error +#define NXP_NATIVE_COMMAND_STATUS_PICC_DISABLED_ERROR (0xCD) +// Number of Applications limited to 28, no additional CreateApplication possible +#define NXP_NATIVE_COMMAND_STATUS_COUNT_ERROR (0xCE) +// Creation of file/application failed because file/application with same number already exists +#define NXP_NATIVE_COMMAND_STATUS_DUBLICATE_ERROR (0xDE) +// Could not complete NV-write operation due to loss of power, internal backup/rollback mechanism activated +#define NXP_NATIVE_COMMAND_STATUS_EEPROM_ERROR (0xEE) +// Specified file number does not exist +#define NXP_NATIVE_COMMAND_STATUS_FILE_NOT_FOUND (0xF0) +// Unrecoverable error within file, file will be disabled +#define NXP_NATIVE_COMMAND_STATUS_FILE_INTEGRITY_ERROR (0xF1) + +typedef uint8_t NxpNativeCommandStatus; + +/** + * @brief Transmit and receive NXP Native Command chunks in poller mode. + * + * Must ONLY be used inside the callback function. + * + * The result_buffer will be filled with any data received as a response to data + * sent from input_buffer, with a timeout defined by the fwt parameter. + * + * The tx_buffer and rx_buffer are used as working areas to handle individual + * command chunks and responses. + * + * @param[in, out] iso14443_4a_poller pointer to the instance to be used in the transaction. + * @param[out] status_code pointer to a status variable to hold the result of the operation. + * @param[in] input_buffer pointer to the buffer containing the data to be transmitted. + * @param[out] result_buffer pointer to the buffer to be filled with received data. + * @param[in] command_mode what command formatting mode to use for the transaction. + * @param[in, out] tx_buffer pointer to the buffer for command construction. + * @param[in, out] rx_buffer pointer to the buffer for response handling. + * @return Iso14443_4aErrorNone and STATUS_OPERATION_OK on success, an error code on failure. + */ +Iso14443_4aError nxp_native_command_iso14443_4a_poller( + Iso14443_4aPoller* iso14443_4a_poller, + NxpNativeCommandStatus* status_code, + const BitBuffer* input_buffer, + BitBuffer* result_buffer, + NxpNativeCommandMode command_mode, + BitBuffer* tx_buffer, + BitBuffer* rx_buffer); diff --git a/lib/nfc/helpers/nxp_native_command_mode.h b/lib/nfc/helpers/nxp_native_command_mode.h new file mode 100644 index 000000000..7684b4d48 --- /dev/null +++ b/lib/nfc/helpers/nxp_native_command_mode.h @@ -0,0 +1,11 @@ +#pragma once + +/** + * @brief Enumeration of possible command modes. + */ +typedef enum { + NxpNativeCommandModePlain, /**< Plain native commands. */ + NxpNativeCommandModeIsoWrapped, /**< ISO 7816-wrapped commands. */ + + NxpNativeCommandModeMAX, +} NxpNativeCommandMode; diff --git a/lib/nfc/protocols/mf_desfire/mf_desfire_i.h b/lib/nfc/protocols/mf_desfire/mf_desfire_i.h index 921bbb9de..9494120d2 100644 --- a/lib/nfc/protocols/mf_desfire/mf_desfire_i.h +++ b/lib/nfc/protocols/mf_desfire/mf_desfire_i.h @@ -2,55 +2,11 @@ #include "mf_desfire.h" +#include + #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; diff --git a/lib/nfc/protocols/mf_desfire/mf_desfire_poller_i.c b/lib/nfc/protocols/mf_desfire/mf_desfire_poller_i.c index 6d8dfda16..f54133e3c 100644 --- a/lib/nfc/protocols/mf_desfire/mf_desfire_poller_i.c +++ b/lib/nfc/protocols/mf_desfire/mf_desfire_poller_i.c @@ -21,11 +21,11 @@ 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: + case NXP_NATIVE_COMMAND_STATUS_OPERATION_OK: return MfDesfireErrorNone; - case MF_DESFIRE_STATUS_AUTHENTICATION_ERROR: + case NXP_NATIVE_COMMAND_STATUS_AUTHENTICATION_ERROR: return MfDesfireErrorAuthentication; - case MF_DESFIRE_STATUS_ILLEGAL_COMMAND_CODE: + case NXP_NATIVE_COMMAND_STATUS_ILLEGAL_COMMAND_CODE: return MfDesfireErrorCommandNotSupported; default: return MfDesfireErrorProtocol; @@ -37,60 +37,22 @@ MfDesfireError mf_desfire_send_chunks( const BitBuffer* tx_buffer, BitBuffer* rx_buffer) { furi_check(instance); - furi_check(instance->iso14443_4a_poller); - furi_check(instance->tx_buffer); - furi_check(instance->rx_buffer); - furi_check(tx_buffer); - furi_check(rx_buffer); - MfDesfireError error = MfDesfireErrorNone; + NxpNativeCommandStatus status_code = NXP_NATIVE_COMMAND_STATUS_OPERATION_OK; + Iso14443_4aError iso14443_4a_error = nxp_native_command_iso14443_4a_poller( + instance->iso14443_4a_poller, + &status_code, + tx_buffer, + rx_buffer, + NxpNativeCommandModePlain, + instance->tx_buffer, + instance->rx_buffer); - 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_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)); - } else { - bit_buffer_reset(rx_buffer); - } - - 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); - - 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 + 1) { - 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 - 1); - } - } - } 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); + if(iso14443_4a_error != Iso14443_4aErrorNone) { + return mf_desfire_process_error(iso14443_4a_error); } - return error; + return mf_desfire_process_status_code(status_code); } MfDesfireError mf_desfire_poller_read_version(MfDesfirePoller* instance, MfDesfireVersion* data) { From 7525662f14ffdbf8a32999e81dd158eab12dcd7e Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 27 Mar 2025 04:45:20 +0000 Subject: [PATCH 62/92] NFC: Sort protocol defs and add missing ones --- lib/nfc/SConscript | 12 ++++++++---- lib/nfc/protocols/nfc_listener_defs.c | 5 +++-- lib/nfc/protocols/nfc_poller_defs.c | 2 +- lib/nfc/protocols/nfc_protocol.c | 6 ++++-- targets/f18/api_symbols.csv | 2 +- targets/f7/api_symbols.csv | 13 ++++++++++++- 6 files changed, 29 insertions(+), 11 deletions(-) diff --git a/lib/nfc/SConscript b/lib/nfc/SConscript index 82b87ea2a..f9f16c759 100644 --- a/lib/nfc/SConscript +++ b/lib/nfc/SConscript @@ -19,37 +19,41 @@ env.Append( File("protocols/iso14443_3b/iso14443_3b.h"), File("protocols/iso14443_4a/iso14443_4a.h"), File("protocols/iso14443_4b/iso14443_4b.h"), + File("protocols/iso15693_3/iso15693_3.h"), + File("protocols/felica/felica.h"), File("protocols/mf_ultralight/mf_ultralight.h"), File("protocols/mf_classic/mf_classic.h"), File("protocols/mf_plus/mf_plus.h"), File("protocols/mf_desfire/mf_desfire.h"), File("protocols/slix/slix.h"), File("protocols/st25tb/st25tb.h"), - File("protocols/felica/felica.h"), # Pollers File("protocols/iso14443_3a/iso14443_3a_poller.h"), File("protocols/iso14443_3b/iso14443_3b_poller.h"), File("protocols/iso14443_4a/iso14443_4a_poller.h"), File("protocols/iso14443_4b/iso14443_4b_poller.h"), + File("protocols/iso15693_3/iso15693_3_poller.h"), + File("protocols/felica/felica_poller.h"), File("protocols/mf_ultralight/mf_ultralight_poller.h"), File("protocols/mf_classic/mf_classic_poller.h"), File("protocols/mf_plus/mf_plus_poller.h"), File("protocols/mf_desfire/mf_desfire_poller.h"), File("protocols/slix/slix_poller.h"), File("protocols/st25tb/st25tb_poller.h"), - File("protocols/felica/felica_poller.h"), # Listeners File("protocols/iso14443_3a/iso14443_3a_listener.h"), File("protocols/iso14443_4a/iso14443_4a_listener.h"), + File("protocols/iso15693_3/iso15693_3_listener.h"), + File("protocols/felica/felica_listener.h"), File("protocols/mf_ultralight/mf_ultralight_listener.h"), File("protocols/mf_classic/mf_classic_listener.h"), - File("protocols/felica/felica_listener.h"), + File("protocols/slix/slix_listener.h"), # Sync API File("protocols/iso14443_3a/iso14443_3a_poller_sync.h"), + File("protocols/felica/felica_poller_sync.h"), File("protocols/mf_ultralight/mf_ultralight_poller_sync.h"), File("protocols/mf_classic/mf_classic_poller_sync.h"), File("protocols/st25tb/st25tb_poller_sync.h"), - File("protocols/felica/felica_poller_sync.h"), # Misc File("helpers/nfc_util.h"), File("helpers/iso14443_crc.h"), diff --git a/lib/nfc/protocols/nfc_listener_defs.c b/lib/nfc/protocols/nfc_listener_defs.c index 2a6167e9c..d0a23ba30 100644 --- a/lib/nfc/protocols/nfc_listener_defs.c +++ b/lib/nfc/protocols/nfc_listener_defs.c @@ -3,10 +3,10 @@ #include #include #include +#include #include #include #include -#include const NfcListenerBase* nfc_listeners_api[NfcProtocolNum] = { [NfcProtocolIso14443_3a] = &nfc_listener_iso14443_3a, @@ -14,10 +14,11 @@ const NfcListenerBase* nfc_listeners_api[NfcProtocolNum] = { [NfcProtocolIso14443_4a] = &nfc_listener_iso14443_4a, [NfcProtocolIso14443_4b] = NULL, [NfcProtocolIso15693_3] = &nfc_listener_iso15693_3, + [NfcProtocolFelica] = &nfc_listener_felica, [NfcProtocolMfUltralight] = &mf_ultralight_listener, [NfcProtocolMfClassic] = &mf_classic_listener, + [NfcProtocolMfPlus] = NULL, [NfcProtocolMfDesfire] = NULL, [NfcProtocolSlix] = &nfc_listener_slix, [NfcProtocolSt25tb] = NULL, - [NfcProtocolFelica] = &nfc_listener_felica, }; diff --git a/lib/nfc/protocols/nfc_poller_defs.c b/lib/nfc/protocols/nfc_poller_defs.c index c007740b7..7581b4694 100644 --- a/lib/nfc/protocols/nfc_poller_defs.c +++ b/lib/nfc/protocols/nfc_poller_defs.c @@ -25,6 +25,6 @@ const NfcPollerBase* nfc_pollers_api[NfcProtocolNum] = { [NfcProtocolMfPlus] = &mf_plus_poller, [NfcProtocolMfDesfire] = &mf_desfire_poller, [NfcProtocolSlix] = &nfc_poller_slix, - /* Add new pollers here */ [NfcProtocolSt25tb] = &nfc_poller_st25tb, + /* Add new pollers here */ }; diff --git a/lib/nfc/protocols/nfc_protocol.c b/lib/nfc/protocols/nfc_protocol.c index 40201843e..92d9d990b 100644 --- a/lib/nfc/protocols/nfc_protocol.c +++ b/lib/nfc/protocols/nfc_protocol.c @@ -22,7 +22,9 @@ * | | | * ISO14443-4A Mf Ultralight Mf Classic * | - * Mf Desfire + * +-----+----+ + * | | + * Mf Desfire Mf Plus * ``` * * When implementing a new protocol, its place in the tree must be determined first. @@ -60,8 +62,8 @@ static const NfcProtocol nfc_protocol_iso14443_3b_children_protocol[] = { /** List of ISO14443-4A child protocols. */ static const NfcProtocol nfc_protocol_iso14443_4a_children_protocol[] = { - NfcProtocolMfDesfire, NfcProtocolMfPlus, + NfcProtocolMfDesfire, }; /** List of ISO115693-3 child protocols. */ diff --git a/targets/f18/api_symbols.csv b/targets/f18/api_symbols.csv index 2074fa131..ae056b56c 100644 --- a/targets/f18/api_symbols.csv +++ b/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,82.1,, +Version,+,82.2,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/bt/bt_service/bt_keys_storage.h,, Header,+,applications/services/cli/cli.h,, diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index 1168a6eea..2696643d8 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,82.1,, +Version,+,82.2,, Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/bt/bt_service/bt_keys_storage.h,, @@ -152,6 +152,9 @@ Header,+,lib/nfc/protocols/iso14443_4a/iso14443_4a_listener.h,, Header,+,lib/nfc/protocols/iso14443_4a/iso14443_4a_poller.h,, Header,+,lib/nfc/protocols/iso14443_4b/iso14443_4b.h,, Header,+,lib/nfc/protocols/iso14443_4b/iso14443_4b_poller.h,, +Header,+,lib/nfc/protocols/iso15693_3/iso15693_3.h,, +Header,+,lib/nfc/protocols/iso15693_3/iso15693_3_listener.h,, +Header,+,lib/nfc/protocols/iso15693_3/iso15693_3_poller.h,, Header,+,lib/nfc/protocols/mf_classic/mf_classic.h,, Header,+,lib/nfc/protocols/mf_classic/mf_classic_listener.h,, Header,+,lib/nfc/protocols/mf_classic/mf_classic_poller.h,, @@ -165,6 +168,7 @@ Header,+,lib/nfc/protocols/mf_ultralight/mf_ultralight_listener.h,, Header,+,lib/nfc/protocols/mf_ultralight/mf_ultralight_poller.h,, Header,+,lib/nfc/protocols/mf_ultralight/mf_ultralight_poller_sync.h,, Header,+,lib/nfc/protocols/slix/slix.h,, +Header,+,lib/nfc/protocols/slix/slix_listener.h,, Header,+,lib/nfc/protocols/slix/slix_poller.h,, Header,+,lib/nfc/protocols/st25tb/st25tb.h,, Header,+,lib/nfc/protocols/st25tb/st25tb_poller.h,, @@ -2196,6 +2200,13 @@ Function,+,iso15693_3_get_uid,const uint8_t*,"const Iso15693_3Data*, size_t*" Function,+,iso15693_3_is_block_locked,_Bool,"const Iso15693_3Data*, uint8_t" Function,+,iso15693_3_is_equal,_Bool,"const Iso15693_3Data*, const Iso15693_3Data*" Function,+,iso15693_3_load,_Bool,"Iso15693_3Data*, FlipperFormat*, uint32_t" +Function,+,iso15693_3_poller_activate,Iso15693_3Error,"Iso15693_3Poller*, Iso15693_3Data*" +Function,+,iso15693_3_poller_get_blocks_security,Iso15693_3Error,"Iso15693_3Poller*, uint8_t*, uint16_t" +Function,+,iso15693_3_poller_get_system_info,Iso15693_3Error,"Iso15693_3Poller*, Iso15693_3SystemInfo*" +Function,+,iso15693_3_poller_inventory,Iso15693_3Error,"Iso15693_3Poller*, uint8_t*" +Function,+,iso15693_3_poller_read_block,Iso15693_3Error,"Iso15693_3Poller*, uint8_t*, uint8_t, uint8_t" +Function,+,iso15693_3_poller_read_blocks,Iso15693_3Error,"Iso15693_3Poller*, uint8_t*, uint16_t, uint8_t" +Function,+,iso15693_3_poller_send_frame,Iso15693_3Error,"Iso15693_3Poller*, const BitBuffer*, BitBuffer*, uint32_t" Function,+,iso15693_3_reset,void,Iso15693_3Data* Function,+,iso15693_3_save,_Bool,"const Iso15693_3Data*, FlipperFormat*" Function,+,iso15693_3_set_uid,_Bool,"Iso15693_3Data*, const uint8_t*, size_t" From 6ebc4c7ad3da23b474b03106969f0b5c88e73af3 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 27 Mar 2025 04:48:19 +0000 Subject: [PATCH 63/92] NFC: Support NTAG4xx detection and basic info --- .../nfc_protocol_support_defs.c | 2 + .../protocol_support/ntag4xx/ntag4xx.c | 133 ++++++++++++ .../protocol_support/ntag4xx/ntag4xx.h | 5 + .../protocol_support/ntag4xx/ntag4xx_render.c | 110 ++++++++++ .../protocol_support/ntag4xx/ntag4xx_render.h | 14 ++ lib/nfc/SConscript | 2 + lib/nfc/protocols/nfc_device_defs.c | 2 + lib/nfc/protocols/nfc_listener_defs.c | 1 + lib/nfc/protocols/nfc_poller_defs.c | 2 + lib/nfc/protocols/nfc_protocol.c | 13 +- lib/nfc/protocols/nfc_protocol.h | 1 + lib/nfc/protocols/ntag4xx/ntag4xx.c | 192 ++++++++++++++++++ lib/nfc/protocols/ntag4xx/ntag4xx.h | 114 +++++++++++ lib/nfc/protocols/ntag4xx/ntag4xx_i.c | 54 +++++ lib/nfc/protocols/ntag4xx/ntag4xx_i.h | 25 +++ lib/nfc/protocols/ntag4xx/ntag4xx_poller.c | 165 +++++++++++++++ lib/nfc/protocols/ntag4xx/ntag4xx_poller.h | 43 ++++ .../protocols/ntag4xx/ntag4xx_poller_defs.h | 5 + lib/nfc/protocols/ntag4xx/ntag4xx_poller_i.c | 52 +++++ lib/nfc/protocols/ntag4xx/ntag4xx_poller_i.h | 40 ++++ targets/f18/api_symbols.csv | 2 +- targets/f7/api_symbols.csv | 18 +- 22 files changed, 990 insertions(+), 5 deletions(-) create mode 100644 applications/main/nfc/helpers/protocol_support/ntag4xx/ntag4xx.c create mode 100644 applications/main/nfc/helpers/protocol_support/ntag4xx/ntag4xx.h create mode 100644 applications/main/nfc/helpers/protocol_support/ntag4xx/ntag4xx_render.c create mode 100644 applications/main/nfc/helpers/protocol_support/ntag4xx/ntag4xx_render.h create mode 100644 lib/nfc/protocols/ntag4xx/ntag4xx.c create mode 100644 lib/nfc/protocols/ntag4xx/ntag4xx.h create mode 100644 lib/nfc/protocols/ntag4xx/ntag4xx_i.c create mode 100644 lib/nfc/protocols/ntag4xx/ntag4xx_i.h create mode 100644 lib/nfc/protocols/ntag4xx/ntag4xx_poller.c create mode 100644 lib/nfc/protocols/ntag4xx/ntag4xx_poller.h create mode 100644 lib/nfc/protocols/ntag4xx/ntag4xx_poller_defs.h create mode 100644 lib/nfc/protocols/ntag4xx/ntag4xx_poller_i.c create mode 100644 lib/nfc/protocols/ntag4xx/ntag4xx_poller_i.h diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_defs.c b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_defs.c index 66839aacc..fa6c3efec 100644 --- a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_defs.c +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_defs.c @@ -21,6 +21,7 @@ #include "mf_desfire/mf_desfire.h" #include "slix/slix.h" #include "st25tb/st25tb.h" +#include "ntag4xx/ntag4xx.h" /** * @brief Array of pointers to concrete protocol support implementations. @@ -43,5 +44,6 @@ const NfcProtocolSupportBase* nfc_protocol_support[NfcProtocolNum] = { [NfcProtocolMfDesfire] = &nfc_protocol_support_mf_desfire, [NfcProtocolSlix] = &nfc_protocol_support_slix, [NfcProtocolSt25tb] = &nfc_protocol_support_st25tb, + [NfcProtocolNtag4xx] = &nfc_protocol_support_ntag4xx, /* Add new protocol support implementations here */ }; diff --git a/applications/main/nfc/helpers/protocol_support/ntag4xx/ntag4xx.c b/applications/main/nfc/helpers/protocol_support/ntag4xx/ntag4xx.c new file mode 100644 index 000000000..82a3d55b6 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/ntag4xx/ntag4xx.c @@ -0,0 +1,133 @@ +#include "ntag4xx.h" +#include "ntag4xx_render.h" + +#include + +#include "nfc/nfc_app_i.h" + +#include "../nfc_protocol_support_common.h" +#include "../nfc_protocol_support_gui_common.h" +#include "../iso14443_4a/iso14443_4a_i.h" + +static void nfc_scene_info_on_enter_ntag4xx(NfcApp* instance) { + const NfcDevice* device = instance->nfc_device; + const Ntag4xxData* data = nfc_device_get_data(device, NfcProtocolNtag4xx); + + FuriString* temp_str = furi_string_alloc(); + nfc_append_filename_string_when_present(instance, temp_str); + furi_string_cat_printf( + temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); + nfc_render_ntag4xx_info(data, NfcProtocolFormatTypeFull, temp_str); + + widget_add_text_scroll_element( + instance->widget, 0, 0, 128, 64, furi_string_get_cstr(temp_str)); + + furi_string_free(temp_str); +} + +static void nfc_scene_more_info_on_enter_ntag4xx(NfcApp* instance) { + const NfcDevice* device = instance->nfc_device; + const Ntag4xxData* data = nfc_device_get_data(device, NfcProtocolNtag4xx); + + furi_string_reset(instance->text_box_store); + nfc_render_ntag4xx_data(data, instance->text_box_store); + + text_box_set_font(instance->text_box, TextBoxFontHex); + text_box_set_text(instance->text_box, furi_string_get_cstr(instance->text_box_store)); + + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewTextBox); +} + +static NfcCommand nfc_scene_read_poller_callback_ntag4xx(NfcGenericEvent event, void* context) { + furi_assert(event.protocol == NfcProtocolNtag4xx); + + NfcCommand command = NfcCommandContinue; + + NfcApp* instance = context; + const Ntag4xxPollerEvent* ntag4xx_event = event.event_data; + + if(ntag4xx_event->type == Ntag4xxPollerEventTypeReadSuccess) { + nfc_device_set_data( + instance->nfc_device, NfcProtocolNtag4xx, nfc_poller_get_data(instance->poller)); + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess); + command = NfcCommandStop; + } else if(ntag4xx_event->type == Ntag4xxPollerEventTypeReadFailed) { + command = NfcCommandReset; + } + + return command; +} + +static void nfc_scene_read_on_enter_ntag4xx(NfcApp* instance) { + nfc_poller_start(instance->poller, nfc_scene_read_poller_callback_ntag4xx, instance); +} + +static void nfc_scene_read_success_on_enter_ntag4xx(NfcApp* instance) { + const NfcDevice* device = instance->nfc_device; + const Ntag4xxData* data = nfc_device_get_data(device, NfcProtocolNtag4xx); + + FuriString* temp_str = furi_string_alloc(); + furi_string_cat_printf( + temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); + nfc_render_ntag4xx_info(data, NfcProtocolFormatTypeShort, temp_str); + + widget_add_text_scroll_element( + instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str)); + + furi_string_free(temp_str); +} + +static void nfc_scene_emulate_on_enter_ntag4xx(NfcApp* instance) { + const Iso14443_4aData* iso14443_4a_data = + nfc_device_get_data(instance->nfc_device, NfcProtocolIso14443_4a); + + instance->listener = + nfc_listener_alloc(instance->nfc, NfcProtocolIso14443_4a, iso14443_4a_data); + nfc_listener_start( + instance->listener, nfc_scene_emulate_listener_callback_iso14443_4a, instance); +} + +const NfcProtocolSupportBase nfc_protocol_support_ntag4xx = { + .features = NfcProtocolFeatureEmulateUid | NfcProtocolFeatureMoreInfo, + + .scene_info = + { + .on_enter = nfc_scene_info_on_enter_ntag4xx, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_more_info = + { + .on_enter = nfc_scene_more_info_on_enter_ntag4xx, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_read = + { + .on_enter = nfc_scene_read_on_enter_ntag4xx, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_read_menu = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_read_success = + { + .on_enter = nfc_scene_read_success_on_enter_ntag4xx, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_saved_menu = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_save_name = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_emulate = + { + .on_enter = nfc_scene_emulate_on_enter_ntag4xx, + .on_event = nfc_protocol_support_common_on_event_empty, + }, +}; diff --git a/applications/main/nfc/helpers/protocol_support/ntag4xx/ntag4xx.h b/applications/main/nfc/helpers/protocol_support/ntag4xx/ntag4xx.h new file mode 100644 index 000000000..09a8388fa --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/ntag4xx/ntag4xx.h @@ -0,0 +1,5 @@ +#pragma once + +#include "../nfc_protocol_support_base.h" + +extern const NfcProtocolSupportBase nfc_protocol_support_ntag4xx; diff --git a/applications/main/nfc/helpers/protocol_support/ntag4xx/ntag4xx_render.c b/applications/main/nfc/helpers/protocol_support/ntag4xx/ntag4xx_render.c new file mode 100644 index 000000000..0cb587726 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/ntag4xx/ntag4xx_render.c @@ -0,0 +1,110 @@ +#include "ntag4xx_render.h" + +#include "../iso14443_4a/iso14443_4a_render.h" + +void nfc_render_ntag4xx_info( + const Ntag4xxData* data, + NfcProtocolFormatType format_type, + FuriString* str) { + nfc_render_iso14443_4a_brief(ntag4xx_get_base_data(data), str); + + const Ntag4xxType type = ntag4xx_get_type_from_version(&data->version); + if(type >= Ntag4xxTypeUnknown) { + furi_string_cat(str, "Memory Size: unknown"); + } else { + size_t size_cc = 32; + size_t size_ndef = 0; + size_t size_proprietary = 0; + bool has_tagtamper = false; + switch(type) { + case Ntag4xxType413DNA: + size_ndef = 128; + size_proprietary = 0; + break; + case Ntag4xxType424DNATT: + has_tagtamper = true; + /* fall-through */ + case Ntag4xxType424DNA: + size_ndef = 256; + size_proprietary = 128; + break; + case Ntag4xxType426QDNATT: + has_tagtamper = true; + /* fall-through */ + case Ntag4xxType426QDNA: + size_ndef = 768; + size_proprietary = 128; + break; + default: + break; + } + furi_string_cat_printf( + str, "\nMemory Size: %zu bytes\n", size_cc + size_ndef + size_proprietary); + furi_string_cat_printf(str, "Usable NDEF Size: %zu bytes\n", size_ndef - sizeof(uint16_t)); + furi_string_cat_printf(str, "Capability Cont.: %zu bytes\n", size_cc); + if(size_proprietary) { + furi_string_cat_printf(str, "Proprietary File: %zu bytes\n", size_proprietary); + } + furi_string_cat_printf(str, "TagTamper: %ssupported", has_tagtamper ? "" : "not "); + } + + if(format_type != NfcProtocolFormatTypeFull) return; + + furi_string_cat(str, "\n\e#ISO14443-4 data"); + nfc_render_iso14443_4a_extra(ntag4xx_get_base_data(data), str); +} + +void nfc_render_ntag4xx_data(const Ntag4xxData* data, FuriString* str) { + nfc_render_ntag4xx_version(&data->version, str); +} + +void nfc_render_ntag4xx_version(const Ntag4xxVersion* data, FuriString* str) { + furi_string_cat_printf( + str, + "%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", + data->uid[0], + data->uid[1], + data->uid[2], + data->uid[3], + data->uid[4], + data->uid[5], + data->uid[6]); + furi_string_cat_printf( + str, + "hw %02x type %02x sub %02x\n" + " maj %02x min %02x\n" + " size %02x proto %02x\n", + data->hw_vendor, + data->hw_type, + data->hw_subtype, + data->hw_major, + data->hw_minor, + data->hw_storage, + data->hw_proto); + furi_string_cat_printf( + str, + "sw %02x type %02x sub %02x\n" + " maj %02x min %02x\n" + " size %02x proto %02x\n", + data->sw_vendor, + data->sw_type, + data->sw_subtype, + data->sw_major, + data->sw_minor, + data->sw_storage, + data->sw_proto); + furi_string_cat_printf( + str, + "batch %02x:%02x:%02x:%02x:%01x\n" + "week %d year %d\n" + "fab key %02x id %02x\n", + data->batch[0], + data->batch[1], + data->batch[2], + data->batch[3], + data->batch_extra, + data->prod_week, + data->prod_year, + (data->fab_key_4b << 1) | (data->fab_key_1b), + data->optional.fab_key_id); +} diff --git a/applications/main/nfc/helpers/protocol_support/ntag4xx/ntag4xx_render.h b/applications/main/nfc/helpers/protocol_support/ntag4xx/ntag4xx_render.h new file mode 100644 index 000000000..ca81cf4c4 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/ntag4xx/ntag4xx_render.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +#include "../nfc_protocol_support_render_common.h" + +void nfc_render_ntag4xx_info( + const Ntag4xxData* data, + NfcProtocolFormatType format_type, + FuriString* str); + +void nfc_render_ntag4xx_data(const Ntag4xxData* data, FuriString* str); + +void nfc_render_ntag4xx_version(const Ntag4xxVersion* data, FuriString* str); diff --git a/lib/nfc/SConscript b/lib/nfc/SConscript index f9f16c759..6d596c0e8 100644 --- a/lib/nfc/SConscript +++ b/lib/nfc/SConscript @@ -27,6 +27,7 @@ env.Append( File("protocols/mf_desfire/mf_desfire.h"), File("protocols/slix/slix.h"), File("protocols/st25tb/st25tb.h"), + File("protocols/ntag4xx/ntag4xx.h"), # Pollers File("protocols/iso14443_3a/iso14443_3a_poller.h"), File("protocols/iso14443_3b/iso14443_3b_poller.h"), @@ -40,6 +41,7 @@ env.Append( File("protocols/mf_desfire/mf_desfire_poller.h"), File("protocols/slix/slix_poller.h"), File("protocols/st25tb/st25tb_poller.h"), + File("protocols/ntag4xx/ntag4xx_poller.h"), # Listeners File("protocols/iso14443_3a/iso14443_3a_listener.h"), File("protocols/iso14443_4a/iso14443_4a_listener.h"), diff --git a/lib/nfc/protocols/nfc_device_defs.c b/lib/nfc/protocols/nfc_device_defs.c index 6a145445c..4d337f377 100644 --- a/lib/nfc/protocols/nfc_device_defs.c +++ b/lib/nfc/protocols/nfc_device_defs.c @@ -24,6 +24,7 @@ #include #include #include +#include /** * @brief List of registered NFC device implementations. @@ -44,5 +45,6 @@ const NfcDeviceBase* nfc_devices[NfcProtocolNum] = { [NfcProtocolMfDesfire] = &nfc_device_mf_desfire, [NfcProtocolSlix] = &nfc_device_slix, [NfcProtocolSt25tb] = &nfc_device_st25tb, + [NfcProtocolNtag4xx] = &nfc_device_ntag4xx, /* Add new protocols here */ }; diff --git a/lib/nfc/protocols/nfc_listener_defs.c b/lib/nfc/protocols/nfc_listener_defs.c index d0a23ba30..43ffd716f 100644 --- a/lib/nfc/protocols/nfc_listener_defs.c +++ b/lib/nfc/protocols/nfc_listener_defs.c @@ -21,4 +21,5 @@ const NfcListenerBase* nfc_listeners_api[NfcProtocolNum] = { [NfcProtocolMfDesfire] = NULL, [NfcProtocolSlix] = &nfc_listener_slix, [NfcProtocolSt25tb] = NULL, + [NfcProtocolNtag4xx] = NULL, }; diff --git a/lib/nfc/protocols/nfc_poller_defs.c b/lib/nfc/protocols/nfc_poller_defs.c index 7581b4694..9c3c88517 100644 --- a/lib/nfc/protocols/nfc_poller_defs.c +++ b/lib/nfc/protocols/nfc_poller_defs.c @@ -12,6 +12,7 @@ #include #include #include +#include const NfcPollerBase* nfc_pollers_api[NfcProtocolNum] = { [NfcProtocolIso14443_3a] = &nfc_poller_iso14443_3a, @@ -26,5 +27,6 @@ const NfcPollerBase* nfc_pollers_api[NfcProtocolNum] = { [NfcProtocolMfDesfire] = &mf_desfire_poller, [NfcProtocolSlix] = &nfc_poller_slix, [NfcProtocolSt25tb] = &nfc_poller_st25tb, + [NfcProtocolNtag4xx] = &ntag4xx_poller, /* Add new pollers here */ }; diff --git a/lib/nfc/protocols/nfc_protocol.c b/lib/nfc/protocols/nfc_protocol.c index 92d9d990b..7d42520ef 100644 --- a/lib/nfc/protocols/nfc_protocol.c +++ b/lib/nfc/protocols/nfc_protocol.c @@ -22,9 +22,9 @@ * | | | * ISO14443-4A Mf Ultralight Mf Classic * | - * +-----+----+ - * | | - * Mf Desfire Mf Plus + * +-----+----+----------+ + * | | | + * Mf Desfire Mf Plus NTAG4xx * ``` * * When implementing a new protocol, its place in the tree must be determined first. @@ -64,6 +64,7 @@ static const NfcProtocol nfc_protocol_iso14443_3b_children_protocol[] = { static const NfcProtocol nfc_protocol_iso14443_4a_children_protocol[] = { NfcProtocolMfPlus, NfcProtocolMfDesfire, + NfcProtocolNtag4xx, }; /** List of ISO115693-3 child protocols. */ @@ -155,6 +156,12 @@ static const NfcProtocolTreeNode nfc_protocol_nodes[NfcProtocolNum] = { .children_num = 0, .children_protocol = NULL, }, + [NfcProtocolNtag4xx] = + { + .parent_protocol = NfcProtocolIso14443_4a, + .children_num = 0, + .children_protocol = NULL, + }, /* Add new protocols here */ }; diff --git a/lib/nfc/protocols/nfc_protocol.h b/lib/nfc/protocols/nfc_protocol.h index cf74972f7..17f31aba2 100644 --- a/lib/nfc/protocols/nfc_protocol.h +++ b/lib/nfc/protocols/nfc_protocol.h @@ -188,6 +188,7 @@ typedef enum { NfcProtocolMfDesfire, NfcProtocolSlix, NfcProtocolSt25tb, + NfcProtocolNtag4xx, /* Add new protocols here */ NfcProtocolNum, /**< Special value representing the number of available protocols. */ diff --git a/lib/nfc/protocols/ntag4xx/ntag4xx.c b/lib/nfc/protocols/ntag4xx/ntag4xx.c new file mode 100644 index 000000000..7b0991903 --- /dev/null +++ b/lib/nfc/protocols/ntag4xx/ntag4xx.c @@ -0,0 +1,192 @@ +#include "ntag4xx_i.h" + +#include + +#define NTAG4XX_PROTOCOL_NAME "NTAG4xx" + +#define NTAG4XX_HW_MAJOR_TYPE_413_DNA (0x10) +#define NTAG4XX_HW_MAJOR_TYPE_424_DNA (0x30) + +#define NTAG4XX_HW_SUBTYPE_TAGTAMPER_FLAG (0x08) + +static const char* ntag4xx_type_strings[] = { + [Ntag4xxType413DNA] = "NTAG413 DNA", + [Ntag4xxType424DNA] = "NTAG424 DNA", + [Ntag4xxType424DNATT] = "NTAG424 DNA TagTamper", + [Ntag4xxType426QDNA] = "NTAG426Q DNA", + [Ntag4xxType426QDNATT] = "NTAG426Q DNA TagTamper", + [Ntag4xxTypeUnknown] = "UNK", +}; + +const NfcDeviceBase nfc_device_ntag4xx = { + .protocol_name = NTAG4XX_PROTOCOL_NAME, + .alloc = (NfcDeviceAlloc)ntag4xx_alloc, + .free = (NfcDeviceFree)ntag4xx_free, + .reset = (NfcDeviceReset)ntag4xx_reset, + .copy = (NfcDeviceCopy)ntag4xx_copy, + .verify = (NfcDeviceVerify)ntag4xx_verify, + .load = (NfcDeviceLoad)ntag4xx_load, + .save = (NfcDeviceSave)ntag4xx_save, + .is_equal = (NfcDeviceEqual)ntag4xx_is_equal, + .get_name = (NfcDeviceGetName)ntag4xx_get_device_name, + .get_uid = (NfcDeviceGetUid)ntag4xx_get_uid, + .set_uid = (NfcDeviceSetUid)ntag4xx_set_uid, + .get_base_data = (NfcDeviceGetBaseData)ntag4xx_get_base_data, +}; + +Ntag4xxData* ntag4xx_alloc(void) { + Ntag4xxData* data = malloc(sizeof(Ntag4xxData)); + data->iso14443_4a_data = iso14443_4a_alloc(); + data->device_name = furi_string_alloc(); + return data; +} + +void ntag4xx_free(Ntag4xxData* data) { + furi_check(data); + + ntag4xx_reset(data); + iso14443_4a_free(data->iso14443_4a_data); + furi_string_free(data->device_name); + free(data); +} + +void ntag4xx_reset(Ntag4xxData* data) { + furi_check(data); + + iso14443_4a_reset(data->iso14443_4a_data); + + memset(&data->version, 0, sizeof(Ntag4xxVersion)); +} + +void ntag4xx_copy(Ntag4xxData* data, const Ntag4xxData* other) { + furi_check(data); + furi_check(other); + + ntag4xx_reset(data); + + iso14443_4a_copy(data->iso14443_4a_data, other->iso14443_4a_data); + + data->version = other->version; +} + +bool ntag4xx_verify(Ntag4xxData* data, const FuriString* device_type) { + UNUSED(data); + UNUSED(device_type); + + return false; +} + +bool ntag4xx_load(Ntag4xxData* data, FlipperFormat* ff, uint32_t version) { + furi_check(data); + furi_check(ff); + + FuriString* prefix = furi_string_alloc(); + + bool success = false; + + do { + if(!iso14443_4a_load(data->iso14443_4a_data, ff, version)) break; + + if(!ntag4xx_version_load(&data->version, ff)) break; + + success = true; + } while(false); + + furi_string_free(prefix); + return success; +} + +bool ntag4xx_save(const Ntag4xxData* data, FlipperFormat* ff) { + furi_check(data); + furi_check(ff); + + 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, NTAG4XX_PROTOCOL_NAME " specific data")) break; + if(!ntag4xx_version_save(&data->version, ff)) break; + + success = true; + } while(false); + + furi_string_free(prefix); + return success; +} + +bool ntag4xx_is_equal(const Ntag4xxData* data, const Ntag4xxData* other) { + furi_check(data); + furi_check(other); + + return iso14443_4a_is_equal(data->iso14443_4a_data, other->iso14443_4a_data) && + memcmp(&data->version, &other->version, sizeof(Ntag4xxVersion)) == 0; +} + +Ntag4xxType ntag4xx_get_type_from_version(const Ntag4xxVersion* const version) { + Ntag4xxType type = Ntag4xxTypeUnknown; + + switch(version->hw_major) { + case NTAG4XX_HW_MAJOR_TYPE_413_DNA: + type = Ntag4xxType413DNA; + break; + case NTAG4XX_HW_MAJOR_TYPE_424_DNA: + if(version->hw_subtype & NTAG4XX_HW_SUBTYPE_TAGTAMPER_FLAG) { + type = Ntag4xxType424DNATT; + } else { + type = Ntag4xxType424DNA; + } + break; + // TODO: there is no info online or in other implementations (NXP TagInfo, NFC Tools, Proxmark3) + // about what the HWMajorVersion is supposed to be for NTAG426Q DNA, and they don't seem to be for sale + // case NTAG4XX_HW_MAJOR_TYPE_426Q_DNA: + // if(version->hw_subtype & NTAG4XX_HW_SUBTYPE_TAGTAMPER_FLAG) { + // type = Ntag4xxType426QDNATT; + // } else { + // type = Ntag4xxType426QDNA; + // } + // break; + default: + break; + } + + return type; +} + +const char* ntag4xx_get_device_name(const Ntag4xxData* data, NfcDeviceNameType name_type) { + furi_check(data); + + const Ntag4xxType type = ntag4xx_get_type_from_version(&data->version); + + if(type == Ntag4xxTypeUnknown) { + furi_string_printf(data->device_name, "Unknown %s", NTAG4XX_PROTOCOL_NAME); + } else { + furi_string_printf(data->device_name, "%s", ntag4xx_type_strings[type]); + if(name_type == NfcDeviceNameTypeShort) { + furi_string_replace(data->device_name, "TagTamper", "TT"); + } + } + + return furi_string_get_cstr(data->device_name); +} + +const uint8_t* ntag4xx_get_uid(const Ntag4xxData* data, size_t* uid_len) { + furi_check(data); + furi_check(uid_len); + + return iso14443_4a_get_uid(data->iso14443_4a_data, uid_len); +} + +bool ntag4xx_set_uid(Ntag4xxData* data, const uint8_t* uid, size_t uid_len) { + furi_check(data); + + return iso14443_4a_set_uid(data->iso14443_4a_data, uid, uid_len); +} + +Iso14443_4aData* ntag4xx_get_base_data(const Ntag4xxData* data) { + furi_check(data); + + return data->iso14443_4a_data; +} diff --git a/lib/nfc/protocols/ntag4xx/ntag4xx.h b/lib/nfc/protocols/ntag4xx/ntag4xx.h new file mode 100644 index 000000000..56e5fbd65 --- /dev/null +++ b/lib/nfc/protocols/ntag4xx/ntag4xx.h @@ -0,0 +1,114 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define NTAG4XX_UID_SIZE (7) +#define NTAG4XX_BATCH_SIZE (4) +#define NTAG4XX_BATCH_EXTRA_BITS 4 +#define NTAG4XX_FAB_KEY_SIZE_BITS_4 4 +#define NTAG4XX_FAB_KEY_SIZE_BITS_1 1 +#define NTAG4XX_PROD_WEEK_SIZE_BITS 7 + +#define NTAG4XX_CMD_GET_VERSION (0x60) + +typedef enum { + Ntag4xxErrorNone, + Ntag4xxErrorNotPresent, + Ntag4xxErrorProtocol, + Ntag4xxErrorTimeout, +} Ntag4xxError; + +typedef enum { + Ntag4xxType413DNA, + Ntag4xxType424DNA, + Ntag4xxType424DNATT, + Ntag4xxType426QDNA, + Ntag4xxType426QDNATT, + + Ntag4xxTypeUnknown, + Ntag4xxTypeNum, +} Ntag4xxType; + +#pragma pack(push, 1) +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[NTAG4XX_UID_SIZE]; + // [36b batch][5b fab key][7b prod week] + // 5b fab key is split 4b in last byte of batch and 1b in prod week + // Due to endianness, they appear swapped in the struct definition + uint8_t batch[NTAG4XX_BATCH_SIZE]; + struct { + uint8_t fab_key_4b : NTAG4XX_FAB_KEY_SIZE_BITS_4; + uint8_t batch_extra : NTAG4XX_BATCH_EXTRA_BITS; + }; + struct { + uint8_t prod_week : NTAG4XX_PROD_WEEK_SIZE_BITS; + uint8_t fab_key_1b : NTAG4XX_FAB_KEY_SIZE_BITS_1; + }; + uint8_t prod_year; + struct { + uint8_t fab_key_id; + } optional; +} Ntag4xxVersion; +#pragma pack(pop) + +typedef struct { + Iso14443_4aData* iso14443_4a_data; + Ntag4xxVersion version; + FuriString* device_name; +} Ntag4xxData; + +extern const NfcDeviceBase nfc_device_ntag4xx; + +// Virtual methods + +Ntag4xxData* ntag4xx_alloc(void); + +void ntag4xx_free(Ntag4xxData* data); + +void ntag4xx_reset(Ntag4xxData* data); + +void ntag4xx_copy(Ntag4xxData* data, const Ntag4xxData* other); + +bool ntag4xx_verify(Ntag4xxData* data, const FuriString* device_type); + +bool ntag4xx_load(Ntag4xxData* data, FlipperFormat* ff, uint32_t version); + +bool ntag4xx_save(const Ntag4xxData* data, FlipperFormat* ff); + +bool ntag4xx_is_equal(const Ntag4xxData* data, const Ntag4xxData* other); + +const char* ntag4xx_get_device_name(const Ntag4xxData* data, NfcDeviceNameType name_type); + +const uint8_t* ntag4xx_get_uid(const Ntag4xxData* data, size_t* uid_len); + +bool ntag4xx_set_uid(Ntag4xxData* data, const uint8_t* uid, size_t uid_len); + +Iso14443_4aData* ntag4xx_get_base_data(const Ntag4xxData* data); + +// Helpers + +Ntag4xxType ntag4xx_get_type_from_version(const Ntag4xxVersion* const version); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/ntag4xx/ntag4xx_i.c b/lib/nfc/protocols/ntag4xx/ntag4xx_i.c new file mode 100644 index 000000000..b4ee3a9eb --- /dev/null +++ b/lib/nfc/protocols/ntag4xx/ntag4xx_i.c @@ -0,0 +1,54 @@ +#include "ntag4xx_i.h" + +#define TAG "Ntag4xx" + +#define NTAG4XX_FFF_VERSION_KEY \ + NTAG4XX_FFF_PICC_PREFIX " " \ + "Version" + +Ntag4xxError ntag4xx_process_error(Iso14443_4aError error) { + switch(error) { + case Iso14443_4aErrorNone: + return Ntag4xxErrorNone; + case Iso14443_4aErrorNotPresent: + return Ntag4xxErrorNotPresent; + case Iso14443_4aErrorTimeout: + return Ntag4xxErrorTimeout; + default: + return Ntag4xxErrorProtocol; + } +} + +Ntag4xxError ntag4xx_process_status_code(uint8_t status_code) { + switch(status_code) { + case NXP_NATIVE_COMMAND_STATUS_OPERATION_OK: + return Ntag4xxErrorNone; + default: + return Ntag4xxErrorProtocol; + } +} + +bool ntag4xx_version_parse(Ntag4xxVersion* data, const BitBuffer* buf) { + const size_t buf_size = bit_buffer_get_size_bytes(buf); + const bool can_parse = buf_size == sizeof(Ntag4xxVersion) || + buf_size == sizeof(Ntag4xxVersion) - sizeof(data->optional); + + if(can_parse) { + bit_buffer_write_bytes(buf, data, sizeof(Ntag4xxVersion)); + if(buf_size < sizeof(Ntag4xxVersion)) { + memset(&data->optional, 0, sizeof(data->optional)); + } + } + + return can_parse && (data->hw_type & 0x0F) == 0x04; +} + +bool ntag4xx_version_load(Ntag4xxVersion* data, FlipperFormat* ff) { + return flipper_format_read_hex( + ff, NTAG4XX_FFF_VERSION_KEY, (uint8_t*)data, sizeof(Ntag4xxVersion)); +} + +bool ntag4xx_version_save(const Ntag4xxVersion* data, FlipperFormat* ff) { + return flipper_format_write_hex( + ff, NTAG4XX_FFF_VERSION_KEY, (const uint8_t*)data, sizeof(Ntag4xxVersion)); +} diff --git a/lib/nfc/protocols/ntag4xx/ntag4xx_i.h b/lib/nfc/protocols/ntag4xx/ntag4xx_i.h new file mode 100644 index 000000000..5a71cf05a --- /dev/null +++ b/lib/nfc/protocols/ntag4xx/ntag4xx_i.h @@ -0,0 +1,25 @@ +#pragma once + +#include "ntag4xx.h" + +#include + +#define NTAG4XX_FFF_PICC_PREFIX "PICC" + +// Internal helpers + +Ntag4xxError ntag4xx_process_error(Iso14443_4aError error); + +Ntag4xxError ntag4xx_process_status_code(uint8_t status_code); + +// Parse internal Ntag4xx structures + +bool ntag4xx_version_parse(Ntag4xxVersion* data, const BitBuffer* buf); + +// Load internal Ntag4xx structures + +bool ntag4xx_version_load(Ntag4xxVersion* data, FlipperFormat* ff); + +// Save internal Ntag4xx structures + +bool ntag4xx_version_save(const Ntag4xxVersion* data, FlipperFormat* ff); diff --git a/lib/nfc/protocols/ntag4xx/ntag4xx_poller.c b/lib/nfc/protocols/ntag4xx/ntag4xx_poller.c new file mode 100644 index 000000000..38e6f19d4 --- /dev/null +++ b/lib/nfc/protocols/ntag4xx/ntag4xx_poller.c @@ -0,0 +1,165 @@ +#include "ntag4xx_poller_i.h" + +#include + +#include + +#define TAG "Ntag4xxPoller" + +#define NTAG4XX_BUF_SIZE (64U) +#define NTAG4XX_RESULT_BUF_SIZE (512U) + +typedef NfcCommand (*Ntag4xxPollerReadHandler)(Ntag4xxPoller* instance); + +static const Ntag4xxData* ntag4xx_poller_get_data(Ntag4xxPoller* instance) { + furi_assert(instance); + + return instance->data; +} + +static Ntag4xxPoller* ntag4xx_poller_alloc(Iso14443_4aPoller* iso14443_4a_poller) { + Ntag4xxPoller* instance = malloc(sizeof(Ntag4xxPoller)); + instance->iso14443_4a_poller = iso14443_4a_poller; + instance->data = ntag4xx_alloc(); + instance->tx_buffer = bit_buffer_alloc(NTAG4XX_BUF_SIZE); + instance->rx_buffer = bit_buffer_alloc(NTAG4XX_BUF_SIZE); + instance->input_buffer = bit_buffer_alloc(NTAG4XX_BUF_SIZE); + instance->result_buffer = bit_buffer_alloc(NTAG4XX_RESULT_BUF_SIZE); + + instance->ntag4xx_event.data = &instance->ntag4xx_event_data; + + instance->general_event.protocol = NfcProtocolNtag4xx; + instance->general_event.event_data = &instance->ntag4xx_event; + instance->general_event.instance = instance; + + return instance; +} + +static void ntag4xx_poller_free(Ntag4xxPoller* instance) { + furi_assert(instance); + + ntag4xx_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 ntag4xx_poller_handler_idle(Ntag4xxPoller* 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 = Ntag4xxPollerStateReadVersion; + return NfcCommandContinue; +} + +static NfcCommand ntag4xx_poller_handler_read_version(Ntag4xxPoller* instance) { + instance->error = ntag4xx_poller_read_version(instance, &instance->data->version); + if(instance->error == Ntag4xxErrorNone) { + FURI_LOG_D(TAG, "Read version success"); + instance->state = Ntag4xxPollerStateReadSuccess; + } else { + FURI_LOG_E(TAG, "Failed to read version"); + iso14443_4a_poller_halt(instance->iso14443_4a_poller); + instance->state = Ntag4xxPollerStateReadFailed; + } + + return NfcCommandContinue; +} + +static NfcCommand ntag4xx_poller_handler_read_failed(Ntag4xxPoller* instance) { + FURI_LOG_D(TAG, "Read Failed"); + iso14443_4a_poller_halt(instance->iso14443_4a_poller); + instance->ntag4xx_event.type = Ntag4xxPollerEventTypeReadFailed; + instance->ntag4xx_event.data->error = instance->error; + NfcCommand command = instance->callback(instance->general_event, instance->context); + instance->state = Ntag4xxPollerStateIdle; + return command; +} + +static NfcCommand ntag4xx_poller_handler_read_success(Ntag4xxPoller* instance) { + FURI_LOG_D(TAG, "Read success"); + iso14443_4a_poller_halt(instance->iso14443_4a_poller); + instance->ntag4xx_event.type = Ntag4xxPollerEventTypeReadSuccess; + NfcCommand command = instance->callback(instance->general_event, instance->context); + return command; +} + +static const Ntag4xxPollerReadHandler ntag4xx_poller_read_handler[Ntag4xxPollerStateNum] = { + [Ntag4xxPollerStateIdle] = ntag4xx_poller_handler_idle, + [Ntag4xxPollerStateReadVersion] = ntag4xx_poller_handler_read_version, + [Ntag4xxPollerStateReadFailed] = ntag4xx_poller_handler_read_failed, + [Ntag4xxPollerStateReadSuccess] = ntag4xx_poller_handler_read_success, +}; + +static void ntag4xx_poller_set_callback( + Ntag4xxPoller* instance, + NfcGenericCallback callback, + void* context) { + furi_assert(instance); + furi_assert(callback); + + instance->callback = callback; + instance->context = context; +} + +static NfcCommand ntag4xx_poller_run(NfcGenericEvent event, void* context) { + furi_assert(event.protocol == NfcProtocolIso14443_4a); + + Ntag4xxPoller* 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 = ntag4xx_poller_read_handler[instance->state](instance); + } else if(iso14443_4a_event->type == Iso14443_4aPollerEventTypeError) { + instance->ntag4xx_event.type = Ntag4xxPollerEventTypeReadFailed; + command = instance->callback(instance->general_event, instance->context); + } + + return command; +} + +static bool ntag4xx_poller_detect(NfcGenericEvent event, void* context) { + furi_assert(event.protocol == NfcProtocolIso14443_4a); + + Ntag4xxPoller* 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) { + do { + Ntag4xxError error = ntag4xx_poller_read_version(instance, &instance->data->version); + if(error != Ntag4xxErrorNone) break; + + protocol_detected = true; + } while(false); + } + + return protocol_detected; +} + +const NfcPollerBase ntag4xx_poller = { + .alloc = (NfcPollerAlloc)ntag4xx_poller_alloc, + .free = (NfcPollerFree)ntag4xx_poller_free, + .set_callback = (NfcPollerSetCallback)ntag4xx_poller_set_callback, + .run = (NfcPollerRun)ntag4xx_poller_run, + .detect = (NfcPollerDetect)ntag4xx_poller_detect, + .get_data = (NfcPollerGetData)ntag4xx_poller_get_data, +}; diff --git a/lib/nfc/protocols/ntag4xx/ntag4xx_poller.h b/lib/nfc/protocols/ntag4xx/ntag4xx_poller.h new file mode 100644 index 000000000..ce7adc785 --- /dev/null +++ b/lib/nfc/protocols/ntag4xx/ntag4xx_poller.h @@ -0,0 +1,43 @@ +#pragma once + +#include "ntag4xx.h" + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Ntag4xxPoller opaque type definition. + */ +typedef struct Ntag4xxPoller Ntag4xxPoller; + +/** + * @brief Enumeration of possible Ntag4xx poller event types. + */ +typedef enum { + Ntag4xxPollerEventTypeReadSuccess, /**< Card was read successfully. */ + Ntag4xxPollerEventTypeReadFailed, /**< Poller failed to read card. */ +} Ntag4xxPollerEventType; + +/** + * @brief Ntag4xx poller event data. + */ +typedef union { + Ntag4xxError error; /**< Error code indicating card reading fail reason. */ +} Ntag4xxPollerEventData; + +/** + * @brief Ntag4xx poller event structure. + * + * Upon emission of an event, an instance of this struct will be passed to the callback. + */ +typedef struct { + Ntag4xxPollerEventType type; /**< Type of emmitted event. */ + Ntag4xxPollerEventData* data; /**< Pointer to event specific data. */ +} Ntag4xxPollerEvent; + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/ntag4xx/ntag4xx_poller_defs.h b/lib/nfc/protocols/ntag4xx/ntag4xx_poller_defs.h new file mode 100644 index 000000000..ac0cdce9b --- /dev/null +++ b/lib/nfc/protocols/ntag4xx/ntag4xx_poller_defs.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +extern const NfcPollerBase ntag4xx_poller; diff --git a/lib/nfc/protocols/ntag4xx/ntag4xx_poller_i.c b/lib/nfc/protocols/ntag4xx/ntag4xx_poller_i.c new file mode 100644 index 000000000..5b8b7191d --- /dev/null +++ b/lib/nfc/protocols/ntag4xx/ntag4xx_poller_i.c @@ -0,0 +1,52 @@ +#include "ntag4xx_poller_i.h" + +#include + +#include "ntag4xx_i.h" + +#define TAG "Ntag4xxPoller" + +Ntag4xxError ntag4xx_poller_send_chunks( + Ntag4xxPoller* instance, + const BitBuffer* tx_buffer, + BitBuffer* rx_buffer) { + furi_check(instance); + + NxpNativeCommandStatus status_code = NXP_NATIVE_COMMAND_STATUS_OPERATION_OK; + Iso14443_4aError iso14443_4a_error = nxp_native_command_iso14443_4a_poller( + instance->iso14443_4a_poller, + &status_code, + tx_buffer, + rx_buffer, + NxpNativeCommandModeIsoWrapped, + instance->tx_buffer, + instance->rx_buffer); + + if(iso14443_4a_error != Iso14443_4aErrorNone) { + return ntag4xx_process_error(iso14443_4a_error); + } + + return ntag4xx_process_status_code(status_code); +} + +Ntag4xxError ntag4xx_poller_read_version(Ntag4xxPoller* instance, Ntag4xxVersion* data) { + furi_check(instance); + + bit_buffer_reset(instance->input_buffer); + bit_buffer_append_byte(instance->input_buffer, NTAG4XX_CMD_GET_VERSION); + + Ntag4xxError error; + + do { + error = + ntag4xx_poller_send_chunks(instance, instance->input_buffer, instance->result_buffer); + + if(error != Ntag4xxErrorNone) break; + + if(!ntag4xx_version_parse(data, instance->result_buffer)) { + error = Ntag4xxErrorProtocol; + } + } while(false); + + return error; +} diff --git a/lib/nfc/protocols/ntag4xx/ntag4xx_poller_i.h b/lib/nfc/protocols/ntag4xx/ntag4xx_poller_i.h new file mode 100644 index 000000000..b77e694ef --- /dev/null +++ b/lib/nfc/protocols/ntag4xx/ntag4xx_poller_i.h @@ -0,0 +1,40 @@ +#pragma once + +#include "ntag4xx_poller.h" + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + Ntag4xxPollerStateIdle, + Ntag4xxPollerStateReadVersion, + Ntag4xxPollerStateReadFailed, + Ntag4xxPollerStateReadSuccess, + + Ntag4xxPollerStateNum, +} Ntag4xxPollerState; + +struct Ntag4xxPoller { + Iso14443_4aPoller* iso14443_4a_poller; + Ntag4xxPollerState state; + Ntag4xxError error; + Ntag4xxData* data; + BitBuffer* tx_buffer; + BitBuffer* rx_buffer; + BitBuffer* input_buffer; + BitBuffer* result_buffer; + Ntag4xxPollerEventData ntag4xx_event_data; + Ntag4xxPollerEvent ntag4xx_event; + NfcGenericEvent general_event; + NfcGenericCallback callback; + void* context; +}; + +Ntag4xxError ntag4xx_poller_read_version(Ntag4xxPoller* instance, Ntag4xxVersion* data); + +#ifdef __cplusplus +} +#endif diff --git a/targets/f18/api_symbols.csv b/targets/f18/api_symbols.csv index ae056b56c..420daf3fd 100644 --- a/targets/f18/api_symbols.csv +++ b/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,82.2,, +Version,+,82.3,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/bt/bt_service/bt_keys_storage.h,, Header,+,applications/services/cli/cli.h,, diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index 2696643d8..937fe2780 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,82.2,, +Version,+,82.3,, Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/bt/bt_service/bt_keys_storage.h,, @@ -167,6 +167,8 @@ Header,+,lib/nfc/protocols/mf_ultralight/mf_ultralight.h,, Header,+,lib/nfc/protocols/mf_ultralight/mf_ultralight_listener.h,, Header,+,lib/nfc/protocols/mf_ultralight/mf_ultralight_poller.h,, Header,+,lib/nfc/protocols/mf_ultralight/mf_ultralight_poller_sync.h,, +Header,+,lib/nfc/protocols/ntag4xx/ntag4xx.h,, +Header,+,lib/nfc/protocols/ntag4xx/ntag4xx_poller.h,, Header,+,lib/nfc/protocols/slix/slix.h,, Header,+,lib/nfc/protocols/slix/slix_listener.h,, Header,+,lib/nfc/protocols/slix/slix_poller.h,, @@ -2895,6 +2897,19 @@ Function,+,notification_internal_message_block,void,"NotificationApp*, const Not Function,+,notification_message,void,"NotificationApp*, const NotificationSequence*" Function,+,notification_message_block,void,"NotificationApp*, const NotificationSequence*" Function,-,nrand48,long,unsigned short[3] +Function,+,ntag4xx_alloc,Ntag4xxData*, +Function,+,ntag4xx_copy,void,"Ntag4xxData*, const Ntag4xxData*" +Function,+,ntag4xx_free,void,Ntag4xxData* +Function,+,ntag4xx_get_base_data,Iso14443_4aData*,const Ntag4xxData* +Function,+,ntag4xx_get_device_name,const char*,"const Ntag4xxData*, NfcDeviceNameType" +Function,+,ntag4xx_get_type_from_version,Ntag4xxType,const Ntag4xxVersion* +Function,+,ntag4xx_get_uid,const uint8_t*,"const Ntag4xxData*, size_t*" +Function,+,ntag4xx_is_equal,_Bool,"const Ntag4xxData*, const Ntag4xxData*" +Function,+,ntag4xx_load,_Bool,"Ntag4xxData*, FlipperFormat*, uint32_t" +Function,+,ntag4xx_reset,void,Ntag4xxData* +Function,+,ntag4xx_save,_Bool,"const Ntag4xxData*, FlipperFormat*" +Function,+,ntag4xx_set_uid,_Bool,"Ntag4xxData*, const uint8_t*, size_t" +Function,+,ntag4xx_verify,_Bool,"Ntag4xxData*, const FuriString*" Function,+,number_input_alloc,NumberInput*, Function,+,number_input_free,void,NumberInput* Function,+,number_input_get_view,View*,NumberInput* @@ -4022,6 +4037,7 @@ Variable,-,nfc_device_mf_classic,const NfcDeviceBase, Variable,-,nfc_device_mf_desfire,const NfcDeviceBase, Variable,-,nfc_device_mf_plus,const NfcDeviceBase, Variable,-,nfc_device_mf_ultralight,const NfcDeviceBase, +Variable,-,nfc_device_ntag4xx,const NfcDeviceBase, Variable,-,nfc_device_st25tb,const NfcDeviceBase, Variable,+,sequence_audiovisual_alert,const NotificationSequence, Variable,+,sequence_blink_blue_10,const NotificationSequence, From d362cb51e39a69b54967fd5b6d471c9b4007039d Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 27 Mar 2025 04:51:03 +0000 Subject: [PATCH 64/92] NFC: Allow choosing DESFire poller cmd mode, fix send_chunks() naming --- .../protocols/mf_desfire/mf_desfire_poller.h | 18 +++++++- .../mf_desfire/mf_desfire_poller_i.c | 42 +++++++++++++------ .../mf_desfire/mf_desfire_poller_i.h | 1 + targets/f18/api_symbols.csv | 2 +- targets/f7/api_symbols.csv | 5 ++- 5 files changed, 52 insertions(+), 16 deletions(-) diff --git a/lib/nfc/protocols/mf_desfire/mf_desfire_poller.h b/lib/nfc/protocols/mf_desfire/mf_desfire_poller.h index 707df42cd..8ac4627bb 100644 --- a/lib/nfc/protocols/mf_desfire/mf_desfire_poller.h +++ b/lib/nfc/protocols/mf_desfire/mf_desfire_poller.h @@ -3,6 +3,7 @@ #include "mf_desfire.h" #include +#include #ifdef __cplusplus extern "C" { @@ -38,6 +39,16 @@ typedef struct { MfDesfirePollerEventData* data; /**< Pointer to event specific data. */ } MfDesfirePollerEvent; +/** + * @brief Change command mode used in poller mode. + * + * @param[in, out] instance pointer to the instance to affect. + * @param[in] command_mode command mode to use in further communication with the card. + */ +void mf_desfire_poller_set_command_mode( + MfDesfirePoller* instance, + NxpNativeCommandMode command_mode); + /** * @brief Transmit and receive MfDesfire chunks in poller mode. * @@ -51,11 +62,16 @@ typedef struct { * @param[out] rx_buffer pointer to the buffer to be filled with received data. * @return MfDesfireErrorNone on success, an error code on failure. */ -MfDesfireError mf_desfire_send_chunks( +MfDesfireError mf_desfire_poller_send_chunks( MfDesfirePoller* instance, const BitBuffer* tx_buffer, BitBuffer* rx_buffer); +/** + * @warning deprecated, use mf_desfire_poller_send_chunks instead + */ +#define mf_desfire_send_chunks mf_desfire_poller_send_chunks + /** * @brief Read MfDesfire card version. * diff --git a/lib/nfc/protocols/mf_desfire/mf_desfire_poller_i.c b/lib/nfc/protocols/mf_desfire/mf_desfire_poller_i.c index f54133e3c..ebb1d91d0 100644 --- a/lib/nfc/protocols/mf_desfire/mf_desfire_poller_i.c +++ b/lib/nfc/protocols/mf_desfire/mf_desfire_poller_i.c @@ -32,7 +32,17 @@ MfDesfireError mf_desfire_process_status_code(uint8_t status_code) { } } -MfDesfireError mf_desfire_send_chunks( +void mf_desfire_poller_set_command_mode( + MfDesfirePoller* instance, + NxpNativeCommandMode command_mode) { + furi_check(instance); + furi_check(instance->state == MfDesfirePollerStateIdle); + furi_check(command_mode < NxpNativeCommandModeMAX); + + instance->command_mode = command_mode; +} + +MfDesfireError mf_desfire_poller_send_chunks( MfDesfirePoller* instance, const BitBuffer* tx_buffer, BitBuffer* rx_buffer) { @@ -44,7 +54,7 @@ MfDesfireError mf_desfire_send_chunks( &status_code, tx_buffer, rx_buffer, - NxpNativeCommandModePlain, + instance->command_mode, instance->tx_buffer, instance->rx_buffer); @@ -64,7 +74,8 @@ MfDesfireError mf_desfire_poller_read_version(MfDesfirePoller* instance, MfDesfi MfDesfireError error; do { - error = mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer); + error = mf_desfire_poller_send_chunks( + instance, instance->input_buffer, instance->result_buffer); if(error != MfDesfireErrorNone) break; @@ -86,7 +97,8 @@ MfDesfireError MfDesfireError error; do { - error = mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer); + error = mf_desfire_poller_send_chunks( + instance, instance->input_buffer, instance->result_buffer); if(error != MfDesfireErrorNone) break; @@ -108,7 +120,8 @@ MfDesfireError MfDesfireError error; do { - error = mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer); + error = mf_desfire_poller_send_chunks( + instance, instance->input_buffer, instance->result_buffer); if(error != MfDesfireErrorNone) break; @@ -132,7 +145,7 @@ MfDesfireError mf_desfire_poller_read_key_version( bit_buffer_set_byte(instance->input_buffer, 1, key_num); MfDesfireError error = - mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer); + mf_desfire_poller_send_chunks(instance, instance->input_buffer, instance->result_buffer); if(error == MfDesfireErrorNone) { if(!mf_desfire_key_version_parse(data, instance->result_buffer)) { error = MfDesfireErrorProtocol; @@ -172,7 +185,8 @@ MfDesfireError MfDesfireError error; do { - error = mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer); + error = mf_desfire_poller_send_chunks( + instance, instance->input_buffer, instance->result_buffer); if(error != MfDesfireErrorNone) break; @@ -205,7 +219,7 @@ MfDesfireError mf_desfire_poller_select_application( instance->input_buffer, (const uint8_t*)id, sizeof(MfDesfireApplicationId)); MfDesfireError error = - mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer); + mf_desfire_poller_send_chunks(instance, instance->input_buffer, instance->result_buffer); return error; } @@ -220,7 +234,8 @@ MfDesfireError mf_desfire_poller_read_file_ids(MfDesfirePoller* instance, Simple MfDesfireError error; do { - error = mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer); + error = mf_desfire_poller_send_chunks( + instance, instance->input_buffer, instance->result_buffer); if(error != MfDesfireErrorNone) break; @@ -255,7 +270,8 @@ MfDesfireError mf_desfire_poller_read_file_settings( MfDesfireError error; do { - error = mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer); + error = mf_desfire_poller_send_chunks( + instance, instance->input_buffer, instance->result_buffer); if(error != MfDesfireErrorNone) break; @@ -316,7 +332,8 @@ static MfDesfireError mf_desfire_poller_read_file( bit_buffer_append_bytes(instance->input_buffer, (const uint8_t*)¤t_offset, 3); bit_buffer_append_bytes(instance->input_buffer, (const uint8_t*)&bytes_to_read, 3); - error = mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer); + error = mf_desfire_poller_send_chunks( + instance, instance->input_buffer, instance->result_buffer); if(error != MfDesfireErrorNone) break; size_t bytes_received = bit_buffer_get_size_bytes(instance->result_buffer); @@ -362,7 +379,8 @@ MfDesfireError mf_desfire_poller_read_file_value( MfDesfireError error; do { - error = mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer); + error = mf_desfire_poller_send_chunks( + instance, instance->input_buffer, instance->result_buffer); if(error != MfDesfireErrorNone) break; diff --git a/lib/nfc/protocols/mf_desfire/mf_desfire_poller_i.h b/lib/nfc/protocols/mf_desfire/mf_desfire_poller_i.h index 19e38bebb..179fd44b5 100644 --- a/lib/nfc/protocols/mf_desfire/mf_desfire_poller_i.h +++ b/lib/nfc/protocols/mf_desfire/mf_desfire_poller_i.h @@ -30,6 +30,7 @@ typedef enum { struct MfDesfirePoller { Iso14443_4aPoller* iso14443_4a_poller; + NxpNativeCommandMode command_mode; MfDesfirePollerSessionState session_state; MfDesfirePollerState state; MfDesfireError error; diff --git a/targets/f18/api_symbols.csv b/targets/f18/api_symbols.csv index 420daf3fd..d21b87668 100644 --- a/targets/f18/api_symbols.csv +++ b/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,82.3,, +Version,+,83.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/bt/bt_service/bt_keys_storage.h,, Header,+,applications/services/cli/cli.h,, diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index 937fe2780..c48d4dd11 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,82.3,, +Version,+,83.0,, Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/bt/bt_service/bt_keys_storage.h,, @@ -2647,9 +2647,10 @@ Function,+,mf_desfire_poller_read_key_version,MfDesfireError,"MfDesfirePoller*, Function,+,mf_desfire_poller_read_key_versions,MfDesfireError,"MfDesfirePoller*, SimpleArray*, uint32_t" Function,+,mf_desfire_poller_read_version,MfDesfireError,"MfDesfirePoller*, MfDesfireVersion*" Function,+,mf_desfire_poller_select_application,MfDesfireError,"MfDesfirePoller*, const MfDesfireApplicationId*" +Function,+,mf_desfire_poller_send_chunks,MfDesfireError,"MfDesfirePoller*, const BitBuffer*, BitBuffer*" +Function,+,mf_desfire_poller_set_command_mode,void,"MfDesfirePoller*, NxpNativeCommandMode" Function,+,mf_desfire_reset,void,MfDesfireData* Function,+,mf_desfire_save,_Bool,"const MfDesfireData*, FlipperFormat*" -Function,+,mf_desfire_send_chunks,MfDesfireError,"MfDesfirePoller*, const BitBuffer*, BitBuffer*" Function,+,mf_desfire_set_uid,_Bool,"MfDesfireData*, const uint8_t*, size_t" Function,+,mf_desfire_verify,_Bool,"MfDesfireData*, const FuriString*" Function,+,mf_plus_alloc,MfPlusData*, From bd6e5f8240c902a0bce53c6cbf254fc7ecdc0c16 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 27 Mar 2025 04:53:40 +0000 Subject: [PATCH 65/92] NFC: Check MIFARE types with HWType lower nibble See: https://www.nxp.com/docs/en/application-note/AN10833.pdf --- lib/nfc/protocols/mf_desfire/mf_desfire_i.c | 2 +- lib/nfc/protocols/mf_plus/mf_plus_i.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/nfc/protocols/mf_desfire/mf_desfire_i.c b/lib/nfc/protocols/mf_desfire/mf_desfire_i.c index d83a91ad1..ba4311527 100644 --- a/lib/nfc/protocols/mf_desfire/mf_desfire_i.c +++ b/lib/nfc/protocols/mf_desfire/mf_desfire_i.c @@ -54,7 +54,7 @@ bool mf_desfire_version_parse(MfDesfireVersion* data, const BitBuffer* buf) { bit_buffer_write_bytes(buf, data, sizeof(MfDesfireVersion)); } - return can_parse; + return can_parse && (data->hw_type & 0x0F) == 0x01; } bool mf_desfire_free_memory_parse(MfDesfireFreeMemory* data, const BitBuffer* buf) { diff --git a/lib/nfc/protocols/mf_plus/mf_plus_i.c b/lib/nfc/protocols/mf_plus/mf_plus_i.c index bd32956d6..8f06a644a 100644 --- a/lib/nfc/protocols/mf_plus/mf_plus_i.c +++ b/lib/nfc/protocols/mf_plus/mf_plus_i.c @@ -27,7 +27,7 @@ MfPlusError mf_plus_get_type_from_version( MfPlusError error = MfPlusErrorProtocol; - if(mf_plus_data->version.hw_type == 0x02 || mf_plus_data->version.hw_type == 0x82) { + if((mf_plus_data->version.hw_type & 0x0F) == 0x02) { error = MfPlusErrorNone; // Mifare Plus EV1/EV2 From 9b3d97087c34bd043d83e2ab2aceaa1ba4b5b705 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 27 Mar 2025 04:55:28 +0000 Subject: [PATCH 66/92] NFC: Refactor MIFARE Plus with NxpNativeCommand helper --- lib/nfc/protocols/mf_plus/mf_plus_i.c | 16 ++------- lib/nfc/protocols/mf_plus/mf_plus_i.h | 5 ++- lib/nfc/protocols/mf_plus/mf_plus_poller_i.c | 38 ++++++++++++-------- 3 files changed, 28 insertions(+), 31 deletions(-) diff --git a/lib/nfc/protocols/mf_plus/mf_plus_i.c b/lib/nfc/protocols/mf_plus/mf_plus_i.c index 8f06a644a..42f16079c 100644 --- a/lib/nfc/protocols/mf_plus/mf_plus_i.c +++ b/lib/nfc/protocols/mf_plus/mf_plus_i.c @@ -238,22 +238,12 @@ MfPlusError } MfPlusError mf_plus_version_parse(MfPlusVersion* data, const BitBuffer* buf) { - bool can_parse = bit_buffer_get_size_bytes(buf) == sizeof(MfPlusVersion); + const bool can_parse = bit_buffer_get_size_bytes(buf) == sizeof(MfPlusVersion); if(can_parse) { bit_buffer_write_bytes(buf, data, sizeof(MfPlusVersion)); - } else if( - bit_buffer_get_size_bytes(buf) == 8 && - bit_buffer_get_byte(buf, 0) == MF_PLUS_STATUS_ADDITIONAL_FRAME) { - // HACK(-nofl): There are supposed to be three parts to the GetVersion command, - // with the second and third parts fetched by sending the AdditionalFrame - // command. I don't know whether the entire MIFARE Plus line uses status as - // the first byte, so let's just assume we only have the first part of - // the response if it's size 8 and starts with the AF status. The second - // part of the response is the same size and status byte, but so far - // we're only reading one response. - can_parse = true; - bit_buffer_write_bytes_mid(buf, data, 1, bit_buffer_get_size_bytes(buf) - 1); + } else { + memset(data, 0, sizeof(MfPlusVersion)); } return can_parse ? MfPlusErrorNone : MfPlusErrorProtocol; diff --git a/lib/nfc/protocols/mf_plus/mf_plus_i.h b/lib/nfc/protocols/mf_plus/mf_plus_i.h index 302f5a178..cadc435b9 100644 --- a/lib/nfc/protocols/mf_plus/mf_plus_i.h +++ b/lib/nfc/protocols/mf_plus/mf_plus_i.h @@ -2,10 +2,9 @@ #include "mf_plus.h" -#define MF_PLUS_FFF_PICC_PREFIX "PICC" +#include -#define MF_PLUS_STATUS_OPERATION_OK (0x90) -#define MF_PLUS_STATUS_ADDITIONAL_FRAME (0xAF) +#define MF_PLUS_FFF_PICC_PREFIX "PICC" MfPlusError mf_plus_get_type_from_version( const Iso14443_4aData* iso14443_4a_data, diff --git a/lib/nfc/protocols/mf_plus/mf_plus_poller_i.c b/lib/nfc/protocols/mf_plus/mf_plus_poller_i.c index cab906f1d..b2e4231ff 100644 --- a/lib/nfc/protocols/mf_plus/mf_plus_poller_i.c +++ b/lib/nfc/protocols/mf_plus/mf_plus_poller_i.c @@ -19,28 +19,36 @@ MfPlusError mf_plus_process_error(Iso14443_4aError error) { } } -MfPlusError mf_plus_poller_send_chunk( +MfPlusError mf_plus_process_status_code(uint8_t status_code) { + switch(status_code) { + case NXP_NATIVE_COMMAND_STATUS_OPERATION_OK: + return MfPlusErrorNone; + default: + return MfPlusErrorProtocol; + } +} + +MfPlusError mf_plus_poller_send_chunks( MfPlusPoller* 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); - Iso14443_4aError iso14443_4a_error = iso14443_4a_poller_send_block( - instance->iso14443_4a_poller, tx_buffer, instance->rx_buffer); - MfPlusError error = mf_plus_process_error(iso14443_4a_error); + NxpNativeCommandStatus status_code = NXP_NATIVE_COMMAND_STATUS_OPERATION_OK; + Iso14443_4aError iso14443_4a_error = nxp_native_command_iso14443_4a_poller( + instance->iso14443_4a_poller, + &status_code, + tx_buffer, + rx_buffer, + NxpNativeCommandModePlain, + instance->tx_buffer, + instance->rx_buffer); - if(error == MfPlusErrorNone) { - bit_buffer_copy(rx_buffer, instance->rx_buffer); + if(iso14443_4a_error != Iso14443_4aErrorNone) { + return mf_plus_process_error(iso14443_4a_error); } - bit_buffer_reset(instance->tx_buffer); - - return error; + return mf_plus_process_status_code(status_code); } MfPlusError mf_plus_poller_read_version(MfPlusPoller* instance, MfPlusVersion* data) { @@ -50,7 +58,7 @@ MfPlusError mf_plus_poller_read_version(MfPlusPoller* instance, MfPlusVersion* d bit_buffer_append_byte(instance->input_buffer, MF_PLUS_CMD_GET_VERSION); MfPlusError error = - mf_plus_poller_send_chunk(instance, instance->input_buffer, instance->result_buffer); + mf_plus_poller_send_chunks(instance, instance->input_buffer, instance->result_buffer); if(error == MfPlusErrorNone) { error = mf_plus_version_parse(data, instance->result_buffer); } From 4717966af80fc5e190076e1f4e45add7baf8a003 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 27 Mar 2025 04:57:24 +0000 Subject: [PATCH 67/92] NFC: Fix detecting MIFARE Plus SE 1K SL1/3 See: https://www.nxp.com/docs/en/application-note/AN10833.pdf --- lib/nfc/protocols/mf_plus/mf_plus_i.c | 64 +++++++++++---------------- 1 file changed, 26 insertions(+), 38 deletions(-) diff --git a/lib/nfc/protocols/mf_plus/mf_plus_i.c b/lib/nfc/protocols/mf_plus/mf_plus_i.c index 42f16079c..b66cf5ea2 100644 --- a/lib/nfc/protocols/mf_plus/mf_plus_i.c +++ b/lib/nfc/protocols/mf_plus/mf_plus_i.c @@ -15,8 +15,8 @@ const uint8_t mf_plus_ats_t1_tk_values[][MF_PLUS_T1_TK_VALUE_LEN] = { {0xC1, 0x05, 0x2F, 0x2F, 0x00, 0x35, 0xC7}, // Mifare Plus S {0xC1, 0x05, 0x2F, 0x2F, 0x01, 0xBC, 0xD6}, // Mifare Plus X - {0xC1, 0x05, 0x2F, 0x2F, 0x00, 0xF6, 0xD1}, // Mifare Plus SE - {0xC1, 0x05, 0x2F, 0x2F, 0x01, 0xF6, 0xD1}, // Mifare Plus SE + {0xC1, 0x05, 0x21, 0x30, 0x00, 0xF6, 0xD1}, // Mifare Plus SE + {0xC1, 0x05, 0x21, 0x30, 0x10, 0xF6, 0xD1}, // Mifare Plus SE }; MfPlusError mf_plus_get_type_from_version( @@ -85,16 +85,15 @@ MfPlusError MfPlusError error = MfPlusErrorProtocol; - if(simple_array_get_count(iso4_data->ats_data.t1_tk) != MF_PLUS_T1_TK_VALUE_LEN) { + const size_t historical_bytes_len = simple_array_get_count(iso4_data->ats_data.t1_tk); + if(historical_bytes_len != MF_PLUS_T1_TK_VALUE_LEN) { return MfPlusErrorProtocol; } + const uint8_t* historical_bytes = simple_array_cget_data(iso4_data->ats_data.t1_tk); switch(iso4_data->iso14443_3a_data->sak) { case 0x08: - if(memcmp( - simple_array_get_data(iso4_data->ats_data.t1_tk), - mf_plus_ats_t1_tk_values[0], - simple_array_get_count(iso4_data->ats_data.t1_tk)) == 0) { + if(memcmp(historical_bytes, mf_plus_ats_t1_tk_values[0], historical_bytes_len) == 0) { // Mifare Plus S 2K SL1 mf_plus_data->type = MfPlusTypeS; mf_plus_data->size = MfPlusSize2K; @@ -102,11 +101,7 @@ MfPlusError FURI_LOG_D(TAG, "Mifare Plus S 2K SL1"); error = MfPlusErrorNone; - } else if( - memcmp( - simple_array_get_data(iso4_data->ats_data.t1_tk), - mf_plus_ats_t1_tk_values[1], - simple_array_get_count(iso4_data->ats_data.t1_tk)) == 0) { + } else if(memcmp(historical_bytes, mf_plus_ats_t1_tk_values[1], historical_bytes_len) == 0) { // Mifare Plus X 2K SL1 mf_plus_data->type = MfPlusTypeX; mf_plus_data->size = MfPlusSize2K; @@ -115,14 +110,8 @@ MfPlusError FURI_LOG_D(TAG, "Mifare Plus X 2K SL1"); error = MfPlusErrorNone; } else if( - memcmp( - simple_array_get_data(iso4_data->ats_data.t1_tk), - mf_plus_ats_t1_tk_values[2], - simple_array_get_count(iso4_data->ats_data.t1_tk)) == 0 || - memcmp( - simple_array_get_data(iso4_data->ats_data.t1_tk), - mf_plus_ats_t1_tk_values[3], - simple_array_get_count(iso4_data->ats_data.t1_tk)) == 0) { + memcmp(historical_bytes, mf_plus_ats_t1_tk_values[2], historical_bytes_len) == 0 || + memcmp(historical_bytes, mf_plus_ats_t1_tk_values[3], historical_bytes_len) == 0) { // Mifare Plus SE 1K SL1 mf_plus_data->type = MfPlusTypeSE; mf_plus_data->size = MfPlusSize1K; @@ -154,10 +143,7 @@ MfPlusError break; case 0x18: - if(memcmp( - simple_array_get_data(iso4_data->ats_data.t1_tk), - mf_plus_ats_t1_tk_values[0], - simple_array_get_count(iso4_data->ats_data.t1_tk)) == 0) { + if(memcmp(historical_bytes, mf_plus_ats_t1_tk_values[0], historical_bytes_len) == 0) { // Mifare Plus S 4K SL1 mf_plus_data->type = MfPlusTypeS; mf_plus_data->size = MfPlusSize4K; @@ -165,11 +151,7 @@ MfPlusError FURI_LOG_D(TAG, "Mifare Plus S 4K SL1"); error = MfPlusErrorNone; - } else if( - memcmp( - simple_array_get_data(iso4_data->ats_data.t1_tk), - mf_plus_ats_t1_tk_values[1], - simple_array_get_count(iso4_data->ats_data.t1_tk)) == 0) { + } else if(memcmp(historical_bytes, mf_plus_ats_t1_tk_values[1], historical_bytes_len) == 0) { // Mifare Plus X 4K SL1 mf_plus_data->type = MfPlusTypeX; mf_plus_data->size = MfPlusSize4K; @@ -183,10 +165,7 @@ MfPlusError break; case 0x20: - if(memcmp( - simple_array_get_data(iso4_data->ats_data.t1_tk), - mf_plus_ats_t1_tk_values[0], - simple_array_get_count(iso4_data->ats_data.t1_tk)) == 0) { + if(memcmp(historical_bytes, mf_plus_ats_t1_tk_values[0], historical_bytes_len) == 0) { // Mifare Plus S 2/4K SL3 FURI_LOG_D(TAG, "Mifare Plus S SL3"); mf_plus_data->type = MfPlusTypeS; @@ -207,21 +186,20 @@ MfPlusError } else { FURI_LOG_D(TAG, "Sak 20 but no known Mifare Plus type (S)"); } - } else if( - memcmp( - simple_array_get_data(iso4_data->ats_data.t1_tk), - mf_plus_ats_t1_tk_values[1], - simple_array_get_count(iso4_data->ats_data.t1_tk)) == 0) { + } else if(memcmp(historical_bytes, mf_plus_ats_t1_tk_values[1], historical_bytes_len) == 0) { + // Mifare Plus X 2/4K SL3 mf_plus_data->type = MfPlusTypeX; mf_plus_data->security_level = MfPlusSecurityLevel3; FURI_LOG_D(TAG, "Mifare Plus X SL3"); if((iso4_data->iso14443_3a_data->atqa[0] & 0x0F) == 0x04) { + // Mifare Plus X 2K SL3 mf_plus_data->size = MfPlusSize2K; FURI_LOG_D(TAG, "Mifare Plus X 2K SL3"); error = MfPlusErrorNone; } else if((iso4_data->iso14443_3a_data->atqa[0] & 0x0F) == 0x02) { + // Mifare Plus X 4K SL3 mf_plus_data->size = MfPlusSize4K; FURI_LOG_D(TAG, "Mifare Plus X 4K SL3"); @@ -229,6 +207,16 @@ MfPlusError } else { FURI_LOG_D(TAG, "Sak 20 but no known Mifare Plus type (X)"); } + } else if( + memcmp(historical_bytes, mf_plus_ats_t1_tk_values[2], historical_bytes_len) == 0 || + memcmp(historical_bytes, mf_plus_ats_t1_tk_values[3], historical_bytes_len) == 0) { + // Mifare Plus SE 1K SL3 + mf_plus_data->type = MfPlusTypeSE; + mf_plus_data->size = MfPlusSize1K; + mf_plus_data->security_level = MfPlusSecurityLevel3; + + FURI_LOG_D(TAG, "Mifare Plus SE 1K SL3"); + error = MfPlusErrorNone; } else { FURI_LOG_D(TAG, "Sak 20 but no known Mifare Plus type"); } From ec97852939494c631d31fb930a56ce05373c1de5 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 27 Mar 2025 04:58:23 +0000 Subject: [PATCH 68/92] NFC: Show MIFARE Plus EV1/2 GetVersion info like DESFire --- .../helpers/protocol_support/mf_plus/mf_plus.c | 18 ++++++++++++++++-- .../protocol_support/mf_plus/mf_plus_render.c | 16 +++++++++++++++- 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/applications/main/nfc/helpers/protocol_support/mf_plus/mf_plus.c b/applications/main/nfc/helpers/protocol_support/mf_plus/mf_plus.c index c7b36e21e..e6746e090 100644 --- a/applications/main/nfc/helpers/protocol_support/mf_plus/mf_plus.c +++ b/applications/main/nfc/helpers/protocol_support/mf_plus/mf_plus.c @@ -25,6 +25,20 @@ static void nfc_scene_info_on_enter_mf_plus(NfcApp* instance) { furi_string_free(temp_str); } + +static void nfc_scene_more_info_on_enter_mf_plus(NfcApp* instance) { + const NfcDevice* device = instance->nfc_device; + const MfPlusData* data = nfc_device_get_data(device, NfcProtocolMfPlus); + + furi_string_reset(instance->text_box_store); + nfc_render_mf_plus_data(data, instance->text_box_store); + + text_box_set_font(instance->text_box, TextBoxFontHex); + text_box_set_text(instance->text_box, furi_string_get_cstr(instance->text_box_store)); + + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewTextBox); +} + static NfcCommand nfc_scene_read_poller_callback_mf_plus(NfcGenericEvent event, void* context) { furi_assert(context); furi_assert(event.protocol == NfcProtocolMfPlus); @@ -78,7 +92,7 @@ static void nfc_scene_emulate_on_enter_mf_plus(NfcApp* instance) { } const NfcProtocolSupportBase nfc_protocol_support_mf_plus = { - .features = NfcProtocolFeatureEmulateUid, + .features = NfcProtocolFeatureEmulateUid | NfcProtocolFeatureMoreInfo, .scene_info = { @@ -87,7 +101,7 @@ const NfcProtocolSupportBase nfc_protocol_support_mf_plus = { }, .scene_more_info = { - .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_enter = nfc_scene_more_info_on_enter_mf_plus, .on_event = nfc_protocol_support_common_on_event_empty, }, .scene_read = diff --git a/applications/main/nfc/helpers/protocol_support/mf_plus/mf_plus_render.c b/applications/main/nfc/helpers/protocol_support/mf_plus/mf_plus_render.c index 8640fa16d..2311004ad 100644 --- a/applications/main/nfc/helpers/protocol_support/mf_plus/mf_plus_render.c +++ b/applications/main/nfc/helpers/protocol_support/mf_plus/mf_plus_render.c @@ -15,7 +15,21 @@ void nfc_render_mf_plus_info( } void nfc_render_mf_plus_data(const MfPlusData* data, FuriString* str) { - nfc_render_mf_plus_version(&data->version, str); + MfPlusVersion empty_version = {0}; + if(memcmp(&data->version, &empty_version, sizeof(MfPlusVersion)) == 0) { + const char* device_name = mf_plus_get_device_name(data, NfcDeviceNameTypeFull); + if(data->type == MfPlusTypeUnknown || data->size == MfPlusSizeUnknown || + data->security_level == MfPlusSecurityLevelUnknown) { + furi_string_cat_printf(str, "This %s", device_name); + furi_string_replace(str, " Unknown", ""); + } else { + furi_string_cat(str, device_name); + } + furi_string_replace(str, "Mifare", "MIFARE"); + furi_string_cat(str, " does not support the GetVersion command, extra info unavailable\n"); + } else { + nfc_render_mf_plus_version(&data->version, str); + } } void nfc_render_mf_plus_version(const MfPlusVersion* data, FuriString* str) { From f85b5c222fccb37fd3c908cc64f9aec25421191c Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 27 Mar 2025 05:51:36 +0000 Subject: [PATCH 69/92] Revert "BitBuffer: Allow copy right/left on same instance" This reverts commit cfeddbf8b6320a21749238d648eff7e69450d850. --- lib/toolbox/bit_buffer.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/toolbox/bit_buffer.c b/lib/toolbox/bit_buffer.c index 2d8057c3d..e261e80d4 100644 --- a/lib/toolbox/bit_buffer.c +++ b/lib/toolbox/bit_buffer.c @@ -60,7 +60,7 @@ void bit_buffer_copy_right(BitBuffer* buf, const BitBuffer* other, size_t start_ furi_check(bit_buffer_get_size_bytes(other) > start_index); furi_check(buf->capacity_bytes >= bit_buffer_get_size_bytes(other) - start_index); - memmove(buf->data, other->data + start_index, bit_buffer_get_size_bytes(other) - start_index); + memcpy(buf->data, other->data + start_index, bit_buffer_get_size_bytes(other) - start_index); buf->size_bits = other->size_bits - start_index * BITS_IN_BYTE; } @@ -70,9 +70,7 @@ void bit_buffer_copy_left(BitBuffer* buf, const BitBuffer* other, size_t end_ind furi_check(bit_buffer_get_capacity_bytes(buf) >= end_index); furi_check(bit_buffer_get_size_bytes(other) >= end_index); - if(buf != other) { - memcpy(buf->data, other->data, end_index); - } + memcpy(buf->data, other->data, end_index); buf->size_bits = end_index * BITS_IN_BYTE; } From 9e8c8addd3c3146f3ac75b7bc8d10033b8927e28 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 27 Mar 2025 05:52:08 +0000 Subject: [PATCH 70/92] Use dedicated buffer --- lib/nfc/protocols/iso14443_4a/iso14443_4a_listener.c | 12 ++++++------ .../protocols/iso14443_4a/iso14443_4a_listener_i.h | 1 + 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/nfc/protocols/iso14443_4a/iso14443_4a_listener.c b/lib/nfc/protocols/iso14443_4a/iso14443_4a_listener.c index 189a23dd2..95bae45d4 100644 --- a/lib/nfc/protocols/iso14443_4a/iso14443_4a_listener.c +++ b/lib/nfc/protocols/iso14443_4a/iso14443_4a_listener.c @@ -16,6 +16,7 @@ static Iso14443_4aListener* instance->data = data; instance->iso14443_4_layer = iso14443_4_layer_alloc(); + instance->rx_buffer = bit_buffer_alloc(ISO14443_4A_LISTENER_BUF_SIZE); instance->tx_buffer = bit_buffer_alloc(ISO14443_4A_LISTENER_BUF_SIZE); instance->iso14443_4a_event.data = &instance->iso14443_4a_event_data; @@ -28,10 +29,9 @@ static Iso14443_4aListener* static void iso14443_4a_listener_free(Iso14443_4aListener* instance) { furi_assert(instance); - furi_assert(instance->data); - furi_assert(instance->tx_buffer); iso14443_4_layer_free(instance->iso14443_4_layer); + bit_buffer_free(instance->rx_buffer); bit_buffer_free(instance->tx_buffer); free(instance); } @@ -88,11 +88,11 @@ static NfcCommand iso14443_4a_listener_run(NfcGenericEvent event, void* context) } } } else { - Iso14443_4LayerResult status = - iso14443_4_layer_decode_command(instance->iso14443_4_layer, rx_buffer, rx_buffer); + Iso14443_4LayerResult status = iso14443_4_layer_decode_command( + instance->iso14443_4_layer, rx_buffer, instance->rx_buffer); if(status & Iso14443_4LayerResultSend) { iso14443_3a_listener_send_standard_frame( - instance->iso14443_3a_listener, rx_buffer); + instance->iso14443_3a_listener, instance->rx_buffer); } if(status & Iso14443_4LayerResultHalt) { iso14443_4a_listener_reset(instance); @@ -104,7 +104,7 @@ static NfcCommand iso14443_4a_listener_run(NfcGenericEvent event, void* context) } if(status & Iso14443_4LayerResultData) { instance->iso14443_4a_event.type = Iso14443_4aListenerEventTypeReceivedData; - instance->iso14443_4a_event.data->buffer = rx_buffer; + instance->iso14443_4a_event.data->buffer = instance->rx_buffer; if(instance->callback) { command = instance->callback(instance->generic_event, instance->context); diff --git a/lib/nfc/protocols/iso14443_4a/iso14443_4a_listener_i.h b/lib/nfc/protocols/iso14443_4a/iso14443_4a_listener_i.h index 46233f21d..c0ed6d1cb 100644 --- a/lib/nfc/protocols/iso14443_4a/iso14443_4a_listener_i.h +++ b/lib/nfc/protocols/iso14443_4a/iso14443_4a_listener_i.h @@ -21,6 +21,7 @@ struct Iso14443_4aListener { Iso14443_4Layer* iso14443_4_layer; Iso14443_4aListenerState state; + BitBuffer* rx_buffer; BitBuffer* tx_buffer; NfcGenericEvent generic_event; From c70626248aad66d90eeabcbfca9f6494fffb4bad Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 27 Mar 2025 06:39:20 +0000 Subject: [PATCH 71/92] Simpler naming scheme --- .../type_4_tag/type_4_tag_render.c | 5 ++++ .../main/nfc/plugins/supported_cards/ndef.c | 14 ++++++--- lib/nfc/protocols/type_4_tag/type_4_tag.c | 29 +++++-------------- lib/nfc/protocols/type_4_tag/type_4_tag.h | 3 +- .../type_4_tag/type_4_tag_poller_i.c | 9 ++---- 5 files changed, 25 insertions(+), 35 deletions(-) diff --git a/applications/main/nfc/helpers/protocol_support/type_4_tag/type_4_tag_render.c b/applications/main/nfc/helpers/protocol_support/type_4_tag/type_4_tag_render.c index 492bbb457..2dc51a6e8 100644 --- a/applications/main/nfc/helpers/protocol_support/type_4_tag/type_4_tag_render.c +++ b/applications/main/nfc/helpers/protocol_support/type_4_tag/type_4_tag_render.c @@ -13,6 +13,11 @@ void nfc_render_type_4_tag_info( if(data->is_tag_specific) { furi_string_cat(str, "\n::::::::::::::::::[Tag Specs]::::::::::::::::::\n"); + furi_string_cat_printf( + str, + "Card: %s\n", + furi_string_empty(data->platform_name) ? "unknown" : + furi_string_get_cstr(data->platform_name)); furi_string_cat_printf( str, "T4T Mapping Version: %u.%u\n", data->t4t_version.major, data->t4t_version.minor); furi_string_cat_printf(str, "NDEF File ID: %04X\n", data->ndef_file_id); diff --git a/applications/main/nfc/plugins/supported_cards/ndef.c b/applications/main/nfc/plugins/supported_cards/ndef.c index 2b7ea2dbc..389e659d3 100644 --- a/applications/main/nfc/plugins/supported_cards/ndef.c +++ b/applications/main/nfc/plugins/supported_cards/ndef.c @@ -41,10 +41,10 @@ #error Invalid NDEF_PROTO specified! #endif -#define NDEF_TITLE(device, parsed_data) \ - furi_string_printf( \ - parsed_data, \ - "\e#NDEF Format Data\nCard type: %s\n", \ +#define NDEF_TITLE(device, parsed_data) \ + furi_string_printf( \ + parsed_data, \ + "\e#NDEF Format Data\nCard: %s\n", \ nfc_device_get_name(device, NfcDeviceNameTypeFull)) // ---=== structures ===--- @@ -1056,6 +1056,12 @@ static bool ndef_t4t_parse(const NfcDevice* device, FuriString* parsed_data) { NDEF_TITLE(device, parsed_data); + furi_string_replace(parsed_data, "Card: ", "Protocol: "); + if(data->is_tag_specific && !furi_string_empty(data->platform_name)) { + furi_string_cat_printf( + parsed_data, "Card: %s\n", furi_string_get_cstr(data->platform_name)); + } + Ndef ndef = { .output = parsed_data, .t4t = diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag.c b/lib/nfc/protocols/type_4_tag/type_4_tag.c index 09d3f5be6..03527b844 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag.c +++ b/lib/nfc/protocols/type_4_tag/type_4_tag.c @@ -1,7 +1,6 @@ #include "type_4_tag_i.h" #define TYPE_4_TAG_PROTOCOL_NAME "Type 4 Tag" -#define TYPE_4_TAG_SHORT_NAME "T4T" const NfcDeviceBase nfc_device_type_4_tag = { .protocol_name = TYPE_4_TAG_PROTOCOL_NAME, @@ -23,8 +22,7 @@ Type4TagData* type_4_tag_alloc(void) { Type4TagData* data = malloc(sizeof(Type4TagData)); data->iso14443_4a_data = iso14443_4a_alloc(); data->device_name = furi_string_alloc(); - data->platform_name_full = furi_string_alloc(); - data->platform_name_short = furi_string_alloc(); + data->platform_name = furi_string_alloc(); data->ndef_data = simple_array_alloc(&simple_array_config_uint8_t); return data; } @@ -34,8 +32,7 @@ void type_4_tag_free(Type4TagData* data) { type_4_tag_reset(data); simple_array_free(data->ndef_data); - furi_string_free(data->platform_name_short); - furi_string_free(data->platform_name_full); + furi_string_free(data->platform_name); furi_string_free(data->device_name); iso14443_4a_free(data->iso14443_4a_data); free(data); @@ -48,8 +45,7 @@ void type_4_tag_reset(Type4TagData* data) { data->is_tag_specific = false; furi_string_reset(data->device_name); - furi_string_reset(data->platform_name_full); - furi_string_reset(data->platform_name_short); + furi_string_reset(data->platform_name); data->t4t_version.value = 0; data->chunk_max_read = 0; data->chunk_max_write = 0; @@ -71,8 +67,7 @@ void type_4_tag_copy(Type4TagData* data, const Type4TagData* other) { data->is_tag_specific = other->is_tag_specific; furi_string_set(data->device_name, other->device_name); - furi_string_set(data->platform_name_full, other->platform_name_full); - furi_string_set(data->platform_name_short, other->platform_name_short); + furi_string_set(data->platform_name, other->platform_name); data->t4t_version.value = other->t4t_version.value; data->chunk_max_read = other->chunk_max_read; data->chunk_max_write = other->chunk_max_write; @@ -150,19 +145,9 @@ bool type_4_tag_is_equal(const Type4TagData* data, const Type4TagData* other) { } const char* type_4_tag_get_device_name(const Type4TagData* data, NfcDeviceNameType name_type) { - FuriString* platform_name = name_type == NfcDeviceNameTypeFull ? data->platform_name_full : - data->platform_name_short; - if(furi_string_empty(platform_name)) { - return TYPE_4_TAG_PROTOCOL_NAME; - } - furi_string_printf( - data->device_name, - "%s%c(%s)", - name_type == NfcDeviceNameTypeFull ? TYPE_4_TAG_PROTOCOL_NAME : TYPE_4_TAG_SHORT_NAME, - name_type == NfcDeviceNameTypeFull ? '\n' : ' ', - furi_string_get_cstr(platform_name)); - furi_string_replace_str(data->device_name, "Mifare", "MIFARE"); - return furi_string_get_cstr(data->device_name); + UNUSED(data); + UNUSED(name_type); + return TYPE_4_TAG_PROTOCOL_NAME; } const uint8_t* type_4_tag_get_uid(const Type4TagData* data, size_t* uid_len) { diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag.h b/lib/nfc/protocols/type_4_tag/type_4_tag.h index 199953bcd..29c41a5be 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag.h +++ b/lib/nfc/protocols/type_4_tag/type_4_tag.h @@ -33,8 +33,7 @@ typedef struct { // Tag specific data bool is_tag_specific; Type4TagPlatform platform; - FuriString* platform_name_full; - FuriString* platform_name_short; + FuriString* platform_name; union { struct { uint8_t minor : 4; diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c b/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c index 25a9f8aaa..fd047f9a1 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c @@ -252,15 +252,10 @@ Type4TagError type_4_tag_poller_detect_platform(Type4TagPoller* instance) { Type4TagError error; if(platform != Type4TagPlatformUnknown) { furi_string_set( - instance->data->platform_name_full, - nfc_device_get_name(device, NfcDeviceNameTypeFull)); - furi_string_set( - instance->data->platform_name_short, - nfc_device_get_name(device, NfcDeviceNameTypeShort)); + instance->data->platform_name, nfc_device_get_name(device, NfcDeviceNameTypeFull)); error = Type4TagErrorNone; } else { - furi_string_reset(instance->data->platform_name_full); - furi_string_reset(instance->data->platform_name_short); + furi_string_reset(instance->data->platform_name); error = Type4TagErrorNotSupported; } instance->data->platform = platform; From 77debdc3f1dbf00005f550dd9242a330560a798a Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Tue, 22 Apr 2025 02:40:26 +0100 Subject: [PATCH 72/92] Update TODOs that are out of scope for the project --- lib/nfc/helpers/iso14443_4_layer.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/lib/nfc/helpers/iso14443_4_layer.c b/lib/nfc/helpers/iso14443_4_layer.c index 9e8c75e41..f011b8ca1 100644 --- a/lib/nfc/helpers/iso14443_4_layer.c +++ b/lib/nfc/helpers/iso14443_4_layer.c @@ -186,14 +186,13 @@ Iso14443_4LayerResult iso14443_4_layer_decode_command( } else if(instance->cid != ISO14443_4_LAYER_CID_NOT_SUPPORTED && instance->cid != 0) { return Iso14443_4LayerResultSkip; } + // TODO: properly handle block chaining if(instance->pcb & ISO14443_4_BLOCK_PCB_I_NAD_MASK) { if(instance->nad == ISO14443_4_LAYER_NAD_NOT_SUPPORTED) { return Iso14443_4LayerResultSkip; } instance->nad = bit_buffer_get_byte(input_data, prologue_len++); - // FIXME: unset NAD when chaining after first block } - // FIXME: chaining bit_buffer_copy_right(block_data, input_data, prologue_len); iso14443_4_layer_update_pcb(instance, false); return Iso14443_4LayerResultData; @@ -218,7 +217,7 @@ Iso14443_4LayerResult iso14443_4_layer_decode_command( } } else if(ISO14443_4_BLOCK_PCB_IS_R_BLOCK(instance->pcb)) { - // FIXME: R blocks + // TODO: properly handle R blocks while chaining iso14443_4_layer_update_pcb(instance, true); instance->pcb |= ISO14443_4_BLOCK_PCB_R_NACK_MASK; bit_buffer_reset(block_data); @@ -240,22 +239,19 @@ bool iso14443_4_layer_encode_response( if(instance->pcb_prev & ISO14443_4_BLOCK_PCB_I_CID_MASK) { bit_buffer_append_byte(block_data, instance->cid); } + // TODO: properly handle block chaining and related R block responses if(instance->pcb_prev & ISO14443_4_BLOCK_PCB_I_NAD_MASK && instance->nad != ISO14443_4_LAYER_NAD_NOT_SET) { bit_buffer_append_byte(block_data, instance->nad); instance->nad = ISO14443_4_LAYER_NAD_NOT_SET; - // FIXME: unset NAD when chaining after first block } else { instance->pcb &= ~ISO14443_4_BLOCK_PCB_I_NAD_MASK; } - // FIXME: chaining instance->pcb &= ~ISO14443_4_BLOCK_PCB_I_CHAIN_MASK; bit_buffer_set_byte(block_data, 0, instance->pcb); bit_buffer_append(block_data, input_data); iso14443_4_layer_update_pcb(instance, false); return true; - - // FIXME: R blocks } return false; } From 5365cfcb049a5a30468fb4c3ae3adce4414584ca Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Fri, 23 May 2025 22:57:45 +0300 Subject: [PATCH 73/92] fix links --- .ci_files/devbuild_msg_discord.txt | 6 +++--- .ci_files/devbuild_msg_telegram.txt | 6 +++--- .ci_files/release_msg_discord.txt | 6 +++--- .ci_files/release_msg_telegram.txt | 6 +++--- .drone.yml | 6 +++--- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/.ci_files/devbuild_msg_discord.txt b/.ci_files/devbuild_msg_discord.txt index 41a70e45e..4b39a0b03 100644 --- a/.ci_files/devbuild_msg_discord.txt +++ b/.ci_files/devbuild_msg_discord.txt @@ -6,9 +6,9 @@ How to [install firmware](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/HowToInstall.md)? ### Install FW via Web Updater: -[Default](https://lab.flipper.net/?url=https://unleashedflip.com/fw/dev/flipper-z-f7-update-(buildnum).tgz&channel=dev-cfw&version=(buildnum)) > ` ` -[Extra apps](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-(buildnum)e.tgz&channel=dev-cfw&version=(buildnum)e) > `e` -[No apps](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-(buildnum)c.tgz&channel=dev-cfw&version=(buildnum)c) > `c` +[Default](https://lab.flipper.net/?url=https://unleashedflip.com/fw/dev/flipper-z-f7-update-(buildnum).tgz&target=f7&channel=dev-cfw&channel=(buildnum)) > ` ` +[Extra apps](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-(buildnum)e.tgz&target=f7&channel=dev-cfw&channel=(buildnum)e) > `e` +[No apps](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-(buildnum)c.tgz&target=f7&channel=dev-cfw&channel=(buildnum)c) > `c` What ` `, `e`, `c` means? -> [versions info](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/CHANGELOG.md#what-e---c-means-what-i-need-to-download-if-i-dont-want-to-use-web-updater) ### Direct tgz download links: [Default](https://unleashedflip.com/fw/dev/flipper-z-f7-update-(buildnum).tgz) > ` ` - [Extra apps](https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-(buildnum)e.tgz) > `e` - [No apps](https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-(buildnum)c.tgz) > `c` diff --git a/.ci_files/devbuild_msg_telegram.txt b/.ci_files/devbuild_msg_telegram.txt index 13d0545dd..73bd900fe 100644 --- a/.ci_files/devbuild_msg_telegram.txt +++ b/.ci_files/devbuild_msg_telegram.txt @@ -7,9 +7,9 @@ How to [install firmware](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/HowToInstall.md)? **Install FW via Web Updater:** -[Default](https://lab.flipper.net/?url=https://unleashedflip.com/fw/dev/flipper-z-f7-update-(buildnum).tgz&channel=dev-cfw&version=(buildnum)) > ` ` -[Extra apps](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-(buildnum)e.tgz&channel=dev-cfw&version=(buildnum)e) > `e` -[No apps](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-(buildnum)c.tgz&channel=dev-cfw&version=(buildnum)c) > `c` +[Default](https://lab.flipper.net/?url=https://unleashedflip.com/fw/dev/flipper-z-f7-update-(buildnum).tgz&target=f7&channel=dev-cfw&channel=(buildnum)) > ` ` +[Extra apps](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-(buildnum)e.tgz&target=f7&channel=dev-cfw&channel=(buildnum)e) > `e` +[No apps](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-(buildnum)c.tgz&target=f7&channel=dev-cfw&channel=(buildnum)c) > `c` What ` `, `e`, `c` means? -> [versions info](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/CHANGELOG.md#what-e---c-means-what-i-need-to-download-if-i-dont-want-to-use-web-updater) diff --git a/.ci_files/release_msg_discord.txt b/.ci_files/release_msg_discord.txt index 8eadaaf1f..a54913902 100644 --- a/.ci_files/release_msg_discord.txt +++ b/.ci_files/release_msg_discord.txt @@ -6,9 +6,9 @@ How to [install firmware](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/HowToInstall.md)? ### Install FW via Web Updater: -[Default](https://lab.flipper.net/?url=https://unleashedflip.com/fw/(releasever)/flipper-z-f7-update-(releasever).tgz&channel=release-cfw&version=(releasever)) > ` ` -[Extra apps](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-(releasever)e.tgz&channel=release-cfw&version=(releasever)e) > `e` -[No apps](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-(releasever)c.tgz&channel=release-cfw&version=(releasever)c) > `c` +[Default](https://lab.flipper.net/?url=https://unleashedflip.com/fw/(releasever)/flipper-z-f7-update-(releasever).tgz&target=f7&channel=release-cfw&channel=(releasever)) > ` ` +[Extra apps](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-(releasever)e.tgz&target=f7&channel=release-cfw&channel=(releasever)e) > `e` +[No apps](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-(releasever)c.tgz&target=f7&channel=release-cfw&channel=(releasever)c) > `c` What ` `, `e`, `c` means? -> [versions info](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/CHANGELOG.md#what-e---c-means-what-i-need-to-download-if-i-dont-want-to-use-web-updater) ### Direct tgz download links: [Default](https://unleashedflip.com/fw/(releasever)/flipper-z-f7-update-(releasever).tgz) > ` ` - [Extra apps](https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-(releasever)e.tgz) > `e` - [No apps](https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-(releasever)c.tgz) > `c` diff --git a/.ci_files/release_msg_telegram.txt b/.ci_files/release_msg_telegram.txt index 6d527970f..1c3d60ba4 100644 --- a/.ci_files/release_msg_telegram.txt +++ b/.ci_files/release_msg_telegram.txt @@ -7,9 +7,9 @@ How to [install firmware](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/HowToInstall.md)? **Install FW via Web Updater:** -[Default](https://lab.flipper.net/?url=https://unleashedflip.com/fw/(releasever)/flipper-z-f7-update-(releasever).tgz&channel=release-cfw&version=(releasever)) > ` ` -[Extra apps](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-(releasever)e.tgz&channel=release-cfw&version=(releasever)e) > `e` -[No apps](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-(releasever)c.tgz&channel=release-cfw&version=(releasever)c) > `c` +[Default](https://lab.flipper.net/?url=https://unleashedflip.com/fw/(releasever)/flipper-z-f7-update-(releasever).tgz&target=f7&channel=release-cfw&channel=(releasever)) > ` ` +[Extra apps](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-(releasever)e.tgz&target=f7&channel=release-cfw&channel=(releasever)e) > `e` +[No apps](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-(releasever)c.tgz&target=f7&channel=release-cfw&channel=(releasever)c) > `c` What ` `, `e`, `c` means? -> [versions info](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/CHANGELOG.md#what-e---c-means-what-i-need-to-download-if-i-dont-want-to-use-web-updater) diff --git a/.drone.yml b/.drone.yml index 347ab1ce1..49edcc4dd 100644 --- a/.drone.yml +++ b/.drone.yml @@ -83,11 +83,11 @@ steps: - ls -laS artifacts-extra-apps/f7-update-${DRONE_TAG}e - sed -i 's/(version)/'${DRONE_TAG}'/g' CHANGELOG.md - echo '# Install FW via Web Updater:' >> CHANGELOG.md - - echo '### [Default](https://lab.flipper.net/?url=https://unleashedflip.com/fw/${DRONE_TAG}/flipper-z-f7-update-'${DRONE_TAG}'.tgz&channel=release-cfw&version='${DRONE_TAG}') > ` `' >> CHANGELOG.md + - echo '### [Default](https://lab.flipper.net/?url=https://unleashedflip.com/fw/${DRONE_TAG}/flipper-z-f7-update-'${DRONE_TAG}'.tgz&target=f7&channel=release-cfw&channel='${DRONE_TAG}') > ` `' >> CHANGELOG.md - echo '' >> CHANGELOG.md - - echo '### [Extra apps](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_TAG}'e.tgz&channel=release-cfw&version='${DRONE_TAG}'e) > `e`' >> CHANGELOG.md + - echo '### [Extra apps](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_TAG}'e.tgz&target=f7&channel=release-cfw&channel='${DRONE_TAG}'e) > `e`' >> CHANGELOG.md - echo '' >> CHANGELOG.md - - echo '### [No apps](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_TAG}'c.tgz&channel=release-cfw&version='${DRONE_TAG}'c) > `c`' >> CHANGELOG.md + - echo '### [No apps](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_TAG}'c.tgz&target=f7&channel=release-cfw&channel='${DRONE_TAG}'c) > `c`' >> CHANGELOG.md environment: FBT_TOOLS_CUSTOM_LINK: from_secret: fbt_link From 4c00313157c2b244db8c7d8ceae97a03a9b0d1ea Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sat, 24 May 2025 00:03:20 +0300 Subject: [PATCH 74/92] upd changelog --- CHANGELOG.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bd219b010..12072c079 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,11 +11,12 @@ * Display: Backlight option "always on" and RGB bug removed (PR #900 | by @Dmitry422) * NFC: Ultralight C - Attempt of authentication with default key (PR #898 | by @mishamyte) * OFW PR 4210: Infrared: Add text scroll to remote buttons (by @956MB) -* OFW PR 4205: fix sample durations when using external CC1101 (by @Aerosnail) -* OFW PR 4206: Stop JS PWM on exit (by @portasynthinca3) -* OFW PR 4212: Remove stupid "!" that broke subghz chat cli (by @GameLord2011) * Apps: **Check out more Apps updates and fixes by following** [this link](https://github.com/xMasterX/all-the-plugins/commits/dev) ## Other changes +* OFW PR 4205: fix sample durations when using external CC1101 (by @Aerosnail) +* OFW PR 4206: Stop JS PWM on exit (by @portasynthinca3) +* OFW PR 4212: Fixed inverted logic condition in subghz chat cli (by @GameLord2011) +* NFC: Fix clipper date timestamp (PR #903 | by @luu176) * Desktop: DEBUG - fix desktop anim switch override by favourite apps * CLI: Various fixes (by @WillyJL) * BadUSB: Fix key combos main keys being case sensitive (by @WillyJL) From 3aa3098d47df47c1f7864881b0e138a1ec7f1b0f Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sat, 24 May 2025 19:49:31 +0300 Subject: [PATCH 75/92] subghz better ignore lists --- .../scenes/subghz_scene_receiver_config.c | 28 +++++++++---------- lib/subghz/protocols/feron.c | 3 +- lib/subghz/protocols/honeywell.c | 2 +- lib/subghz/protocols/honeywell_wdb.c | 2 +- lib/subghz/protocols/kia.c | 2 +- lib/subghz/protocols/legrand.c | 3 +- lib/subghz/protocols/magellan.c | 2 +- lib/subghz/protocols/scher_khan.c | 2 +- lib/subghz/protocols/star_line.c | 2 +- lib/subghz/types.h | 4 +-- 10 files changed, 26 insertions(+), 24 deletions(-) diff --git a/applications/main/subghz/scenes/subghz_scene_receiver_config.c b/applications/main/subghz/scenes/subghz_scene_receiver_config.c index e3806eac6..23415474c 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver_config.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver_config.c @@ -8,9 +8,9 @@ enum SubGhzSettingIndex { SubGhzSettingIndexHopping, SubGhzSettingIndexModulation, SubGhzSettingIndexBinRAW, - SubGhzSettingIndexIgnoreStarline, + SubGhzSettingIndexIgnoreCars, SubGhzSettingIndexIgnoreAlarms, - SubGhzSettingIndexIgnoreMagellan, + SubGhzSettingIndexIgnoreSensors, SubGhzSettingIndexIgnorePrinceton, SubGhzSettingIndexIgnoreNiceFlorS, SubGhzSettingIndexDeleteOldSignals, @@ -301,16 +301,16 @@ static inline bool subghz_scene_receiver_config_ignore_filter_get_index( return READ_BIT(filter, flag) > 0; } -static void subghz_scene_receiver_config_set_starline(VariableItem* item) { - subghz_scene_receiver_config_set_ignore_filter(item, SubGhzProtocolFlag_StarLine); +static void subghz_scene_receiver_config_set_cars(VariableItem* item) { + subghz_scene_receiver_config_set_ignore_filter(item, SubGhzProtocolFlag_Cars); } -static void subghz_scene_receiver_config_set_auto_alarms(VariableItem* item) { +static void subghz_scene_receiver_config_set_alarms(VariableItem* item) { subghz_scene_receiver_config_set_ignore_filter(item, SubGhzProtocolFlag_Alarms); } -static void subghz_scene_receiver_config_set_magellan(VariableItem* item) { - subghz_scene_receiver_config_set_ignore_filter(item, SubGhzProtocolFlag_Magellan); +static void subghz_scene_receiver_config_set_sensors(VariableItem* item) { + subghz_scene_receiver_config_set_ignore_filter(item, SubGhzProtocolFlag_Sensors); } static void subghz_scene_receiver_config_set_princeton(VariableItem* item) { @@ -447,13 +447,13 @@ void subghz_scene_receiver_config_on_enter(void* context) { SubGhzCustomEventManagerSet) { item = variable_item_list_add( subghz->variable_item_list, - "Ignore Starline", + "Ignore Cars", COMBO_BOX_COUNT, - subghz_scene_receiver_config_set_starline, + subghz_scene_receiver_config_set_cars, subghz); value_index = subghz_scene_receiver_config_ignore_filter_get_index( - subghz->ignore_filter, SubGhzProtocolFlag_StarLine); + subghz->ignore_filter, SubGhzProtocolFlag_Cars); variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_text(item, combobox_text[value_index]); @@ -461,7 +461,7 @@ void subghz_scene_receiver_config_on_enter(void* context) { subghz->variable_item_list, "Ignore Alarms", COMBO_BOX_COUNT, - subghz_scene_receiver_config_set_auto_alarms, + subghz_scene_receiver_config_set_alarms, subghz); value_index = subghz_scene_receiver_config_ignore_filter_get_index( @@ -471,13 +471,13 @@ void subghz_scene_receiver_config_on_enter(void* context) { item = variable_item_list_add( subghz->variable_item_list, - "Ignore Magellan", + "Ignore Sensors", COMBO_BOX_COUNT, - subghz_scene_receiver_config_set_magellan, + subghz_scene_receiver_config_set_sensors, subghz); value_index = subghz_scene_receiver_config_ignore_filter_get_index( - subghz->ignore_filter, SubGhzProtocolFlag_Magellan); + subghz->ignore_filter, SubGhzProtocolFlag_Sensors); variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_text(item, combobox_text[value_index]); diff --git a/lib/subghz/protocols/feron.c b/lib/subghz/protocols/feron.c index 1096f07a7..0fcb14c1b 100644 --- a/lib/subghz/protocols/feron.c +++ b/lib/subghz/protocols/feron.c @@ -60,7 +60,8 @@ const SubGhzProtocol subghz_protocol_feron = { .name = SUBGHZ_PROTOCOL_FERON_NAME, .type = SubGhzProtocolTypeStatic, .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | - SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send | + SubGhzProtocolFlag_Sensors, .decoder = &subghz_protocol_feron_decoder, .encoder = &subghz_protocol_feron_encoder, diff --git a/lib/subghz/protocols/honeywell.c b/lib/subghz/protocols/honeywell.c index e76bb2822..8d8dc22d4 100644 --- a/lib/subghz/protocols/honeywell.c +++ b/lib/subghz/protocols/honeywell.c @@ -364,7 +364,7 @@ const SubGhzProtocol subghz_protocol_honeywell = { .type = SubGhzProtocolTypeStatic, .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | - SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send | SubGhzProtocolFlag_Sensors, .encoder = &subghz_protocol_honeywell_encoder, .decoder = &subghz_protocol_honeywell_decoder, diff --git a/lib/subghz/protocols/honeywell_wdb.c b/lib/subghz/protocols/honeywell_wdb.c index fcf282201..16545b8a0 100644 --- a/lib/subghz/protocols/honeywell_wdb.c +++ b/lib/subghz/protocols/honeywell_wdb.c @@ -74,7 +74,7 @@ const SubGhzProtocol subghz_protocol_honeywell_wdb = { .type = SubGhzProtocolTypeStatic, .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | - SubGhzProtocolFlag_Send, + SubGhzProtocolFlag_Send | SubGhzProtocolFlag_Sensors, .decoder = &subghz_protocol_honeywell_wdb_decoder, .encoder = &subghz_protocol_honeywell_wdb_encoder, diff --git a/lib/subghz/protocols/kia.c b/lib/subghz/protocols/kia.c index 1edd367c2..5b1c9887d 100644 --- a/lib/subghz/protocols/kia.c +++ b/lib/subghz/protocols/kia.c @@ -64,7 +64,7 @@ const SubGhzProtocol subghz_protocol_kia = { .name = SUBGHZ_PROTOCOL_KIA_NAME, .type = SubGhzProtocolTypeDynamic, .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_FM | SubGhzProtocolFlag_Decodable | - SubGhzProtocolFlag_Alarms, + SubGhzProtocolFlag_Cars, .decoder = &subghz_protocol_kia_decoder, .encoder = &subghz_protocol_kia_encoder, diff --git a/lib/subghz/protocols/legrand.c b/lib/subghz/protocols/legrand.c index 9459fa2e7..94a45694c 100644 --- a/lib/subghz/protocols/legrand.c +++ b/lib/subghz/protocols/legrand.c @@ -67,7 +67,8 @@ const SubGhzProtocol subghz_protocol_legrand = { .name = SUBGHZ_PROTOCOL_LEGRAND_NAME, .type = SubGhzProtocolTypeStatic, .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | - SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send | + SubGhzProtocolFlag_Sensors, .decoder = &subghz_protocol_legrand_decoder, .encoder = &subghz_protocol_legrand_encoder, diff --git a/lib/subghz/protocols/magellan.c b/lib/subghz/protocols/magellan.c index 260b11e75..4382a3db5 100644 --- a/lib/subghz/protocols/magellan.c +++ b/lib/subghz/protocols/magellan.c @@ -65,7 +65,7 @@ const SubGhzProtocol subghz_protocol_magellan = { .type = SubGhzProtocolTypeStatic, .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send | - SubGhzProtocolFlag_Magellan, + SubGhzProtocolFlag_Sensors, .decoder = &subghz_protocol_magellan_decoder, .encoder = &subghz_protocol_magellan_encoder, diff --git a/lib/subghz/protocols/scher_khan.c b/lib/subghz/protocols/scher_khan.c index d1aad4ee6..e3bc4f079 100644 --- a/lib/subghz/protocols/scher_khan.c +++ b/lib/subghz/protocols/scher_khan.c @@ -70,7 +70,7 @@ const SubGhzProtocol subghz_protocol_scher_khan = { .name = SUBGHZ_PROTOCOL_SCHER_KHAN_NAME, .type = SubGhzProtocolTypeDynamic, .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_FM | SubGhzProtocolFlag_Decodable | - SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Alarms, + SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Cars, .decoder = &subghz_protocol_scher_khan_decoder, .encoder = &subghz_protocol_scher_khan_encoder, diff --git a/lib/subghz/protocols/star_line.c b/lib/subghz/protocols/star_line.c index 0005ad5fc..991957abb 100644 --- a/lib/subghz/protocols/star_line.c +++ b/lib/subghz/protocols/star_line.c @@ -80,7 +80,7 @@ const SubGhzProtocol subghz_protocol_star_line = { .type = SubGhzProtocolTypeDynamic, .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send | - SubGhzProtocolFlag_StarLine, + SubGhzProtocolFlag_Cars, .decoder = &subghz_protocol_star_line_decoder, .encoder = &subghz_protocol_star_line_encoder, diff --git a/lib/subghz/types.h b/lib/subghz/types.h index 31264a0ad..cd7f74ba9 100644 --- a/lib/subghz/types.h +++ b/lib/subghz/types.h @@ -125,9 +125,9 @@ typedef enum { SubGhzProtocolFlag_Load = (1 << 8), SubGhzProtocolFlag_Send = (1 << 9), SubGhzProtocolFlag_BinRAW = (1 << 10), - SubGhzProtocolFlag_StarLine = (1 << 11), + SubGhzProtocolFlag_Cars = (1 << 11), SubGhzProtocolFlag_Alarms = (1 << 12), - SubGhzProtocolFlag_Magellan = (1 << 13), + SubGhzProtocolFlag_Sensors = (1 << 13), SubGhzProtocolFlag_Princeton = (1 << 14), SubGhzProtocolFlag_NiceFlorS = (1 << 15), } SubGhzProtocolFlag; From 54c3c6772891b8359646f14e42108693a9564b03 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sat, 24 May 2025 19:58:10 +0300 Subject: [PATCH 76/92] upd changelog --- CHANGELOG.md | 2 +- ReadMe.md | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 12072c079..d2f239aab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ ## Main changes - Current API: 86.0 * iButton: TM01x Dallas write support (PR #899 | by @Leptopt1los) -* SubGHz: Rename and extend Alarms ignore option (add Hollarm & GangQi) +* SubGHz: Rename and extend Alarms, Sensors, Cars ignore options (Alarms: Hollarm, GangQi | Cars: Kia, Starline, ScherKhan | Sensors: Magellan, Honeywell, Honeywell WDB (doorbells), Legrand (doorbells), Feron (RGB lights)) * SubGHz: V2 Phoenix show counter value * SubGHz: Add keeloq ironlogic (aka il100) smart clone cloners support (thanks to Vitaly for RAWs) * SubGHz: Fix CAME 24bit decoder diff --git a/ReadMe.md b/ReadMe.md index 2711ea794..395222fff 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -80,6 +80,7 @@ Before getting started: > - FAAC SLH, BFT Mitto / Somfy Telis / Nice Flor S / CAME Atomo, etc. manual creation with programming new remote into receiver (use button 0xF for BFT Mitto, 0x8 (Prog) on Somfy Telis, (right arrow button for other protocols)) > - Debug mode counter increase settings (+1 → +5, +10, default: +1) > - Debug PIN output settings for protocol development +> - Ignore options - Alarms: Hollarm, GangQi | Cars: Kia, Starline, ScherKhan | Sensors: Magellan, Honeywell, Honeywell WDB (doorbells), Legrand (doorbells), Feron (RGB lights) > >
From f1f1a8920441de05275b28b120af13c24d61c715 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Mon, 2 Jun 2025 00:54:30 +0300 Subject: [PATCH 77/92] fix links [ci skip] --- .ci_files/devbuild_msg_discord.txt | 6 +++--- .ci_files/devbuild_msg_telegram.txt | 6 +++--- .ci_files/release_msg_discord.txt | 6 +++--- .ci_files/release_msg_telegram.txt | 6 +++--- .drone.yml | 6 +++--- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/.ci_files/devbuild_msg_discord.txt b/.ci_files/devbuild_msg_discord.txt index 4b39a0b03..f3f12461d 100644 --- a/.ci_files/devbuild_msg_discord.txt +++ b/.ci_files/devbuild_msg_discord.txt @@ -6,9 +6,9 @@ How to [install firmware](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/HowToInstall.md)? ### Install FW via Web Updater: -[Default](https://lab.flipper.net/?url=https://unleashedflip.com/fw/dev/flipper-z-f7-update-(buildnum).tgz&target=f7&channel=dev-cfw&channel=(buildnum)) > ` ` -[Extra apps](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-(buildnum)e.tgz&target=f7&channel=dev-cfw&channel=(buildnum)e) > `e` -[No apps](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-(buildnum)c.tgz&target=f7&channel=dev-cfw&channel=(buildnum)c) > `c` +[Default](https://lab.flipper.net/?url=https://unleashedflip.com/fw/dev/flipper-z-f7-update-(buildnum).tgz&target=f7&channel=dev-cfw&version=(buildnum)) > ` ` +[Extra apps](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-(buildnum)e.tgz&target=f7&channel=dev-cfw&version=(buildnum)e) > `e` +[No apps](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-(buildnum)c.tgz&target=f7&channel=dev-cfw&version=(buildnum)c) > `c` What ` `, `e`, `c` means? -> [versions info](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/CHANGELOG.md#what-e---c-means-what-i-need-to-download-if-i-dont-want-to-use-web-updater) ### Direct tgz download links: [Default](https://unleashedflip.com/fw/dev/flipper-z-f7-update-(buildnum).tgz) > ` ` - [Extra apps](https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-(buildnum)e.tgz) > `e` - [No apps](https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-(buildnum)c.tgz) > `c` diff --git a/.ci_files/devbuild_msg_telegram.txt b/.ci_files/devbuild_msg_telegram.txt index 73bd900fe..833a8158f 100644 --- a/.ci_files/devbuild_msg_telegram.txt +++ b/.ci_files/devbuild_msg_telegram.txt @@ -7,9 +7,9 @@ How to [install firmware](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/HowToInstall.md)? **Install FW via Web Updater:** -[Default](https://lab.flipper.net/?url=https://unleashedflip.com/fw/dev/flipper-z-f7-update-(buildnum).tgz&target=f7&channel=dev-cfw&channel=(buildnum)) > ` ` -[Extra apps](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-(buildnum)e.tgz&target=f7&channel=dev-cfw&channel=(buildnum)e) > `e` -[No apps](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-(buildnum)c.tgz&target=f7&channel=dev-cfw&channel=(buildnum)c) > `c` +[Default](https://lab.flipper.net/?url=https://unleashedflip.com/fw/dev/flipper-z-f7-update-(buildnum).tgz&target=f7&channel=dev-cfw&version=(buildnum)) > ` ` +[Extra apps](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-(buildnum)e.tgz&target=f7&channel=dev-cfw&version=(buildnum)e) > `e` +[No apps](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-(buildnum)c.tgz&target=f7&channel=dev-cfw&version=(buildnum)c) > `c` What ` `, `e`, `c` means? -> [versions info](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/CHANGELOG.md#what-e---c-means-what-i-need-to-download-if-i-dont-want-to-use-web-updater) diff --git a/.ci_files/release_msg_discord.txt b/.ci_files/release_msg_discord.txt index a54913902..3536fbfb6 100644 --- a/.ci_files/release_msg_discord.txt +++ b/.ci_files/release_msg_discord.txt @@ -6,9 +6,9 @@ How to [install firmware](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/HowToInstall.md)? ### Install FW via Web Updater: -[Default](https://lab.flipper.net/?url=https://unleashedflip.com/fw/(releasever)/flipper-z-f7-update-(releasever).tgz&target=f7&channel=release-cfw&channel=(releasever)) > ` ` -[Extra apps](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-(releasever)e.tgz&target=f7&channel=release-cfw&channel=(releasever)e) > `e` -[No apps](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-(releasever)c.tgz&target=f7&channel=release-cfw&channel=(releasever)c) > `c` +[Default](https://lab.flipper.net/?url=https://unleashedflip.com/fw/(releasever)/flipper-z-f7-update-(releasever).tgz&target=f7&channel=release-cfw&version=(releasever)) > ` ` +[Extra apps](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-(releasever)e.tgz&target=f7&channel=release-cfw&version=(releasever)e) > `e` +[No apps](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-(releasever)c.tgz&target=f7&channel=release-cfw&version=(releasever)c) > `c` What ` `, `e`, `c` means? -> [versions info](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/CHANGELOG.md#what-e---c-means-what-i-need-to-download-if-i-dont-want-to-use-web-updater) ### Direct tgz download links: [Default](https://unleashedflip.com/fw/(releasever)/flipper-z-f7-update-(releasever).tgz) > ` ` - [Extra apps](https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-(releasever)e.tgz) > `e` - [No apps](https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-(releasever)c.tgz) > `c` diff --git a/.ci_files/release_msg_telegram.txt b/.ci_files/release_msg_telegram.txt index 1c3d60ba4..534aeb3ba 100644 --- a/.ci_files/release_msg_telegram.txt +++ b/.ci_files/release_msg_telegram.txt @@ -7,9 +7,9 @@ How to [install firmware](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/HowToInstall.md)? **Install FW via Web Updater:** -[Default](https://lab.flipper.net/?url=https://unleashedflip.com/fw/(releasever)/flipper-z-f7-update-(releasever).tgz&target=f7&channel=release-cfw&channel=(releasever)) > ` ` -[Extra apps](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-(releasever)e.tgz&target=f7&channel=release-cfw&channel=(releasever)e) > `e` -[No apps](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-(releasever)c.tgz&target=f7&channel=release-cfw&channel=(releasever)c) > `c` +[Default](https://lab.flipper.net/?url=https://unleashedflip.com/fw/(releasever)/flipper-z-f7-update-(releasever).tgz&target=f7&channel=release-cfw&version=(releasever)) > ` ` +[Extra apps](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-(releasever)e.tgz&target=f7&channel=release-cfw&version=(releasever)e) > `e` +[No apps](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-(releasever)c.tgz&target=f7&channel=release-cfw&version=(releasever)c) > `c` What ` `, `e`, `c` means? -> [versions info](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/CHANGELOG.md#what-e---c-means-what-i-need-to-download-if-i-dont-want-to-use-web-updater) diff --git a/.drone.yml b/.drone.yml index 49edcc4dd..0878d455e 100644 --- a/.drone.yml +++ b/.drone.yml @@ -83,11 +83,11 @@ steps: - ls -laS artifacts-extra-apps/f7-update-${DRONE_TAG}e - sed -i 's/(version)/'${DRONE_TAG}'/g' CHANGELOG.md - echo '# Install FW via Web Updater:' >> CHANGELOG.md - - echo '### [Default](https://lab.flipper.net/?url=https://unleashedflip.com/fw/${DRONE_TAG}/flipper-z-f7-update-'${DRONE_TAG}'.tgz&target=f7&channel=release-cfw&channel='${DRONE_TAG}') > ` `' >> CHANGELOG.md + - echo '### [Default](https://lab.flipper.net/?url=https://unleashedflip.com/fw/${DRONE_TAG}/flipper-z-f7-update-'${DRONE_TAG}'.tgz&target=f7&channel=release-cfw&version='${DRONE_TAG}') > ` `' >> CHANGELOG.md - echo '' >> CHANGELOG.md - - echo '### [Extra apps](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_TAG}'e.tgz&target=f7&channel=release-cfw&channel='${DRONE_TAG}'e) > `e`' >> CHANGELOG.md + - echo '### [Extra apps](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_TAG}'e.tgz&target=f7&channel=release-cfw&version='${DRONE_TAG}'e) > `e`' >> CHANGELOG.md - echo '' >> CHANGELOG.md - - echo '### [No apps](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_TAG}'c.tgz&target=f7&channel=release-cfw&channel='${DRONE_TAG}'c) > `c`' >> CHANGELOG.md + - echo '### [No apps](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_TAG}'c.tgz&target=f7&channel=release-cfw&version='${DRONE_TAG}'c) > `c`' >> CHANGELOG.md environment: FBT_TOOLS_CUSTOM_LINK: from_secret: fbt_link From 407e482566597ec9d526d4db7a574c1a52206919 Mon Sep 17 00:00:00 2001 From: WillyJL <49810075+WillyJL@users.noreply.github.com> Date: Wed, 11 Jun 2025 04:02:42 +0100 Subject: [PATCH 78/92] Loader: Fix misplaced ApplicationBeforeLoad events --- applications/services/loader/loader.c | 22 +++++++++---------- .../services/loader/loader_applications.c | 1 + 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/applications/services/loader/loader.c b/applications/services/loader/loader.c index 01de5af21..74dd4bc3e 100644 --- a/applications/services/loader/loader.c +++ b/applications/services/loader/loader.c @@ -654,10 +654,6 @@ static LoaderMessageLoaderStatusResult loader_do_start_by_name( status.value = loader_make_success_status(error_message); status.error = LoaderStatusErrorUnknown; - LoaderEvent event; - event.type = LoaderEventTypeApplicationBeforeLoad; - furi_pubsub_publish(loader->pubsub, &event); - do { // check lock if(loader_do_is_locked(loader)) { @@ -677,6 +673,17 @@ static LoaderMessageLoaderStatusResult loader_do_start_by_name( break; } + // check Applications + if(strcmp(name, LOADER_APPLICATIONS_NAME) == 0) { + loader_do_applications_show(loader); + status.value = loader_make_success_status(error_message); + break; + } + + LoaderEvent event; + event.type = LoaderEventTypeApplicationBeforeLoad; + furi_pubsub_publish(loader->pubsub, &event); + // check internal apps { const FlipperInternalApplication* app = loader_find_application_by_name(name); @@ -687,13 +694,6 @@ static LoaderMessageLoaderStatusResult loader_do_start_by_name( } } - // check Applications - if(strcmp(name, LOADER_APPLICATIONS_NAME) == 0) { - loader_do_applications_show(loader); - status.value = loader_make_success_status(error_message); - break; - } - // check External Applications { const char* path = loader_find_external_application_by_name(name); diff --git a/applications/services/loader/loader_applications.c b/applications/services/loader/loader_applications.c index b83083000..64b471cc9 100644 --- a/applications/services/loader/loader_applications.c +++ b/applications/services/loader/loader_applications.c @@ -141,6 +141,7 @@ static void } furi_pubsub_unsubscribe(loader_get_pubsub(app->loader), subscription); + furi_thread_flags_clear(APPLICATION_STOP_EVENT); } static int32_t loader_applications_thread(void* p) { From d0e5dbe63eb32b65ff9a747dd504c44acfe3505d Mon Sep 17 00:00:00 2001 From: WillyJL <49810075+WillyJL@users.noreply.github.com> Date: Wed, 11 Jun 2025 04:03:17 +0100 Subject: [PATCH 79/92] Power: Arm auto-poweroff when app chaining finished --- applications/services/power/power_service/power.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/applications/services/power/power_service/power.c b/applications/services/power/power_service/power.c index 31fcfcbd2..44247394f 100644 --- a/applications/services/power/power_service/power.c +++ b/applications/services/power/power_service/power.c @@ -487,9 +487,7 @@ static void power_loader_callback(const void* message, void* context) { power->app_running = true; power_auto_poweroff_disarm(power); // arm timer if some apps was not loaded or was stoped - } else if( - event->type == LoaderEventTypeApplicationLoadFailed || - event->type == LoaderEventTypeApplicationStopped) { + } else if(event->type == LoaderEventTypeNoMoreAppsInQueue) { power->app_running = false; power_auto_poweroff_arm(power); } From 98a44779990cad77405cb1483c4d866f691f1aa6 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Wed, 11 Jun 2025 18:10:38 +0300 Subject: [PATCH 80/92] upd changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d2f239aab..cc296bb35 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ * SubGHz: Fix Rename scene bug, that was replacing file name with random name when Rename is opened then closed then opened again * Display: Backlight option "always on" and RGB bug removed (PR #900 | by @Dmitry422) * NFC: Ultralight C - Attempt of authentication with default key (PR #898 | by @mishamyte) +* System: Loader - Fix misplaced ApplicationBeforeLoad events (PR #905 | by @WillyJL) * OFW PR 4210: Infrared: Add text scroll to remote buttons (by @956MB) * Apps: **Check out more Apps updates and fixes by following** [this link](https://github.com/xMasterX/all-the-plugins/commits/dev) ## Other changes From 9671c878bc92ff4fbe7dd0838ec75abf1978cdca Mon Sep 17 00:00:00 2001 From: WillyJL <49810075+WillyJL@users.noreply.github.com> Date: Tue, 24 Jun 2025 04:08:16 +0100 Subject: [PATCH 81/92] Sub-GHz: Move Weather, POCSAG & TPMS to ext apps --- CHANGELOG.md | 15 ++++- ReadMe.md | 1 - applications/external | 2 +- .../scenes/subghz_scene_receiver_config.c | 57 ++++++++++--------- applications/main/subghz/subghz_history.c | 10 ++-- applications/main/subghz/views/receiver.c | 2 +- lib/subghz/protocols/protocol_items.c | 54 +++++++++--------- targets/f7/api_symbols.csv | 2 +- 8 files changed, 78 insertions(+), 65 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 86caef760..1e9938930 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,18 @@ +### Breaking Changes: +- Sub-GHz: Removed Weather Station, POCSAG and TPMS protocols from main app, now found in external apps (by @WillyJL) + - Momentum previously supported these external app's protocols in the main Sub-GHz app + - However, with more protocols added over time to the main app, they are now too many + - The Flipper CPU cannot keep up with all the data received with this number of protocols + - This caused some signals to not be received when they should, and in some cases could freeze the device + - Since Weather Station, POCSAG and TPMS are available as external apps, they're now removed from the main Sub-GHz app + - You can now find this functionality in `Apps > Sub-GHz`, where there are dedicated apps for these 3 functions + ### Added: - Apps: - Sub-GHz: Sub-GHz Playlist Creator (by @coolerUA) + - Sub-GHz: Weather Station (by @Skorpionm) + - Sub-GHz: POCSAG Pager (by @xMasterX) + - Sub-GHz: TPMS Reader (by @wosk) - NFC: Ventra ULEV1 parser (by @hazardousvoltage) - Infrared: "Decode only" mode to ignore RAW signals, make buttons in learn scene more intuitive (by @WillyJL) - UL: Sub-GHz: Add keeloq ironlogic aka il100 smart clone cloners support (by @xMasterX & Vitaly) @@ -50,4 +62,5 @@ - UL: Fix Rename scene bug, that was replacing file name with random name when Rename is opened then closed then opened again (by @xMasterX) ### Removed: -- Nothing +- Sub-GHz: Removed Weather Station, POCSAG and TPMS protocols from main app, now found in external apps (by @WillyJL) + - See breaking changes notice above diff --git a/ReadMe.md b/ReadMe.md index b6212c8d2..1887b2e2d 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -122,7 +122,6 @@ Note that this repo is always updated with the great work from our friends at [U - Advanced and optimized Level System (Up to 30 levels) - Desktop Keybind system for full key and press/hold remapping - Storage backend with instant rename and virtual mounting for disk images -- Weather Station, POCSAG, TPMS protocols and more integrated into Sub-GHz - Expanded Sub-GHz App (Duplicate detection & Ignore, Autosave, History improvements) - Improved Error Messages (Showing source file paths) ``` diff --git a/applications/external b/applications/external index 3c42633d4..ae21e1fee 160000 --- a/applications/external +++ b/applications/external @@ -1 +1 @@ -Subproject commit 3c42633d437d011314c20d04ad6421f507b3cc35 +Subproject commit ae21e1feee4121fd7935167b991e588e18343e4c diff --git a/applications/main/subghz/scenes/subghz_scene_receiver_config.c b/applications/main/subghz/scenes/subghz_scene_receiver_config.c index b444d1581..478c1d931 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver_config.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver_config.c @@ -19,8 +19,8 @@ enum SubGhzSettingIndex { SubGhzSettingIndexIgnoreMagellan, SubGhzSettingIndexIgnorePrinceton, SubGhzSettingIndexIgnoreNiceFlorS, - SubGhzSettingIndexIgnoreWeather, - SubGhzSettingIndexIgnoreTPMS, + // SubGhzSettingIndexIgnoreWeather, + // SubGhzSettingIndexIgnoreTPMS, SubGhzSettingIndexSound, SubGhzSettingIndexResetToDefault, SubGhzSettingIndexLock, @@ -431,12 +431,13 @@ static void subghz_scene_receiver_config_set_niceflors(VariableItem* item) { subghz_scene_receiver_config_set_ignore_filter(item, SubGhzProtocolFilter_NiceFlorS); } -static void subghz_scene_receiver_config_set_weather(VariableItem* item) { - subghz_scene_receiver_config_set_ignore_filter(item, SubGhzProtocolFilter_Weather); -} -static void subghz_scene_receiver_config_set_tpms(VariableItem* item) { - subghz_scene_receiver_config_set_ignore_filter(item, SubGhzProtocolFilter_TPMS); -} +// static void subghz_scene_receiver_config_set_weather(VariableItem* item) { +// subghz_scene_receiver_config_set_ignore_filter(item, SubGhzProtocolFilter_Weather); +// } + +// static void subghz_scene_receiver_config_set_tpms(VariableItem* item) { +// subghz_scene_receiver_config_set_ignore_filter(item, SubGhzProtocolFilter_TPMS); +// } static void subghz_scene_receiver_config_var_list_enter_callback(void* context, uint32_t index) { furi_assert(context); @@ -664,29 +665,29 @@ void subghz_scene_receiver_config_on_enter(void* context) { variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_text(item, combobox_text[value_index]); - item = variable_item_list_add( - subghz->variable_item_list, - "Ignore Weather", - COMBO_BOX_COUNT, - subghz_scene_receiver_config_set_weather, - subghz); + // item = variable_item_list_add( + // subghz->variable_item_list, + // "Ignore Weather", + // COMBO_BOX_COUNT, + // subghz_scene_receiver_config_set_weather, + // subghz); - value_index = subghz_scene_receiver_config_ignore_filter_get_index( - subghz->ignore_filter, SubGhzProtocolFilter_Weather); - variable_item_set_current_value_index(item, value_index); - variable_item_set_current_value_text(item, combobox_text[value_index]); + // value_index = subghz_scene_receiver_config_ignore_filter_get_index( + // subghz->ignore_filter, SubGhzProtocolFilter_Weather); + // variable_item_set_current_value_index(item, value_index); + // variable_item_set_current_value_text(item, combobox_text[value_index]); - item = variable_item_list_add( - subghz->variable_item_list, - "Ignore TPMS", - COMBO_BOX_COUNT, - subghz_scene_receiver_config_set_tpms, - subghz); + // item = variable_item_list_add( + // subghz->variable_item_list, + // "Ignore TPMS", + // COMBO_BOX_COUNT, + // subghz_scene_receiver_config_set_tpms, + // subghz); - value_index = subghz_scene_receiver_config_ignore_filter_get_index( - subghz->ignore_filter, SubGhzProtocolFilter_TPMS); - variable_item_set_current_value_index(item, value_index); - variable_item_set_current_value_text(item, combobox_text[value_index]); + // value_index = subghz_scene_receiver_config_ignore_filter_get_index( + // subghz->ignore_filter, SubGhzProtocolFilter_TPMS); + // variable_item_set_current_value_index(item, value_index); + // variable_item_set_current_value_text(item, combobox_text[value_index]); } // Enable speaker, will send all incoming noises and signals to speaker so you can listen how your remote sounds like :) diff --git a/applications/main/subghz/subghz_history.c b/applications/main/subghz/subghz_history.c index eefa798ed..44ac74fce 100644 --- a/applications/main/subghz/subghz_history.c +++ b/applications/main/subghz/subghz_history.c @@ -266,11 +266,11 @@ bool subghz_history_add_to_history( SubGhzHistoryItem* item = SubGhzHistoryItemArray_push_raw(instance->history->data); item->preset = malloc(sizeof(SubGhzRadioPreset)); item->type = decoder_base->protocol->type; - if(decoder_base->protocol->filter & SubGhzProtocolFilter_Weather) { - // Other code uses protocol type to check if signal is usable - // so we can't change the actual protocol type, we fake it here - item->type = SubGhzProtocolWeatherStation; - } + // if(decoder_base->protocol->filter & SubGhzProtocolFilter_Weather) { + // // Other code uses protocol type to check if signal is usable + // // so we can't change the actual protocol type, we fake it here + // item->type = SubGhzProtocolWeatherStation; + // } item->preset->frequency = preset->frequency; item->preset->name = furi_string_alloc(); furi_string_set(item->preset->name, preset->name); diff --git a/applications/main/subghz/views/receiver.c b/applications/main/subghz/views/receiver.c index af4a68f1d..a122e1f17 100644 --- a/applications/main/subghz/views/receiver.c +++ b/applications/main/subghz/views/receiver.c @@ -38,7 +38,7 @@ static const Icon* ReceiverItemIcons[] = { [SubGhzProtocolTypeUnknown] = &I_Quest_7x8, [SubGhzProtocolTypeStatic] = &I_Static_9x7, [SubGhzProtocolTypeDynamic] = &I_Dynamic_9x7, - [SubGhzProtocolWeatherStation] = &I_Weather_7x8, + // [SubGhzProtocolWeatherStation] = &I_Weather_7x8, [SubGhzProtocolTypeBinRAW] = &I_Raw_9x7, }; diff --git a/lib/subghz/protocols/protocol_items.c b/lib/subghz/protocols/protocol_items.c index 9f0f7307e..fc52f3aa6 100644 --- a/lib/subghz/protocols/protocol_items.c +++ b/lib/subghz/protocols/protocol_items.c @@ -43,33 +43,33 @@ const SubGhzProtocol* const subghz_protocol_registry_items[] = { &subghz_protocol_dooya, &subghz_protocol_alutech_at_4n, &subghz_protocol_kinggates_stylo_4k, - &ws_protocol_infactory, - &ws_protocol_thermopro_tx4, - &ws_protocol_nexus_th, - &ws_protocol_gt_wt_02, - &ws_protocol_gt_wt_03, - &ws_protocol_acurite_606tx, - &ws_protocol_acurite_609txc, - &ws_protocol_acurite_986, - &ws_protocol_bresser_3ch, // Should be before lacrosse - &ws_protocol_lacrosse_tx, - &ws_protocol_lacrosse_tx141thbv2, - &ws_protocol_oregon2, - &ws_protocol_oregon3, - &ws_protocol_acurite_592txr, - &ws_protocol_ambient_weather, - &ws_protocol_solight_te44, // Should be before auriol - &ws_protocol_auriol_th, - &ws_protocol_oregon_v1, - &ws_protocol_tx_8300, - &ws_protocol_wendox_w6726, - &ws_protocol_auriol_ahfl, - &ws_protocol_kedsum_th, - &ws_protocol_emose601x, - &ws_protocol_acurite_5n1, - &ws_protocol_vauno_en8822c, - &subghz_protocol_pocsag, - &tpms_protocol_schrader_gg4, + // &ws_protocol_infactory, + // &ws_protocol_thermopro_tx4, + // &ws_protocol_nexus_th, + // &ws_protocol_gt_wt_02, + // &ws_protocol_gt_wt_03, + // &ws_protocol_acurite_606tx, + // &ws_protocol_acurite_609txc, + // &ws_protocol_acurite_986, + // &ws_protocol_bresser_3ch, // Should be before lacrosse + // &ws_protocol_lacrosse_tx, + // &ws_protocol_lacrosse_tx141thbv2, + // &ws_protocol_oregon2, + // &ws_protocol_oregon3, + // &ws_protocol_acurite_592txr, + // &ws_protocol_ambient_weather, + // &ws_protocol_solight_te44, // Should be before auriol + // &ws_protocol_auriol_th, + // &ws_protocol_oregon_v1, + // &ws_protocol_tx_8300, + // &ws_protocol_wendox_w6726, + // &ws_protocol_auriol_ahfl, + // &ws_protocol_kedsum_th, + // &ws_protocol_emose601x, + // &ws_protocol_acurite_5n1, + // &ws_protocol_vauno_en8822c, + // &subghz_protocol_pocsag, + // &tpms_protocol_schrader_gg4, &subghz_protocol_bin_raw, &subghz_protocol_mastercode, &subghz_protocol_x10, diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index df0b83a92..e34aead96 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -4105,7 +4105,7 @@ Variable,+,I_Volup_8x6,const Icon, Variable,+,I_WarningDolphinFlip_45x42,const Icon, Variable,+,I_WarningDolphin_45x42,const Icon, Variable,-,I_Warning_30x23,const Icon, -Variable,+,I_Weather_7x8,const Icon, +Variable,-,I_Weather_7x8,const Icon, Variable,+,I_arrow_nano_down,const Icon, Variable,+,I_arrow_nano_up,const Icon, Variable,+,I_back_10px,const Icon, From 25a48d7ec284cead4aaf8695f4826cdc2cddf356 Mon Sep 17 00:00:00 2001 From: WillyJL <49810075+WillyJL@users.noreply.github.com> Date: Tue, 24 Jun 2025 04:10:38 +0100 Subject: [PATCH 82/92] Furi: Re-enable __FURI_TRACE crash messages since removing all those subghz protocols freed some flash --- CHANGELOG.md | 1 + furi/core/check.h | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e9938930..29e1f5ec5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,6 +38,7 @@ - VGM Tool: Fixed RGB firmware UART regression (by @WillyJL) - UL: Sub-GHz Playlist: Add support for custom modulation presets, remake with txrx library and support for dynamic signals, cleanup code (by @xMasterX) - RFID: Add DEZ10 representation to EM410X (by @realcatgirly) +- Furi: Re-enabled file paths in furi_check crash messages (by @WillyJL) - OFW: Infrared: Add text scroll to remote buttons (by @956MB) - Sub-GHz: - UL: Rename and extend Alarms ignore option with Hollarm & GangQi (by @xMasterX) diff --git a/furi/core/check.h b/furi/core/check.h index dfac95754..f64bf17d4 100644 --- a/furi/core/check.h +++ b/furi/core/check.h @@ -22,7 +22,7 @@ extern "C" { // When enabled will use file paths instead of "furi_check failed" #if !defined(FURI_RAM_EXEC) && !defined(FURI_DEBUG) -// #define __FURI_TRACE +#define __FURI_TRACE #endif // Flags instead of pointers will save ~4 bytes on furi_assert and furi_check calls. From 03741da5a880748fb660db00be87852917148208 Mon Sep 17 00:00:00 2001 From: WillyJL <49810075+WillyJL@users.noreply.github.com> Date: Tue, 24 Jun 2025 04:15:38 +0100 Subject: [PATCH 83/92] Update apps - Metroflip: Added 80+ card AIDs, more AIDs for DESFire, Calypso card saving, fixed DESFire parsing, Navigo crash, Clipper timestamp (by luu176) --- CHANGELOG.md | 1 + applications/external | 2 +- scripts/fbt_tools/fbt_extapps.py | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 29e1f5ec5..c7f02d282 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,7 @@ - Picopass: Bugfixes and refactoring (by @bettse) - Portal Of Flipper: Implement auth for the xbox 360 (by @sanjay900) - Quac: Fix link imports not working, fix RAW Sub-GHz files (by @xMasterX & @WillyJL), add Sub-GHz duration setting (by @rdefeo) + - Metroflip: Added 80+ card AIDs, more AIDs for DESFire, Calypso card saving, fixed DESFire parsing, Navigo crash, Clipper timestamp (by @luu176) - Seos Compatible: Add support for reading Seader files that have SIO, Add custom zero key ADF OID (by @bettse) - WiFi Marauder: Support for new commands from ESP32Marauder 1.6.x (by @justcallmekoko) - VGM Tool: Fixed RGB firmware UART regression (by @WillyJL) diff --git a/applications/external b/applications/external index ae21e1fee..ddb800eab 160000 --- a/applications/external +++ b/applications/external @@ -1 +1 @@ -Subproject commit ae21e1feee4121fd7935167b991e588e18343e4c +Subproject commit ddb800eab514905ea4462ec4e6f4fa03b3f26d41 diff --git a/scripts/fbt_tools/fbt_extapps.py b/scripts/fbt_tools/fbt_extapps.py index 800184494..3cbda0bfe 100644 --- a/scripts/fbt_tools/fbt_extapps.py +++ b/scripts/fbt_tools/fbt_extapps.py @@ -329,6 +329,7 @@ def _validate_app_imports(target, source, env): "metroflip_", "bit_slice_to_dec", "byte_to_binary", + "read_calypso_data", "read_file", "apdu_success", "select_app", From 0b53be5cbdfccabc2ec6e49f8976002ac3b2dff5 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Wed, 25 Jun 2025 02:50:18 +0300 Subject: [PATCH 84/92] Add DEZ10 by realcatgirly https://github.com/Next-Flip/Momentum-Firmware/pull/418/files --- applications/main/lfrfid/scenes/lfrfid_scene_read_success.c | 3 +-- lib/lfrfid/protocols/protocol_em4100.c | 6 ++++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_read_success.c b/applications/main/lfrfid/scenes/lfrfid_scene_read_success.c index b0e373ea5..354783ff6 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_read_success.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_read_success.c @@ -41,8 +41,7 @@ void lfrfid_scene_read_success_on_enter(void* context) { furi_string_cat_printf(display_text, "\n%s", furi_string_get_cstr(rendered_data)); furi_string_free(rendered_data); - widget_add_text_box_element( - widget, 0, 16, 128, 52, AlignLeft, AlignTop, furi_string_get_cstr(display_text), true); + widget_add_text_scroll_element(widget, 0, 16, 128, 35, furi_string_get_cstr(display_text)); widget_add_button_element(widget, GuiButtonTypeLeft, "Retry", lfrfid_widget_callback, app); widget_add_button_element(widget, GuiButtonTypeRight, "More", lfrfid_widget_callback, app); diff --git a/lib/lfrfid/protocols/protocol_em4100.c b/lib/lfrfid/protocols/protocol_em4100.c index ed18133dc..9ff6724cb 100644 --- a/lib/lfrfid/protocols/protocol_em4100.c +++ b/lib/lfrfid/protocols/protocol_em4100.c @@ -374,11 +374,13 @@ void protocol_em4100_render_data(ProtocolEM4100* protocol, FuriString* result) { furi_string_printf( result, "FC: %03u Card: %05hu CL:%hhu\n" - "DEZ 8: %08lu", + "DEZ 8: %08lu\n" + "DEZ 10: %010lu", data[2], (uint16_t)((data[3] << 8) | (data[4])), protocol->clock_per_bit, - (uint32_t)((data[2] << 16) | (data[3] << 8) | (data[4]))); + (uint32_t)((data[2] << 16) | (data[3] << 8) | (data[4])), + (uint32_t)((data[1] << 24) | (data[2] << 16) | (data[3] << 8) | (data[4]))); } const ProtocolBase protocol_em4100 = { From 1b754c1482fedb85235819ed34f5446ac3d56bb8 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Wed, 25 Jun 2025 02:57:05 +0300 Subject: [PATCH 85/92] Fix possible frequency analyzer deadlock when holding Ok by WillyJL --- .../scenes/subghz_scene_frequency_analyzer.c | 2 +- .../main/subghz/views/subghz_frequency_analyzer.c | 14 ++++++-------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/applications/main/subghz/scenes/subghz_scene_frequency_analyzer.c b/applications/main/subghz/scenes/subghz_scene_frequency_analyzer.c index 9e5289c54..7c9c5192d 100644 --- a/applications/main/subghz/scenes/subghz_scene_frequency_analyzer.c +++ b/applications/main/subghz/scenes/subghz_scene_frequency_analyzer.c @@ -71,7 +71,7 @@ bool subghz_scene_frequency_analyzer_on_event(void* context, SceneManagerEvent e } else if(event.event == SubGhzCustomEventViewFreqAnalOkLong) { // Don't need to save, we already saved on short event (and on exit event too) subghz_rx_key_state_set(subghz, SubGhzRxKeyStateIDLE); - scene_manager_set_scene_state(subghz->scene_manager, SubGhzSceneStart, 10); + scene_manager_previous_scene(subghz->scene_manager); // Stops the worker scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiver); return true; } diff --git a/applications/main/subghz/views/subghz_frequency_analyzer.c b/applications/main/subghz/views/subghz_frequency_analyzer.c index b070d6f83..49eabc94b 100644 --- a/applications/main/subghz/views/subghz_frequency_analyzer.c +++ b/applications/main/subghz/views/subghz_frequency_analyzer.c @@ -255,9 +255,9 @@ bool subghz_frequency_analyzer_input(InputEvent* event, void* context) { need_redraw = true; } } else if( - (event->type != InputTypeRelease && event->type != InputTypeRepeat) && + (event->type == InputTypeShort || event->type == InputTypeLong) && event->key == InputKeyOk) { - need_redraw = true; + need_redraw = false; bool updated = false; uint32_t frequency_to_save; with_view_model( @@ -286,21 +286,19 @@ bool subghz_frequency_analyzer_input(InputEvent* event, void* context) { instance->worker, frequency_candidate); if(frequency_candidate > 0 && frequency_candidate != model->frequency_to_save) { model->frequency_to_save = frequency_candidate; + frequency_to_save = frequency_candidate; updated = true; } }, - true); + false); if(updated) { instance->callback(SubGhzCustomEventViewFreqAnalOkShort, instance->context); } - // First the device receives short, then when user release button we get long + // If it was a long press also send a second event if(event->type == InputTypeLong && frequency_to_save > 0) { - // Stop worker - if(subghz_frequency_analyzer_worker_is_running(instance->worker)) { - subghz_frequency_analyzer_worker_stop(instance->worker); - } + // Worker stopped on app thread instead of GUI thread when switching scene in callback instance->callback(SubGhzCustomEventViewFreqAnalOkLong, instance->context); } From 9cfa400ff4e11048564ef6b45982f4b7847d1ddf Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Wed, 25 Jun 2025 02:57:45 +0300 Subject: [PATCH 86/92] upd changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cc296bb35..dec3fd29b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,8 @@ * OFW PR 4210: Infrared: Add text scroll to remote buttons (by @956MB) * Apps: **Check out more Apps updates and fixes by following** [this link](https://github.com/xMasterX/all-the-plugins/commits/dev) ## Other changes +* SubGHz: Fix possible frequency analyzer deadlock when holding Ok (by @WillyJL) +* RFID: Add DEZ10 representation to EM410X (by @realcatgirly) * OFW PR 4205: fix sample durations when using external CC1101 (by @Aerosnail) * OFW PR 4206: Stop JS PWM on exit (by @portasynthinca3) * OFW PR 4212: Fixed inverted logic condition in subghz chat cli (by @GameLord2011) From 5ee3f7c68d253f3b6cb16bb4e1b94d11dca72fe1 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Fri, 27 Jun 2025 21:17:13 +0300 Subject: [PATCH 87/92] fix lfrfid write very strange issue with lcd backlight --- lib/lfrfid/lfrfid_worker_modes.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/lfrfid/lfrfid_worker_modes.c b/lib/lfrfid/lfrfid_worker_modes.c index 645d2bd82..036e9cf24 100644 --- a/lib/lfrfid/lfrfid_worker_modes.c +++ b/lib/lfrfid/lfrfid_worker_modes.c @@ -523,6 +523,7 @@ static void lfrfid_worker_mode_write_process(LFRFIDWorker* worker) { while(!lfrfid_worker_check_for_stop(worker)) { FURI_LOG_D(TAG, "Data write"); + furi_delay_ms(5); // halt uint16_t skips = 0; for(size_t i = 0; i < LFRFIDWriteTypeMax; i++) { memset(request, 0, sizeof(LFRFIDWriteRequest)); @@ -626,6 +627,7 @@ static void lfrfid_worker_mode_write_and_set_pass_process(LFRFIDWorker* worker) if(can_be_written) { while(!lfrfid_worker_check_for_stop(worker)) { FURI_LOG_D(TAG, "Data write with pass"); + furi_delay_ms(5); // halt LfRfid* app = worker->cb_ctx; uint32_t pass = bit_lib_bytes_to_num_be(app->password, 4); From 91a235b395eb1ac0dab0c02d204d4b21b2f73afc Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Fri, 27 Jun 2025 21:22:52 +0300 Subject: [PATCH 88/92] upd changelog --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dec3fd29b..a16db022e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,8 +14,9 @@ * OFW PR 4210: Infrared: Add text scroll to remote buttons (by @956MB) * Apps: **Check out more Apps updates and fixes by following** [this link](https://github.com/xMasterX/all-the-plugins/commits/dev) ## Other changes +* RFID 125khz: Fix strange bug with LCD backlight going off after doing "Write" * SubGHz: Fix possible frequency analyzer deadlock when holding Ok (by @WillyJL) -* RFID: Add DEZ10 representation to EM410X (by @realcatgirly) +* RFID 125khz: Add DEZ10 representation to EM410X (by @realcatgirly) * OFW PR 4205: fix sample durations when using external CC1101 (by @Aerosnail) * OFW PR 4206: Stop JS PWM on exit (by @portasynthinca3) * OFW PR 4212: Fixed inverted logic condition in subghz chat cli (by @GameLord2011) From aa6433341c06ed4d472180d4d65ad78f20eb7d61 Mon Sep 17 00:00:00 2001 From: WillyJL <49810075+WillyJL@users.noreply.github.com> Date: Sun, 29 Jun 2025 01:54:33 +0100 Subject: [PATCH 89/92] Fix typos --- .../type_4_tag/type_4_tag_poller_i.c | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c b/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c index fd047f9a1..52f3d6220 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c @@ -72,7 +72,7 @@ Type4TagError type_4_tag_apdu_trx(Type4TagPoller* instance, BitBuffer* tx_buf, B } } -static Type4TagError type_5_tag_poller_iso_select_name( +static Type4TagError type_4_tag_poller_iso_select_name( Type4TagPoller* instance, const uint8_t* name, uint8_t name_len) { @@ -96,7 +96,7 @@ static Type4TagError type_5_tag_poller_iso_select_name( } static Type4TagError - type_5_tag_poller_iso_select_file(Type4TagPoller* instance, uint16_t file_id) { + type_4_tag_poller_iso_select_file(Type4TagPoller* instance, uint16_t file_id) { static const uint8_t type_4_tag_iso_select_file_apdu[] = { TYPE_4_TAG_ISO_SELECT_CMD, TYPE_4_TAG_ISO_SELECT_P1_BY_EF_ID, @@ -118,7 +118,7 @@ static Type4TagError return error; } -static Type4TagError type_5_tag_poller_iso_read( +static Type4TagError type_4_tag_poller_iso_read( Type4TagPoller* instance, uint16_t offset, uint16_t length, @@ -168,7 +168,7 @@ static Type4TagError type_5_tag_poller_iso_read( return Type4TagErrorNone; } -static Type4TagError type_5_tag_poller_iso_write( +static Type4TagError type_4_tag_poller_iso_write( Type4TagPoller* instance, uint16_t offset, uint16_t length, @@ -268,7 +268,7 @@ Type4TagError type_4_tag_poller_select_app(Type4TagPoller* instance) { furi_check(instance); FURI_LOG_D(TAG, "Select application"); - return type_5_tag_poller_iso_select_name( + return type_4_tag_poller_iso_select_name( instance, type_4_tag_iso_df_name, sizeof(type_4_tag_iso_df_name)); } @@ -279,19 +279,19 @@ Type4TagError type_4_tag_poller_read_cc(Type4TagPoller* instance) { do { FURI_LOG_D(TAG, "Select CC"); - error = type_5_tag_poller_iso_select_file(instance, TYPE_4_TAG_T4T_CC_EF_ID); + error = type_4_tag_poller_iso_select_file(instance, TYPE_4_TAG_T4T_CC_EF_ID); if(error != Type4TagErrorNone) break; FURI_LOG_D(TAG, "Read CC len"); uint16_t cc_len; uint8_t cc_len_be[sizeof(cc_len)]; - error = type_5_tag_poller_iso_read(instance, 0, sizeof(cc_len_be), cc_len_be); + error = type_4_tag_poller_iso_read(instance, 0, sizeof(cc_len_be), cc_len_be); if(error != Type4TagErrorNone) break; cc_len = bit_lib_bytes_to_num_be(cc_len_be, sizeof(cc_len_be)); FURI_LOG_D(TAG, "Read CC"); uint8_t cc_buf[cc_len]; - error = type_5_tag_poller_iso_read(instance, 0, sizeof(cc_buf), cc_buf); + error = type_4_tag_poller_iso_read(instance, 0, sizeof(cc_buf), cc_buf); if(error != Type4TagErrorNone) break; error = type_4_tag_cc_parse(instance->data, cc_buf, sizeof(cc_buf)); @@ -311,13 +311,13 @@ Type4TagError type_4_tag_poller_read_ndef(Type4TagPoller* instance) { do { FURI_LOG_D(TAG, "Select NDEF"); - error = type_5_tag_poller_iso_select_file(instance, instance->data->ndef_file_id); + error = type_4_tag_poller_iso_select_file(instance, instance->data->ndef_file_id); if(error != Type4TagErrorNone) break; FURI_LOG_D(TAG, "Read NDEF len"); uint16_t ndef_len; uint8_t ndef_len_be[sizeof(ndef_len)]; - error = type_5_tag_poller_iso_read(instance, 0, sizeof(ndef_len_be), ndef_len_be); + error = type_4_tag_poller_iso_read(instance, 0, sizeof(ndef_len_be), ndef_len_be); if(error != Type4TagErrorNone) break; ndef_len = bit_lib_bytes_to_num_be(ndef_len_be, sizeof(ndef_len_be)); @@ -329,7 +329,7 @@ Type4TagError type_4_tag_poller_read_ndef(Type4TagPoller* instance) { FURI_LOG_D(TAG, "Read NDEF"); simple_array_init(instance->data->ndef_data, ndef_len); uint8_t* ndef_buf = simple_array_get_data(instance->data->ndef_data); - error = type_5_tag_poller_iso_read(instance, sizeof(ndef_len), ndef_len, ndef_buf); + error = type_4_tag_poller_iso_read(instance, sizeof(ndef_len), ndef_len, ndef_buf); if(error != Type4TagErrorNone) break; FURI_LOG_D( @@ -405,7 +405,7 @@ Type4TagError type_4_tag_poller_create_cc(Type4TagPoller* instance) { } FURI_LOG_D(TAG, "Select CC"); - error = type_5_tag_poller_iso_select_file(instance, TYPE_4_TAG_T4T_CC_EF_ID); + error = type_4_tag_poller_iso_select_file(instance, TYPE_4_TAG_T4T_CC_EF_ID); if(error != Type4TagErrorNone) break; FURI_LOG_D(TAG, "Write DESFire CC"); @@ -419,7 +419,7 @@ Type4TagError type_4_tag_poller_create_cc(Type4TagPoller* instance) { instance->data->is_tag_specific = true; uint8_t cc_buf[TYPE_4_TAG_T4T_CC_MIN_SIZE]; type_4_tag_cc_dump(instance->data, cc_buf, sizeof(cc_buf)); - error = type_5_tag_poller_iso_write(instance, 0, sizeof(cc_buf), cc_buf); + error = type_4_tag_poller_iso_write(instance, 0, sizeof(cc_buf), cc_buf); if(error != Type4TagErrorNone) break; error = Type4TagErrorNone; @@ -473,14 +473,14 @@ Type4TagError type_4_tag_poller_write_ndef(Type4TagPoller* instance) { do { FURI_LOG_D(TAG, "Select NDEF"); - error = type_5_tag_poller_iso_select_file(instance, instance->data->ndef_file_id); + error = type_4_tag_poller_iso_select_file(instance, instance->data->ndef_file_id); if(error != Type4TagErrorNone) break; FURI_LOG_D(TAG, "Write NDEF len"); uint16_t ndef_len = simple_array_get_count(instance->data->ndef_data); uint8_t ndef_len_be[sizeof(ndef_len)]; bit_lib_num_to_bytes_be(ndef_len, sizeof(ndef_len_be), ndef_len_be); - error = type_5_tag_poller_iso_write(instance, 0, sizeof(ndef_len_be), ndef_len_be); + error = type_4_tag_poller_iso_write(instance, 0, sizeof(ndef_len_be), ndef_len_be); if(error != Type4TagErrorNone) break; if(ndef_len == 0) { @@ -490,7 +490,7 @@ Type4TagError type_4_tag_poller_write_ndef(Type4TagPoller* instance) { FURI_LOG_D(TAG, "Write NDEF"); uint8_t* ndef_buf = simple_array_get_data(instance->data->ndef_data); - error = type_5_tag_poller_iso_write(instance, sizeof(ndef_len), ndef_len, ndef_buf); + error = type_4_tag_poller_iso_write(instance, sizeof(ndef_len), ndef_len, ndef_buf); if(error != Type4TagErrorNone) break; FURI_LOG_D( From a47f387de11ac21d72230530612f3c139888300a Mon Sep 17 00:00:00 2001 From: WillyJL <49810075+WillyJL@users.noreply.github.com> Date: Sun, 29 Jun 2025 01:56:41 +0100 Subject: [PATCH 90/92] smh --nobuild --- applications/main/subghz/scenes/subghz_scene_receiver_config.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/main/subghz/scenes/subghz_scene_receiver_config.c b/applications/main/subghz/scenes/subghz_scene_receiver_config.c index 35e1a809b..ff7f67988 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver_config.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver_config.c @@ -419,7 +419,7 @@ static void subghz_scene_receiver_config_set_alarms(VariableItem* item) { subghz_scene_receiver_config_set_ignore_filter(item, SubGhzProtocolFilter_Alarms); } -static void subghz_scene_receiver_config_set_Sensors(VariableItem* item) { +static void subghz_scene_receiver_config_set_sensors(VariableItem* item) { subghz_scene_receiver_config_set_ignore_filter(item, SubGhzProtocolFilter_Sensors); } From b71e7e05f56f46539fc1204a20d7488f4e45d8df Mon Sep 17 00:00:00 2001 From: WillyJL <49810075+WillyJL@users.noreply.github.com> Date: Sun, 29 Jun 2025 04:21:17 +0100 Subject: [PATCH 91/92] NFC Maker: Type 4 Tag support --nobuild --- CHANGELOG.md | 1 + applications/external | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c2ebcc412..65841e939 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -53,6 +53,7 @@ - Portal Of Flipper: Implement auth for the xbox 360 (by @sanjay900) - Quac: Fix link imports not working, fix RAW Sub-GHz files (by @xMasterX & @WillyJL), add Sub-GHz duration setting (by @rdefeo) - Metroflip: Added 80+ card AIDs, more AIDs for DESFire, Calypso card saving, fixed DESFire parsing, Navigo crash, Clipper timestamp (by @luu176) + - NFC Maker: Type 4 Tag support, options for saving as NTAG413 DNA, NTAG424 DNA, MIFARE DESFire, Generic Type 4 Tag (by @WillyJL) - Seos Compatible: Add support for reading Seader files that have SIO, Add custom zero key ADF OID (by @bettse) - WiFi Marauder: Support for new commands from ESP32Marauder 1.6.x (by @justcallmekoko) - VGM Tool: Fixed RGB firmware UART regression (by @WillyJL) diff --git a/applications/external b/applications/external index ddb800eab..bb0acee3e 160000 --- a/applications/external +++ b/applications/external @@ -1 +1 @@ -Subproject commit ddb800eab514905ea4462ec4e6f4fa03b3f26d41 +Subproject commit bb0acee3e6ed08edd6144f4a8c81bcb1375229dd From f044a37ec7c328903e275bee1001814bef39e0ec Mon Sep 17 00:00:00 2001 From: WillyJL <49810075+WillyJL@users.noreply.github.com> Date: Sun, 29 Jun 2025 04:41:15 +0100 Subject: [PATCH 92/92] Update apps - ESP Flasher: Bump Marauder 1.7.1 (by justcallmekoko) - NFC Magic: Fix Ultimate Magic Gen4 Max Block Number Set Incorrectly for the NTAG Protocol (by alfie65536) --- CHANGELOG.md | 15 ++++++++------- applications/external | 2 +- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 65841e939..b64ff1530 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ - Sub-GHz: Removed Weather Station, POCSAG and TPMS protocols from main app, now found in external apps (by @WillyJL) - Momentum previously supported these external app's protocols in the main Sub-GHz app - However, with more protocols added over time to the main app, they are now too many - - The Flipper CPU cannot keep up with all the data received with this number of protocols + - The Flipper CPU cannot keep up with all the data received with this number of protocols in some modulations - This caused some signals to not be received when they should, and in some cases could freeze the device - Since Weather Station, POCSAG and TPMS are available as external apps, they're now removed from the main Sub-GHz app - You can now find this functionality in `Apps > Sub-GHz`, where there are dedicated apps for these 3 functions @@ -30,7 +30,7 @@ - Exposed some APIs for apps to use that were meant to be public: - ISO14443-3A listener (emulation) - ISO15693-3 device (data), poller (reading), listener (emulation) - - Cleanup/reorder protocol definitions for tidyness + - Cleanup/reorder protocol definitions for tidiness - Ventra ULEV1 parser (by @hazardousvoltage) - Infrared: "Decode only" mode to ignore RAW signals, make buttons in learn scene more intuitive (by @WillyJL) - GUI: Added `submenu_remove_item()` to API, was needed for NFC Type 4 related changes (by @WillyJL) @@ -44,19 +44,20 @@ - Blackhat: Fix Run Script command (by @o7-machinehum), fix NULL ptr scrolling menu suboptions, more wlan selectors and stop options (by @WillyJL) - Camera Suite: Handle 128x128 image, fix image rotation bug (by @rnadyrshin) - Combo Cracker: Many usability improvements (by @CharlesTheGreat77) - - ESP Flasher: Bump Marauder 1.7.0 (by @justcallmekoko), FlipperHTTP 2.0 (by @jblanked) + - ESP Flasher: Bump Marauder 1.7.1 (by @justcallmekoko), FlipperHTTP 2.0 (by @jblanked) - Flame RNG: New App Icon (by @Kuronons), Improved the RNG using the hardware RNG and some bit mixing (by @OrionW06) - FlipDownloader: Add Picoware (by @jblanked) - FlipWiFi: Added Deauthentication mode (by @jblanked) + - Metroflip: Added 80+ card AIDs, more AIDs for DESFire, Calypso card saving, fixed DESFire parsing, Navigo crash, Clipper timestamp (by @luu176) + - NFC Magic: Fix Ultimate Magic Gen4 Max Block Number Set Incorrectly for the NTAG Protocol (by @alfie65536) + - NFC Maker: Type 4 Tag support, options for saving as NTAG413 DNA, NTAG424 DNA, MIFARE DESFire, Generic Type 4 Tag (by @WillyJL) - Passy: Capitalize document number (by @bettse) - Picopass: Bugfixes and refactoring (by @bettse) - Portal Of Flipper: Implement auth for the xbox 360 (by @sanjay900) - Quac: Fix link imports not working, fix RAW Sub-GHz files (by @xMasterX & @WillyJL), add Sub-GHz duration setting (by @rdefeo) - - Metroflip: Added 80+ card AIDs, more AIDs for DESFire, Calypso card saving, fixed DESFire parsing, Navigo crash, Clipper timestamp (by @luu176) - - NFC Maker: Type 4 Tag support, options for saving as NTAG413 DNA, NTAG424 DNA, MIFARE DESFire, Generic Type 4 Tag (by @WillyJL) - Seos Compatible: Add support for reading Seader files that have SIO, Add custom zero key ADF OID (by @bettse) - - WiFi Marauder: Support for new commands from ESP32Marauder 1.6.x (by @justcallmekoko) - VGM Tool: Fixed RGB firmware UART regression (by @WillyJL) + - WiFi Marauder: Support for new commands from ESP32Marauder 1.6.x (by @justcallmekoko) - UL: Sub-GHz Playlist: Add support for custom modulation presets, remake with txrx library and support for dynamic signals, cleanup code (by @xMasterX) - RFID: Add DEZ10 representation to EM410X (by @realcatgirly) - Furi: Re-enabled file paths in furi_check crash messages (by @WillyJL) @@ -85,7 +86,7 @@ - UL: Fix CAME 24bit decoder (by @xMasterX) - UL: Tune holtek ht12x to decode holtek only and not conflict with came 12bit (by @xMasterX) - UL: Fix Rename scene bug, that was replacing file name with random name when Rename is opened then closed then opened again (by @xMasterX) -- RFID: Fix strange bug with LCD backlight going off after doing "Write" (by @xMasterX) +- UL: RFID: Fix strange bug with LCD backlight going off after doing "Write" (by @xMasterX) ### Removed: - Sub-GHz: Removed Weather Station, POCSAG and TPMS protocols from main app, now found in external apps (by @WillyJL) diff --git a/applications/external b/applications/external index bb0acee3e..66f4fbefa 160000 --- a/applications/external +++ b/applications/external @@ -1 +1 @@ -Subproject commit bb0acee3e6ed08edd6144f4a8c81bcb1375229dd +Subproject commit 66f4fbefa46e6bd649eab4c8a969b1f2b4fc8e8c