diff --git a/application.fam b/application.fam index 357a79c7e93..be8cdc9ac13 100644 --- a/application.fam +++ b/application.fam @@ -4,6 +4,9 @@ App( apptype=FlipperAppType.EXTERNAL, targets=["f7"], entry_point="picopass_app", + sources=[ + "*.c", "!plugin/*.c", + ], requires=[ "storage", "gui", @@ -23,3 +26,12 @@ App( fap_icon_assets="icons", fap_file_assets="files", ) + +App( + appid="plugin_wiegand", + apptype=FlipperAppType.PLUGIN, + entry_point="plugin_wiegand_ep", + requires=["picopass"], + sources=["plugin/wiegand.c"], + fal_embedded=True, +) diff --git a/picopass.c b/picopass.c index 9b5532e534d..d9ca9d8fa60 100644 --- a/picopass.c +++ b/picopass.c @@ -97,6 +97,27 @@ Picopass* picopass_alloc() { view_dispatcher_add_view( picopass->view_dispatcher, PicopassViewLoclass, loclass_get_view(picopass->loclass)); + picopass->plugin_manager = + plugin_manager_alloc(PLUGIN_APP_ID, PLUGIN_API_VERSION, firmware_api_interface); + + picopass->plugin_wiegand = NULL; + if(plugin_manager_load_all(picopass->plugin_manager, APP_DATA_PATH("plugins")) != + PluginManagerErrorNone) { + FURI_LOG_E(TAG, "Failed to load all libs"); + } else { + uint32_t plugin_count = plugin_manager_get_count(picopass->plugin_manager); + FURI_LOG_I(TAG, "Loaded %lu plugin(s)", plugin_count); + + for(uint32_t i = 0; i < plugin_count; i++) { + const PluginWiegand* plugin = plugin_manager_get_ep(picopass->plugin_manager, i); + FURI_LOG_I(TAG, "plugin name: %s", plugin->name); + if(strcmp(plugin->name, "Plugin Wiegand") == 0) { + // Have to cast to drop "const" qualifier + picopass->plugin_wiegand = (PluginWiegand*)plugin; + } + } + } + return picopass; } @@ -158,6 +179,8 @@ void picopass_free(Picopass* picopass) { furi_record_close(RECORD_NOTIFICATION); picopass->notifications = NULL; + plugin_manager_free(picopass->plugin_manager); + free(picopass); } diff --git a/picopass_i.h b/picopass_i.h index 5ec15c4bdd2..0dd687dc170 100644 --- a/picopass_i.h +++ b/picopass_i.h @@ -34,6 +34,11 @@ #include "protocol/picopass_poller.h" #include "protocol/picopass_listener.h" +#include "plugin/interface.h" +#include +#include +#include + #define PICOPASS_TEXT_STORE_SIZE 129 #define PICOPASS_ICLASS_ELITE_DICT_FLIPPER_NAME APP_ASSETS_PATH("iclass_elite_dict.txt") @@ -107,6 +112,9 @@ struct Picopass { DictAttack* dict_attack; Loclass* loclass; + PluginManager* plugin_manager; + PluginWiegand* plugin_wiegand; + PicopassDictAttackContext dict_attack_ctx; PicopassWriteKeyContext write_key_context; PicopassLoclassContext loclass_context; diff --git a/plugin b/plugin new file mode 160000 index 00000000000..30d5c440e6c --- /dev/null +++ b/plugin @@ -0,0 +1 @@ +Subproject commit 30d5c440e6ca1aa92c56999758b38c52f3972c8a diff --git a/scenes/picopass_scene_card_menu.c b/scenes/picopass_scene_card_menu.c index d9e27ea5c4b..9d44f5642d9 100644 --- a/scenes/picopass_scene_card_menu.c +++ b/scenes/picopass_scene_card_menu.c @@ -4,6 +4,7 @@ enum SubmenuIndex { SubmenuIndexSave, SubmenuIndexSaveAsLF, SubmenuIndexSaveAsSeader, + SubmenuIndexParse, SubmenuIndexChangeKey, SubmenuIndexWrite, SubmenuIndexEmulate, @@ -22,6 +23,7 @@ void picopass_scene_card_menu_on_enter(void* context) { PicopassPacs* pacs = &picopass->dev->dev_data.pacs; PicopassBlock* card_data = picopass->dev->dev_data.card_data; PicopassDeviceAuthMethod auth = picopass->dev->dev_data.auth; + PluginWiegand* plugin = picopass->plugin_wiegand; bool SE = card_data[PICOPASS_ICLASS_PACS_CFG_BLOCK_INDEX].valid && 0x30 == card_data[PICOPASS_ICLASS_PACS_CFG_BLOCK_INDEX].data[0]; @@ -59,6 +61,23 @@ void picopass_scene_card_menu_on_enter(void* context) { SubmenuIndexSaveAsLF, picopass_scene_card_menu_submenu_callback, picopass); + + if(plugin) { + // Convert from byte array to uint64_t + uint64_t credential = 0; + memcpy(&credential, pacs->credential, sizeof(uint64_t)); + credential = __builtin_bswap64(credential); + + size_t format_count = plugin->count(pacs->bitLength, credential); + if(format_count > 0) { + submenu_add_item( + submenu, + "Parse", + SubmenuIndexParse, + picopass_scene_card_menu_submenu_callback, + picopass); + } + } } if(auth == PicopassDeviceAuthMethodNone || auth == PicopassDeviceAuthMethodKey) { @@ -131,6 +150,11 @@ bool picopass_scene_card_menu_on_event(void* context, SceneManagerEvent event) { picopass->scene_manager, PicopassSceneCardMenu, SubmenuIndexChangeKey); scene_manager_next_scene(picopass->scene_manager, PicopassSceneKeyMenu); consumed = true; + } else if(event.event == SubmenuIndexParse) { + scene_manager_set_scene_state( + picopass->scene_manager, PicopassSceneCardMenu, SubmenuIndexParse); + scene_manager_next_scene(picopass->scene_manager, PicopassSceneFormats); + consumed = true; } } else if(event.type == SceneManagerEventTypeBack) { consumed = scene_manager_search_and_switch_to_previous_scene( diff --git a/scenes/picopass_scene_config.h b/scenes/picopass_scene_config.h index 0830834f811..9eb5f7f5ff6 100644 --- a/scenes/picopass_scene_config.h +++ b/scenes/picopass_scene_config.h @@ -21,3 +21,4 @@ ADD_SCENE(picopass, loclass, Loclass) ADD_SCENE(picopass, key_input, KeyInput) ADD_SCENE(picopass, nr_mac_saved, NrMacSaved) ADD_SCENE(picopass, more_info, MoreInfo) +ADD_SCENE(picopass, formats, Formats) diff --git a/scenes/picopass_scene_device_info.c b/scenes/picopass_scene_device_info.c index c08adfda236..4d4aba5ae9b 100644 --- a/scenes/picopass_scene_device_info.c +++ b/scenes/picopass_scene_device_info.c @@ -23,6 +23,7 @@ void picopass_scene_device_info_on_enter(void* context) { // Setup view PicopassBlock* card_data = picopass->dev->dev_data.card_data; PicopassPacs* pacs = &picopass->dev->dev_data.pacs; + PluginWiegand* plugin = picopass->plugin_wiegand; Widget* widget = picopass->widget; uint8_t csn[PICOPASS_BLOCK_LEN] = {0}; @@ -77,10 +78,27 @@ void picopass_scene_device_info_on_enter(void* context) { "Back", picopass_scene_device_info_widget_callback, picopass); + + if(plugin) { + // Convert from byte array to uint64_t + uint64_t credential = 0; + memcpy(&credential, pacs->credential, sizeof(uint64_t)); + credential = __builtin_bswap64(credential); + + size_t format_count = plugin->count(pacs->bitLength, credential); + if(format_count > 0) { + widget_add_button_element( + picopass->widget, + GuiButtonTypeCenter, + "Parse", + picopass_scene_device_info_widget_callback, + picopass); + } + } widget_add_button_element( picopass->widget, GuiButtonTypeRight, - "More", + "Raw", picopass_scene_device_info_widget_callback, picopass); @@ -97,6 +115,9 @@ bool picopass_scene_device_info_on_event(void* context, SceneManagerEvent event) } else if(event.event == GuiButtonTypeRight) { scene_manager_next_scene(picopass->scene_manager, PicopassSceneMoreInfo); consumed = true; + } else if(event.event == GuiButtonTypeCenter) { + scene_manager_next_scene(picopass->scene_manager, PicopassSceneFormats); + consumed = true; } else if(event.event == PicopassCustomEventViewExit) { view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewWidget); consumed = true; diff --git a/scenes/picopass_scene_formats.c b/scenes/picopass_scene_formats.c new file mode 100644 index 00000000000..5cf97b16392 --- /dev/null +++ b/scenes/picopass_scene_formats.c @@ -0,0 +1,54 @@ +#include "../picopass_i.h" +#include + +void picopass_scene_formats_on_enter(void* context) { + Picopass* picopass = context; + PluginWiegand* plugin = picopass->plugin_wiegand; + PicopassDevice* dev = picopass->dev; + PicopassDeviceData dev_data = dev->dev_data; + PicopassPacs pacs = dev_data.pacs; + + FuriString* str = picopass->text_box_store; + furi_string_reset(str); + + // Convert from byte array to uint64_t + uint64_t credential = 0; + memcpy(&credential, pacs.credential, sizeof(pacs.credential)); + credential = __builtin_bswap64(credential); + + if(plugin) { + FuriString* description = furi_string_alloc(); + size_t format_count = plugin->count(pacs.bitLength, credential); + for(size_t i = 0; i < format_count; i++) { + plugin->description(pacs.bitLength, credential, i, description); + + furi_string_cat_printf(str, "%s\n", furi_string_get_cstr(description)); + } + furi_string_free(description); + } + + text_box_set_font(picopass->text_box, TextBoxFontHex); + text_box_set_text(picopass->text_box, furi_string_get_cstr(picopass->text_box_store)); + view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewTextBox); +} + +bool picopass_scene_formats_on_event(void* context, SceneManagerEvent event) { + Picopass* picopass = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeLeft) { + consumed = scene_manager_previous_scene(picopass->scene_manager); + } + } else if(event.type == SceneManagerEventTypeBack) { + consumed = scene_manager_previous_scene(picopass->scene_manager); + } + return consumed; +} + +void picopass_scene_formats_on_exit(void* context) { + Picopass* picopass = context; + + // Clear views + text_box_reset(picopass->text_box); +}