From 0161d49d80111328b4f27803528858872c5a4aea Mon Sep 17 00:00:00 2001 From: Eric Betts Date: Tue, 28 Mar 2023 01:21:14 -0700 Subject: [PATCH] Elite progress (#2481) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * WIP: builds * can read standard * Test standard picopass dictiony during attack * correctly save diversified key * read card on success * more logs * update file location * Call setup methods * backbutton and attempt at skip * fixed skip * remove found key state * rename dictionary attack * move notification * center button back to start menu * wait for card * Picopass: proper integer formatting * Picopass: even more proper integer formatting * remove nextState Co-authored-by: あく --- applications/external/picopass/picopass.c | 9 + .../external/picopass/picopass_device.h | 9 + applications/external/picopass/picopass_i.h | 4 + .../external/picopass/picopass_worker.c | 145 ++++++++- .../external/picopass/picopass_worker.h | 7 +- .../picopass/scenes/picopass_scene_config.h | 1 + .../scenes/picopass_scene_elite_dict_attack.c | 170 +++++++++++ .../scenes/picopass_scene_read_card_success.c | 22 ++ .../picopass/scenes/picopass_scene_start.c | 15 +- .../external/picopass/views/dict_attack.c | 281 ++++++++++++++++++ .../external/picopass/views/dict_attack.h | 44 +++ 11 files changed, 693 insertions(+), 14 deletions(-) create mode 100644 applications/external/picopass/scenes/picopass_scene_elite_dict_attack.c create mode 100644 applications/external/picopass/views/dict_attack.c create mode 100644 applications/external/picopass/views/dict_attack.h diff --git a/applications/external/picopass/picopass.c b/applications/external/picopass/picopass.c index 5d1cee70..6737d807 100644 --- a/applications/external/picopass/picopass.c +++ b/applications/external/picopass/picopass.c @@ -73,6 +73,12 @@ Picopass* picopass_alloc() { view_dispatcher_add_view( picopass->view_dispatcher, PicopassViewWidget, widget_get_view(picopass->widget)); + picopass->dict_attack = dict_attack_alloc(); + view_dispatcher_add_view( + picopass->view_dispatcher, + PicopassViewDictAttack, + dict_attack_get_view(picopass->dict_attack)); + return picopass; } @@ -103,6 +109,9 @@ void picopass_free(Picopass* picopass) { view_dispatcher_remove_view(picopass->view_dispatcher, PicopassViewWidget); widget_free(picopass->widget); + view_dispatcher_remove_view(picopass->view_dispatcher, PicopassViewDictAttack); + dict_attack_free(picopass->dict_attack); + // Worker picopass_worker_stop(picopass->worker); picopass_worker_free(picopass->worker); diff --git a/applications/external/picopass/picopass_device.h b/applications/external/picopass/picopass_device.h index d7d0977d..7fc35ebd 100644 --- a/applications/external/picopass/picopass_device.h +++ b/applications/external/picopass/picopass_device.h @@ -27,8 +27,16 @@ #define PICOPASS_APP_EXTENSION ".picopass" #define PICOPASS_APP_SHADOW_EXTENSION ".pas" +#define PICOPASS_DICT_KEY_BATCH_SIZE 10 + typedef void (*PicopassLoadingCallback)(void* context, bool state); +typedef struct { + IclassEliteDict* dict; + IclassEliteDictType type; + uint8_t current_sector; +} IclassEliteDictAttackData; + typedef enum { PicopassDeviceEncryptionUnknown = 0, PicopassDeviceEncryptionNone = 0x14, @@ -69,6 +77,7 @@ typedef struct { typedef struct { PicopassBlock AA1[PICOPASS_MAX_APP_LIMIT]; PicopassPacs pacs; + IclassEliteDictAttackData iclass_elite_dict_attack_data; } PicopassDeviceData; typedef struct { diff --git a/applications/external/picopass/picopass_i.h b/applications/external/picopass/picopass_i.h index 54533e82..9147cfa0 100644 --- a/applications/external/picopass/picopass_i.h +++ b/applications/external/picopass/picopass_i.h @@ -21,6 +21,7 @@ #include #include "scenes/picopass_scene.h" +#include "views/dict_attack.h" #include #include @@ -36,6 +37,7 @@ enum PicopassCustomEvent { PicopassCustomEventWorkerExit, PicopassCustomEventByteInputDone, PicopassCustomEventTextInputDone, + PicopassCustomEventDictAttackSkip, }; typedef enum { @@ -60,6 +62,7 @@ struct Picopass { Loading* loading; TextInput* text_input; Widget* widget; + DictAttack* dict_attack; }; typedef enum { @@ -68,6 +71,7 @@ typedef enum { PicopassViewLoading, PicopassViewTextInput, PicopassViewWidget, + PicopassViewDictAttack, } PicopassView; Picopass* picopass_alloc(); diff --git a/applications/external/picopass/picopass_worker.c b/applications/external/picopass/picopass_worker.c index e61b67d9..174413ba 100644 --- a/applications/external/picopass/picopass_worker.c +++ b/applications/external/picopass/picopass_worker.c @@ -23,7 +23,7 @@ PicopassWorker* picopass_worker_alloc() { // Worker thread attributes picopass_worker->thread = - furi_thread_alloc_ex("PicopassWorker", 8192, picopass_worker_task, picopass_worker); + furi_thread_alloc_ex("PicopassWorker", 8 * 1024, picopass_worker_task, picopass_worker); picopass_worker->callback = NULL; picopass_worker->context = NULL; @@ -66,14 +66,12 @@ void picopass_worker_start( void picopass_worker_stop(PicopassWorker* picopass_worker) { furi_assert(picopass_worker); - if(picopass_worker->state == PicopassWorkerStateBroken || - picopass_worker->state == PicopassWorkerStateReady) { - return; - } - picopass_worker_disable_field(ERR_NONE); + furi_assert(picopass_worker->thread); - picopass_worker_change_state(picopass_worker, PicopassWorkerStateStop); - furi_thread_join(picopass_worker->thread); + if(furi_thread_get_state(picopass_worker->thread) != FuriThreadStateStopped) { + picopass_worker_change_state(picopass_worker, PicopassWorkerStateStop); + furi_thread_join(picopass_worker->thread); + } } void picopass_worker_change_state(PicopassWorker* picopass_worker, PicopassWorkerState state) { @@ -460,6 +458,132 @@ ReturnCode picopass_write_block(PicopassBlock* AA1, uint8_t blockNo, uint8_t* ne return ERR_NONE; } +void picopass_worker_elite_dict_attack(PicopassWorker* picopass_worker) { + furi_assert(picopass_worker); + furi_assert(picopass_worker->callback); + + picopass_device_data_clear(picopass_worker->dev_data); + PicopassDeviceData* dev_data = picopass_worker->dev_data; + PicopassBlock* AA1 = dev_data->AA1; + PicopassPacs* pacs = &dev_data->pacs; + + for(size_t i = 0; i < PICOPASS_MAX_APP_LIMIT; i++) { + memset(AA1[i].data, 0, sizeof(AA1[i].data)); + } + memset(pacs, 0, sizeof(PicopassPacs)); + + IclassEliteDictAttackData* dict_attack_data = + &picopass_worker->dev_data->iclass_elite_dict_attack_data; + bool elite = (dict_attack_data->type != IclassStandardDictTypeFlipper); + + rfalPicoPassReadCheckRes rcRes; + rfalPicoPassCheckRes chkRes; + + ReturnCode err; + uint8_t mac[4] = {0}; + uint8_t ccnr[12] = {0}; + + size_t index = 0; + uint8_t key[PICOPASS_BLOCK_LEN] = {0}; + + // Load dictionary + IclassEliteDict* dict = dict_attack_data->dict; + if(!dict) { + FURI_LOG_E(TAG, "Dictionary not found"); + picopass_worker->callback(PicopassWorkerEventNoDictFound, picopass_worker->context); + return; + } + + do { + if(picopass_detect_card(1000) == ERR_NONE) { + picopass_worker->callback(PicopassWorkerEventCardDetected, picopass_worker->context); + + // Process first found device + err = picopass_read_preauth(AA1); + if(err != ERR_NONE) { + FURI_LOG_E(TAG, "picopass_read_preauth error %d", err); + picopass_worker->callback(PicopassWorkerEventAborted, picopass_worker->context); + return; + } + + // Thank you proxmark! + pacs->legacy = picopass_is_memset(AA1[5].data, 0xFF, 8); + pacs->se_enabled = (memcmp(AA1[5].data, "\xff\xff\xff\x00\x06\xff\xff\xff", 8) == 0); + if(pacs->se_enabled) { + FURI_LOG_D(TAG, "SE enabled"); + picopass_worker->callback(PicopassWorkerEventAborted, picopass_worker->context); + return; + } + + break; + } else { + picopass_worker->callback(PicopassWorkerEventNoCardDetected, picopass_worker->context); + } + if(picopass_worker->state != PicopassWorkerStateEliteDictAttack) break; + + furi_delay_ms(100); + } while(true); + + FURI_LOG_D( + TAG, "Start Dictionary attack, Key Count %lu", iclass_elite_dict_get_total_keys(dict)); + while(iclass_elite_dict_get_next_key(dict, key)) { + FURI_LOG_T(TAG, "Key %zu", index); + if(++index % PICOPASS_DICT_KEY_BATCH_SIZE == 0) { + picopass_worker->callback( + PicopassWorkerEventNewDictKeyBatch, picopass_worker->context); + } + + err = rfalPicoPassPollerReadCheck(&rcRes); + if(err != ERR_NONE) { + FURI_LOG_E(TAG, "rfalPicoPassPollerReadCheck error %d", err); + break; + } + memcpy(ccnr, rcRes.CCNR, sizeof(rcRes.CCNR)); // last 4 bytes left 0 + + uint8_t* csn = AA1[PICOPASS_CSN_BLOCK_INDEX].data; + uint8_t* div_key = AA1[PICOPASS_KD_BLOCK_INDEX].data; + + loclass_iclass_calc_div_key(csn, key, div_key, elite); + loclass_opt_doReaderMAC(ccnr, div_key, mac); + + err = rfalPicoPassPollerCheck(mac, &chkRes); + if(err == ERR_NONE) { + FURI_LOG_I(TAG, "Found key"); + memcpy(pacs->key, key, PICOPASS_BLOCK_LEN); + err = picopass_read_card(AA1); + if(err != ERR_NONE) { + FURI_LOG_E(TAG, "picopass_read_card error %d", err); + picopass_worker->callback(PicopassWorkerEventFail, picopass_worker->context); + break; + } + + err = picopass_device_parse_credential(AA1, pacs); + if(err != ERR_NONE) { + FURI_LOG_E(TAG, "picopass_device_parse_credential error %d", err); + picopass_worker->callback(PicopassWorkerEventFail, picopass_worker->context); + break; + } + + err = picopass_device_parse_wiegand(pacs->credential, &pacs->record); + if(err != ERR_NONE) { + FURI_LOG_E(TAG, "picopass_device_parse_wiegand error %d", err); + picopass_worker->callback(PicopassWorkerEventFail, picopass_worker->context); + break; + } + picopass_worker->callback(PicopassWorkerEventSuccess, picopass_worker->context); + break; + } + + if(picopass_worker->state != PicopassWorkerStateEliteDictAttack) break; + } + FURI_LOG_D(TAG, "Dictionary complete"); + if(picopass_worker->state == PicopassWorkerStateEliteDictAttack) { + picopass_worker->callback(PicopassWorkerEventSuccess, picopass_worker->context); + } else { + picopass_worker->callback(PicopassWorkerEventAborted, picopass_worker->context); + } +} + int32_t picopass_worker_task(void* context) { PicopassWorker* picopass_worker = context; @@ -470,9 +594,12 @@ int32_t picopass_worker_task(void* context) { picopass_worker_write(picopass_worker); } else if(picopass_worker->state == PicopassWorkerStateWriteKey) { picopass_worker_write_key(picopass_worker); + } else if(picopass_worker->state == PicopassWorkerStateEliteDictAttack) { + picopass_worker_elite_dict_attack(picopass_worker); + } else { + FURI_LOG_W(TAG, "Unknown state %d", picopass_worker->state); } picopass_worker_disable_field(ERR_NONE); - picopass_worker_change_state(picopass_worker, PicopassWorkerStateReady); return 0; diff --git a/applications/external/picopass/picopass_worker.h b/applications/external/picopass/picopass_worker.h index f5e9f303..e9d37481 100644 --- a/applications/external/picopass/picopass_worker.h +++ b/applications/external/picopass/picopass_worker.h @@ -14,6 +14,7 @@ typedef enum { PicopassWorkerStateDetect, PicopassWorkerStateWrite, PicopassWorkerStateWriteKey, + PicopassWorkerStateEliteDictAttack, // Transition PicopassWorkerStateStop, } PicopassWorkerState; @@ -27,8 +28,10 @@ typedef enum { PicopassWorkerEventFail, PicopassWorkerEventNoCardDetected, PicopassWorkerEventSeEnabled, - - PicopassWorkerEventStartReading, + PicopassWorkerEventAborted, + PicopassWorkerEventCardDetected, + PicopassWorkerEventNewDictKeyBatch, + PicopassWorkerEventNoDictFound, } PicopassWorkerEvent; typedef void (*PicopassWorkerCallback)(PicopassWorkerEvent event, void* context); diff --git a/applications/external/picopass/scenes/picopass_scene_config.h b/applications/external/picopass/scenes/picopass_scene_config.h index f5a90d46..8ea97049 100644 --- a/applications/external/picopass/scenes/picopass_scene_config.h +++ b/applications/external/picopass/scenes/picopass_scene_config.h @@ -14,3 +14,4 @@ ADD_SCENE(picopass, write_card_success, WriteCardSuccess) ADD_SCENE(picopass, read_factory_success, ReadFactorySuccess) ADD_SCENE(picopass, write_key, WriteKey) ADD_SCENE(picopass, key_menu, KeyMenu) +ADD_SCENE(picopass, elite_dict_attack, EliteDictAttack) diff --git a/applications/external/picopass/scenes/picopass_scene_elite_dict_attack.c b/applications/external/picopass/scenes/picopass_scene_elite_dict_attack.c new file mode 100644 index 00000000..c76a8ffa --- /dev/null +++ b/applications/external/picopass/scenes/picopass_scene_elite_dict_attack.c @@ -0,0 +1,170 @@ +#include "../picopass_i.h" +#include + +#define TAG "IclassEliteDictAttack" + +typedef enum { + DictAttackStateIdle, + DictAttackStateUserDictInProgress, + DictAttackStateFlipperDictInProgress, + DictAttackStateStandardDictInProgress, +} DictAttackState; + +void picopass_dict_attack_worker_callback(PicopassWorkerEvent event, void* context) { + furi_assert(context); + Picopass* picopass = context; + view_dispatcher_send_custom_event(picopass->view_dispatcher, event); +} + +void picopass_dict_attack_result_callback(void* context) { + furi_assert(context); + Picopass* picopass = context; + view_dispatcher_send_custom_event( + picopass->view_dispatcher, PicopassCustomEventDictAttackSkip); +} + +static void + picopass_scene_elite_dict_attack_prepare_view(Picopass* picopass, DictAttackState state) { + IclassEliteDictAttackData* dict_attack_data = + &picopass->dev->dev_data.iclass_elite_dict_attack_data; + PicopassWorkerState worker_state = PicopassWorkerStateReady; + IclassEliteDict* dict = NULL; + + // Identify scene state + if(state == DictAttackStateIdle) { + if(iclass_elite_dict_check_presence(IclassEliteDictTypeUser)) { + FURI_LOG_D(TAG, "Starting with user dictionary"); + state = DictAttackStateUserDictInProgress; + } else { + FURI_LOG_D(TAG, "Starting with standard dictionary"); + state = DictAttackStateStandardDictInProgress; + } + } else if(state == DictAttackStateUserDictInProgress) { + FURI_LOG_D(TAG, "Moving from user dictionary to standard dictionary"); + state = DictAttackStateStandardDictInProgress; + } else if(state == DictAttackStateStandardDictInProgress) { + FURI_LOG_D(TAG, "Moving from standard dictionary to elite dictionary"); + state = DictAttackStateFlipperDictInProgress; + } + + // Setup view + if(state == DictAttackStateUserDictInProgress) { + worker_state = PicopassWorkerStateEliteDictAttack; + dict_attack_set_header(picopass->dict_attack, "Elite User Dictionary"); + dict_attack_data->type = IclassEliteDictTypeUser; + dict = iclass_elite_dict_alloc(IclassEliteDictTypeUser); + + // If failed to load user dictionary - try the system dictionary + if(!dict) { + FURI_LOG_E(TAG, "User dictionary not found"); + state = DictAttackStateStandardDictInProgress; + } + } + if(state == DictAttackStateStandardDictInProgress) { + worker_state = PicopassWorkerStateEliteDictAttack; + dict_attack_set_header(picopass->dict_attack, "Standard System Dictionary"); + dict_attack_data->type = IclassStandardDictTypeFlipper; + dict = iclass_elite_dict_alloc(IclassStandardDictTypeFlipper); + + if(!dict) { + FURI_LOG_E(TAG, "Flipper standard dictionary not found"); + state = DictAttackStateFlipperDictInProgress; + } + } + if(state == DictAttackStateFlipperDictInProgress) { + worker_state = PicopassWorkerStateEliteDictAttack; + dict_attack_set_header(picopass->dict_attack, "Elite System Dictionary"); + dict_attack_data->type = IclassEliteDictTypeFlipper; + dict = iclass_elite_dict_alloc(IclassEliteDictTypeFlipper); + if(!dict) { + FURI_LOG_E(TAG, "Flipper Elite dictionary not found"); + // Pass through to let the worker handle the failure + } + } + // Free previous dictionary + if(dict_attack_data->dict) { + iclass_elite_dict_free(dict_attack_data->dict); + } + dict_attack_data->dict = dict; + scene_manager_set_scene_state(picopass->scene_manager, PicopassSceneEliteDictAttack, state); + dict_attack_set_callback( + picopass->dict_attack, picopass_dict_attack_result_callback, picopass); + dict_attack_set_current_sector(picopass->dict_attack, 0); + dict_attack_set_card_detected(picopass->dict_attack); + dict_attack_set_total_dict_keys( + picopass->dict_attack, dict ? iclass_elite_dict_get_total_keys(dict) : 0); + picopass_worker_start( + picopass->worker, + worker_state, + &picopass->dev->dev_data, + picopass_dict_attack_worker_callback, + picopass); +} + +void picopass_scene_elite_dict_attack_on_enter(void* context) { + Picopass* picopass = context; + picopass_scene_elite_dict_attack_prepare_view(picopass, DictAttackStateIdle); + view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewDictAttack); + picopass_blink_start(picopass); + notification_message(picopass->notifications, &sequence_display_backlight_enforce_on); +} + +bool picopass_scene_elite_dict_attack_on_event(void* context, SceneManagerEvent event) { + Picopass* picopass = context; + bool consumed = false; + + uint32_t state = + scene_manager_get_scene_state(picopass->scene_manager, PicopassSceneEliteDictAttack); + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == PicopassWorkerEventSuccess || + event.event == PicopassWorkerEventAborted) { + if(state == DictAttackStateUserDictInProgress || + state == DictAttackStateStandardDictInProgress) { + picopass_worker_stop(picopass->worker); + picopass_scene_elite_dict_attack_prepare_view(picopass, state); + consumed = true; + } else { + scene_manager_next_scene(picopass->scene_manager, PicopassSceneReadCardSuccess); + consumed = true; + } + } else if(event.event == PicopassWorkerEventCardDetected) { + dict_attack_set_card_detected(picopass->dict_attack); + consumed = true; + } else if(event.event == PicopassWorkerEventNoCardDetected) { + dict_attack_set_card_removed(picopass->dict_attack); + consumed = true; + } else if(event.event == PicopassWorkerEventNewDictKeyBatch) { + dict_attack_inc_current_dict_key(picopass->dict_attack, PICOPASS_DICT_KEY_BATCH_SIZE); + consumed = true; + } else if(event.event == PicopassCustomEventDictAttackSkip) { + if(state == DictAttackStateUserDictInProgress) { + picopass_worker_stop(picopass->worker); + consumed = true; + } else if(state == DictAttackStateFlipperDictInProgress) { + picopass_worker_stop(picopass->worker); + consumed = true; + } else if(state == DictAttackStateStandardDictInProgress) { + picopass_worker_stop(picopass->worker); + consumed = true; + } + } + } else if(event.type == SceneManagerEventTypeBack) { + consumed = scene_manager_previous_scene(picopass->scene_manager); + } + return consumed; +} + +void picopass_scene_elite_dict_attack_on_exit(void* context) { + Picopass* picopass = context; + IclassEliteDictAttackData* dict_attack_data = + &picopass->dev->dev_data.iclass_elite_dict_attack_data; + // Stop worker + picopass_worker_stop(picopass->worker); + if(dict_attack_data->dict) { + iclass_elite_dict_free(dict_attack_data->dict); + dict_attack_data->dict = NULL; + } + dict_attack_reset(picopass->dict_attack); + picopass_blink_stop(picopass); + notification_message(picopass->notifications, &sequence_display_backlight_enforce_auto); +} diff --git a/applications/external/picopass/scenes/picopass_scene_read_card_success.c b/applications/external/picopass/scenes/picopass_scene_read_card_success.c index f078d460..198b21d9 100644 --- a/applications/external/picopass/scenes/picopass_scene_read_card_success.c +++ b/applications/external/picopass/scenes/picopass_scene_read_card_success.c @@ -47,8 +47,21 @@ void picopass_scene_read_card_success_on_enter(void* context) { if(pacs->se_enabled) { furi_string_cat_printf(credential_str, "SE enabled"); } + + widget_add_button_element( + widget, + GuiButtonTypeCenter, + "Menu", + picopass_scene_read_card_success_widget_callback, + picopass); } else if(empty) { furi_string_cat_printf(wiegand_str, "Empty"); + widget_add_button_element( + widget, + GuiButtonTypeCenter, + "Menu", + picopass_scene_read_card_success_widget_callback, + picopass); } else if(pacs->record.bitLength == 0 || pacs->record.bitLength == 255) { // Neither of these are valid. Indicates the block was all 0x00 or all 0xff furi_string_cat_printf(wiegand_str, "Invalid PACS"); @@ -56,6 +69,12 @@ void picopass_scene_read_card_success_on_enter(void* context) { if(pacs->se_enabled) { furi_string_cat_printf(credential_str, "SE enabled"); } + widget_add_button_element( + widget, + GuiButtonTypeCenter, + "Menu", + picopass_scene_read_card_success_widget_callback, + picopass); } else { size_t bytesLength = 1 + pacs->record.bitLength / 8; furi_string_set(credential_str, ""); @@ -137,6 +156,9 @@ bool picopass_scene_read_card_success_on_event(void* context, SceneManagerEvent picopass_device_set_name(picopass->dev, ""); scene_manager_next_scene(picopass->scene_manager, PicopassSceneCardMenu); consumed = true; + } else if(event.event == GuiButtonTypeCenter) { + consumed = scene_manager_search_and_switch_to_another_scene( + picopass->scene_manager, PicopassSceneStart); } } return consumed; diff --git a/applications/external/picopass/scenes/picopass_scene_start.c b/applications/external/picopass/scenes/picopass_scene_start.c index d33a1d26..8f7b627a 100644 --- a/applications/external/picopass/scenes/picopass_scene_start.c +++ b/applications/external/picopass/scenes/picopass_scene_start.c @@ -1,10 +1,8 @@ #include "../picopass_i.h" enum SubmenuIndex { SubmenuIndexRead, - SubmenuIndexRunScript, + SubmenuIndexEliteDictAttack, SubmenuIndexSaved, - SubmenuIndexAddManually, - SubmenuIndexDebug, }; void picopass_scene_start_submenu_callback(void* context, uint32_t index) { @@ -17,6 +15,12 @@ void picopass_scene_start_on_enter(void* context) { Submenu* submenu = picopass->submenu; submenu_add_item( submenu, "Read Card", SubmenuIndexRead, picopass_scene_start_submenu_callback, picopass); + submenu_add_item( + submenu, + "Elite Dict. Attack", + SubmenuIndexEliteDictAttack, + picopass_scene_start_submenu_callback, + picopass); submenu_add_item( submenu, "Saved", SubmenuIndexSaved, picopass_scene_start_submenu_callback, picopass); @@ -43,6 +47,11 @@ bool picopass_scene_start_on_event(void* context, SceneManagerEvent event) { picopass->scene_manager, PicopassSceneStart, SubmenuIndexSaved); scene_manager_next_scene(picopass->scene_manager, PicopassSceneFileSelect); consumed = true; + } else if(event.event == SubmenuIndexEliteDictAttack) { + scene_manager_set_scene_state( + picopass->scene_manager, PicopassSceneStart, SubmenuIndexEliteDictAttack); + scene_manager_next_scene(picopass->scene_manager, PicopassSceneEliteDictAttack); + consumed = true; } } diff --git a/applications/external/picopass/views/dict_attack.c b/applications/external/picopass/views/dict_attack.c new file mode 100644 index 00000000..fb7335f6 --- /dev/null +++ b/applications/external/picopass/views/dict_attack.c @@ -0,0 +1,281 @@ +#include "dict_attack.h" + +#include + +typedef enum { + DictAttackStateRead, + DictAttackStateCardRemoved, +} DictAttackState; + +struct DictAttack { + View* view; + DictAttackCallback callback; + void* context; +}; + +typedef struct { + DictAttackState state; + MfClassicType type; + FuriString* header; + uint8_t sectors_total; + uint8_t sectors_read; + uint8_t sector_current; + uint8_t keys_total; + uint8_t keys_found; + uint16_t dict_keys_total; + uint16_t dict_keys_current; + bool is_key_attack; + uint8_t key_attack_current_sector; +} DictAttackViewModel; + +static void dict_attack_draw_callback(Canvas* canvas, void* model) { + DictAttackViewModel* m = model; + if(m->state == DictAttackStateCardRemoved) { + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 64, 4, AlignCenter, AlignTop, "Lost the tag!"); + canvas_set_font(canvas, FontSecondary); + elements_multiline_text_aligned( + canvas, 64, 23, AlignCenter, AlignTop, "Make sure the tag is\npositioned correctly."); + } else if(m->state == DictAttackStateRead) { + char draw_str[32] = {}; + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned( + canvas, 64, 0, AlignCenter, AlignTop, furi_string_get_cstr(m->header)); + if(m->is_key_attack) { + snprintf( + draw_str, + sizeof(draw_str), + "Reuse key check for sector: %d", + m->key_attack_current_sector); + } else { + snprintf(draw_str, sizeof(draw_str), "Unlocking sector: %d", m->sector_current); + } + canvas_draw_str_aligned(canvas, 0, 10, AlignLeft, AlignTop, draw_str); + float dict_progress = m->dict_keys_total == 0 ? + 0 : + (float)(m->dict_keys_current) / (float)(m->dict_keys_total); + float progress = m->sectors_total == 0 ? 0 : + ((float)(m->sector_current) + dict_progress) / + (float)(m->sectors_total); + if(progress > 1.0) { + progress = 1.0; + } + if(m->dict_keys_current == 0) { + // Cause when people see 0 they think it's broken + snprintf(draw_str, sizeof(draw_str), "%d/%d", 1, m->dict_keys_total); + } else { + snprintf( + draw_str, sizeof(draw_str), "%d/%d", m->dict_keys_current, m->dict_keys_total); + } + elements_progress_bar_with_text(canvas, 0, 20, 128, dict_progress, draw_str); + canvas_set_font(canvas, FontSecondary); + snprintf(draw_str, sizeof(draw_str), "Keys found: %d/%d", m->keys_found, m->keys_total); + canvas_draw_str_aligned(canvas, 0, 33, AlignLeft, AlignTop, draw_str); + snprintf( + draw_str, sizeof(draw_str), "Sectors Read: %d/%d", m->sectors_read, m->sectors_total); + canvas_draw_str_aligned(canvas, 0, 43, AlignLeft, AlignTop, draw_str); + } + elements_button_center(canvas, "Skip"); +} + +static bool dict_attack_input_callback(InputEvent* event, void* context) { + DictAttack* dict_attack = context; + bool consumed = false; + if(event->type == InputTypeShort && event->key == InputKeyOk) { + if(dict_attack->callback) { + dict_attack->callback(dict_attack->context); + } + consumed = true; + } + return consumed; +} + +DictAttack* dict_attack_alloc() { + DictAttack* dict_attack = malloc(sizeof(DictAttack)); + dict_attack->view = view_alloc(); + view_allocate_model(dict_attack->view, ViewModelTypeLocking, sizeof(DictAttackViewModel)); + view_set_draw_callback(dict_attack->view, dict_attack_draw_callback); + view_set_input_callback(dict_attack->view, dict_attack_input_callback); + view_set_context(dict_attack->view, dict_attack); + with_view_model( + dict_attack->view, + DictAttackViewModel * model, + { model->header = furi_string_alloc(); }, + false); + return dict_attack; +} + +void dict_attack_free(DictAttack* dict_attack) { + furi_assert(dict_attack); + with_view_model( + dict_attack->view, + DictAttackViewModel * model, + { furi_string_free(model->header); }, + false); + view_free(dict_attack->view); + free(dict_attack); +} + +void dict_attack_reset(DictAttack* dict_attack) { + furi_assert(dict_attack); + with_view_model( + dict_attack->view, + DictAttackViewModel * model, + { + model->state = DictAttackStateRead; + model->type = MfClassicType1k; + model->sectors_total = 1; + model->sectors_read = 0; + model->sector_current = 0; + model->keys_total = 0; + model->keys_found = 0; + model->dict_keys_total = 0; + model->dict_keys_current = 0; + model->is_key_attack = false; + furi_string_reset(model->header); + }, + false); +} + +View* dict_attack_get_view(DictAttack* dict_attack) { + furi_assert(dict_attack); + return dict_attack->view; +} + +void dict_attack_set_callback(DictAttack* dict_attack, DictAttackCallback callback, void* context) { + furi_assert(dict_attack); + furi_assert(callback); + dict_attack->callback = callback; + dict_attack->context = context; +} + +void dict_attack_set_header(DictAttack* dict_attack, const char* header) { + furi_assert(dict_attack); + furi_assert(header); + + with_view_model( + dict_attack->view, + DictAttackViewModel * model, + { furi_string_set(model->header, header); }, + true); +} + +void dict_attack_set_card_detected(DictAttack* dict_attack) { + furi_assert(dict_attack); + with_view_model( + dict_attack->view, + DictAttackViewModel * model, + { + model->state = DictAttackStateRead; + model->sectors_total = 1; + model->keys_total = model->sectors_total; + }, + true); +} + +void dict_attack_set_card_removed(DictAttack* dict_attack) { + furi_assert(dict_attack); + with_view_model( + dict_attack->view, + DictAttackViewModel * model, + { model->state = DictAttackStateCardRemoved; }, + true); +} + +void dict_attack_set_sector_read(DictAttack* dict_attack, uint8_t sec_read) { + furi_assert(dict_attack); + with_view_model( + dict_attack->view, DictAttackViewModel * model, { model->sectors_read = sec_read; }, true); +} + +void dict_attack_set_keys_found(DictAttack* dict_attack, uint8_t keys_found) { + furi_assert(dict_attack); + with_view_model( + dict_attack->view, DictAttackViewModel * model, { model->keys_found = keys_found; }, true); +} + +void dict_attack_set_current_sector(DictAttack* dict_attack, uint8_t curr_sec) { + furi_assert(dict_attack); + with_view_model( + dict_attack->view, + DictAttackViewModel * model, + { + model->sector_current = curr_sec; + model->dict_keys_current = 0; + }, + true); +} + +void dict_attack_inc_current_sector(DictAttack* dict_attack) { + furi_assert(dict_attack); + with_view_model( + dict_attack->view, + DictAttackViewModel * model, + { + if(model->sector_current < model->sectors_total) { + model->sector_current++; + model->dict_keys_current = 0; + } + }, + true); +} + +void dict_attack_inc_keys_found(DictAttack* dict_attack) { + furi_assert(dict_attack); + with_view_model( + dict_attack->view, + DictAttackViewModel * model, + { + if(model->keys_found < model->keys_total) { + model->keys_found++; + } + }, + true); +} + +void dict_attack_set_total_dict_keys(DictAttack* dict_attack, uint16_t dict_keys_total) { + furi_assert(dict_attack); + with_view_model( + dict_attack->view, + DictAttackViewModel * model, + { model->dict_keys_total = dict_keys_total; }, + true); +} + +void dict_attack_inc_current_dict_key(DictAttack* dict_attack, uint16_t keys_tried) { + furi_assert(dict_attack); + with_view_model( + dict_attack->view, + DictAttackViewModel * model, + { + if(model->dict_keys_current + keys_tried < model->dict_keys_total) { + model->dict_keys_current += keys_tried; + } + }, + true); +} + +void dict_attack_set_key_attack(DictAttack* dict_attack, bool is_key_attack, uint8_t sector) { + furi_assert(dict_attack); + with_view_model( + dict_attack->view, + DictAttackViewModel * model, + { + model->is_key_attack = is_key_attack; + model->key_attack_current_sector = sector; + }, + true); +} + +void dict_attack_inc_key_attack_current_sector(DictAttack* dict_attack) { + furi_assert(dict_attack); + with_view_model( + dict_attack->view, + DictAttackViewModel * model, + { + if(model->key_attack_current_sector < model->sectors_total) { + model->key_attack_current_sector++; + } + }, + true); +} diff --git a/applications/external/picopass/views/dict_attack.h b/applications/external/picopass/views/dict_attack.h new file mode 100644 index 00000000..bdfa3e95 --- /dev/null +++ b/applications/external/picopass/views/dict_attack.h @@ -0,0 +1,44 @@ +#pragma once +#include +#include +#include + +#include + +typedef struct DictAttack DictAttack; + +typedef void (*DictAttackCallback)(void* context); + +DictAttack* dict_attack_alloc(); + +void dict_attack_free(DictAttack* dict_attack); + +void dict_attack_reset(DictAttack* dict_attack); + +View* dict_attack_get_view(DictAttack* dict_attack); + +void dict_attack_set_callback(DictAttack* dict_attack, DictAttackCallback callback, void* context); + +void dict_attack_set_header(DictAttack* dict_attack, const char* header); + +void dict_attack_set_card_detected(DictAttack* dict_attack); + +void dict_attack_set_card_removed(DictAttack* dict_attack); + +void dict_attack_set_sector_read(DictAttack* dict_attack, uint8_t sec_read); + +void dict_attack_set_keys_found(DictAttack* dict_attack, uint8_t keys_found); + +void dict_attack_set_current_sector(DictAttack* dict_attack, uint8_t curr_sec); + +void dict_attack_inc_current_sector(DictAttack* dict_attack); + +void dict_attack_inc_keys_found(DictAttack* dict_attack); + +void dict_attack_set_total_dict_keys(DictAttack* dict_attack, uint16_t dict_keys_total); + +void dict_attack_inc_current_dict_key(DictAttack* dict_attack, uint16_t keys_tried); + +void dict_attack_set_key_attack(DictAttack* dict_attack, bool is_key_attack, uint8_t sector); + +void dict_attack_inc_key_attack_current_sector(DictAttack* dict_attack);