diff --git a/applications/main/nfc/api/nfc_app_api_table_i.h b/applications/main/nfc/api/nfc_app_api_table_i.h index 790fa5766..d31857b09 100644 --- a/applications/main/nfc/api/nfc_app_api_table_i.h +++ b/applications/main/nfc/api/nfc_app_api_table_i.h @@ -1,5 +1,8 @@ #include "gallagher/gallagher_util.h" #include "mosgortrans/mosgortrans_util.h" +#include "../nfc_app_i.h" +#include "../helpers/protocol_support/nfc_protocol_support_gui_common.h" +#include "../helpers/protocol_support/nfc_protocol_support_unlock_helper.h" /* * A list of app's private functions and objects to expose for plugins. @@ -22,4 +25,20 @@ static constexpr auto nfc_app_api_table = sort(create_array_t( (FuriString * str, const char* name, uint8_t prefix_separator_cnt, - uint8_t suffix_separator_cnt)))); + uint8_t suffix_separator_cnt)), + API_METHOD( + nfc_append_filename_string_when_present, + void, + (NfcApp * instance, FuriString* string)), + API_METHOD(nfc_protocol_support_common_submenu_callback, void, (void* context, uint32_t index)), + API_METHOD( + nfc_protocol_support_common_widget_callback, + void, + (GuiButtonType result, InputType type, void* context)), + API_METHOD(nfc_protocol_support_common_on_enter_empty, void, (NfcApp * instance)), + API_METHOD( + nfc_protocol_support_common_on_event_empty, + bool, + (NfcApp * instance, SceneManagerEvent event)), + API_METHOD(nfc_unlock_helper_setup_from_state, void, (NfcApp * instance)), + API_METHOD(nfc_unlock_helper_card_detected_handler, void, (NfcApp * instance)))); diff --git a/applications/main/nfc/application.fam b/applications/main/nfc/application.fam index 96ac4c3b7..5699685d3 100644 --- a/applications/main/nfc/application.fam +++ b/applications/main/nfc/application.fam @@ -18,6 +18,205 @@ App( fap_category="NFC", ) +# Protocol support plugins + +App( + appid="nfc_iso14443_3a", + targets=["f7"], + apptype=FlipperAppType.PLUGIN, + entry_point="nfc_iso14443_3a_ep", + requires=["nfc"], + sources=[ + "helpers/protocol_support/iso14443_3a/*.c", + ], + fal_embedded=True, +) + +App( + appid="nfc_iso14443_3b", + targets=["f7"], + apptype=FlipperAppType.PLUGIN, + entry_point="nfc_iso14443_3b_ep", + requires=["nfc"], + sources=[ + "helpers/protocol_support/iso14443_3b/*.c", + ], + fal_embedded=True, +) + +App( + appid="nfc_iso14443_4a", + targets=["f7"], + apptype=FlipperAppType.PLUGIN, + entry_point="nfc_iso14443_4a_ep", + requires=["nfc"], + sources=[ + "helpers/protocol_support/iso14443_4a/*.c", + "helpers/protocol_support/iso14443_3a/*.c", + ], + fal_embedded=True, +) + +App( + appid="nfc_iso14443_4b", + targets=["f7"], + apptype=FlipperAppType.PLUGIN, + entry_point="nfc_iso14443_4b_ep", + requires=["nfc"], + sources=[ + "helpers/protocol_support/iso14443_4b/*.c", + "helpers/protocol_support/iso14443_3b/*.c", + ], + fal_embedded=True, +) + +App( + appid="nfc_iso15693_3", + targets=["f7"], + apptype=FlipperAppType.PLUGIN, + entry_point="nfc_iso15693_3_ep", + requires=["nfc"], + sources=[ + "helpers/protocol_support/iso15693_3/*.c", + ], + fal_embedded=True, +) + +App( + appid="nfc_felica", + targets=["f7"], + apptype=FlipperAppType.PLUGIN, + entry_point="nfc_felica_ep", + requires=["nfc"], + sources=[ + "helpers/protocol_support/felica/*.c", + "helpers/felica_*.c", + ], + fal_embedded=True, +) + +App( + appid="nfc_mf_ultralight", + targets=["f7"], + apptype=FlipperAppType.PLUGIN, + entry_point="nfc_mf_ultralight_ep", + requires=["nfc"], + sources=[ + "helpers/protocol_support/mf_ultralight/*.c", + "helpers/protocol_support/iso14443_3a/*.c", + "helpers/mf_ultralight_*.c", + ], + fap_libs=["mbedtls"], + fal_embedded=True, +) + +App( + appid="nfc_mf_classic", + targets=["f7"], + apptype=FlipperAppType.PLUGIN, + entry_point="nfc_mf_classic_ep", + requires=["nfc"], + sources=[ + "helpers/protocol_support/mf_classic/*.c", + "helpers/protocol_support/iso14443_3a/*.c", + "helpers/mf_classic_*.c", + ], + fal_embedded=True, +) + +App( + appid="nfc_mf_plus", + targets=["f7"], + apptype=FlipperAppType.PLUGIN, + entry_point="nfc_mf_plus_ep", + requires=["nfc"], + sources=[ + "helpers/protocol_support/mf_plus/*.c", + "helpers/protocol_support/iso14443_4a/*.c", + "helpers/protocol_support/iso14443_3a/*.c", + ], + fal_embedded=True, +) + +App( + appid="nfc_mf_desfire", + targets=["f7"], + apptype=FlipperAppType.PLUGIN, + entry_point="nfc_mf_desfire_ep", + requires=["nfc"], + sources=[ + "helpers/protocol_support/mf_desfire/*.c", + "helpers/protocol_support/iso14443_4a/*.c", + "helpers/protocol_support/iso14443_3a/*.c", + ], + fal_embedded=True, +) + +App( + appid="nfc_slix", + targets=["f7"], + apptype=FlipperAppType.PLUGIN, + entry_point="nfc_slix_ep", + requires=["nfc"], + sources=[ + "helpers/protocol_support/slix/*.c", + "helpers/protocol_support/iso15693_3/*.c", + ], + fal_embedded=True, +) + +App( + appid="nfc_st25tb", + targets=["f7"], + apptype=FlipperAppType.PLUGIN, + entry_point="nfc_st25tb_ep", + requires=["nfc"], + sources=[ + "helpers/protocol_support/st25tb/*.c", + ], + fal_embedded=True, +) + + +App( + appid="nfc_ntag4xx", + targets=["f7"], + apptype=FlipperAppType.PLUGIN, + entry_point="nfc_ntag4xx_ep", + requires=["nfc"], + sources=[ + "helpers/protocol_support/ntag4xx/*.c", + "helpers/protocol_support/iso14443_4a/*.c", + "helpers/protocol_support/iso14443_3a/*.c", + ], + fal_embedded=True, +) +App( + appid="nfc_type_4_tag", + targets=["f7"], + apptype=FlipperAppType.PLUGIN, + entry_point="nfc_type_4_tag_ep", + requires=["nfc"], + sources=[ + "helpers/protocol_support/type_4_tag/*.c", + "helpers/protocol_support/iso14443_4a/*.c", + "helpers/protocol_support/iso14443_3a/*.c", + ], + fal_embedded=True, +) + +App( + appid="nfc_emv", + targets=["f7"], + apptype=FlipperAppType.PLUGIN, + entry_point="nfc_emv_ep", + requires=["nfc"], + sources=[ + "helpers/protocol_support/emv/*.c", + ], + fal_embedded=True, +) + # Parser plugins App( @@ -29,6 +228,15 @@ App( sources=["plugins/supported_cards/all_in_one.c"], ) +App( + appid="smartrider_parser", + apptype=FlipperAppType.PLUGIN, + entry_point="smartrider_plugin_ep", + targets=["f7"], + requires=["nfc"], + sources=["plugins/supported_cards/smartrider.c"], +) + App( appid="microel_parser", apptype=FlipperAppType.PLUGIN, @@ -254,6 +462,7 @@ App( requires=["nfc"], sources=["plugins/supported_cards/ndef.c"], ) + App( appid="ndef_mfc_parser", apptype=FlipperAppType.PLUGIN, @@ -274,6 +483,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="itso_parser", apptype=FlipperAppType.PLUGIN, @@ -320,6 +539,33 @@ App( sources=["plugins/supported_cards/disney_infinity.c"], ) +App( + appid="sonicare_parser", + apptype=FlipperAppType.PLUGIN, + entry_point="sonicare_plugin_ep", + targets=["f7"], + requires=["nfc"], + sources=["plugins/supported_cards/sonicare.c"], +) + +App( + appid="csc_parser", + apptype=FlipperAppType.PLUGIN, + entry_point="csc_plugin_ep", + targets=["f7"], + requires=["nfc"], + sources=["plugins/supported_cards/csc.c"], +) + +App( + appid="ventra_parser", + apptype=FlipperAppType.PLUGIN, + entry_point="ventra_plugin_ep", + targets=["f7"], + requires=["nfc"], + sources=["plugins/supported_cards/ventra.c"], +) + App( appid="cli_nfc", targets=["f7"], diff --git a/applications/main/nfc/helpers/nfc_custom_event.h b/applications/main/nfc/helpers/nfc_custom_event.h index 16fbc4749..de8cf23bb 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, @@ -30,4 +31,6 @@ typedef enum { NfcCustomEventPollerFailure, NfcCustomEventListenerUpdate, + + NfcCustomEventEmulationTimeExpired, } NfcCustomEvent; diff --git a/applications/main/nfc/helpers/nfc_supported_cards.c b/applications/main/nfc/helpers/nfc_supported_cards.c index 6513eef5f..5c974f463 100644 --- a/applications/main/nfc/helpers/nfc_supported_cards.c +++ b/applications/main/nfc/helpers/nfc_supported_cards.c @@ -1,11 +1,9 @@ #include "nfc_supported_cards.h" -#include "../api/nfc_app_api_interface.h" #include "../plugins/supported_cards/nfc_supported_card_plugin.h" #include #include -#include #include #include @@ -52,12 +50,9 @@ struct NfcSupportedCards { NfcSupportedCardsLoadContext* load_context; }; -NfcSupportedCards* nfc_supported_cards_alloc(void) { +NfcSupportedCards* nfc_supported_cards_alloc(CompositeApiResolver* api_resolver) { NfcSupportedCards* instance = malloc(sizeof(NfcSupportedCards)); - - instance->api_resolver = composite_api_resolver_alloc(); - composite_api_resolver_add(instance->api_resolver, firmware_api_interface); - composite_api_resolver_add(instance->api_resolver, nfc_application_api_interface); + instance->api_resolver = api_resolver; NfcSupportedCardsPluginCache_init(instance->plugins_cache_arr); @@ -76,7 +71,6 @@ void nfc_supported_cards_free(NfcSupportedCards* instance) { } NfcSupportedCardsPluginCache_clear(instance->plugins_cache_arr); - composite_api_resolver_free(instance->api_resolver); free(instance); } diff --git a/applications/main/nfc/helpers/nfc_supported_cards.h b/applications/main/nfc/helpers/nfc_supported_cards.h index 5eaee360a..363abe30b 100644 --- a/applications/main/nfc/helpers/nfc_supported_cards.h +++ b/applications/main/nfc/helpers/nfc_supported_cards.h @@ -7,6 +7,7 @@ #pragma once #include +#include #include #include @@ -25,7 +26,7 @@ typedef struct NfcSupportedCards NfcSupportedCards; * * @return pointer to allocated NfcSupportedCards instance. */ -NfcSupportedCards* nfc_supported_cards_alloc(void); +NfcSupportedCards* nfc_supported_cards_alloc(CompositeApiResolver* api_resolver); /** * @brief Delete an NfcSupportedCards instance diff --git a/applications/main/nfc/helpers/protocol_support/emv/emv.c b/applications/main/nfc/helpers/protocol_support/emv/emv.c index 06e2ca624..a2232b00e 100644 --- a/applications/main/nfc/helpers/protocol_support/emv/emv.c +++ b/applications/main/nfc/helpers/protocol_support/emv/emv.c @@ -128,4 +128,11 @@ const NfcProtocolSupportBase nfc_protocol_support_emv = { .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, + }, }; + +NFC_PROTOCOL_SUPPORT_PLUGIN(emv, NfcProtocolEmv); diff --git a/applications/main/nfc/helpers/protocol_support/felica/felica.c b/applications/main/nfc/helpers/protocol_support/felica/felica.c index 561cd4d2e..0c4234bad 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 = { @@ -213,4 +204,11 @@ 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, + }, }; + +NFC_PROTOCOL_SUPPORT_PLUGIN(felica, NfcProtocolFelica); 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..adcaaf776 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 = { @@ -144,4 +136,11 @@ 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, + }, }; + +NFC_PROTOCOL_SUPPORT_PLUGIN(iso14443_3a, NfcProtocolIso14443_3a); 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..1a9931b28 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 = { @@ -111,4 +98,11 @@ 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, + }, }; + +NFC_PROTOCOL_SUPPORT_PLUGIN(iso14443_3b, NfcProtocolIso14443_3b); 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 17435ccd4..03f5b5ffd 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 = { @@ -147,4 +139,11 @@ 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, + }, }; + +NFC_PROTOCOL_SUPPORT_PLUGIN(iso14443_4a, NfcProtocolIso14443_4a); 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..9cd3829f9 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 = { @@ -116,4 +98,11 @@ 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, + }, }; + +NFC_PROTOCOL_SUPPORT_PLUGIN(iso14443_4b, NfcProtocolIso14443_4b); 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..4c69fbe81 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 = { @@ -161,4 +153,11 @@ 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, + }, }; + +NFC_PROTOCOL_SUPPORT_PLUGIN(iso15693_3, NfcProtocolIso15693_3); 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 4f8540d6e..fdf7fb35a 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,10 +12,9 @@ enum { SubmenuIndexDetectReader = SubmenuIndexCommonMax, - SubmenuIndexWrite, - SubmenuIndexUpdate, SubmenuIndexDictAttack, SubmenuIndexCrackNonces, + SubmenuIndexUpdate, }; static void nfc_scene_info_on_enter_mf_classic(NfcApp* instance) { @@ -115,6 +114,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, @@ -160,6 +162,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, @@ -175,12 +179,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, @@ -215,9 +213,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; } else if(event.event == SubmenuIndexCrackNonces) { scene_manager_set_scene_state( instance->scene_manager, NfcSceneSaveConfirm, NfcSceneSaveConfirmStateCrackNonces); @@ -236,18 +231,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; } } @@ -267,8 +259,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 = { @@ -310,4 +365,11 @@ 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, + }, }; + +NFC_PROTOCOL_SUPPORT_PLUGIN(mf_classic, NfcProtocolMfClassic); 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..785ccc140 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,11 @@ 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, + }, }; + +NFC_PROTOCOL_SUPPORT_PLUGIN(mf_desfire, NfcProtocolMfDesfire); 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..2960886f7 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 = @@ -120,4 +134,11 @@ 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, + }, }; + +NFC_PROTOCOL_SUPPORT_PLUGIN(mf_plus, NfcProtocolMfPlus); 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/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..8eb42b89b 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,19 +249,57 @@ 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; } } 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 +341,11 @@ 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, + }, }; + +NFC_PROTOCOL_SUPPORT_PLUGIN(mf_ultralight, NfcProtocolMfUltralight); 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 fe3193385..008f9c317 100644 --- a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c @@ -9,9 +9,13 @@ #include "nfc/nfc_app_i.h" -#include "nfc_protocol_support_defs.h" +#include "nfc_protocol_support_base.h" #include "nfc_protocol_support_gui_common.h" +#include + +#define TAG "NfcProtocolSupport" + /** * @brief Common scene entry handler. * @@ -46,6 +50,147 @@ typedef struct { static const NfcProtocolSupportCommonSceneBase nfc_protocol_support_scenes[]; +const NfcProtocolSupportBase nfc_protocol_support_empty = { + .features = NfcProtocolFeatureNone, + + .scene_info = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_more_info = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_read = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .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_protocol_support_common_on_enter_empty, + .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_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, + }, +}; + +struct NfcProtocolSupport { + NfcProtocol protocol; + PluginManager* plugin_manager; + const NfcProtocolSupportBase* base; +}; + +const char* nfc_protocol_support_plugin_names[NfcProtocolNum] = { + [NfcProtocolIso14443_3a] = "iso14443_3a", + [NfcProtocolIso14443_3b] = "iso14443_3b", + [NfcProtocolIso14443_4a] = "iso14443_4a", + [NfcProtocolIso14443_4b] = "iso14443_4b", + [NfcProtocolIso15693_3] = "iso15693_3", + [NfcProtocolFelica] = "felica", + [NfcProtocolMfUltralight] = "mf_ultralight", + [NfcProtocolMfClassic] = "mf_classic", + [NfcProtocolMfPlus] = "mf_plus", + [NfcProtocolMfDesfire] = "mf_desfire", + [NfcProtocolSlix] = "slix", + [NfcProtocolSt25tb] = "st25tb", + [NfcProtocolNtag4xx] = "ntag4xx", + [NfcProtocolType4Tag] = "type_4_tag", + [NfcProtocolEmv] = "emv", + /* Add new protocol support plugin names here */ +}; + +void nfc_protocol_support_alloc(NfcProtocol protocol, void* context) { + furi_assert(context); + + NfcApp* instance = context; + + NfcProtocolSupport* protocol_support = malloc(sizeof(NfcProtocolSupport)); + protocol_support->protocol = protocol; + + const char* protocol_name = nfc_protocol_support_plugin_names[protocol]; + FuriString* plugin_path = + furi_string_alloc_printf(APP_ASSETS_PATH("plugins/nfc_%s.fal"), protocol_name); + FURI_LOG_D(TAG, "Loading %s", furi_string_get_cstr(plugin_path)); + + protocol_support->plugin_manager = plugin_manager_alloc( + NFC_PROTOCOL_SUPPORT_PLUGIN_APP_ID, + NFC_PROTOCOL_SUPPORT_PLUGIN_API_VERSION, + composite_api_resolver_get(instance->api_resolver)); + do { + if(plugin_manager_load_single( + protocol_support->plugin_manager, furi_string_get_cstr(plugin_path)) != + PluginManagerErrorNone) { + break; + } + const NfcProtocolSupportPlugin* plugin = + plugin_manager_get_ep(protocol_support->plugin_manager, 0); + + if(plugin->protocol != protocol) { + break; + } + + protocol_support->base = plugin->base; + } while(false); + if(!protocol_support->base) { + protocol_support->base = &nfc_protocol_support_empty; + plugin_manager_free(protocol_support->plugin_manager); + protocol_support->plugin_manager = NULL; + } + + furi_string_free(plugin_path); + + instance->protocol_support = protocol_support; +} + +void nfc_protocol_support_free(void* context) { + furi_assert(context); + + NfcApp* instance = context; + + if(instance->protocol_support->plugin_manager) { + plugin_manager_free(instance->protocol_support->plugin_manager); + } + free(instance->protocol_support); + instance->protocol_support = NULL; +} + +static const NfcProtocolSupportBase* + nfc_protocol_support_get(NfcProtocol protocol, void* context) { + furi_assert(context); + + NfcApp* instance = context; + + if(instance->protocol_support && instance->protocol_support->protocol != protocol) { + nfc_protocol_support_free(instance); + } + if(!instance->protocol_support) { + nfc_protocol_support_alloc(protocol, instance); + } + + return instance->protocol_support->base; +} + // Interface functions void nfc_protocol_support_on_enter(NfcProtocolSupportScene scene, void* context) { furi_assert(scene < NfcProtocolSupportSceneCount); @@ -74,17 +219,23 @@ void nfc_protocol_support_on_exit(NfcProtocolSupportScene scene, void* context) nfc_protocol_support_scenes[scene].on_exit(instance); } -bool nfc_protocol_support_has_feature(NfcProtocol protocol, NfcProtocolFeature feature) { - return nfc_protocol_support[protocol]->features & feature; +bool nfc_protocol_support_has_feature( + NfcProtocol protocol, + void* context, + NfcProtocolFeature feature) { + furi_assert(context); + + NfcApp* instance = context; + return nfc_protocol_support_get(protocol, instance)->features & feature; } // Common scene handlers // SceneInfo static void nfc_protocol_support_scene_info_on_enter(NfcApp* instance) { const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device); - nfc_protocol_support[protocol]->scene_info.on_enter(instance); + nfc_protocol_support_get(protocol, instance)->scene_info.on_enter(instance); - if(nfc_protocol_support_has_feature(protocol, NfcProtocolFeatureMoreInfo)) { + if(nfc_protocol_support_has_feature(protocol, instance, NfcProtocolFeatureMoreInfo)) { widget_add_button_element( instance->widget, GuiButtonTypeRight, @@ -124,7 +275,7 @@ static void nfc_protocol_support_scene_info_on_exit(NfcApp* instance) { // SceneMoreInfo static void nfc_protocol_support_scene_more_info_on_enter(NfcApp* instance) { const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device); - nfc_protocol_support[protocol]->scene_more_info.on_enter(instance); + nfc_protocol_support_get(protocol, instance)->scene_more_info.on_enter(instance); } static bool @@ -132,7 +283,8 @@ static bool bool consumed = false; const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device); - consumed = nfc_protocol_support[protocol]->scene_more_info.on_event(instance, event); + consumed = + nfc_protocol_support_get(protocol, instance)->scene_more_info.on_event(instance, event); return consumed; } @@ -157,7 +309,7 @@ static void nfc_protocol_support_scene_read_on_enter(NfcApp* instance) { //nfc_supported_cards_load_cache(instance->nfc_supported_cards); // Start poller with the appropriate callback - nfc_protocol_support[protocol]->scene_read.on_enter(instance); + nfc_protocol_support_get(protocol, instance)->scene_read.on_enter(instance); nfc_blink_read_start(instance); } @@ -186,7 +338,8 @@ static bool nfc_protocol_support_scene_read_on_event(NfcApp* instance, SceneMana } else { const NfcProtocol protocol = nfc_detected_protocols_get_selected(instance->detected_protocols); - consumed = nfc_protocol_support[protocol]->scene_read.on_event(instance, event); + consumed = nfc_protocol_support_get(protocol, instance) + ->scene_read.on_event(instance, event); } } else if(event.event == NfcCustomEventPollerFailure) { nfc_poller_stop(instance->poller); @@ -199,7 +352,8 @@ static bool nfc_protocol_support_scene_read_on_event(NfcApp* instance, SceneMana } else if(event.event == NfcCustomEventCardDetected) { const NfcProtocol protocol = nfc_detected_protocols_get_selected(instance->detected_protocols); - consumed = nfc_protocol_support[protocol]->scene_read.on_event(instance, event); + consumed = + nfc_protocol_support_get(protocol, instance)->scene_read.on_event(instance, event); } } else if(event.type == SceneManagerEventTypeBack) { nfc_poller_stop(instance->poller); @@ -241,7 +395,7 @@ static void nfc_protocol_support_scene_read_menu_on_enter(NfcApp* instance) { instance); } - if(nfc_protocol_support_has_feature(protocol, NfcProtocolFeatureEmulateUid)) { + if(nfc_protocol_support_has_feature(protocol, instance, NfcProtocolFeatureEmulateUid)) { submenu_add_item( submenu, "Emulate UID", @@ -249,7 +403,7 @@ static void nfc_protocol_support_scene_read_menu_on_enter(NfcApp* instance) { nfc_protocol_support_common_submenu_callback, instance); - } else if(nfc_protocol_support_has_feature(protocol, NfcProtocolFeatureEmulateFull)) { + } else if(nfc_protocol_support_has_feature(protocol, instance, NfcProtocolFeatureEmulateFull)) { submenu_add_item( submenu, "Emulate", @@ -258,7 +412,16 @@ static void nfc_protocol_support_scene_read_menu_on_enter(NfcApp* instance) { instance); } - nfc_protocol_support[protocol]->scene_read_menu.on_enter(instance); + if(nfc_protocol_support_has_feature(protocol, instance, NfcProtocolFeatureWrite)) { + submenu_add_item( + submenu, + "Write", + SubmenuIndexCommonWrite, + nfc_protocol_support_common_submenu_callback, + instance); + } + + nfc_protocol_support_get(protocol, instance)->scene_read_menu.on_enter(instance); submenu_add_item( submenu, @@ -291,9 +454,17 @@ 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 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); + consumed = nfc_protocol_support_get(protocol, instance) + ->scene_read_menu.on_event(instance, event); } } else if(event.type == SceneManagerEventTypeBack) { @@ -312,13 +483,17 @@ static void nfc_protocol_support_scene_read_saved_menu_on_exit(NfcApp* instance) static void nfc_protocol_support_scene_read_success_on_enter(NfcApp* instance) { Widget* widget = instance->widget; + popup_set_header(instance->popup, "Parsing", 85, 27, AlignCenter, AlignTop); + popup_set_icon(instance->popup, 12, 23, &A_Loading_24); + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup); + FuriString* temp_str = furi_string_alloc(); if(nfc_supported_cards_parse(instance->nfc_supported_cards, instance->nfc_device, temp_str)) { widget_add_text_scroll_element( instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str)); } else { const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device); - nfc_protocol_support[protocol]->scene_read_success.on_enter(instance); + nfc_protocol_support_get(protocol, instance)->scene_read_success.on_enter(instance); } furi_string_free(temp_str); @@ -366,7 +541,7 @@ static void nfc_protocol_support_scene_saved_menu_on_enter(NfcApp* instance) { Submenu* submenu = instance->submenu; // Header submenu items - if(nfc_protocol_support_has_feature(protocol, NfcProtocolFeatureEmulateUid)) { + if(nfc_protocol_support_has_feature(protocol, instance, NfcProtocolFeatureEmulateUid)) { submenu_add_item( submenu, "Emulate UID", @@ -374,7 +549,7 @@ static void nfc_protocol_support_scene_saved_menu_on_enter(NfcApp* instance) { nfc_protocol_support_common_submenu_callback, instance); - } else if(nfc_protocol_support_has_feature(protocol, NfcProtocolFeatureEmulateFull)) { + } else if(nfc_protocol_support_has_feature(protocol, instance, NfcProtocolFeatureEmulateFull)) { submenu_add_item( submenu, "Emulate", @@ -383,7 +558,16 @@ static void nfc_protocol_support_scene_saved_menu_on_enter(NfcApp* instance) { instance); } - if(nfc_protocol_support_has_feature(protocol, NfcProtocolFeatureEditUid)) { + if(nfc_protocol_support_has_feature(protocol, instance, NfcProtocolFeatureWrite)) { + submenu_add_item( + submenu, + "Write", + SubmenuIndexCommonWrite, + nfc_protocol_support_common_submenu_callback, + instance); + } + + if(nfc_protocol_support_has_feature(protocol, instance, NfcProtocolFeatureEditUid)) { submenu_add_item( submenu, "Edit UID", @@ -393,7 +577,7 @@ static void nfc_protocol_support_scene_saved_menu_on_enter(NfcApp* instance) { } // Protocol-dependent menu items - nfc_protocol_support[protocol]->scene_saved_menu.on_enter(instance); + nfc_protocol_support_get(protocol, instance)->scene_saved_menu.on_enter(instance); // Trailer submenu items if(nfc_has_shadow_file(instance)) { @@ -456,12 +640,19 @@ 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; } else { const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device); - consumed = nfc_protocol_support[protocol]->scene_saved_menu.on_event(instance, event); + consumed = nfc_protocol_support_get(protocol, instance) + ->scene_saved_menu.on_event(instance, event); } } else if(event.type == SceneManagerEventTypeBack) { @@ -480,8 +671,18 @@ static void nfc_protocol_support_scene_save_name_on_enter(NfcApp* instance) { bool name_is_empty = furi_string_empty(instance->file_name); if(name_is_empty) { furi_string_set(instance->file_path, NFC_APP_FOLDER); - name_generator_make_auto_basic( - instance->text_store, NFC_TEXT_STORE_SIZE, NFC_APP_FILENAME_PREFIX); + FuriString* prefix = furi_string_alloc(); + furi_string_set(prefix, nfc_device_get_name(instance->nfc_device, NfcDeviceNameTypeFull)); + furi_string_replace(prefix, "Mifare", "MF"); + furi_string_replace(prefix, " Classic", "C"); // MFC + furi_string_replace(prefix, "Desfire", "Des"); // MF Des + furi_string_replace(prefix, "Ultralight", "UL"); // MF UL + furi_string_replace(prefix, " Plus", "+"); // NTAG I2C+ + furi_string_replace(prefix, " (Unknown)", ""); + furi_string_replace_all(prefix, " ", "_"); + name_generator_make_auto( + instance->text_store, NFC_TEXT_STORE_SIZE, furi_string_get_cstr(prefix)); + furi_string_free(prefix); furi_string_set(folder_path, NFC_APP_FOLDER); } else { nfc_text_store_set(instance, "%s", furi_string_get_cstr(instance->file_name)); @@ -527,8 +728,8 @@ static bool DolphinDeedNfcSave); const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device); - consumed = - nfc_protocol_support[protocol]->scene_save_name.on_event(instance, event); + consumed = nfc_protocol_support_get(protocol, instance) + ->scene_save_name.on_event(instance, event); } else { consumed = scene_manager_search_and_switch_to_previous_scene( instance->scene_manager, NfcSceneStart); @@ -570,9 +771,9 @@ static void nfc_protocol_support_scene_emulate_on_enter(NfcApp* instance) { FuriString* temp_str = furi_string_alloc(); const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device); - widget_add_icon_element(widget, 0, 3, &I_NFC_dolphin_emulation_51x64); + widget_add_icon_element(widget, 0, 0, &I_NFC_dolphin_emulation_51x64); - if(nfc_protocol_support_has_feature(protocol, NfcProtocolFeatureEmulateUid)) { + if(nfc_protocol_support_has_feature(protocol, instance, NfcProtocolFeatureEmulateUid)) { widget_add_string_element( widget, 90, 26, AlignCenter, AlignCenter, FontPrimary, "Emulating UID"); @@ -613,7 +814,7 @@ static void nfc_protocol_support_scene_emulate_on_enter(NfcApp* instance) { furi_string_reset(instance->text_box_store); // instance->listener is allocated in the respective on_enter() handler - nfc_protocol_support[protocol]->scene_emulate.on_enter(instance); + nfc_protocol_support_get(protocol, instance)->scene_emulate.on_enter(instance); scene_manager_set_scene_state( instance->scene_manager, NfcSceneEmulate, NfcSceneEmulateStateWidget); @@ -692,6 +893,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_get(protocol, instance)->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); } @@ -709,7 +1095,7 @@ static void nfc_protocol_support_scene_rpc_setup_ui_and_emulate(NfcApp* instance nfc_blink_emulate_start(instance); const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device); - nfc_protocol_support[protocol]->scene_emulate.on_enter(instance); + nfc_protocol_support_get(protocol, instance)->scene_emulate.on_enter(instance); instance->rpc_state = NfcRpcStateEmulating; } @@ -807,6 +1193,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.h b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.h index d3efc3a41..157d00b83 100644 --- a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.h +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.h @@ -40,7 +40,7 @@ * * | Filename | Explanation | * |:-----------------------|:------------| - * | protocol_name.h | Interface structure declaration used in `nfc_protocol_support_defs.c`. | + * | protocol_name.h | Interface structure declaration. | * | protocol_name.c | Protocol-specific scene implemenatations and definitions. | * | protocol_name_render.h | Protocol-specific rendering (formatting) functions. Used for converting protocol data into textual descriptions. | * | protocol_name_render.c | Implementations for functions declared in `protocol_name_render.h`.| @@ -65,8 +65,13 @@ * * After completing the protocol support, it must be registered within the application in order for it to be usable. * - * In nfc_protocol_support_defs.c, include the `protocol_name.h` file and add a new entry in the `nfc_protocol_support[]` - * array under the appropriate index. + * In `protocol_name.c`, add `NFC_PROTOCOL_SUPPORT_PLUGIN(protocol_name, NfcProtocolName)` at the bottom, + * below the `NfcProtocolSupportBase` structure definition. + * + * In `application.fam`, add a new entry for the plugin, following the other examples. + * + * In nfc_protocol_support.c, add a new entry in the `nfc_protocol_support_plugin_names[]` + * array under the appropriate index with the name of the plugin (without the `nfc_` prefix). * * ## Done! * @@ -80,6 +85,10 @@ #include "nfc_protocol_support_common.h" +typedef struct NfcProtocolSupport NfcProtocolSupport; + +void nfc_protocol_support_free(void* context); + /** * @brief Abstract interface for on_enter() scene handler. * @@ -113,4 +122,7 @@ bool nfc_protocol_support_on_event( */ void nfc_protocol_support_on_exit(NfcProtocolSupportScene scene, void* context); -bool nfc_protocol_support_has_feature(NfcProtocol protocol, NfcProtocolFeature feature); +bool nfc_protocol_support_has_feature( + NfcProtocol protocol, + void* context, + NfcProtocolFeature feature); 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..04648120e 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 @@ -9,6 +9,8 @@ #include "../../nfc_app.h" #include "../../nfc_app_i.h" +#include + /** * @brief Scene entry handler. * @@ -114,4 +116,47 @@ 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; + +/** + * @brief Unique string identifier for protocol support plugins. + */ +#define NFC_PROTOCOL_SUPPORT_PLUGIN_APP_ID "NfcProtocolSupportPlugin" + +/** + * @brief Currently supported plugin API version. + */ +#define NFC_PROTOCOL_SUPPORT_PLUGIN_API_VERSION 1 + +/** + * @brief Protocol support plugin interface. + */ +typedef struct { + NfcProtocol protocol; /**< Identifier of the protocol this plugin implements. */ + const NfcProtocolSupportBase* base; /**< Pointer to the protocol support interface. */ +} NfcProtocolSupportPlugin; + +#define NFC_PROTOCOL_SUPPORT_PLUGIN(name, protocol) \ + static const NfcProtocolSupportPlugin nfc_protocol_support_##name##_desc = { \ + protocol, \ + &nfc_protocol_support_##name, \ + }; \ + \ + static const FlipperAppPluginDescriptor plugin_descriptor_##name = { \ + .appid = NFC_PROTOCOL_SUPPORT_PLUGIN_APP_ID, \ + .ep_api_version = NFC_PROTOCOL_SUPPORT_PLUGIN_API_VERSION, \ + .entry_point = &nfc_protocol_support_##name##_desc, \ + }; \ + \ + const FlipperAppPluginDescriptor* nfc_##name##_ep(void) { \ + return &plugin_descriptor_##name; \ + } 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_defs.c b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_defs.c deleted file mode 100644 index a80cd6cc0..000000000 --- a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_defs.c +++ /dev/null @@ -1,49 +0,0 @@ -/** - * @file nfc_protocol_support_defs.c - * @brief Application-level protocol support definitions. - * - * This file is to be modified whenever support for - * a new protocol is to be added. - */ -#include "nfc_protocol_support_base.h" - -#include - -#include "iso14443_3a/iso14443_3a.h" -#include "iso14443_3b/iso14443_3b.h" -#include "iso14443_4a/iso14443_4a.h" -#include "iso14443_4b/iso14443_4b.h" -#include "iso15693_3/iso15693_3.h" -#include "felica/felica.h" -#include "mf_ultralight/mf_ultralight.h" -#include "mf_classic/mf_classic.h" -#include "mf_plus/mf_plus.h" -#include "mf_desfire/mf_desfire.h" -#include "emv/emv.h" -#include "slix/slix.h" -#include "st25tb/st25tb.h" - -/** - * @brief Array of pointers to concrete protocol support implementations. - * - * When adding support for a new protocol, add it to the end of this array - * under its respective index. - * - * @see nfc_protocol.h - */ -const NfcProtocolSupportBase* nfc_protocol_support[NfcProtocolNum] = { - [NfcProtocolIso14443_3a] = &nfc_protocol_support_iso14443_3a, - [NfcProtocolIso14443_3b] = &nfc_protocol_support_iso14443_3b, - [NfcProtocolIso14443_4a] = &nfc_protocol_support_iso14443_4a, - [NfcProtocolIso14443_4b] = &nfc_protocol_support_iso14443_4b, - [NfcProtocolIso15693_3] = &nfc_protocol_support_iso15693_3, - [NfcProtocolFelica] = &nfc_protocol_support_felica, - [NfcProtocolMfUltralight] = &nfc_protocol_support_mf_ultralight, - [NfcProtocolMfClassic] = &nfc_protocol_support_mf_classic, - [NfcProtocolMfPlus] = &nfc_protocol_support_mf_plus, - [NfcProtocolMfDesfire] = &nfc_protocol_support_mf_desfire, - [NfcProtocolSlix] = &nfc_protocol_support_slix, - [NfcProtocolSt25tb] = &nfc_protocol_support_st25tb, - [NfcProtocolEmv] = &nfc_protocol_support_emv, - /* Add new protocol support implementations here */ -}; diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_defs.h b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_defs.h deleted file mode 100644 index 7a9d5b637..000000000 --- a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_defs.h +++ /dev/null @@ -1,12 +0,0 @@ -/** - * @file nfc_protocol_support_defs.h - * @brief Application-level protocol support declarations. - */ -#pragma once - -#include "nfc_protocol_support_base.h" - -/** - * @brief Declaraion of array of pointers to protocol support implementations. - */ -extern const NfcProtocolSupportBase* nfc_protocol_support[]; 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/nfc_protocol_support_gui_common.h b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_gui_common.h index 3230f1a7e..4e4edb080 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. */ @@ -23,6 +24,10 @@ enum { SubmenuIndexCommonMax, /**< Special value, internal use. */ }; +#ifdef __cplusplus +extern "C" { +#endif + /** * @brief Common submenu callback. * @@ -84,3 +89,7 @@ void nfc_protocol_support_common_on_enter_empty(NfcApp* instance); * @returns always true. */ bool nfc_protocol_support_common_on_event_empty(NfcApp* instance, SceneManagerEvent event); + +#ifdef __cplusplus +} +#endif diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_unlock_helper.h b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_unlock_helper.h index 34f52496b..9cb35971e 100644 --- a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_unlock_helper.h +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_unlock_helper.h @@ -5,5 +5,13 @@ typedef enum { NfcSceneReadMenuStateCardFound, } NfcSceneUnlockReadState; +#ifdef __cplusplus +extern "C" { +#endif + void nfc_unlock_helper_setup_from_state(NfcApp* instance); void nfc_unlock_helper_card_detected_handler(NfcApp* instance); + +#ifdef __cplusplus +} +#endif 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..1ea8951f9 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/ntag4xx/ntag4xx.c @@ -0,0 +1,140 @@ +#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, + }, +}; + +NFC_PROTOCOL_SUPPORT_PLUGIN(ntag4xx, NfcProtocolNtag4xx); 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/applications/main/nfc/helpers/protocol_support/slix/slix.c b/applications/main/nfc/helpers/protocol_support/slix/slix.c index 35592eaa1..32094140d 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 = { @@ -157,4 +149,11 @@ 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, + }, }; + +NFC_PROTOCOL_SUPPORT_PLUGIN(slix, NfcProtocolSlix); diff --git a/applications/main/nfc/helpers/protocol_support/st25tb/st25tb.c b/applications/main/nfc/helpers/protocol_support/st25tb/st25tb.c index 0305d614c..af123aee4 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 = { @@ -108,4 +99,11 @@ 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, + }, }; + +NFC_PROTOCOL_SUPPORT_PLUGIN(st25tb, NfcProtocolSt25tb); 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..3b1fc91f6 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/type_4_tag/type_4_tag.c @@ -0,0 +1,260 @@ +#include "type_4_tag.h" +#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" + +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) { + 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)); + 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_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 NfcCommand + nfc_scene_emulate_listener_callback_type_4_tag(NfcGenericEvent event, void* context) { + furi_assert(event.protocol == NfcProtocolType4Tag); + + NfcApp* instance = context; + Type4TagListenerEvent* type_4_tag_event = event.event_data; + + if(type_4_tag_event->type == Type4TagListenerEventTypeCustomCommand) { + 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( + instance->text_box_store, + " %02X", + bit_buffer_get_byte(type_4_tag_event->data->buffer, i)); + } + furi_string_push_back(instance->text_box_store, '\n'); + view_dispatcher_send_custom_event( + instance->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_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 | + NfcProtocolFeatureWrite, + + .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_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_type_4_tag, + .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_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, + }, +}; + +NFC_PROTOCOL_SUPPORT_PLUGIN(type_4_tag, NfcProtocolType4Tag); 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..2dc51a6e8 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/type_4_tag/type_4_tag_render.c @@ -0,0 +1,59 @@ +#include "type_4_tag_render.h" + +#include "../iso14443_4a/iso14443_4a_render.h" + +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:::::::::::::::[Stored NDEF]:::::::::::::::\n"); + furi_string_cat_printf(str, "Current NDEF Size: %lu", simple_array_get_count(data->ndef_data)); + + 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); + 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: %02X%s\n", + data->ndef_read_lock, + data->ndef_read_lock == 0 ? " (unlocked)" : ""); + furi_string_cat_printf( + str, + "Write Lock: %02X%s", + data->ndef_write_lock, + data->ndef_write_lock == 0 ? " (unlocked)" : ""); + } + + 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) { + 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) { + 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..abb45317c --- /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 (4U) + +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/applications/main/nfc/nfc_app.c b/applications/main/nfc/nfc_app.c index c8973fb0d..5f95e4859 100644 --- a/applications/main/nfc/nfc_app.c +++ b/applications/main/nfc/nfc_app.c @@ -1,7 +1,9 @@ #include "nfc_app_i.h" +#include "api/nfc_app_api_interface.h" #include "helpers/protocol_support/nfc_protocol_support.h" #include +#include bool nfc_custom_event_callback(void* context, uint32_t event) { furi_assert(context); @@ -49,12 +51,16 @@ NfcApp* nfc_app_alloc(void) { instance->nfc = nfc_alloc(); + instance->api_resolver = composite_api_resolver_alloc(); + composite_api_resolver_add(instance->api_resolver, firmware_api_interface); + composite_api_resolver_add(instance->api_resolver, nfc_application_api_interface); + instance->detected_protocols = nfc_detected_protocols_alloc(); instance->felica_auth = felica_auth_alloc(); instance->mf_ul_auth = mf_ultralight_auth_alloc(); instance->slix_unlock = slix_unlock_alloc(); instance->mfc_key_cache = mf_classic_key_cache_alloc(); - instance->nfc_supported_cards = nfc_supported_cards_alloc(); + instance->nfc_supported_cards = nfc_supported_cards_alloc(instance->api_resolver); // Nfc device instance->nfc_device = nfc_device_alloc(); @@ -148,6 +154,9 @@ void nfc_app_free(NfcApp* instance) { slix_unlock_free(instance->slix_unlock); mf_classic_key_cache_free(instance->mfc_key_cache); nfc_supported_cards_free(instance->nfc_supported_cards); + if(instance->protocol_support) { + nfc_protocol_support_free(instance); + } // Nfc device nfc_device_free(instance->nfc_device); @@ -415,6 +424,11 @@ bool nfc_load_from_file_select(NfcApp* instance) { if(!dialog_file_browser_show( instance->dialogs, instance->file_path, instance->file_path, &browser_options)) break; + + nfc_show_loading_popup(instance, true); + nfc_supported_cards_load_cache(instance->nfc_supported_cards); + nfc_show_loading_popup(instance, false); + success = nfc_load_file(instance, instance->file_path, true); } while(!success); @@ -464,7 +478,7 @@ static bool nfc_is_hal_ready(void) { static void nfc_show_initial_scene_for_device(NfcApp* nfc) { NfcProtocol prot = nfc_device_get_protocol(nfc->nfc_device); uint32_t scene = nfc_protocol_support_has_feature( - prot, NfcProtocolFeatureEmulateFull | NfcProtocolFeatureEmulateUid) ? + prot, nfc, NfcProtocolFeatureEmulateFull | NfcProtocolFeatureEmulateUid) ? NfcSceneEmulate : NfcSceneSavedMenu; // Load plugins (parsers) in case if we are in the saved menu @@ -523,11 +537,6 @@ int32_t nfc_app(void* p) { } else { view_dispatcher_attach_to_gui( nfc->view_dispatcher, nfc->gui, ViewDispatcherTypeFullscreen); - // Load plugins (parsers) one time in case if we running app normally - nfc_show_loading_popup(nfc, true); - nfc_supported_cards_load_cache(nfc->nfc_supported_cards); - nfc_show_loading_popup(nfc, false); - // Switch to the initial scene scene_manager_next_scene(nfc->scene_manager, NfcSceneStart); } diff --git a/applications/main/nfc/nfc_app_i.h b/applications/main/nfc/nfc_app_i.h index 78f6e8e98..4336d25e0 100644 --- a/applications/main/nfc/nfc_app_i.h +++ b/applications/main/nfc/nfc_app_i.h @@ -32,10 +32,12 @@ #include "helpers/mfkey32_logger.h" #include "helpers/nfc_emv_parser.h" #include "helpers/mf_classic_key_cache.h" +#include "helpers/protocol_support/nfc_protocol_support.h" #include "helpers/nfc_supported_cards.h" #include "helpers/felica_auth.h" #include "helpers/slix_unlock.h" +#include #include #include #include @@ -149,6 +151,8 @@ struct NfcApp { Mfkey32Logger* mfkey32_logger; MfUserDict* mf_user_dict; MfClassicKeyCache* mfc_key_cache; + CompositeApiResolver* api_resolver; + NfcProtocolSupport* protocol_support; NfcSupportedCards* nfc_supported_cards; NfcDevice* nfc_device; @@ -176,6 +180,10 @@ typedef enum { NfcSceneSaveConfirmStateCrackNonces, } NfcSceneSaveConfirmState; +#ifdef __cplusplus +extern "C" { +#endif + int32_t nfc_task(void* p); void nfc_text_store_set(NfcApp* nfc, const char* text, ...); @@ -213,3 +221,7 @@ void nfc_make_app_folder(NfcApp* instance); void nfc_append_filename_string_when_present(NfcApp* instance, FuriString* string); void nfc_app_run_external(NfcApp* nfc, const char* app_path); + +#ifdef __cplusplus +} +#endif diff --git a/applications/main/nfc/plugins/supported_cards/csc.c b/applications/main/nfc/plugins/supported_cards/csc.c new file mode 100644 index 000000000..a96d74007 --- /dev/null +++ b/applications/main/nfc/plugins/supported_cards/csc.c @@ -0,0 +1,149 @@ +/* +* Parser for CSC Service Works Reloadable Cash Card (US) +* Date created 2024/5/26 +* Zinong Li +* Discord @torron0483 +* Github @zinongli +*/ + +#include "nfc_supported_card_plugin.h" +#include +#include +#include +#include + +#include +#include + +#define TAG "CSC" + +bool csc_parse(const NfcDevice* device, FuriString* parsed_data) { + furi_assert(device); + const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic); + bool parsed = false; + + do { + if(data->type != MfClassicType1k) break; // Check card type + + // Verify memory format (checksum is later) + const uint8_t refill_block_num = 2; + const uint8_t current_balance_block_num = 4; + const uint8_t current_balance_copy_block_num = 8; + + const uint8_t* current_balance_block_start_ptr = + &data->block[current_balance_block_num].data[0]; + const uint8_t* current_balance_copy_block_start_ptr = + &data->block[current_balance_copy_block_num].data[0]; + + uint32_t current_balance_and_times = + bit_lib_bytes_to_num_le(current_balance_block_start_ptr, 4); + uint32_t current_balance_and_times_copy = + bit_lib_bytes_to_num_le(current_balance_copy_block_start_ptr, 4); + + // Failed verification if balance != backup + if(current_balance_and_times != current_balance_and_times_copy) { + FURI_LOG_D(TAG, "Backup verification failed"); + break; + } + + // Even if balance = 0, e.g. new card, refilled times can't be zero + if(current_balance_and_times == 0 || current_balance_and_times_copy == 0) { + FURI_LOG_D(TAG, "Value bytes empty"); + break; + } + + // Parse data + const uint8_t card_lives_block_num = 9; + const uint8_t refill_sign_block_num = 13; + + const uint8_t* refilled_balance_block_start_ptr = &data->block[refill_block_num].data[9]; + const uint8_t* refill_times_block_start_ptr = &data->block[refill_block_num].data[5]; + const uint8_t* card_lives_block_start_ptr = &data->block[card_lives_block_num].data[0]; + const uint8_t* refill_sign_block_start_ptr = &data->block[refill_sign_block_num].data[0]; + + uint32_t refilled_balance = bit_lib_bytes_to_num_le(refilled_balance_block_start_ptr, 2); + uint32_t refilled_balance_dollar = refilled_balance / 100; + uint8_t refilled_balance_cent = refilled_balance % 100; + + uint32_t current_balance = bit_lib_bytes_to_num_le(current_balance_block_start_ptr, 2); + uint32_t current_balance_dollar = current_balance / 100; + uint8_t current_balance_cent = current_balance % 100; + + // How many times it can still be used + uint32_t card_lives = bit_lib_bytes_to_num_le(card_lives_block_start_ptr, 2); + + uint32_t refill_times = bit_lib_bytes_to_num_le(refill_times_block_start_ptr, 2); + // This is zero when you buy the card. but after refilling it, the refilling machine will leave a non-zero signature here + uint64_t refill_sign = bit_lib_bytes_to_num_le(refill_sign_block_start_ptr, 8); + + size_t uid_len = 0; + const uint8_t* uid = mf_classic_get_uid(data, &uid_len); + uint32_t card_uid = bit_lib_bytes_to_num_le(uid, 4); + + // Last byte of refill block is checksum + const uint8_t* checksum_block = data->block[refill_block_num].data; + uint8_t xor_result = 0; + for(size_t i = 0; i < 16; ++i) { + xor_result ^= checksum_block[i]; + } + + if(refill_sign == 0 && refill_times == 1) { + // New cards don't comply to checksum but refill time should be once + furi_string_printf( + parsed_data, + "\e#CSC Service Works\n" + "UID: %lu\n" + "New Card\n" + "Card Value: %lu.%02u USD\n" + "Card Usages Left: %lu", + card_uid, + refilled_balance_dollar, + refilled_balance_cent, + card_lives); + } else { + if(xor_result != 0) { + FURI_LOG_D(TAG, "Checksum failed"); + break; + } + furi_string_printf( + parsed_data, + "\e#CSC Service Works\n" + "UID: %lu\n" + "Balance: %lu.%02u USD\n" + "Last Top-up: %lu.%02u USD\n" + "Top-up Count: %lu\n" + "Card Usages Left: %lu", + card_uid, + current_balance_dollar, + current_balance_cent, + refilled_balance_dollar, + refilled_balance_cent, + refill_times, + card_lives); + } + + parsed = true; + } while(false); + + return parsed; +} + +/* Actual implementation of app<>plugin interface */ +static const NfcSupportedCardsPlugin csc_plugin = { + .protocol = NfcProtocolMfClassic, + .verify = NULL, + .read = NULL, + .parse = csc_parse, +}; + +/* Plugin descriptor to comply with basic plugin specification */ +static const FlipperAppPluginDescriptor csc_plugin_descriptor = { + .appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID, + .ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION, + .entry_point = &csc_plugin, +}; + +/* Plugin entry point - must return a pointer to const descriptor */ +const FlipperAppPluginDescriptor* csc_plugin_ep(void) { + return &csc_plugin_descriptor; +} diff --git a/applications/main/nfc/plugins/supported_cards/emv.c b/applications/main/nfc/plugins/supported_cards/emv.c index 97b16acf8..2f5b84790 100644 --- a/applications/main/nfc/plugins/supported_cards/emv.c +++ b/applications/main/nfc/plugins/supported_cards/emv.c @@ -199,6 +199,6 @@ static const FlipperAppPluginDescriptor emv_plugin_descriptor = { }; /* Plugin entry point - must return a pointer to const descriptor */ -const FlipperAppPluginDescriptor* emv_plugin_ep() { +const FlipperAppPluginDescriptor* emv_plugin_ep(void) { return &emv_plugin_descriptor; } diff --git a/applications/main/nfc/plugins/supported_cards/kazan.c b/applications/main/nfc/plugins/supported_cards/kazan.c index 403685468..65838e25c 100644 --- a/applications/main/nfc/plugins/supported_cards/kazan.c +++ b/applications/main/nfc/plugins/supported_cards/kazan.c @@ -223,7 +223,7 @@ static bool kazan_read(Nfc* nfc, NfcDevice* device) { break; } - if(!mf_classic_is_card_read(data)) { + if(error == MfClassicErrorPartialRead) { error = mf_classic_poller_sync_read(nfc, &keys_v2, data); if(error == MfClassicErrorNotPresent) { FURI_LOG_W(TAG, "Failed to read data: keys_v1"); @@ -231,7 +231,7 @@ static bool kazan_read(Nfc* nfc, NfcDevice* device) { } } - if(!mf_classic_is_card_read(data)) { + if(error == MfClassicErrorPartialRead) { error = mf_classic_poller_sync_read(nfc, &keys_v3, data); if(error == MfClassicErrorNotPresent) { FURI_LOG_W(TAG, "Failed to read data: keys_v3"); @@ -241,7 +241,7 @@ static bool kazan_read(Nfc* nfc, NfcDevice* device) { nfc_device_set_data(device, NfcProtocolMfClassic, data); - is_read = mf_classic_is_card_read(data); + is_read = (error == MfClassicErrorNone); } while(false); mf_classic_free(data); @@ -409,6 +409,6 @@ static const FlipperAppPluginDescriptor kazan_plugin_descriptor = { }; /* Plugin entry point - must return a pointer to const descriptor */ -const FlipperAppPluginDescriptor* kazan_plugin_ep() { +const FlipperAppPluginDescriptor* kazan_plugin_ep(void) { return &kazan_plugin_descriptor; } diff --git a/applications/main/nfc/plugins/supported_cards/metromoney.c b/applications/main/nfc/plugins/supported_cards/metromoney.c index 3a3d1fe6e..089bb48bd 100644 --- a/applications/main/nfc/plugins/supported_cards/metromoney.c +++ b/applications/main/nfc/plugins/supported_cards/metromoney.c @@ -185,6 +185,6 @@ static const FlipperAppPluginDescriptor metromoney_plugin_descriptor = { }; /* Plugin entry point - must return a pointer to const descriptor */ -const FlipperAppPluginDescriptor* metromoney_plugin_ep() { +const FlipperAppPluginDescriptor* metromoney_plugin_ep(void) { return &metromoney_plugin_descriptor; } diff --git a/applications/main/nfc/plugins/supported_cards/mizip.c b/applications/main/nfc/plugins/supported_cards/mizip.c index c00fef6b5..240e6dc2f 100644 --- a/applications/main/nfc/plugins/supported_cards/mizip.c +++ b/applications/main/nfc/plugins/supported_cards/mizip.c @@ -199,7 +199,7 @@ static bool mizip_parse(const NfcDevice* device, FuriString* parsed_data) { MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, cfg.verify_sector); uint64_t key = bit_lib_bytes_to_num_be(sec_tr->key_b.data, 6); - if(key != cfg.keys[cfg.verify_sector].b) return false; + if(key != cfg.keys[cfg.verify_sector].b) break; //Get UID uint8_t uid[UID_LENGTH]; diff --git a/applications/main/nfc/plugins/supported_cards/ndef.c b/applications/main/nfc/plugins/supported_cards/ndef.c index 06982e111..86d31052e 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 #include @@ -31,7 +32,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! @@ -40,10 +42,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 ===--- @@ -151,6 +153,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; @@ -230,6 +237,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); @@ -1039,6 +1053,44 @@ 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); + + 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 = + { + .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 ===--- @@ -1056,6 +1108,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 }; diff --git a/applications/main/nfc/plugins/supported_cards/plantain.c b/applications/main/nfc/plugins/supported_cards/plantain.c index 49bbaebe8..add7ab560 100644 --- a/applications/main/nfc/plugins/supported_cards/plantain.c +++ b/applications/main/nfc/plugins/supported_cards/plantain.c @@ -247,7 +247,7 @@ static bool plantain_parse(const NfcDevice* device, FuriString* parsed_data) { // Print card number with 4-digit groups. "3" in "3078" denotes a ticket type "3 - full ticket", will differ on discounted cards. furi_string_cat_printf(parsed_data, "Number: "); FuriString* card_number_s = furi_string_alloc(); - furi_string_cat_printf(card_number_s, "%llu", card_number); + furi_string_cat_printf(card_number_s, "%lld", card_number); FuriString* tmp_s = furi_string_alloc_set_str("9643 3078 "); for(uint8_t i = 0; i < 24; i += 4) { for(uint8_t j = 0; j < 4; j++) { diff --git a/applications/main/nfc/plugins/supported_cards/smartrider.c b/applications/main/nfc/plugins/supported_cards/smartrider.c new file mode 100644 index 000000000..073f0410d --- /dev/null +++ b/applications/main/nfc/plugins/supported_cards/smartrider.c @@ -0,0 +1,334 @@ +#include "nfc_supported_card_plugin.h" +#include +#include +#include +#include +#include + +#define MAX_TRIPS 10 +#define TAG "SmartRider" +#define MAX_BLOCKS 64 +#define MAX_DATE_ITERATIONS 366 + +static const uint8_t STANDARD_KEYS[3][6] = { + {0x20, 0x31, 0xD1, 0xE5, 0x7A, 0x3B}, + {0x4C, 0xA6, 0x02, 0x9F, 0x94, 0x73}, + {0x19, 0x19, 0x53, 0x98, 0xE3, 0x2F}}; + +typedef struct { + uint32_t timestamp; + uint16_t cost; + uint16_t transaction_number; + uint16_t journey_number; + char route[5]; + uint8_t tap_on : 1; + uint8_t block; +} __attribute__((packed)) TripData; + +typedef struct { + uint32_t balance; + uint16_t issued_days; + uint16_t expiry_days; + uint16_t purchase_cost; + uint16_t auto_load_threshold; + uint16_t auto_load_value; + char card_serial_number[11]; + uint8_t token; + TripData trips[MAX_TRIPS]; + uint8_t trip_count; +} __attribute__((packed)) SmartRiderData; + +static const char* const CONCESSION_TYPES[] = { + "Pre-issue", + "Standard Fare", + "Student", + NULL, + "Tertiary", + NULL, + "Seniors", + "Health Care", + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + "PTA Staff", + "Pensioner", + "Free Travel"}; + +static inline const char* get_concession_type(uint8_t token) { + return (token <= 0x10) ? CONCESSION_TYPES[token] : "Unknown"; +} + +static bool authenticate_and_read( + Nfc* nfc, + uint8_t sector, + const uint8_t* key, + MfClassicKeyType key_type, + MfClassicBlock* block_data) { + MfClassicKey mf_key; + memcpy(mf_key.data, key, 6); + uint8_t block = mf_classic_get_first_block_num_of_sector(sector); + + if(mf_classic_poller_sync_auth(nfc, block, &mf_key, key_type, NULL) != MfClassicErrorNone) { + FURI_LOG_D(TAG, "Authentication failed for sector %d key type %d", sector, key_type); + return false; + } + + if(mf_classic_poller_sync_read_block(nfc, block, &mf_key, key_type, block_data) != + MfClassicErrorNone) { + FURI_LOG_D(TAG, "Read failed for sector %d", sector); + return false; + } + + return true; +} + +static bool smartrider_verify(Nfc* nfc) { + furi_assert(nfc); + MfClassicBlock block_data; + + for(int i = 0; i < 3; i++) { + if(!authenticate_and_read( + nfc, + i * 6, + STANDARD_KEYS[i], + i % 2 == 0 ? MfClassicKeyTypeA : MfClassicKeyTypeB, + &block_data) || + memcmp(block_data.data, STANDARD_KEYS[i], 6) != 0) { + FURI_LOG_D(TAG, "Authentication or key mismatch for key %d", i); + return false; + } + } + + FURI_LOG_I(TAG, "SmartRider card verified"); + return true; +} + +static inline bool + parse_trip_data(const MfClassicBlock* block_data, TripData* trip, uint8_t block_number) { + trip->timestamp = bit_lib_bytes_to_num_le(block_data->data + 3, 4); + trip->tap_on = (block_data->data[7] & 0x10) == 0x10; + memcpy(trip->route, block_data->data + 8, 4); + trip->route[4] = '\0'; + trip->cost = bit_lib_bytes_to_num_le(block_data->data + 13, 2); + trip->transaction_number = bit_lib_bytes_to_num_le(block_data->data, 2); + trip->journey_number = bit_lib_bytes_to_num_le(block_data->data + 2, 2); + trip->block = block_number; + return true; +} + +static bool smartrider_read(Nfc* nfc, NfcDevice* device) { + furi_assert(nfc); + furi_assert(device); + MfClassicData* data = mf_classic_alloc(); + nfc_device_copy_data(device, NfcProtocolMfClassic, data); + + MfClassicType type; + if(mf_classic_poller_sync_detect_type(nfc, &type) != MfClassicErrorNone || + type != MfClassicType1k) { + mf_classic_free(data); + return false; + } + data->type = type; + + MfClassicDeviceKeys keys = {.key_a_mask = 0, .key_b_mask = 0}; + for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) { + memcpy(keys.key_a[i].data, STANDARD_KEYS[i == 0 ? 0 : 1], sizeof(STANDARD_KEYS[0])); + if(i > 0) { + memcpy(keys.key_b[i].data, STANDARD_KEYS[2], sizeof(STANDARD_KEYS[0])); + FURI_BIT_SET(keys.key_b_mask, i); + } + FURI_BIT_SET(keys.key_a_mask, i); + } + + MfClassicError error = mf_classic_poller_sync_read(nfc, &keys, data); + if(error != MfClassicErrorNone) { + FURI_LOG_W(TAG, "Failed to read data"); + mf_classic_free(data); + return false; + } + + nfc_device_set_data(device, NfcProtocolMfClassic, data); + mf_classic_free(data); + return true; +} + +static bool is_leap_year(uint16_t year) { + return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0); +} + +static void calculate_date(uint32_t timestamp, char* date_str, size_t date_str_size) { + uint32_t seconds_since_2000 = timestamp; + uint32_t days_since_2000 = seconds_since_2000 / 86400; + uint16_t year = 2000; + uint8_t month = 1; + uint16_t day = 1; + + static const uint16_t days_in_month[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + + while(days_since_2000 >= (is_leap_year(year) ? 366 : 365)) { + days_since_2000 -= (is_leap_year(year) ? 366 : 365); + year++; + } + + for(month = 0; month < 12; month++) { + uint16_t dim = days_in_month[month]; + if(month == 1 && is_leap_year(year)) { + dim++; + } + if(days_since_2000 < dim) { + break; + } + days_since_2000 -= dim; + } + + day = days_since_2000 + 1; + month++; // Adjust month to 1-based + + if(date_str_size > 0) { + size_t written = 0; + written += snprintf(date_str + written, date_str_size - written, "%02u", day); + if(written < date_str_size - 1) { + written += snprintf(date_str + written, date_str_size - written, "/"); + } + if(written < date_str_size - 1) { + written += snprintf(date_str + written, date_str_size - written, "%02u", month); + } + if(written < date_str_size - 1) { + written += snprintf(date_str + written, date_str_size - written, "/"); + } + if(written < date_str_size - 1) { + snprintf(date_str + written, date_str_size - written, "%02u", year % 100); + } + } else { + // If the buffer size is 0, do nothing + } +} + +static bool smartrider_parse(const NfcDevice* device, FuriString* parsed_data) { + furi_assert(device); + furi_assert(parsed_data); + const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic); + SmartRiderData sr_data = {0}; + + if(data->type != MfClassicType1k) { + FURI_LOG_E(TAG, "Invalid card type"); + return false; + } + + const MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, 0); + if(!sec_tr || memcmp(sec_tr->key_a.data, STANDARD_KEYS[0], 6) != 0) { + FURI_LOG_E(TAG, "Key verification failed for sector 0"); + return false; + } + + static const uint8_t required_blocks[] = {14, 4, 5, 1, 52, 50, 0}; + for(size_t i = 0; i < COUNT_OF(required_blocks); i++) { + if(required_blocks[i] >= MAX_BLOCKS || + !mf_classic_is_block_read(data, required_blocks[i])) { + FURI_LOG_E(TAG, "Required block %d is not read or out of range", required_blocks[i]); + return false; + } + } + + sr_data.balance = bit_lib_bytes_to_num_le(data->block[14].data + 7, 2); + sr_data.issued_days = bit_lib_bytes_to_num_le(data->block[4].data + 16, 2); + sr_data.expiry_days = bit_lib_bytes_to_num_le(data->block[4].data + 18, 2); + sr_data.auto_load_threshold = bit_lib_bytes_to_num_le(data->block[4].data + 20, 2); + sr_data.auto_load_value = bit_lib_bytes_to_num_le(data->block[4].data + 22, 2); + sr_data.token = data->block[5].data[8]; + sr_data.purchase_cost = bit_lib_bytes_to_num_le(data->block[0].data + 14, 2); + + snprintf( + sr_data.card_serial_number, + sizeof(sr_data.card_serial_number), + "%02X%02X%02X%02X%02X", + data->block[1].data[6], + data->block[1].data[7], + data->block[1].data[8], + data->block[1].data[9], + data->block[1].data[10]); + + for(uint8_t block_number = 40; block_number <= 52 && sr_data.trip_count < MAX_TRIPS; + block_number++) { + if((block_number != 43 && block_number != 47 && block_number != 51) && + mf_classic_is_block_read(data, block_number) && + parse_trip_data( + &data->block[block_number], &sr_data.trips[sr_data.trip_count], block_number)) { + sr_data.trip_count++; + } + } + + // Sort trips by timestamp (descending order) + for(uint8_t i = 0; i < sr_data.trip_count - 1; i++) { + for(uint8_t j = 0; j < sr_data.trip_count - i - 1; j++) { + if(sr_data.trips[j].timestamp < sr_data.trips[j + 1].timestamp) { + TripData temp = sr_data.trips[j]; + sr_data.trips[j] = sr_data.trips[j + 1]; + sr_data.trips[j + 1] = temp; + } + } + } + + furi_string_printf( + parsed_data, + "\e#SmartRider\nBalance: $%lu.%02lu\nConcession: %s\nSerial: %s%s\n" + "Total Cost: $%u.%02u\nAuto-Load: $%u.%02u/$%u.%02u\n\e#Tag On/Off History\n", + sr_data.balance / 100, + sr_data.balance % 100, + get_concession_type(sr_data.token), + memcmp(sr_data.card_serial_number, "00", 2) == 0 ? "SR0" : "", + memcmp(sr_data.card_serial_number, "00", 2) == 0 ? sr_data.card_serial_number + 2 : + sr_data.card_serial_number, + sr_data.purchase_cost / 100, + sr_data.purchase_cost % 100, + sr_data.auto_load_threshold / 100, + sr_data.auto_load_threshold % 100, + sr_data.auto_load_value / 100, + sr_data.auto_load_value % 100); + + for(uint8_t i = 0; i < sr_data.trip_count; i++) { + char date_str[9]; + calculate_date(sr_data.trips[i].timestamp, date_str, sizeof(date_str)); + + uint32_t cost = sr_data.trips[i].cost; + if(cost > 0) { + furi_string_cat_printf( + parsed_data, + "%s %c $%lu.%02lu %s\n", + date_str, + sr_data.trips[i].tap_on ? '+' : '-', + cost / 100, + cost % 100, + sr_data.trips[i].route); + } else { + furi_string_cat_printf( + parsed_data, + "%s %c %s\n", + date_str, + sr_data.trips[i].tap_on ? '+' : '-', + sr_data.trips[i].route); + } + } + + return true; +} +static const NfcSupportedCardsPlugin smartrider_plugin = { + .protocol = NfcProtocolMfClassic, + .verify = smartrider_verify, + .read = smartrider_read, + .parse = smartrider_parse, +}; + +__attribute__((used)) const FlipperAppPluginDescriptor* smartrider_plugin_ep() { + static const FlipperAppPluginDescriptor plugin_descriptor = { + .appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID, + .ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION, + .entry_point = &smartrider_plugin, + }; + return &plugin_descriptor; +} + +// made with love by jay candel <3 diff --git a/applications/main/nfc/plugins/supported_cards/sonicare.c b/applications/main/nfc/plugins/supported_cards/sonicare.c new file mode 100644 index 000000000..3e2a9b15a --- /dev/null +++ b/applications/main/nfc/plugins/supported_cards/sonicare.c @@ -0,0 +1,111 @@ +// Parser for Philips Sonicare toothbrush heads. +// Made by @Sil333033 +// Thanks to Cyrill Künzi for this research! https://kuenzi.dev/toothbrush/ + +#include "nfc_supported_card_plugin.h" + +#include +#include + +#define TAG "Sonicare" + +typedef enum { + SonicareHeadWhite, + SonicareHeadBlack, + SonicareHeadUnkown, +} SonicareHead; + +static SonicareHead sonicare_get_head_type(const MfUltralightData* data) { + // data.page[34].data got 4 bytes + // 31:32:31:34 for black (not sure) + // 31:31:31:31 for white (not sure) + // the data should be in here based on the research, but i cant find it. + // page 34 byte 0 is always 0x30 for the white brushes i have, so i guess thats white + // TODO: Get a black brush and test this + + if(data->page[34].data[0] == 0x30) { + return SonicareHeadWhite; + } else { + return SonicareHeadUnkown; + } +} + +static uint32_t sonicare_get_seconds_brushed(const MfUltralightData* data) { + uint32_t seconds_brushed = 0; + + seconds_brushed += data->page[36].data[0]; + seconds_brushed += data->page[36].data[1] << 8; + + return seconds_brushed; +} + +static bool sonicare_parse(const NfcDevice* device, FuriString* parsed_data) { + furi_assert(device); + furi_assert(parsed_data); + + const MfUltralightData* data = nfc_device_get_data(device, NfcProtocolMfUltralight); + + bool parsed = false; + + do { + // Check for NDEF link match + const char* test = "philips.com/nfcbrushheadtap"; + // Data is a array of arrays, cast to char array and compare + if(strncmp(test, (const char*)&data->page[5].data[3], strlen(test)) != 0) { + FURI_LOG_D(TAG, "Not a Philips Sonicare head"); + break; + } + + const SonicareHead head_type = sonicare_get_head_type(data); + const uint32_t seconds_brushed = sonicare_get_seconds_brushed(data); + + FuriString* head_type_str = furi_string_alloc(); + + switch(head_type) { + case SonicareHeadWhite: + furi_string_printf(head_type_str, "White"); + break; + case SonicareHeadBlack: + furi_string_printf(head_type_str, "Black"); + break; + case SonicareHeadUnkown: + default: + furi_string_printf(head_type_str, "Unknown"); + break; + } + + furi_string_printf( + parsed_data, + "\e#Philips Sonicare head\nColor: %s\nTime brushed: %02.0f:%02.0f:%02ld\n", + furi_string_get_cstr(head_type_str), + floor(seconds_brushed / 3600), + floor((seconds_brushed / 60) % 60), + seconds_brushed % 60); + + furi_string_free(head_type_str); + + parsed = true; + } while(false); + + return parsed; +} + +/* Actual implementation of app<>plugin interface */ +static const NfcSupportedCardsPlugin sonicare_plugin = { + .protocol = NfcProtocolMfUltralight, + .verify = NULL, + .read = NULL, + .parse = sonicare_parse, +}; + +/* Plugin descriptor to comply with basic plugin specification */ +static const FlipperAppPluginDescriptor sonicare_plugin_descriptor = { + .appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID, + .ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION, + .entry_point = &sonicare_plugin, +}; + +/* Plugin entry point - must return a pointer to const descriptor */ +const FlipperAppPluginDescriptor* sonicare_plugin_ep(void) { + return &sonicare_plugin_descriptor; +} diff --git a/applications/main/nfc/plugins/supported_cards/two_cities.c b/applications/main/nfc/plugins/supported_cards/two_cities.c index dc87d3072..240c6c585 100644 --- a/applications/main/nfc/plugins/supported_cards/two_cities.c +++ b/applications/main/nfc/plugins/supported_cards/two_cities.c @@ -112,7 +112,7 @@ static bool two_cities_parse(const NfcDevice* device, FuriString* parsed_data) { // Verify key MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, 4); uint64_t key = bit_lib_bytes_to_num_be(sec_tr->key_a.data, 6); - if(key != two_cities_4k_keys[4].a) return false; + if(key != two_cities_4k_keys[4].a) break; // ===== // PLANTAIN diff --git a/applications/main/nfc/plugins/supported_cards/ventra.c b/applications/main/nfc/plugins/supported_cards/ventra.c new file mode 100644 index 000000000..9abdc1ed7 --- /dev/null +++ b/applications/main/nfc/plugins/supported_cards/ventra.c @@ -0,0 +1,292 @@ +// Parser for CTA Ventra Ultralight cards +// Made by @hazardousvoltage +// Based on my own research, with... +// Credit to https://www.lenrek.net/experiments/compass-tickets/ & MetroDroid project for underlying info +// +// This parser can decode the paper single-use and single/multi-day paper passes using Ultralight EV1 +// The plastic cards are DESFire and fully locked down, not much useful info extractable +// TODO: +// - Sort the duplicate/rare ticket types +// - Database of stop IDs for trains? Buses there's just too damn many, but you can find them here: +// https://data.cityofchicago.org/Transportation/CTA-Bus-Stops-kml/84eu-buny/about_data +// - Generalize to handle all known Cubic Nextfare Ultralight systems? Anyone wants to send me specimen dumps, hit me up on Discord. + +#include "nfc_supported_card_plugin.h" + +#include +#include +#include "datetime.h" +#include + +#define TAG "Ventra" + +DateTime ventra_exp_date = {0}, ventra_validity_date = {0}; +uint8_t ventra_high_seq = 0, ventra_cur_blk = 0, ventra_mins_active = 0; + +uint32_t time_now() { + return furi_hal_rtc_get_timestamp(); +} + +static DateTime dt_delta(DateTime dt, uint8_t delta_days) { + // returns shifted DateTime, from initial DateTime and time offset in seconds + DateTime dt_shifted = {0}; + datetime_timestamp_to_datetime( + datetime_datetime_to_timestamp(&dt) - (uint64_t)delta_days * 86400, &dt_shifted); + return dt_shifted; +} + +/* +static long dt_diff(DateTime dta, DateTime dtb) { + // returns difference in seconds between two DateTimes + long diff; + diff = datetime_datetime_to_timestamp(&dta) - datetime_datetime_to_timestamp(&dtb); + return diff; +} +*/ + +// Card is expired if: +// - Hard expiration date passed (90 days from purchase, encoded in product record) +// - Soft expiration date passed: +// - For passes, n days after first use +// - For tickets, 2 hours after first use +// Calculating these is dumber than it needs to be, see xact record parser. +bool isExpired(void) { + uint32_t ts_hard_exp = datetime_datetime_to_timestamp(&ventra_exp_date); + uint32_t ts_soft_exp = datetime_datetime_to_timestamp(&ventra_validity_date); + uint32_t ts_now = time_now(); + return (ts_now >= ts_hard_exp || ts_now > ts_soft_exp); +} + +static FuriString* ventra_parse_xact(const MfUltralightData* data, uint8_t blk, bool is_pass) { + FuriString* ventra_xact_str = furi_string_alloc(); + uint16_t ts = data->page[blk].data[0] | data->page[blk].data[1] << 8; + uint8_t tran_type = ts & 0x1F; + ts >>= 5; + uint8_t day = data->page[blk].data[2]; + uint32_t work = data->page[blk + 1].data[0] | data->page[blk + 1].data[1] << 8 | + data->page[blk + 1].data[2] << 16; + uint8_t seq = work & 0x7F; + uint16_t exp = (work >> 7) & 0x7FF; + uint8_t exp_day = data->page[blk + 2].data[0]; + uint16_t locus = data->page[blk + 2].data[1] | data->page[blk + 2].data[2] << 8; + uint8_t line = data->page[blk + 2].data[3]; + + // This computes the block timestamp, based on the card expiration date and delta from it + DateTime dt = dt_delta(ventra_exp_date, day); + dt.hour = (ts & 0x7FF) / 60; + dt.minute = (ts & 0x7FF) % 60; + + // If sequence is 0, block isn't used yet (new card with only one active block, typically the first one. + // Otherwise, the block with higher sequence is the latest transaction, and the other block is prior transaction. + // Not necessarily in that order on the card. We need the latest data to compute validity and pretty-print them + // in reverse chrono. So this mess sets some globals as to which block is current, computes the validity times, etc. + if(seq == 0) { + furi_string_printf(ventra_xact_str, "-- EMPTY --"); + return (ventra_xact_str); + } + if(seq > ventra_high_seq) { + ventra_high_seq = seq; + ventra_cur_blk = blk; + ventra_mins_active = data->page[blk + 1].data[3]; + // Figure out the soft expiration. For passes it's easy, the readers update the "exp" field in the transaction record. + // Tickets, not so much, readers don't update "exp", but each xact record has "minutes since last tap" which is + // updated and carried forward. That, plus transaction timestamp, gives the expiration time. + if(tran_type == 6) { // Furthermore, purchase transactions set bogus expiration dates + if(is_pass) { + ventra_validity_date = dt_delta(ventra_exp_date, exp_day); + ventra_validity_date.hour = (exp & 0x7FF) / 60; + ventra_validity_date.minute = (exp & 0x7FF) % 60; + } else { + uint32_t validity_ts = datetime_datetime_to_timestamp(&dt); + validity_ts += (120 - ventra_mins_active) * 60; + datetime_timestamp_to_datetime(validity_ts, &ventra_validity_date); + } + } + } + + // Type 0 = Purchase, 1 = Train ride, 2 = Bus ride + // TODO: Check PACE and see if it uses a different line code + char linemap[3] = "PTB"; + char* xact_fmt = (line == 2) ? "%c %5d %04d-%02d-%02d %02d:%02d" : + "%c %04X %04d-%02d-%02d %02d:%02d"; + // I like a nice concise display showing all the relevant infos without having to scroll... + // Line StopID DateTime + furi_string_printf( + ventra_xact_str, + xact_fmt, + (line < 3) ? linemap[line] : '?', + locus, + dt.year, + dt.month, + dt.day, + dt.hour, + dt.minute); + return (ventra_xact_str); +} + +static bool ventra_parse(const NfcDevice* device, FuriString* parsed_data) { + furi_assert(device); + furi_assert(parsed_data); + + const MfUltralightData* data = nfc_device_get_data(device, NfcProtocolMfUltralight); + + bool parsed = false; + + do { + // This test can probably be improved -- it matches every Ventra I've seen, but will also match others + // in the same family. Or maybe we just generalize this parser. + if(data->page[4].data[0] != 0x0A || data->page[4].data[1] != 4 || + data->page[4].data[2] != 0 || data->page[6].data[0] != 0 || + data->page[6].data[1] != 0 || data->page[6].data[2] != 0) { + FURI_LOG_D(TAG, "Not Ventra Ultralight"); + break; + } + + // Parse the product record, display interesting data & extract info needed to parse transaction blocks + // Had this in its own function, ended up just setting a bunch of shitty globals, so inlined it instead. + FuriString* ventra_prod_str = furi_string_alloc(); + uint8_t otp = data->page[3].data[0]; + uint8_t prod_code = data->page[5].data[2]; + bool is_pass = false; + switch(prod_code) { + case 2: + case 0x1F: // Only ever seen one of these, it parses like a Single + furi_string_cat_printf(ventra_prod_str, "Single"); + break; + case 3: + case 0x3F: + is_pass = true; + furi_string_cat_printf(ventra_prod_str, "1-Day"); + break; + case 4: // Last I checked, 3 day passes only available at airport TVMs & social service agencies + is_pass = true; + furi_string_cat_printf(ventra_prod_str, "3-Day"); + break; + default: + is_pass = + true; // There are some card types I don't know what they are, but they parse like a pass, not a ticket. + furi_string_cat_printf(ventra_prod_str, "0x%02X", data->page[5].data[2]); + break; + } + + uint16_t date_y = data->page[4].data[3] | (data->page[5].data[0] << 8); + uint8_t date_d = date_y & 0x1F; + uint8_t date_m = (date_y >> 5) & 0x0F; + date_y >>= 9; + date_y += 2000; + ventra_exp_date.day = date_d; + ventra_exp_date.month = date_m; + ventra_exp_date.year = date_y; + ventra_validity_date = ventra_exp_date; // Until we know otherwise + + // Parse the transaction blocks. This sets a few sloppy globals, but it's too complex and repetitive to inline. + FuriString* ventra_xact_str1 = ventra_parse_xact(data, 8, is_pass); + FuriString* ventra_xact_str2 = ventra_parse_xact(data, 12, is_pass); + + uint8_t card_state = 1; + uint8_t rides_left = 0; + + char* card_states[5] = {"???", "NEW", "ACT", "USED", "EXP"}; + + if(ventra_high_seq > 1) card_state = 2; + // On "ticket" product, the OTP bits mark off rides used. Bit 0 seems to be unused, the next 3 are set as rides are used. + // Some, not all, readers will set the high bits to 0x7 when a card is tapped after it's expired or depleted. Have not + // seen other combinations, but if we do, we'll make a nice ???. 1-day passes set the OTP bit 1 on first use. 3-day + // passes do not. But we don't really care, since they don't matter on passes, unless you're trying to rollback one. + if(!is_pass) { + switch(otp) { + case 0: + rides_left = 3; + break; + case 2: + card_state = 2; + rides_left = 2; + break; + case 6: + card_state = 2; + rides_left = 1; + break; + case 0x0E: + case 0x7E: + card_state = 3; + rides_left = 0; + break; + default: + card_state = 0; + rides_left = 0; + break; + } + } + if(isExpired()) { + card_state = 4; + rides_left = 0; + } + + furi_string_printf( + parsed_data, + "\e#Ventra %s (%s)\n", + furi_string_get_cstr(ventra_prod_str), + card_states[card_state]); + + furi_string_cat_printf( + parsed_data, + "Exp: %04d-%02d-%02d %02d:%02d\n", + ventra_validity_date.year, + ventra_validity_date.month, + ventra_validity_date.day, + ventra_validity_date.hour, + ventra_validity_date.minute); + + if(rides_left) { + furi_string_cat_printf(parsed_data, "Rides left: %d\n", rides_left); + } + + furi_string_cat_printf( + parsed_data, + "%s\n", + furi_string_get_cstr(ventra_cur_blk == 8 ? ventra_xact_str1 : ventra_xact_str2)); + + furi_string_cat_printf( + parsed_data, + "%s\n", + furi_string_get_cstr(ventra_cur_blk == 8 ? ventra_xact_str2 : ventra_xact_str1)); + + furi_string_cat_printf( + parsed_data, "TVM ID: %02X%02X\n", data->page[7].data[1], data->page[7].data[0]); + furi_string_cat_printf(parsed_data, "Tx count: %d\n", ventra_high_seq); + furi_string_cat_printf( + parsed_data, + "Hard Expiry: %04d-%02d-%02d", + ventra_exp_date.year, + ventra_exp_date.month, + ventra_exp_date.day); + + furi_string_free(ventra_prod_str); + furi_string_free(ventra_xact_str1); + furi_string_free(ventra_xact_str2); + + parsed = true; + } while(false); + + return parsed; +} + +/* Actual implementation of app<>plugin interface */ +static const NfcSupportedCardsPlugin ventra_plugin = { + .protocol = NfcProtocolMfUltralight, + .verify = NULL, + .read = NULL, + .parse = ventra_parse, +}; + +/* Plugin descriptor to comply with basic plugin specification */ +static const FlipperAppPluginDescriptor ventra_plugin_descriptor = { + .appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID, + .ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION, + .entry_point = &ventra_plugin, +}; + +/* Plugin entry point - must return a pointer to const descriptor */ +const FlipperAppPluginDescriptor* ventra_plugin_ep(void) { + return &ventra_plugin_descriptor; +} diff --git a/applications/main/nfc/plugins/supported_cards/zolotaya_korona.c b/applications/main/nfc/plugins/supported_cards/zolotaya_korona.c index cb0254b14..8ba36605b 100644 --- a/applications/main/nfc/plugins/supported_cards/zolotaya_korona.c +++ b/applications/main/nfc/plugins/supported_cards/zolotaya_korona.c @@ -209,6 +209,6 @@ static const FlipperAppPluginDescriptor zolotaya_korona_plugin_descriptor = { }; /* Plugin entry point - must return a pointer to const descriptor */ -const FlipperAppPluginDescriptor* zolotaya_korona_plugin_ep() { +const FlipperAppPluginDescriptor* zolotaya_korona_plugin_ep(void) { return &zolotaya_korona_plugin_descriptor; } diff --git a/applications/main/nfc/plugins/supported_cards/zolotaya_korona_online.c b/applications/main/nfc/plugins/supported_cards/zolotaya_korona_online.c index f2eccd7c0..0e39b8a58 100644 --- a/applications/main/nfc/plugins/supported_cards/zolotaya_korona_online.c +++ b/applications/main/nfc/plugins/supported_cards/zolotaya_korona_online.c @@ -139,6 +139,6 @@ static const FlipperAppPluginDescriptor zolotaya_korona_online_plugin_descriptor }; /* Plugin entry point - must return a pointer to const descriptor */ -const FlipperAppPluginDescriptor* zolotaya_korona_online_plugin_ep() { +const FlipperAppPluginDescriptor* zolotaya_korona_online_plugin_ep(void) { return &zolotaya_korona_online_plugin_descriptor; } diff --git a/applications/main/nfc/scenes/nfc_scene_config.h b/applications/main/nfc/scenes/nfc_scene_config.h index 55f8aa3be..b902bc940 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) @@ -48,10 +45,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_detect.c b/applications/main/nfc/scenes/nfc_scene_detect.c index 7ef3f9d87..a824c990d 100644 --- a/applications/main/nfc/scenes/nfc_scene_detect.c +++ b/applications/main/nfc/scenes/nfc_scene_detect.c @@ -16,6 +16,10 @@ void nfc_scene_detect_scan_callback(NfcScannerEvent event, void* context) { void nfc_scene_detect_on_enter(void* context) { NfcApp* instance = context; + nfc_show_loading_popup(instance, true); + nfc_supported_cards_load_cache(instance->nfc_supported_cards); + nfc_show_loading_popup(instance, false); + // Setup view popup_reset(instance->popup); popup_set_header(instance->popup, "Reading", 97, 15, AlignCenter, AlignTop); diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c index 024bc5c1e..500dd759a 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c @@ -236,7 +236,6 @@ void nfc_scene_mf_classic_dict_attack_on_enter(void* context) { dict_attack_set_card_state(instance->dict_attack, true); view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewDictAttack); nfc_blink_read_start(instance); - notification_message(instance->notifications, &sequence_display_backlight_enforce_on); instance->poller = nfc_poller_alloc(instance->nfc, NfcProtocolMfClassic); nfc_poller_start(instance->poller, nfc_dict_attack_worker_callback, instance); @@ -392,5 +391,4 @@ void nfc_scene_mf_classic_dict_attack_on_exit(void* context) { } nfc_blink_stop(instance); - notification_message(instance->notifications, &sequence_display_backlight_enforce_auto); } 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_read.c b/applications/main/nfc/scenes/nfc_scene_read.c index e9603afd1..ff30eb318 100644 --- a/applications/main/nfc/scenes/nfc_scene_read.c +++ b/applications/main/nfc/scenes/nfc_scene_read.c @@ -1,6 +1,12 @@ #include "../helpers/protocol_support/nfc_protocol_support.h" +#include "../nfc_app_i.h" void nfc_scene_read_on_enter(void* context) { + NfcApp* instance = context; + nfc_show_loading_popup(instance, true); + nfc_supported_cards_load_cache(instance->nfc_supported_cards); + nfc_show_loading_popup(instance, false); + nfc_protocol_support_on_enter(NfcProtocolSupportSceneRead, context); } diff --git a/applications/main/nfc/scenes/nfc_scene_start.c b/applications/main/nfc/scenes/nfc_scene_start.c index b6e8f8298..23343daf8 100644 --- a/applications/main/nfc/scenes/nfc_scene_start.c +++ b/applications/main/nfc/scenes/nfc_scene_start.c @@ -56,37 +56,25 @@ bool nfc_scene_start_on_event(void* context, SceneManagerEvent event) { bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { + consumed = true; if(event.event == SubmenuIndexRead) { - scene_manager_set_scene_state(nfc->scene_manager, NfcSceneStart, SubmenuIndexRead); scene_manager_next_scene(nfc->scene_manager, NfcSceneDetect); dolphin_deed(DolphinDeedNfcRead); - consumed = true; } else if(event.event == SubmenuIndexDetectReader) { - scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneStart, SubmenuIndexDetectReader); scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicDetectReader); - consumed = true; } else if(event.event == SubmenuIndexSaved) { - // Save the scene state explicitly in each branch, so that - // if the user cancels loading a file, the Saved menu item - // is properly reselected. - scene_manager_set_scene_state(nfc->scene_manager, NfcSceneStart, SubmenuIndexSaved); scene_manager_next_scene(nfc->scene_manager, NfcSceneFileSelect); - consumed = true; } else if(event.event == SubmenuIndexExtraAction) { - scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneStart, SubmenuIndexExtraAction); scene_manager_next_scene(nfc->scene_manager, NfcSceneExtraActions); - consumed = true; } else if(event.event == SubmenuIndexAddManually) { - scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneStart, SubmenuIndexAddManually); scene_manager_next_scene(nfc->scene_manager, NfcSceneSetType); - consumed = true; } else if(event.event == SubmenuIndexDebug) { - scene_manager_set_scene_state(nfc->scene_manager, NfcSceneStart, SubmenuIndexDebug); scene_manager_next_scene(nfc->scene_manager, NfcSceneDebug); - consumed = true; + } else { + consumed = false; + } + if(consumed) { + scene_manager_set_scene_state(nfc->scene_manager, NfcSceneStart, event.event); } } return consumed; 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); +} diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index e2aa3d897..73ae7daae 100755 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -155,6 +155,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,, @@ -167,11 +170,17 @@ 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,, 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,, Header,+,lib/one_wire/one_wire_slave.h,, @@ -2166,6 +2175,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* @@ -2212,6 +2224,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*" @@ -2256,6 +2269,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" @@ -2682,6 +2702,8 @@ 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_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*" @@ -2698,9 +2720,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*, @@ -2914,9 +2937,6 @@ Function,+,nfc_iso14443a_listener_tx_custom_parity,NfcError,"Nfc*, const BitBuff Function,+,nfc_iso14443a_poller_trx_custom_parity,NfcError,"Nfc*, const BitBuffer*, BitBuffer*, uint32_t" Function,+,nfc_iso14443a_poller_trx_sdd_frame,NfcError,"Nfc*, const BitBuffer*, BitBuffer*, uint32_t" Function,+,nfc_iso14443a_poller_trx_short_frame,NfcError,"Nfc*, NfcIso14443aShortFrame, BitBuffer*, uint32_t" -Function,+,nfc_iso15693_detect_mode,NfcError,Nfc* -Function,+,nfc_iso15693_force_1outof256,NfcError,Nfc* -Function,+,nfc_iso15693_force_1outof4,NfcError,Nfc* Function,+,nfc_iso15693_listener_tx_sof,NfcError,Nfc* Function,+,nfc_listener_alloc,NfcListener*,"Nfc*, NfcProtocol, const NfcDeviceData*" Function,+,nfc_listener_free,void,NfcListener* @@ -2956,6 +2976,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* const +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* @@ -3726,6 +3759,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* @@ -4117,7 +4162,9 @@ 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, Variable,+,sequence_blink_blue_10,const NotificationSequence, Variable,+,sequence_blink_blue_100,const NotificationSequence,