From c8a5f308846e9308859a5fcc84c12d14ba066d19 Mon Sep 17 00:00:00 2001 From: CinderSocket Date: Sun, 8 Mar 2026 15:32:52 -0700 Subject: [PATCH 1/3] Unify hf reading and let the SAM drive the timing, not the HF interface --- sam_api.c | 10 +- scenes/seader_scene_apdu_runner.c | 2 +- scenes/seader_scene_config.h | 4 +- ...r_scene_read_14a.c => seader_scene_read.c} | 21 +- scenes/seader_scene_read_card_success.c | 3 + scenes/seader_scene_read_common.c | 17 +- scenes/seader_scene_read_common.h | 3 + scenes/seader_scene_read_config_card.c | 15 +- scenes/seader_scene_read_mfc.c | 59 ---- scenes/seader_scene_read_picopass.c | 54 ---- scenes/seader_scene_sam_present.c | 31 +-- scenes/seader_scene_start.c | 7 +- scenes/seader_scene_virtual_credential.c | 2 +- seader.c | 22 +- seader_bridge.h | 1 + seader_worker.c | 256 +++++++++++++----- seader_worker.h | 3 +- seader_worker_i.h | 2 - 18 files changed, 259 insertions(+), 253 deletions(-) rename scenes/{seader_scene_read_14a.c => seader_scene_read.c} (70%) delete mode 100644 scenes/seader_scene_read_mfc.c delete mode 100644 scenes/seader_scene_read_picopass.c diff --git a/sam_api.c b/sam_api.c index edd37d0..ee93001 100644 --- a/sam_api.c +++ b/sam_api.c @@ -659,10 +659,12 @@ bool seader_unpack_pacs(Seader* seader, uint8_t* buf, size_t size) { if(pac.size <= sizeof(seader_credential->credential)) { // TODO: make credential into a 12 byte array seader_credential->bit_length = pac.size * 8 - pac.bits_unused; - memcpy(&seader_credential->credential, pac.buf, pac.size); - seader_credential->credential = __builtin_bswap64(seader_credential->credential); - seader_credential->credential = seader_credential->credential >> - (64 - seader_credential->bit_length); + uint64_t credential_val = 0; + memcpy(&credential_val, pac.buf, pac.size); + credential_val = __builtin_bswap64(credential_val); + // After bswap64, the bits are left-aligned in the 64-bit word + // We need to shift them right by (64 - bit_length) to get the value + seader_credential->credential = credential_val >> (64 - seader_credential->bit_length); FURI_LOG_D( TAG, diff --git a/scenes/seader_scene_apdu_runner.c b/scenes/seader_scene_apdu_runner.c index f69dd00..e68e0cc 100644 --- a/scenes/seader_scene_apdu_runner.c +++ b/scenes/seader_scene_apdu_runner.c @@ -5,7 +5,7 @@ char seader_scene_apdu_runner_update_text[24]; -void seader_apdu_runner_worker_callback(SeaderWorkerEvent event, void* context) { +void seader_apdu_runner_worker_callback(uint32_t event, void* context) { Seader* seader = context; view_dispatcher_send_custom_event(seader->view_dispatcher, event); } diff --git a/scenes/seader_scene_config.h b/scenes/seader_scene_config.h index 27309dc..09e9323 100644 --- a/scenes/seader_scene_config.h +++ b/scenes/seader_scene_config.h @@ -2,8 +2,7 @@ ADD_SCENE(seader, start, Start) ADD_SCENE(seader, sam_present, SamPresent) ADD_SCENE(seader, sam_missing, SamMissing) ADD_SCENE(seader, sam_wrong, SamWrong) -ADD_SCENE(seader, read_picopass, ReadPicopass) -ADD_SCENE(seader, read_14a, Read14a) +ADD_SCENE(seader, read, Read) ADD_SCENE(seader, read_config_card, ReadConfigCard) ADD_SCENE(seader, read_card_success, ReadCardSuccess) ADD_SCENE(seader, read_config_card_success, ReadConfigCardSuccess) @@ -18,5 +17,4 @@ ADD_SCENE(seader, credential_info, CredentialInfo) ADD_SCENE(seader, sam_info, SamInfo) ADD_SCENE(seader, virtual_credential, VirtualCredential) ADD_SCENE(seader, formats, Formats) -ADD_SCENE(seader, read_mfc, ReadMfc) ADD_SCENE(seader, apdu_runner, APDURunner) diff --git a/scenes/seader_scene_read_14a.c b/scenes/seader_scene_read.c similarity index 70% rename from scenes/seader_scene_read_14a.c rename to scenes/seader_scene_read.c index 5b8acde..3b89b13 100644 --- a/scenes/seader_scene_read_14a.c +++ b/scenes/seader_scene_read.c @@ -2,28 +2,31 @@ #include "seader_scene_read_common.h" #include -void seader_scene_read_14a_on_enter(void* context) { +void seader_scene_read_on_enter(void* context) { Seader* seader = context; dolphin_deed(DolphinDeedNfcRead); // Setup view Popup* popup = seader->popup; - popup_set_header(popup, "Detecting\n14a\ncard", 68, 30, AlignLeft, AlignTop); + popup_set_header(popup, "Detecting\nHF card...", 68, 30, AlignLeft, AlignTop); popup_set_icon(popup, 0, 3, &I_RFIDDolphinReceive_97x61); // Start worker view_dispatcher_switch_to_view(seader->view_dispatcher, SeaderViewPopup); seader_scene_read_prepare(seader); - seader->poller = nfc_poller_alloc(seader->nfc, NfcProtocolIso14443_4a); seader_credential_clear(seader->credential); - seader->credential->type = SeaderCredentialType14A; - nfc_poller_start(seader->poller, seader_worker_poller_callback_iso14443_4a, seader); + seader_worker_start( + seader->worker, + SeaderWorkerStateReading, + seader->uart, + seader_sam_check_worker_callback, + seader); seader_blink_start(seader); } -bool seader_scene_read_14a_on_event(void* context, SceneManagerEvent event) { +bool seader_scene_read_on_event(void* context, SceneManagerEvent event) { Seader* seader = context; bool consumed = false; @@ -35,7 +38,8 @@ bool seader_scene_read_14a_on_event(void* context, SceneManagerEvent event) { Popup* popup = seader->popup; popup_set_header(popup, "DON'T\nMOVE", 68, 30, AlignLeft, AlignTop); consumed = true; - } else if(event.event == SeaderCustomEventPollerSuccess) { + } else if(event.event == SeaderCustomEventPollerSuccess || + event.event == SeaderWorkerEventSuccess) { scene_manager_next_scene(seader->scene_manager, SeaderSceneReadCardSuccess); consumed = true; } @@ -48,7 +52,8 @@ bool seader_scene_read_14a_on_event(void* context, SceneManagerEvent event) { return consumed; } -void seader_scene_read_14a_on_exit(void* context) { +void seader_scene_read_on_exit(void* context) { Seader* seader = context; + seader_worker_stop(seader->worker); seader_scene_read_cleanup(seader); } diff --git a/scenes/seader_scene_read_card_success.c b/scenes/seader_scene_read_card_success.c index d615e4a..e5a86c5 100644 --- a/scenes/seader_scene_read_card_success.c +++ b/scenes/seader_scene_read_card_success.c @@ -83,6 +83,7 @@ void seader_scene_read_card_success_on_enter(void* context) { if(plugin && credential->bit_length > 0) { size_t format_count = plugin->count(credential->bit_length, credential->credential); + FURI_LOG_D(TAG, "Plugin present, bit_length=%d, format_count=%zu", credential->bit_length, format_count); if(format_count > 0) { widget_add_button_element( seader->widget, @@ -91,6 +92,8 @@ void seader_scene_read_card_success_on_enter(void* context) { seader_scene_read_card_success_widget_callback, seader); } + } else { + FURI_LOG_D(TAG, "Plugin=%p, bit_length=%d", plugin, credential->bit_length); } widget_add_string_element( diff --git a/scenes/seader_scene_read_common.c b/scenes/seader_scene_read_common.c index 0a6e069..0341d3a 100644 --- a/scenes/seader_scene_read_common.c +++ b/scenes/seader_scene_read_common.c @@ -3,6 +3,11 @@ #include "../seader_i.h" #include "../trace_log.h" +void seader_sam_check_worker_callback(uint32_t event, void* context) { + Seader* seader = context; + view_dispatcher_send_custom_event(seader->view_dispatcher, event); +} + void seader_scene_read_prepare(Seader* seader) { furi_assert(seader); FURI_LOG_D("SceneRead", "Prepare session sam=%d", seader->samCommand); @@ -34,18 +39,6 @@ void seader_scene_read_cleanup(Seader* seader) { seader_send_no_card_detected(seader); } - if(seader->poller) { - nfc_poller_stop(seader->poller); - nfc_poller_free(seader->poller); - seader->poller = NULL; - } - - if(seader->picopass_poller) { - picopass_poller_stop(seader->picopass_poller); - picopass_poller_free(seader->picopass_poller); - seader->picopass_poller = NULL; - } - popup_reset(seader->popup); seader_worker_reset_poller_session(seader->worker); if(seader->sam_state == SeaderSamStateIdle) { diff --git a/scenes/seader_scene_read_common.h b/scenes/seader_scene_read_common.h index 6261c0a..d7f17b9 100644 --- a/scenes/seader_scene_read_common.h +++ b/scenes/seader_scene_read_common.h @@ -1,6 +1,9 @@ #pragma once +#include + typedef struct Seader Seader; +void seader_sam_check_worker_callback(uint32_t event, void* context); void seader_scene_read_prepare(Seader* seader); void seader_scene_read_cleanup(Seader* seader); diff --git a/scenes/seader_scene_read_config_card.c b/scenes/seader_scene_read_config_card.c index 73dc1c4..b72babf 100644 --- a/scenes/seader_scene_read_config_card.c +++ b/scenes/seader_scene_read_config_card.c @@ -2,7 +2,7 @@ #include "seader_scene_read_common.h" #include -void seader_read_config_card_worker_callback(SeaderWorkerEvent event, void* context) { +void seader_read_config_card_worker_callback(uint32_t event, void* context) { UNUSED(event); Seader* seader = context; view_dispatcher_send_custom_event(seader->view_dispatcher, SeaderCustomEventWorkerExit); @@ -20,10 +20,15 @@ void seader_scene_read_config_card_on_enter(void* context) { view_dispatcher_switch_to_view(seader->view_dispatcher, SeaderViewPopup); seader_scene_read_prepare(seader); - seader->poller = nfc_poller_alloc(seader->nfc, NfcProtocolIso14443_4a); seader_credential_clear(seader->credential); seader->credential->type = SeaderCredentialTypeConfig; - nfc_poller_start(seader->poller, seader_worker_poller_callback_iso14443_4a, seader); + + seader_worker_start( + seader->worker, + SeaderWorkerStateReading, + seader->uart, + seader_sam_check_worker_callback, + seader); seader_blink_start(seader); } @@ -33,7 +38,8 @@ bool seader_scene_read_config_card_on_event(void* context, SceneManagerEvent eve bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SeaderCustomEventWorkerExit) { + if(event.event == SeaderCustomEventWorkerExit || + event.event == SeaderWorkerEventSuccess) { scene_manager_next_scene(seader->scene_manager, SeaderSceneReadConfigCardSuccess); consumed = true; } else if(event.event == SeaderCustomEventPollerSuccess) { @@ -51,5 +57,6 @@ bool seader_scene_read_config_card_on_event(void* context, SceneManagerEvent eve void seader_scene_read_config_card_on_exit(void* context) { Seader* seader = context; + seader_worker_stop(seader->worker); seader_scene_read_cleanup(seader); } diff --git a/scenes/seader_scene_read_mfc.c b/scenes/seader_scene_read_mfc.c deleted file mode 100644 index c8d5188..0000000 --- a/scenes/seader_scene_read_mfc.c +++ /dev/null @@ -1,59 +0,0 @@ -#include "../seader_i.h" -#include "seader_scene_read_common.h" -#include - -#define TAG "SceneReadNfc" - -void seader_scene_read_mfc_on_enter(void* context) { - Seader* seader = context; - dolphin_deed(DolphinDeedNfcRead); - - // Setup view - Popup* popup = seader->popup; - popup_set_header(popup, "Detecting\nMFC\ncard", 68, 30, AlignLeft, AlignTop); - popup_set_icon(popup, 0, 3, &I_RFIDDolphinReceive_97x61); - - // Start worker - view_dispatcher_switch_to_view(seader->view_dispatcher, SeaderViewPopup); - - seader_scene_read_prepare(seader); - seader->poller = nfc_poller_alloc(seader->nfc, NfcProtocolMfClassic); - - seader_credential_clear(seader->credential); - seader->credential->type = SeaderCredentialTypeMifareClassic; - - FURI_LOG_W(TAG, "Start poller"); - nfc_poller_start(seader->poller, seader_worker_poller_callback_mfc, seader); - - seader_blink_start(seader); -} - -bool seader_scene_read_mfc_on_event(void* context, SceneManagerEvent event) { - Seader* seader = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SeaderCustomEventWorkerExit) { - scene_manager_next_scene(seader->scene_manager, SeaderSceneReadCardSuccess); - consumed = true; - } else if(event.event == SeaderCustomEventPollerDetect) { - Popup* popup = seader->popup; - popup_set_header(popup, "DON'T\nMOVE", 68, 30, AlignLeft, AlignTop); - consumed = true; - } else if(event.event == SeaderCustomEventPollerSuccess) { - scene_manager_next_scene(seader->scene_manager, SeaderSceneReadCardSuccess); - consumed = true; - } - } else if(event.type == SceneManagerEventTypeBack) { - scene_manager_search_and_switch_to_previous_scene( - seader->scene_manager, SeaderSceneSamPresent); - consumed = true; - } - - return consumed; -} - -void seader_scene_read_mfc_on_exit(void* context) { - Seader* seader = context; - seader_scene_read_cleanup(seader); -} diff --git a/scenes/seader_scene_read_picopass.c b/scenes/seader_scene_read_picopass.c deleted file mode 100644 index 519de66..0000000 --- a/scenes/seader_scene_read_picopass.c +++ /dev/null @@ -1,54 +0,0 @@ -#include "../seader_i.h" -#include "seader_scene_read_common.h" -#include - -void seader_scene_read_picopass_on_enter(void* context) { - Seader* seader = context; - dolphin_deed(DolphinDeedNfcRead); - - // Setup view - Popup* popup = seader->popup; - popup_set_header(popup, "Detecting\npicopass\ncard", 68, 30, AlignLeft, AlignTop); - popup_set_icon(popup, 0, 3, &I_RFIDDolphinReceive_97x61); - - // Start worker - view_dispatcher_switch_to_view(seader->view_dispatcher, SeaderViewPopup); - - seader_scene_read_prepare(seader); - seader_credential_clear(seader->credential); - seader->credential->type = SeaderCredentialTypePicopass; - seader->picopass_poller = picopass_poller_alloc(seader->nfc); - picopass_poller_start(seader->picopass_poller, seader_worker_poller_callback_picopass, seader); - - seader_blink_start(seader); -} - -bool seader_scene_read_picopass_on_event(void* context, SceneManagerEvent event) { - Seader* seader = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SeaderCustomEventWorkerExit) { - scene_manager_next_scene(seader->scene_manager, SeaderSceneReadCardSuccess); - consumed = true; - } else if(event.event == SeaderCustomEventPollerDetect) { - Popup* popup = seader->popup; - popup_set_header(popup, "DON'T\nMOVE", 68, 30, AlignLeft, AlignTop); - consumed = true; - } else if(event.event == SeaderCustomEventPollerSuccess) { - scene_manager_next_scene(seader->scene_manager, SeaderSceneReadCardSuccess); - consumed = true; - } - - } else if(event.type == SceneManagerEventTypeBack) { - scene_manager_search_and_switch_to_previous_scene( - seader->scene_manager, SeaderSceneSamPresent); - consumed = true; - } - return consumed; -} - -void seader_scene_read_picopass_on_exit(void* context) { - Seader* seader = context; - seader_scene_read_cleanup(seader); -} diff --git a/scenes/seader_scene_sam_present.c b/scenes/seader_scene_sam_present.c index 34e11b1..b661eb8 100644 --- a/scenes/seader_scene_sam_present.c +++ b/scenes/seader_scene_sam_present.c @@ -1,13 +1,11 @@ #include "../seader_i.h" enum SubmenuIndex { - SubmenuIndexReadPicopass, - SubmenuIndexRead14a, - SubmenuIndexReadMfc, - SubmenuIndexReadConfigCard, + SubmenuIndexRead, SubmenuIndexSaved, SubmenuIndexAPDURunner, SubmenuIndexSamInfo, SubmenuIndexFwVersion, + SubmenuIndexReadConfigCard, }; static uint8_t fwChecks = 3; @@ -26,20 +24,8 @@ void seader_scene_sam_present_on_update(void* context) { submenu_add_item( submenu, - "Read Picopass", - SubmenuIndexReadPicopass, - seader_scene_sam_present_submenu_callback, - seader); - submenu_add_item( - submenu, - "Read 14443A", - SubmenuIndexRead14a, - seader_scene_sam_present_submenu_callback, - seader); - submenu_add_item( - submenu, - "Read MFC", - SubmenuIndexReadMfc, + "Read HF", + SubmenuIndexRead, seader_scene_sam_present_submenu_callback, seader); submenu_add_item( @@ -95,14 +81,9 @@ bool seader_scene_sam_present_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { scene_manager_set_scene_state(seader->scene_manager, SeaderSceneSamPresent, event.event); - if(event.event == SubmenuIndexReadPicopass) { - scene_manager_next_scene(seader->scene_manager, SeaderSceneReadPicopass); + if(event.event == SubmenuIndexRead) { + scene_manager_next_scene(seader->scene_manager, SeaderSceneRead); consumed = true; - } else if(event.event == SubmenuIndexRead14a) { - scene_manager_next_scene(seader->scene_manager, SeaderSceneRead14a); - consumed = true; - } else if(event.event == SubmenuIndexReadMfc) { - scene_manager_next_scene(seader->scene_manager, SeaderSceneReadMfc); } else if(event.event == SubmenuIndexReadConfigCard) { scene_manager_set_scene_state( seader->scene_manager, SeaderSceneSamPresent, SubmenuIndexReadConfigCard); diff --git a/scenes/seader_scene_start.c b/scenes/seader_scene_start.c index 76cfe2f..33d6a27 100644 --- a/scenes/seader_scene_start.c +++ b/scenes/seader_scene_start.c @@ -1,4 +1,6 @@ #include "../seader_i.h" +#include "seader_scene_read_common.h" + enum SubmenuIndex { SubmenuIndexSamPresent, SubmenuIndexSamMissing, @@ -9,11 +11,6 @@ static void seader_scene_start_detect_callback(void* context) { view_dispatcher_send_custom_event(seader->view_dispatcher, SeaderWorkerEventSamMissing); } -void seader_sam_check_worker_callback(SeaderWorkerEvent event, void* context) { - Seader* seader = context; - view_dispatcher_send_custom_event(seader->view_dispatcher, event); -} - void seader_scene_start_submenu_callback(void* context, uint32_t index) { Seader* seader = context; view_dispatcher_send_custom_event(seader->view_dispatcher, index); diff --git a/scenes/seader_scene_virtual_credential.c b/scenes/seader_scene_virtual_credential.c index d675939..bcdb5ff 100644 --- a/scenes/seader_scene_virtual_credential.c +++ b/scenes/seader_scene_virtual_credential.c @@ -1,7 +1,7 @@ #include "../seader_i.h" #include -void seader_virtual_credential_worker_callback(SeaderWorkerEvent event, void* context) { +void seader_virtual_credential_worker_callback(uint32_t event, void* context) { Seader* seader = context; view_dispatcher_send_custom_event(seader->view_dispatcher, event); } diff --git a/seader.c b/seader.c index fcdad58..3758792 100644 --- a/seader.c +++ b/seader.c @@ -49,11 +49,8 @@ Seader* seader_alloc() { seader->credential = seader_credential_alloc(); - seader->nfc = nfc_alloc(); - - // Nfc device - seader->nfc_device = nfc_device_alloc(); - nfc_device_set_loading_callback(seader->nfc_device, seader_show_loading_popup, seader); + seader->nfc = NULL; + seader->nfc_device = NULL; // Open GUI record seader->gui = furi_record_open(RECORD_GUI); @@ -104,6 +101,7 @@ Seader* seader_alloc() { plugin_manager_alloc(PLUGIN_APP_ID, PLUGIN_API_VERSION, firmware_api_interface); seader->plugin_wiegand = NULL; + FURI_LOG_I(TAG, "Loading plugins from %s", APP_ASSETS_PATH("plugins")); if(plugin_manager_load_all(seader->plugin_manager, APP_ASSETS_PATH("plugins")) != PluginManagerErrorNone) { FURI_LOG_E(TAG, "Failed to load all libs"); @@ -113,8 +111,9 @@ Seader* seader_alloc() { for(uint32_t i = 0; i < plugin_count; i++) { const PluginWiegand* plugin = plugin_manager_get_ep(seader->plugin_manager, i); - FURI_LOG_I(TAG, "plugin name: %s", plugin->name); + FURI_LOG_I(TAG, "plugin index %lu, name: %s", i, plugin->name); if(strcmp(plugin->name, "Plugin Wiegand") == 0) { + FURI_LOG_I(TAG, "Wiegand plugin found and assigned"); // Have to cast to drop "const" qualifier seader->plugin_wiegand = (PluginWiegand*)plugin; } @@ -137,10 +136,15 @@ void seader_free(Seader* seader) { seader_credential_free(seader->credential); seader->credential = NULL; - nfc_free(seader->nfc); + if(seader->nfc) { + nfc_free(seader->nfc); + seader->nfc = NULL; + } - // Nfc device - nfc_device_free(seader->nfc_device); + if(seader->nfc_device) { + nfc_device_free(seader->nfc_device); + seader->nfc_device = NULL; + } // Submenu view_dispatcher_remove_view(seader->view_dispatcher, SeaderViewMenu); diff --git a/seader_bridge.h b/seader_bridge.h index 2a5a4a2..4d4ec1d 100644 --- a/seader_bridge.h +++ b/seader_bridge.h @@ -14,6 +14,7 @@ #define SEADER_CCID_SLOT_COUNT (2U) typedef struct BitBuffer BitBuffer; +typedef struct SeaderWorker SeaderWorker; typedef struct { uint8_t uart_ch; diff --git a/seader_worker.c b/seader_worker.c index 7231d3f..991f53d 100644 --- a/seader_worker.c +++ b/seader_worker.c @@ -16,6 +16,8 @@ // Forward declaration void seader_send_card_detected(SeaderUartBridge* seader_uart, CardDetails_t* cardDetails); +void seader_worker_reading(Seader* seader); +void seader_worker_poller_conversation(Seader* seader, SeaderPollerContainer* spc); /***************************** Seader Worker API *******************************/ @@ -26,7 +28,6 @@ SeaderWorker* seader_worker_alloc() { seader_worker->thread = furi_thread_alloc_ex("SeaderWorker", 8192, seader_worker_task, seader_worker); seader_worker->messages = furi_message_queue_alloc(3, sizeof(SeaderAPDU)); - seader_worker->mq_mutex = furi_mutex_alloc(FuriMutexTypeNormal); seader_worker->callback = NULL; seader_worker->context = NULL; @@ -43,7 +44,6 @@ void seader_worker_free(SeaderWorker* seader_worker) { furi_thread_free(seader_worker->thread); furi_message_queue_free(seader_worker->messages); - furi_mutex_free(seader_worker->mq_mutex); furi_record_close(RECORD_STORAGE); @@ -63,22 +63,25 @@ void seader_worker_start( furi_assert(seader_worker); furi_assert(uart); + if(furi_thread_get_state(seader_worker->thread) != FuriThreadStateStopped) { + seader_worker_stop(seader_worker); + } + seader_worker->stage = SeaderPollerEventTypeCardDetect; seader_worker->callback = callback; seader_worker->context = context; seader_worker->uart = uart; - seader_worker_change_state(seader_worker, state); + seader_worker->state = state; furi_thread_start(seader_worker->thread); } void seader_worker_stop(SeaderWorker* seader_worker) { furi_assert(seader_worker); - if(seader_worker->state == SeaderWorkerStateBroken || - seader_worker->state == SeaderWorkerStateReady) { + if(furi_thread_get_state(seader_worker->thread) == FuriThreadStateStopped) { return; } - seader_worker_change_state(seader_worker, SeaderWorkerStateStop); + seader_worker->state = SeaderWorkerStateStop; furi_thread_join(seader_worker->thread); } @@ -114,10 +117,7 @@ void seader_worker_reset_poller_session(SeaderWorker* seader_worker) { seader_worker->stage, furi_message_queue_get_count(seader_worker->messages)); - if(furi_mutex_acquire(seader_worker->mq_mutex, FuriWaitForever) == FuriStatusOk) { - furi_message_queue_reset(seader_worker->messages); - furi_mutex_release(seader_worker->mq_mutex); - } + furi_message_queue_reset(seader_worker->messages); seader_worker->stage = SeaderPollerEventTypeCardDetect; } @@ -163,10 +163,7 @@ bool seader_process_success_response(Seader* seader, uint8_t* apdu, size_t len) seaderApdu.len = len; memcpy(seaderApdu.buf, apdu, len); - if(furi_mutex_acquire(seader_worker->mq_mutex, FuriWaitForever) == FuriStatusOk) { - furi_message_queue_put(seader_worker->messages, &seaderApdu, FuriWaitForever); - furi_mutex_release(seader_worker->mq_mutex); - } + furi_message_queue_put(seader_worker->messages, &seaderApdu, FuriWaitForever); } } return true; @@ -239,33 +236,31 @@ void seader_worker_virtual_credential(Seader* seader) { uint8_t dead_loops = 20; while(running) { - if(furi_mutex_acquire(seader_worker->mq_mutex, 0) == FuriStatusOk) { - uint32_t count = furi_message_queue_get_count(seader_worker->messages); - if(count > 0) { - FURI_LOG_I(TAG, "Dequeue SAM message [%ld messages]", count); + uint32_t count = furi_message_queue_get_count(seader_worker->messages); + if(count > 0) { + FURI_LOG_I(TAG, "Dequeue SAM message [%ld messages]", count); - SeaderAPDU seaderApdu = {}; - FuriStatus status = - furi_message_queue_get(seader_worker->messages, &seaderApdu, FuriWaitForever); - if(status != FuriStatusOk) { - FURI_LOG_W(TAG, "furi_message_queue_get fail %d", status); - view_dispatcher_send_custom_event( - seader->view_dispatcher, SeaderCustomEventWorkerExit); - } - if(seader_process_success_response_i( - seader, seaderApdu.buf, seaderApdu.len, true, NULL)) { - // no-op - } else { - FURI_LOG_I(TAG, "Response false"); - running = false; - } + SeaderAPDU seaderApdu = {}; + FuriStatus status = + furi_message_queue_get(seader_worker->messages, &seaderApdu, FuriWaitForever); + if(status != FuriStatusOk) { + FURI_LOG_W(TAG, "furi_message_queue_get fail %d", status); + view_dispatcher_send_custom_event( + seader->view_dispatcher, SeaderCustomEventWorkerExit); + } + if(seader_process_success_response_i( + seader, seaderApdu.buf, seaderApdu.len, true, NULL)) { + // no-op + } else { + FURI_LOG_I(TAG, "Response false"); + running = false; } - furi_mutex_release(seader_worker->mq_mutex); } else { dead_loops--; running = (dead_loops > 0); FURI_LOG_D( TAG, "Dead loops: %d -> Running: %s", dead_loops, running ? "true" : "false"); + if(running) furi_delay_ms(10); // Don't tight loop if empty } running = (seader_worker->stage != SeaderPollerEventTypeComplete); } @@ -292,50 +287,164 @@ int32_t seader_worker_task(void* context) { FURI_LOG_D(TAG, "APDU Runner"); seader_apdu_runner_init(seader); return 0; + } else if(seader_worker->state == SeaderWorkerStateReading) { + FURI_LOG_D(TAG, "Reading mode started"); + seader_worker_reading(seader); } seader_worker_change_state(seader_worker, SeaderWorkerStateReady); return 0; } -void seader_worker_poller_conversation(Seader* seader, SeaderPollerContainer* spc) { +void seader_worker_reading(Seader* seader) { SeaderWorker* seader_worker = seader->worker; - seader_trace( - TAG, - "conversation stage=%d queued=%ld", - seader_worker->stage, - furi_message_queue_get_count(seader_worker->messages)); + FURI_LOG_I(TAG, "Reading loop started"); - if(furi_mutex_acquire(seader_worker->mq_mutex, 0) == FuriStatusOk) { - furi_thread_set_current_priority(FuriThreadPriorityHighest); - uint32_t count = furi_message_queue_get_count(seader_worker->messages); - if(count > 0) { - FURI_LOG_I(TAG, "Dequeue SAM message [%ld messages]", count); - seader_trace(TAG, "dequeue count=%ld", count); + seader->nfc = nfc_alloc(); + seader->nfc_device = nfc_device_alloc(); + nfc_device_set_loading_callback(seader->nfc_device, seader_show_loading_popup, seader); - SeaderAPDU seaderApdu = {}; - FuriStatus status = - furi_message_queue_get(seader_worker->messages, &seaderApdu, FuriWaitForever); - if(status != FuriStatusOk) { - FURI_LOG_W(TAG, "furi_message_queue_get fail %d", status); - seader_worker->stage = SeaderPollerEventTypeComplete; - view_dispatcher_send_custom_event( - seader->view_dispatcher, SeaderCustomEventWorkerExit); - } + while(seader_worker->state == SeaderWorkerStateReading) { + bool detected = false; + SeaderPollerEventType result_stage = SeaderPollerEventTypeFail; - if(seader_process_success_response_i( - seader, seaderApdu.buf, seaderApdu.len, true, spc)) { - // no-op + // 1. HF Discovery (ISO14443-4A) + NfcPoller* poller_detect = nfc_poller_alloc(seader->nfc, NfcProtocolIso14443_4a); + if(nfc_poller_detect(poller_detect)) { + FURI_LOG_I(TAG, "Detected ISO14443-4A card"); + nfc_poller_free(poller_detect); + poller_detect = NULL; + + seader->poller = nfc_poller_alloc(seader->nfc, NfcProtocolIso14443_4a); + seader_worker->stage = SeaderPollerEventTypeCardDetect; + seader->credential->type = SeaderCredentialType14A; + nfc_poller_start( + seader->poller, seader_worker_poller_callback_iso14443_4a, seader); + detected = true; + } else { + nfc_poller_free(poller_detect); + poller_detect = NULL; + } + + // 2. HF Discovery (Mifare Classic) + if(!detected) { + poller_detect = nfc_poller_alloc(seader->nfc, NfcProtocolMfClassic); + if(nfc_poller_detect(poller_detect)) { + FURI_LOG_I(TAG, "Detected Mifare Classic card"); + nfc_poller_free(poller_detect); + poller_detect = NULL; + + seader->poller = nfc_poller_alloc(seader->nfc, NfcProtocolMfClassic); + seader_worker->stage = SeaderPollerEventTypeCardDetect; + seader->credential->type = SeaderCredentialTypeMifareClassic; + nfc_poller_start(seader->poller, seader_worker_poller_callback_mfc, seader); + detected = true; } else { - FURI_LOG_I(TAG, "Response false"); - view_dispatcher_send_custom_event( - seader->view_dispatcher, SeaderCustomEventWorkerExit); - seader_worker->stage = SeaderPollerEventTypeComplete; + nfc_poller_free(poller_detect); + poller_detect = NULL; } } - furi_mutex_release(seader_worker->mq_mutex); - } else { - furi_thread_set_current_priority(FuriThreadPriorityLowest); + + // 3. HF Discovery (Picopass/iCLASS) + if(!detected) { + seader->picopass_poller = picopass_poller_alloc(seader->nfc); + seader_worker->stage = SeaderPollerEventTypeCardDetect; + seader->credential->type = SeaderCredentialTypePicopass; + + picopass_poller_start(seader->picopass_poller, seader_worker_poller_callback_picopass, seader); + + // Wait up to 100ms for picopass detection + for(int i = 0; i < 10; i++) { + if(seader_worker->stage != SeaderPollerEventTypeCardDetect) { + detected = true; + break; + } + furi_delay_ms(10); + } + if(!detected) { + picopass_poller_stop(seader->picopass_poller); + picopass_poller_free(seader->picopass_poller); + seader->picopass_poller = NULL; + } + } + + if(detected) { + // Wait for conversation to finish + while(seader_worker->stage != SeaderPollerEventTypeComplete && + seader_worker->stage != SeaderPollerEventTypeFail && + seader_worker->state == SeaderWorkerStateReading) { + // The conversation is handled by the poller callback thread. + // We just wait here for it to finish. + furi_delay_ms(10); + } + result_stage = seader_worker->stage; + + // Cleanup poller + if(seader->poller) { + nfc_poller_stop(seader->poller); + nfc_poller_free(seader->poller); + seader->poller = NULL; + } + if(seader->picopass_poller) { + picopass_poller_stop(seader->picopass_poller); + picopass_poller_free(seader->picopass_poller); + seader->picopass_poller = NULL; + } + + if(result_stage == SeaderPollerEventTypeComplete) { + // Notify UI of success + if(seader_worker->callback) { + seader_worker->callback(SeaderWorkerEventSuccess, seader_worker->context); + } + break; + } + } + + if(seader_worker->state == SeaderWorkerStateReading) { + furi_delay_ms(50); + } + } + + nfc_free(seader->nfc); + seader->nfc = NULL; + nfc_device_free(seader->nfc_device); + seader->nfc_device = NULL; + + FURI_LOG_I(TAG, "Reading loop stopped"); +} + +void seader_worker_poller_conversation(Seader* seader, SeaderPollerContainer* spc) { + SeaderWorker* seader_worker = seader->worker; + + furi_thread_set_current_priority(FuriThreadPriorityHighest); + + while(seader_worker->stage == SeaderPollerEventTypeConversation && + seader_worker->state == SeaderWorkerStateReading) { + + SeaderAPDU seaderApdu = {}; + // Short wait for SAM message + FuriStatus status = furi_message_queue_get(seader_worker->messages, &seaderApdu, 100); + + if(status == FuriStatusOk) { + FURI_LOG_D(TAG, "Dequeue SAM message [%d bytes]", seaderApdu.len); + if(seader_process_success_response_i( + seader, seaderApdu.buf, seaderApdu.len, true, spc)) { + // message was processed, loop again to see if SAM has more to say + } else { + FURI_LOG_I(TAG, "Response false, ending conversation"); + seader_worker->stage = SeaderPollerEventTypeComplete; + view_dispatcher_send_custom_event( + seader->view_dispatcher, SeaderCustomEventWorkerExit); + } + } else if(status == FuriStatusErrorTimeout) { + // No message yet, keep looping to stay in callback + // This is "properly idling" while waiting for SAM + } else { + FURI_LOG_W(TAG, "furi_message_queue_get fail %d", status); + seader_worker->stage = SeaderPollerEventTypeFail; + view_dispatcher_send_custom_event( + seader->view_dispatcher, SeaderCustomEventWorkerExit); + } } } @@ -416,6 +525,11 @@ NfcCommand seader_worker_poller_callback_iso14443_4a(NfcGenericEvent event, void free(ats); + if(seader_worker->state == SeaderWorkerStateReading) { + seader_worker->stage = SeaderPollerEventTypeConversation; + return NfcCommandContinue; + } + // nfc_set_fdt_poll_fc(event.instance, SEADER_POLLER_MAX_FWT); furi_thread_set_current_priority(FuriThreadPriorityLowest); seader_worker->stage = SeaderPollerEventTypeConversation; @@ -485,6 +599,12 @@ NfcCommand seader_worker_poller_callback_mfc(NfcGenericEvent event, void* contex size_t uid_len = 0; const uint8_t* uid = mf_classic_get_uid(mfc_data, &uid_len); seader_worker_card_detect(seader, sak, NULL, uid, uid_len, NULL, 0); + + if(seader_worker->state == SeaderWorkerStateReading) { + seader_worker->stage = SeaderPollerEventTypeConversation; + return NfcCommandContinue; + } + furi_thread_set_current_priority(FuriThreadPriorityLowest); seader_worker->stage = SeaderPollerEventTypeConversation; } else if(seader_worker->stage == SeaderPollerEventTypeConversation) { @@ -534,6 +654,12 @@ NfcCommand seader_worker_poller_callback_picopass(PicopassPollerEvent event, voi } uint8_t* csn = picopass_poller_get_csn(instance); seader_worker_card_detect(seader, 0, NULL, csn, sizeof(PicopassSerialNum), NULL, 0); + + if(seader_worker->state == SeaderWorkerStateReading) { + seader_worker->stage = SeaderPollerEventTypeConversation; + return NfcCommandContinue; + } + furi_thread_set_current_priority(FuriThreadPriorityLowest); seader_worker->stage = SeaderPollerEventTypeConversation; } else if(seader_worker->stage == SeaderPollerEventTypeConversation) { diff --git a/seader_worker.h b/seader_worker.h index 81e45e8..08396e2 100644 --- a/seader_worker.h +++ b/seader_worker.h @@ -21,6 +21,7 @@ typedef enum { SeaderWorkerStateCheckSam, SeaderWorkerStateVirtualCredential, SeaderWorkerStateAPDURunner, + SeaderWorkerStateReading, // Transition SeaderWorkerStateStop, } SeaderWorkerState; @@ -51,7 +52,7 @@ typedef enum { SeaderPollerEventTypeFail, } SeaderPollerEventType; -typedef void (*SeaderWorkerCallback)(SeaderWorkerEvent event, void* context); +typedef void (*SeaderWorkerCallback)(uint32_t event, void* context); SeaderWorker* seader_worker_alloc(); diff --git a/seader_worker_i.h b/seader_worker_i.h index 4716f05..62df0c2 100644 --- a/seader_worker_i.h +++ b/seader_worker_i.h @@ -28,8 +28,6 @@ struct SeaderWorker { Storage* storage; uint8_t sam_version[2]; FuriMessageQueue* messages; - FuriMutex* mq_mutex; - SeaderUartBridge* uart; SeaderWorkerCallback callback; void* context; From 28a630dcede572d9ed33c1c08dce2fdd91c9dd7f Mon Sep 17 00:00:00 2001 From: CinderSocket Date: Sun, 8 Mar 2026 16:16:32 -0700 Subject: [PATCH 2/3] Use PACS2-only HF credential parsing --- lib/asn1/FrameProtocol.c | 5 +- lib/asn1/SamCommand.c | 28 ++---- lib/asn1/SamCommand.h | 6 +- sam_api.c | 198 +++++++++++++++++---------------------- seader.asn1 | 1 - seader_i.h | 1 - 6 files changed, 98 insertions(+), 141 deletions(-) diff --git a/lib/asn1/FrameProtocol.c b/lib/asn1/FrameProtocol.c index 593dde0..a282074 100644 --- a/lib/asn1/FrameProtocol.c +++ b/lib/asn1/FrameProtocol.c @@ -17,9 +17,9 @@ static const asn_INTEGER_enum_map_t asn_MAP_FrameProtocol_value2enum_1[] = { { 4, 6, "iclass" } }; static const unsigned int asn_MAP_FrameProtocol_enum2value_1[] = { - 0, /* none(0) */ 2, /* iclass(4) */ - 1 /* nfc(2) */ + 1, /* nfc(2) */ + 0 /* none(0) */ }; static const asn_INTEGER_specifics_t asn_SPC_FrameProtocol_specs_1 = { asn_MAP_FrameProtocol_value2enum_1, /* "tag" => N; sorted by tag */ @@ -47,3 +47,4 @@ asn_TYPE_descriptor_t asn_DEF_FrameProtocol = { 0, 0, /* Defined elsewhere */ &asn_SPC_FrameProtocol_specs_1 /* Additional specs */ }; + diff --git a/lib/asn1/SamCommand.c b/lib/asn1/SamCommand.c index 3036e24..c72630a 100644 --- a/lib/asn1/SamCommand.c +++ b/lib/asn1/SamCommand.c @@ -8,15 +8,6 @@ #include "SamCommand.h" asn_TYPE_member_t asn_MBR_SamCommand_1[] = { - { ATF_NOFLAGS, 0, offsetof(struct SamCommand, choice.requestPacs), - (ASN_TAG_CLASS_CONTEXT | (1 << 2)), - -1, /* IMPLICIT tag at current level */ - &asn_DEF_RequestPacs, - 0, - { 0, 0, 0 }, - 0, 0, /* No default value */ - "requestPacs" - }, { ATF_NOFLAGS, 0, offsetof(struct SamCommand, choice.version), (ASN_TAG_CLASS_CONTEXT | (2 << 2)), -1, /* IMPLICIT tag at current level */ @@ -82,14 +73,13 @@ asn_TYPE_member_t asn_MBR_SamCommand_1[] = { }, }; static const asn_TYPE_tag2member_t asn_MAP_SamCommand_tag2el_1[] = { - { (ASN_TAG_CLASS_CONTEXT | (1 << 2)), 0, 0, 0 }, /* requestPacs */ - { (ASN_TAG_CLASS_CONTEXT | (2 << 2)), 1, 0, 0 }, /* version */ - { (ASN_TAG_CLASS_CONTEXT | (13 << 2)), 2, 0, 0 }, /* cardDetected */ - { (ASN_TAG_CLASS_CONTEXT | (20 << 2)), 3, 0, 0 }, /* processSNMPMessage */ - { (ASN_TAG_CLASS_CONTEXT | (22 << 2)), 4, 0, 0 }, /* serialNumber */ - { (ASN_TAG_CLASS_CONTEXT | (26 << 2)), 7, 0, 0 }, /* processConfigCard */ - { (ASN_TAG_CLASS_CONTEXT | (30 << 2)), 6, 0, 0 }, /* requestPacs2 */ - { (ASN_TAG_CLASS_CONTEXT | (43 << 2)), 5, 0, 0 } /* getItemKCV */ + { (ASN_TAG_CLASS_CONTEXT | (2 << 2)), 0, 0, 0 }, /* version */ + { (ASN_TAG_CLASS_CONTEXT | (13 << 2)), 1, 0, 0 }, /* cardDetected */ + { (ASN_TAG_CLASS_CONTEXT | (20 << 2)), 2, 0, 0 }, /* processSNMPMessage */ + { (ASN_TAG_CLASS_CONTEXT | (22 << 2)), 3, 0, 0 }, /* serialNumber */ + { (ASN_TAG_CLASS_CONTEXT | (26 << 2)), 6, 0, 0 }, /* processConfigCard */ + { (ASN_TAG_CLASS_CONTEXT | (30 << 2)), 5, 0, 0 }, /* requestPacs2 */ + { (ASN_TAG_CLASS_CONTEXT | (43 << 2)), 4, 0, 0 } /* getItemKCV */ }; asn_CHOICE_specifics_t asn_SPC_SamCommand_specs_1 = { sizeof(struct SamCommand), @@ -97,7 +87,7 @@ asn_CHOICE_specifics_t asn_SPC_SamCommand_specs_1 = { offsetof(struct SamCommand, present), sizeof(((struct SamCommand *)0)->present), asn_MAP_SamCommand_tag2el_1, - 8, /* Count of tags in the map */ + 7, /* Count of tags in the map */ 0, 0, -1 /* Extensions start */ }; @@ -111,7 +101,7 @@ asn_TYPE_descriptor_t asn_DEF_SamCommand = { 0, /* No tags (count) */ { 0, 0, CHOICE_constraint }, asn_MBR_SamCommand_1, - 8, /* Elements count */ + 7, /* Elements count */ &asn_SPC_SamCommand_specs_1 /* Additional specs */ }; diff --git a/lib/asn1/SamCommand.h b/lib/asn1/SamCommand.h index 0329cfb..8dee7a3 100644 --- a/lib/asn1/SamCommand.h +++ b/lib/asn1/SamCommand.h @@ -12,11 +12,11 @@ #include /* Including external dependencies */ -#include "RequestPacs.h" #include #include "CardDetected.h" #include #include "NoArguments.h" +#include "RequestPacs.h" #include #ifdef __cplusplus @@ -26,7 +26,6 @@ extern "C" { /* Dependencies */ typedef enum SamCommand_PR { SamCommand_PR_NOTHING, /* No components present */ - SamCommand_PR_requestPacs, SamCommand_PR_version, SamCommand_PR_cardDetected, SamCommand_PR_processSNMPMessage, @@ -40,7 +39,6 @@ typedef enum SamCommand_PR { typedef struct SamCommand { SamCommand_PR present; union SamCommand_u { - RequestPacs_t requestPacs; NULL_t version; CardDetected_t cardDetected; OCTET_STRING_t processSNMPMessage; @@ -57,7 +55,7 @@ typedef struct SamCommand { /* Implementation */ extern asn_TYPE_descriptor_t asn_DEF_SamCommand; extern asn_CHOICE_specifics_t asn_SPC_SamCommand_specs_1; -extern asn_TYPE_member_t asn_MBR_SamCommand_1[8]; +extern asn_TYPE_member_t asn_MBR_SamCommand_1[7]; #ifdef __cplusplus } diff --git a/sam_api.c b/sam_api.c index ee93001..cd8fcb1 100644 --- a/sam_api.c +++ b/sam_api.c @@ -180,10 +180,8 @@ static void seader_sam_set_state( static SeaderSamIntent seader_sam_card_intent(const Seader* seader) { if(seader->credential->type == SeaderCredentialTypeConfig) { return SeaderSamIntentConfig; - } else if(seader->is_debug_enabled) { - return SeaderSamIntentReadPacs2; } else { - return SeaderSamIntentReadPacs; + return SeaderSamIntentReadPacs2; } } @@ -500,24 +498,6 @@ void seader_send_response( seader_send_payload(seader, &payload, from, to, replyTo); } -void seader_send_request_pacs(Seader* seader) { - RequestPacs_t requestPacs = {0}; - requestPacs.contentElementTag = ContentElementTag_implicitFormatPhysicalAccessBits; - - SamCommand_t samCommand = {0}; - samCommand.present = SamCommand_PR_requestPacs; - seader_sam_set_state( - seader, SeaderSamStateConversation, SeaderSamIntentReadPacs, samCommand.present); - samCommand.choice.requestPacs = requestPacs; - - Payload_t payload = {0}; - payload.present = Payload_PR_samCommand; - payload.choice.samCommand = samCommand; - - seader_send_payload( - seader, &payload, ExternalApplicationA, SAMInterface, ExternalApplicationA); -} - void seader_send_request_pacs2(Seader* seader) { OCTET_STRING_t oid = { .buf = (uint8_t*)seader_oid, @@ -607,83 +587,86 @@ void seader_send_no_card_detected(Seader* seader) { ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_CardDetails, &cardDetails); } -bool seader_unpack_pacs(Seader* seader, uint8_t* buf, size_t size) { - SeaderCredential* seader_credential = seader->credential; - PAC_t pac = {0}; - PAC_t* pac_p = &pac; - bool rtn = false; - - asn_dec_rval_t rval = asn_decode(0, ATS_DER, &asn_DEF_PAC, (void**)&pac_p, buf, size); - - if(rval.code == RC_OK) { -#ifdef ASN1_DEBUG - char pacDebug[384] = {0}; - (&asn_DEF_PAC) - ->op->print_struct(&asn_DEF_PAC, &pac, 1, seader_print_struct_callback, pacDebug); - if(strlen(pacDebug) > 0) { - FURI_LOG_D(TAG, "Received pac: %s", pacDebug); - } -#endif - - if(seader_credential->sio[0] == 0x30) { - seader_log_hex_data(TAG, "SIO", seader_credential->sio, seader_credential->sio_len); - -#ifdef ASN1_DEBUG - SIO_t sio = {0}; - SIO_t* sio_p = &sio; - rval = asn_decode( - 0, - ATS_DER, - &asn_DEF_SIO, - (void**)&sio_p, - seader_credential->sio, - seader_credential->sio_len); - - if(rval.code == RC_OK) { - FURI_LOG_D(TAG, "Decoded SIO"); - char sioDebug[384] = {0}; - (&asn_DEF_SIO) - ->op->print_struct( - &asn_DEF_SIO, &sio, 1, seader_print_struct_callback, sioDebug); - if(strlen(sioDebug) > 0) { - FURI_LOG_D(TAG, "SIO: %s", sioDebug); - } - } else { - FURI_LOG_W(TAG, "Failed to decode SIO %d consumed", rval.consumed); - } - - ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_SIO, &sio); -#endif - } - - if(pac.size <= sizeof(seader_credential->credential)) { - // TODO: make credential into a 12 byte array - seader_credential->bit_length = pac.size * 8 - pac.bits_unused; - uint64_t credential_val = 0; - memcpy(&credential_val, pac.buf, pac.size); - credential_val = __builtin_bswap64(credential_val); - // After bswap64, the bits are left-aligned in the 64-bit word - // We need to shift them right by (64 - bit_length) to get the value - seader_credential->credential = credential_val >> (64 - seader_credential->bit_length); - - FURI_LOG_D( - TAG, - "credential (%d) %016llx", - seader_credential->bit_length, - seader_credential->credential); - - rtn = true; - } else { - // PACS too big (probably bad data) - seader_abort_active_read(seader); - } - } else { - FURI_LOG_W(TAG, "Failed to decode PAC %d consumed, size %d", rval.consumed, size); - seader_abort_active_read(seader); +static bool seader_store_pacs_bits( + SeaderCredential* credential, + const uint8_t* payload, + size_t payload_size, + uint8_t unused_bits) { + if(!credential || !payload || payload_size == 0 || payload_size > sizeof(credential->credential) || + unused_bits > 7) { + return false; } - ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_PAC, &pac); - return rtn; + const uint8_t bit_length = payload_size * 8 - unused_bits; + if(bit_length == 0) { + return false; + } + + uint64_t credential_val = 0; + memcpy(&credential_val, payload, payload_size); + credential_val = __builtin_bswap64(credential_val); + + credential->bit_length = bit_length; + credential->credential = credential_val >> (64 - bit_length); + return true; +} + +static bool seader_unpack_pacs2_bits(Seader* seader, const OCTET_STRING_t* pacs_bits) { + SeaderCredential* seader_credential = seader->credential; + if(!pacs_bits || !pacs_bits->buf || pacs_bits->size < 2) { + FURI_LOG_W(TAG, "Malformed pacs2 bits"); + return false; + } + + seader_log_hex_data(TAG, "PACS2 bits", pacs_bits->buf, pacs_bits->size); + + if(seader_credential->sio[0] == 0x30) { + seader_log_hex_data(TAG, "SIO", seader_credential->sio, seader_credential->sio_len); +#ifdef ASN1_DEBUG + asn_dec_rval_t rval; + SIO_t sio = {0}; + SIO_t* sio_p = &sio; + rval = asn_decode( + 0, + ATS_DER, + &asn_DEF_SIO, + (void**)&sio_p, + seader_credential->sio, + seader_credential->sio_len); + + if(rval.code == RC_OK) { + FURI_LOG_D(TAG, "Decoded SIO"); + char sioDebug[384] = {0}; + (&asn_DEF_SIO)->op->print_struct( + &asn_DEF_SIO, &sio, 1, seader_print_struct_callback, sioDebug); + if(strlen(sioDebug) > 0) { + FURI_LOG_D(TAG, "SIO: %s", sioDebug); + } + } else { + FURI_LOG_W(TAG, "Failed to decode SIO %d consumed", rval.consumed); + } + + ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_SIO, &sio); +#endif + } + + const uint8_t unused_bits = pacs_bits->buf[0]; + const uint8_t* payload = pacs_bits->buf + 1; + const size_t payload_size = pacs_bits->size - 1; + FURI_LOG_D(TAG, "PACS2 unused_bits=%u payload_size=%zu", unused_bits, payload_size); + + if(!seader_store_pacs_bits(seader_credential, payload, payload_size, unused_bits)) { + FURI_LOG_W(TAG, "Failed to store PACS2 bits"); + return false; + } + + FURI_LOG_D( + TAG, + "credential (%d) %016llx", + seader_credential->bit_length, + seader_credential->credential); + + return true; } // 800201298106683d052026b6820101 @@ -839,7 +822,6 @@ static void seader_abort_active_read(Seader* seader) { } bool seader_parse_sam_response2(Seader* seader, SamResponse2_t* samResponse) { - uint8_t buffer[10]; switch(samResponse->present) { case SamResponse2_PR_pacs: FURI_LOG_I(TAG, "samResponse2 SamResponse2_PR_pacs"); @@ -857,14 +839,13 @@ bool seader_parse_sam_response2(Seader* seader, SamResponse2_t* samResponse) { Pacs2_t pacs2 = samResponse->choice.pacs; OCTET_STRING_t* pacs = pacs2.bits; - buffer[0] = 0x03; - buffer[1] = pacs->size & 0xFF; - memcpy(buffer + 2, pacs->buf, pacs->size); - if(seader_unpack_pacs(seader, buffer, pacs->size + 2)) { + if(seader_unpack_pacs2_bits(seader, pacs)) { view_dispatcher_send_custom_event( seader->view_dispatcher, SeaderCustomEventPollerSuccess); seader_sam_set_state( seader, SeaderSamStateIdle, SeaderSamIntentNone, SamCommand_PR_NOTHING); + } else { + seader_abort_active_read(seader); } break; case SamResponse2_PR_NOTHING: @@ -886,15 +867,7 @@ bool seader_parse_sam_response(Seader* seader, SamResponse_t* samResponse) { switch(seader->sam_state) { case SeaderSamStateConversation: case SeaderSamStateFinishing: - if(seader->sam_intent == SeaderSamIntentReadPacs) { - FURI_LOG_I(TAG, "samResponse read PACS"); - if(seader_unpack_pacs(seader, samResponse->buf, samResponse->size)) { - view_dispatcher_send_custom_event( - seader->view_dispatcher, SeaderCustomEventPollerSuccess); - seader_sam_set_state( - seader, SeaderSamStateIdle, SeaderSamIntentNone, SamCommand_PR_NOTHING); - } - } else if(seader->sam_intent == SeaderSamIntentConfig) { + if(seader->sam_intent == SeaderSamIntentConfig) { FURI_LOG_I(TAG, "samResponse config"); seader_worker->stage = SeaderPollerEventTypeFail; seader_sam_set_state( @@ -921,8 +894,6 @@ bool seader_parse_sam_response(Seader* seader, SamResponse_t* samResponse) { seader_send_process_config_card(seader); } else if(seader->sam_intent == SeaderSamIntentReadPacs2) { seader_send_request_pacs2(seader); - } else if(seader->sam_intent == SeaderSamIntentReadPacs) { - seader_send_request_pacs(seader); } else { FURI_LOG_W(TAG, "Unexpected detect intent=%d", seader->sam_intent); seader_abort_active_read(seader); @@ -1379,8 +1350,7 @@ void seader_parse_nfc_off(Seader* seader) { seader_send_response(seader, &response, ExternalApplicationA, SAMInterface, 0); if(seader->sam_state == SeaderSamStateConversation && - (seader->sam_intent == SeaderSamIntentReadPacs || - seader->sam_intent == SeaderSamIntentReadPacs2 || + (seader->sam_intent == SeaderSamIntentReadPacs2 || seader->sam_intent == SeaderSamIntentConfig)) { seader_sam_set_state( seader, SeaderSamStateFinishing, seader->sam_intent, seader->samCommand); diff --git a/seader.asn1 b/seader.asn1 index 67b85d8..472761c 100644 --- a/seader.asn1 +++ b/seader.asn1 @@ -22,7 +22,6 @@ ErrorResponse ::= SEQUENCE { } SamCommand ::= CHOICE { - requestPacs [1] RequestPacs, version [2] NULL, cardDetected [13] CardDetected, processSNMPMessage [20] OCTET STRING, diff --git a/seader_i.h b/seader_i.h index de5f92b..535e8b6 100644 --- a/seader_i.h +++ b/seader_i.h @@ -110,7 +110,6 @@ typedef enum { typedef enum { SeaderSamIntentNone, - SeaderSamIntentReadPacs, SeaderSamIntentReadPacs2, SeaderSamIntentConfig, SeaderSamIntentMaintenance, From 8eaa4dfc5ee238c92042218c10b3fc18403a037d Mon Sep 17 00:00:00 2001 From: CinderSocket Date: Sun, 8 Mar 2026 16:32:28 -0700 Subject: [PATCH 3/3] Run ufbt format on unified HF changes --- sam_api.c | 8 ++++---- scenes/seader_scene_read.c | 5 +++-- scenes/seader_scene_read_card_success.c | 6 +++++- scenes/seader_scene_read_config_card.c | 3 +-- scenes/seader_scene_sam_present.c | 6 +----- seader_worker.c | 23 +++++++++++------------ 6 files changed, 25 insertions(+), 26 deletions(-) diff --git a/sam_api.c b/sam_api.c index cd8fcb1..b90bc67 100644 --- a/sam_api.c +++ b/sam_api.c @@ -592,8 +592,8 @@ static bool seader_store_pacs_bits( const uint8_t* payload, size_t payload_size, uint8_t unused_bits) { - if(!credential || !payload || payload_size == 0 || payload_size > sizeof(credential->credential) || - unused_bits > 7) { + if(!credential || !payload || payload_size == 0 || + payload_size > sizeof(credential->credential) || unused_bits > 7) { return false; } @@ -637,8 +637,8 @@ static bool seader_unpack_pacs2_bits(Seader* seader, const OCTET_STRING_t* pacs_ if(rval.code == RC_OK) { FURI_LOG_D(TAG, "Decoded SIO"); char sioDebug[384] = {0}; - (&asn_DEF_SIO)->op->print_struct( - &asn_DEF_SIO, &sio, 1, seader_print_struct_callback, sioDebug); + (&asn_DEF_SIO) + ->op->print_struct(&asn_DEF_SIO, &sio, 1, seader_print_struct_callback, sioDebug); if(strlen(sioDebug) > 0) { FURI_LOG_D(TAG, "SIO: %s", sioDebug); } diff --git a/scenes/seader_scene_read.c b/scenes/seader_scene_read.c index 3b89b13..47c7ecf 100644 --- a/scenes/seader_scene_read.c +++ b/scenes/seader_scene_read.c @@ -38,8 +38,9 @@ bool seader_scene_read_on_event(void* context, SceneManagerEvent event) { Popup* popup = seader->popup; popup_set_header(popup, "DON'T\nMOVE", 68, 30, AlignLeft, AlignTop); consumed = true; - } else if(event.event == SeaderCustomEventPollerSuccess || - event.event == SeaderWorkerEventSuccess) { + } else if( + event.event == SeaderCustomEventPollerSuccess || + event.event == SeaderWorkerEventSuccess) { scene_manager_next_scene(seader->scene_manager, SeaderSceneReadCardSuccess); consumed = true; } diff --git a/scenes/seader_scene_read_card_success.c b/scenes/seader_scene_read_card_success.c index e5a86c5..8897dbb 100644 --- a/scenes/seader_scene_read_card_success.c +++ b/scenes/seader_scene_read_card_success.c @@ -83,7 +83,11 @@ void seader_scene_read_card_success_on_enter(void* context) { if(plugin && credential->bit_length > 0) { size_t format_count = plugin->count(credential->bit_length, credential->credential); - FURI_LOG_D(TAG, "Plugin present, bit_length=%d, format_count=%zu", credential->bit_length, format_count); + FURI_LOG_D( + TAG, + "Plugin present, bit_length=%d, format_count=%zu", + credential->bit_length, + format_count); if(format_count > 0) { widget_add_button_element( seader->widget, diff --git a/scenes/seader_scene_read_config_card.c b/scenes/seader_scene_read_config_card.c index b72babf..0fbf2d2 100644 --- a/scenes/seader_scene_read_config_card.c +++ b/scenes/seader_scene_read_config_card.c @@ -38,8 +38,7 @@ bool seader_scene_read_config_card_on_event(void* context, SceneManagerEvent eve bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SeaderCustomEventWorkerExit || - event.event == SeaderWorkerEventSuccess) { + if(event.event == SeaderCustomEventWorkerExit || event.event == SeaderWorkerEventSuccess) { scene_manager_next_scene(seader->scene_manager, SeaderSceneReadConfigCardSuccess); consumed = true; } else if(event.event == SeaderCustomEventPollerSuccess) { diff --git a/scenes/seader_scene_sam_present.c b/scenes/seader_scene_sam_present.c index b661eb8..c216c75 100644 --- a/scenes/seader_scene_sam_present.c +++ b/scenes/seader_scene_sam_present.c @@ -23,11 +23,7 @@ void seader_scene_sam_present_on_update(void* context) { submenu_reset(submenu); submenu_add_item( - submenu, - "Read HF", - SubmenuIndexRead, - seader_scene_sam_present_submenu_callback, - seader); + submenu, "Read HF", SubmenuIndexRead, seader_scene_sam_present_submenu_callback, seader); submenu_add_item( submenu, "Saved", SubmenuIndexSaved, seader_scene_sam_present_submenu_callback, seader); diff --git a/seader_worker.c b/seader_worker.c index 991f53d..bb3e15a 100644 --- a/seader_worker.c +++ b/seader_worker.c @@ -318,8 +318,7 @@ void seader_worker_reading(Seader* seader) { seader->poller = nfc_poller_alloc(seader->nfc, NfcProtocolIso14443_4a); seader_worker->stage = SeaderPollerEventTypeCardDetect; seader->credential->type = SeaderCredentialType14A; - nfc_poller_start( - seader->poller, seader_worker_poller_callback_iso14443_4a, seader); + nfc_poller_start(seader->poller, seader_worker_poller_callback_iso14443_4a, seader); detected = true; } else { nfc_poller_free(poller_detect); @@ -350,9 +349,10 @@ void seader_worker_reading(Seader* seader) { seader->picopass_poller = picopass_poller_alloc(seader->nfc); seader_worker->stage = SeaderPollerEventTypeCardDetect; seader->credential->type = SeaderCredentialTypePicopass; - - picopass_poller_start(seader->picopass_poller, seader_worker_poller_callback_picopass, seader); - + + picopass_poller_start( + seader->picopass_poller, seader_worker_poller_callback_picopass, seader); + // Wait up to 100ms for picopass detection for(int i = 0; i < 10; i++) { if(seader_worker->stage != SeaderPollerEventTypeCardDetect) { @@ -370,7 +370,7 @@ void seader_worker_reading(Seader* seader) { if(detected) { // Wait for conversation to finish - while(seader_worker->stage != SeaderPollerEventTypeComplete && + while(seader_worker->stage != SeaderPollerEventTypeComplete && seader_worker->stage != SeaderPollerEventTypeFail && seader_worker->state == SeaderWorkerStateReading) { // The conversation is handled by the poller callback thread. @@ -396,7 +396,7 @@ void seader_worker_reading(Seader* seader) { if(seader_worker->callback) { seader_worker->callback(SeaderWorkerEventSuccess, seader_worker->context); } - break; + break; } } @@ -415,16 +415,15 @@ void seader_worker_reading(Seader* seader) { void seader_worker_poller_conversation(Seader* seader, SeaderPollerContainer* spc) { SeaderWorker* seader_worker = seader->worker; - + furi_thread_set_current_priority(FuriThreadPriorityHighest); - - while(seader_worker->stage == SeaderPollerEventTypeConversation && + + while(seader_worker->stage == SeaderPollerEventTypeConversation && seader_worker->state == SeaderWorkerStateReading) { - SeaderAPDU seaderApdu = {}; // Short wait for SAM message FuriStatus status = furi_message_queue_get(seader_worker->messages, &seaderApdu, 100); - + if(status == FuriStatusOk) { FURI_LOG_D(TAG, "Dequeue SAM message [%d bytes]", seaderApdu.len); if(seader_process_success_response_i(