From 92122b2cdf50da2182bf6a89abe047f2e2bd7db2 Mon Sep 17 00:00:00 2001 From: noproto Date: Tue, 3 Sep 2024 15:19:12 -0400 Subject: [PATCH] NFC app UI updates, MVP --- applications/main/nfc/nfc_app_i.h | 3 + .../scenes/nfc_scene_mf_classic_dict_attack.c | 22 +++-- applications/main/nfc/views/dict_attack.c | 84 ++++++++++++++++++- applications/main/nfc/views/dict_attack.h | 6 ++ .../protocols/mf_classic/mf_classic_poller.c | 6 +- .../protocols/mf_classic/mf_classic_poller.h | 3 + .../mf_classic/mf_classic_poller_i.h | 1 - 7 files changed, 115 insertions(+), 10 deletions(-) diff --git a/applications/main/nfc/nfc_app_i.h b/applications/main/nfc/nfc_app_i.h index 798c3a5a6..5310b8979 100644 --- a/applications/main/nfc/nfc_app_i.h +++ b/applications/main/nfc/nfc_app_i.h @@ -97,6 +97,9 @@ typedef struct { bool is_key_attack; uint8_t key_attack_current_sector; bool is_card_present; + uint8_t nested_phase; + uint8_t prng_type; + uint8_t backdoor; } NfcMfClassicDictAttackContext; struct NfcApp { diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c index 5aee8ddd8..af329a67c 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c @@ -5,6 +5,8 @@ #define TAG "NfcMfClassicDictAttack" +// TODO: Update progress bar with nested attacks + typedef enum { DictAttackStateUserDictInProgress, DictAttackStateSystemDictInProgress, @@ -58,6 +60,9 @@ NfcCommand nfc_dict_attack_worker_callback(NfcGenericEvent event, void* context) instance->nfc_dict_context.sectors_read = data_update->sectors_read; instance->nfc_dict_context.keys_found = data_update->keys_found; instance->nfc_dict_context.current_sector = data_update->current_sector; + instance->nfc_dict_context.nested_phase = data_update->nested_phase; + instance->nfc_dict_context.prng_type = data_update->prng_type; + instance->nfc_dict_context.backdoor = data_update->backdoor; view_dispatcher_send_custom_event( instance->view_dispatcher, NfcCustomEventDictAttackDataUpdate); } else if(mfc_event->type == MfClassicPollerEventTypeNextSector) { @@ -117,6 +122,9 @@ static void nfc_scene_mf_classic_dict_attack_update_view(NfcApp* instance) { dict_attack_set_keys_found(instance->dict_attack, mfc_dict->keys_found); dict_attack_set_current_dict_key(instance->dict_attack, mfc_dict->dict_keys_current); dict_attack_set_current_sector(instance->dict_attack, mfc_dict->current_sector); + dict_attack_set_nested_phase(instance->dict_attack, mfc_dict->nested_phase); + dict_attack_set_prng_type(instance->dict_attack, mfc_dict->prng_type); + dict_attack_set_backdoor(instance->dict_attack, mfc_dict->backdoor); } } @@ -125,6 +133,13 @@ static void nfc_scene_mf_classic_dict_attack_prepare_view(NfcApp* instance) { scene_manager_get_scene_state(instance->scene_manager, NfcSceneMfClassicDictAttack); if(state == DictAttackStateUserDictInProgress) { do { + // TODO: Check for errors + storage_common_remove(instance->storage, NFC_APP_MF_CLASSIC_DICT_SYSTEM_NESTED_PATH); + storage_common_copy( + instance->storage, + NFC_APP_MF_CLASSIC_DICT_SYSTEM_PATH, + NFC_APP_MF_CLASSIC_DICT_SYSTEM_NESTED_PATH); + if(!keys_dict_check_presence(NFC_APP_MF_CLASSIC_DICT_USER_PATH)) { state = DictAttackStateSystemDictInProgress; break; @@ -149,13 +164,6 @@ static void nfc_scene_mf_classic_dict_attack_prepare_view(NfcApp* instance) { } while(false); } if(state == DictAttackStateSystemDictInProgress) { - // TODO: Check for errors - storage_common_remove(instance->storage, NFC_APP_MF_CLASSIC_DICT_SYSTEM_NESTED_PATH); - storage_common_copy( - instance->storage, - NFC_APP_MF_CLASSIC_DICT_SYSTEM_PATH, - NFC_APP_MF_CLASSIC_DICT_SYSTEM_NESTED_PATH); - instance->nfc_dict_context.dict = keys_dict_alloc( NFC_APP_MF_CLASSIC_DICT_SYSTEM_PATH, KeysDictModeOpenExisting, sizeof(MfClassicKey)); dict_attack_set_header(instance->dict_attack, "MF Classic System Dictionary"); diff --git a/applications/main/nfc/views/dict_attack.c b/applications/main/nfc/views/dict_attack.c index 14298a6aa..83cf9a4bf 100644 --- a/applications/main/nfc/views/dict_attack.c +++ b/applications/main/nfc/views/dict_attack.c @@ -10,6 +10,30 @@ struct DictAttack { void* context; }; +typedef enum { + MfClassicNestedPhaseNone, + MfClassicNestedPhaseAnalyzePRNG, + MfClassicNestedPhaseDictAttack, + MfClassicNestedPhaseDictAttackResume, + MfClassicNestedPhaseCalibrate, + MfClassicNestedPhaseCollectNtEnc, + MfClassicNestedPhaseFinished, +} MfClassicNestedPhase; + +typedef enum { + MfClassicPrngTypeUnknown, // Tag not yet tested + MfClassicPrngTypeNoTag, // No tag detected during test + MfClassicPrngTypeWeak, // Weak PRNG, standard Nested + MfClassicPrngTypeHard, // Hard PRNG, Hardnested +} MfClassicPrngType; + +typedef enum { + MfClassicBackdoorUnknown, // Tag not yet tested + MfClassicBackdoorNone, // No observed backdoor + MfClassicBackdoorAuth1, // Tag responds to v1 auth backdoor + MfClassicBackdoorAuth2, // Tag responds to v2 auth backdoor (static encrypted nonce) +} MfClassicBackdoor; + typedef struct { FuriString* header; bool card_detected; @@ -21,6 +45,9 @@ typedef struct { size_t dict_keys_current; bool is_key_attack; uint8_t key_attack_current_sector; + MfClassicNestedPhase nested_phase; + MfClassicPrngType prng_type; + MfClassicBackdoor backdoor; } DictAttackViewModel; static void dict_attack_draw_callback(Canvas* canvas, void* model) { @@ -34,8 +61,39 @@ static void dict_attack_draw_callback(Canvas* canvas, void* model) { } else { char draw_str[32] = {}; canvas_set_font(canvas, FontSecondary); + + switch(m->nested_phase) { + case MfClassicNestedPhaseAnalyzePRNG: + furi_string_set(m->header, "PRNG Analysis"); + break; + case MfClassicNestedPhaseDictAttack: + case MfClassicNestedPhaseDictAttackResume: + furi_string_set(m->header, "Nested Dictionary"); + break; + case MfClassicNestedPhaseCalibrate: + furi_string_set(m->header, "Calibration"); + break; + case MfClassicNestedPhaseCollectNtEnc: + furi_string_set(m->header, "Nonce Collection"); + break; + default: + break; + } + + if(m->prng_type == MfClassicPrngTypeHard) { + furi_string_cat(m->header, " (Hard)"); + } + + if(m->backdoor != MfClassicBackdoorNone && m->backdoor != MfClassicBackdoorUnknown) { + if(m->nested_phase != MfClassicNestedPhaseNone) { + furi_string_cat(m->header, " (Backdoor)"); + } else { + furi_string_set(m->header, "Backdoor Read"); + } + } + canvas_draw_str_aligned( - canvas, 64, 0, AlignCenter, AlignTop, furi_string_get_cstr(m->header)); + canvas, 0, 0, AlignLeft, AlignTop, furi_string_get_cstr(m->header)); if(m->is_key_attack) { snprintf( draw_str, @@ -132,6 +190,9 @@ void dict_attack_reset(DictAttack* instance) { model->dict_keys_total = 0; model->dict_keys_current = 0; model->is_key_attack = false; + model->nested_phase = MfClassicNestedPhaseNone; + model->prng_type = MfClassicPrngTypeUnknown; + model->backdoor = MfClassicBackdoorUnknown; furi_string_reset(model->header); }, false); @@ -242,3 +303,24 @@ void dict_attack_reset_key_attack(DictAttack* instance) { with_view_model( instance->view, DictAttackViewModel * model, { model->is_key_attack = false; }, true); } + +void dict_attack_set_nested_phase(DictAttack* instance, uint8_t nested_phase) { + furi_assert(instance); + + with_view_model( + instance->view, DictAttackViewModel * model, { model->nested_phase = nested_phase; }, true); +} + +void dict_attack_set_prng_type(DictAttack* instance, uint8_t prng_type) { + furi_assert(instance); + + with_view_model( + instance->view, DictAttackViewModel * model, { model->prng_type = prng_type; }, true); +} + +void dict_attack_set_backdoor(DictAttack* instance, uint8_t backdoor) { + furi_assert(instance); + + with_view_model( + instance->view, DictAttackViewModel * model, { model->backdoor = backdoor; }, true); +} diff --git a/applications/main/nfc/views/dict_attack.h b/applications/main/nfc/views/dict_attack.h index 30f3b3c44..9afbeaf84 100644 --- a/applications/main/nfc/views/dict_attack.h +++ b/applications/main/nfc/views/dict_attack.h @@ -45,6 +45,12 @@ void dict_attack_set_key_attack(DictAttack* instance, uint8_t sector); void dict_attack_reset_key_attack(DictAttack* instance); +void dict_attack_set_nested_phase(DictAttack* instance, uint8_t nested_phase); + +void dict_attack_set_prng_type(DictAttack* instance, uint8_t prng_type); + +void dict_attack_set_backdoor(DictAttack* instance, uint8_t backdoor); + #ifdef __cplusplus } #endif diff --git a/lib/nfc/protocols/mf_classic/mf_classic_poller.c b/lib/nfc/protocols/mf_classic/mf_classic_poller.c index 33973afe6..de258b4d5 100644 --- a/lib/nfc/protocols/mf_classic/mf_classic_poller.c +++ b/lib/nfc/protocols/mf_classic/mf_classic_poller.c @@ -68,6 +68,9 @@ static NfcCommand mf_classic_poller_handle_data_update(MfClassicPoller* instance mf_classic_get_read_sectors_and_keys( instance->data, &data_update->sectors_read, &data_update->keys_found); data_update->current_sector = instance->mode_ctx.dict_attack_ctx.current_sector; + data_update->nested_phase = instance->mode_ctx.dict_attack_ctx.nested_phase; + data_update->prng_type = instance->mode_ctx.dict_attack_ctx.prng_type; + data_update->backdoor = instance->mode_ctx.dict_attack_ctx.backdoor; instance->mfc_event.type = MfClassicPollerEventTypeDataUpdate; return instance->callback(instance->general_event, instance->context); } @@ -1723,7 +1726,8 @@ bool is_valid_sum(uint16_t sum) { NfcCommand mf_classic_poller_handler_nested_controller(MfClassicPoller* instance) { // Iterate through keys - NfcCommand command = NfcCommandContinue; + //NfcCommand command = NfcCommandContinue; + NfcCommand command = mf_classic_poller_handle_data_update(instance); MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx; bool initial_dict_attack_iter = false; if(dict_attack_ctx->nested_phase == MfClassicNestedPhaseNone) { diff --git a/lib/nfc/protocols/mf_classic/mf_classic_poller.h b/lib/nfc/protocols/mf_classic/mf_classic_poller.h index b26378d33..0501de21e 100644 --- a/lib/nfc/protocols/mf_classic/mf_classic_poller.h +++ b/lib/nfc/protocols/mf_classic/mf_classic_poller.h @@ -77,6 +77,9 @@ typedef struct { uint8_t sectors_read; /**< Number of sectors read. */ uint8_t keys_found; /**< Number of keys found. */ uint8_t current_sector; /**< Current sector number. */ + uint8_t nested_phase; /**< Nested attack phase. */ + uint8_t prng_type; /**< PRNG (weak or hard). */ + uint8_t backdoor; /**< Backdoor type. */ } MfClassicPollerEventDataUpdate; /** diff --git a/lib/nfc/protocols/mf_classic/mf_classic_poller_i.h b/lib/nfc/protocols/mf_classic/mf_classic_poller_i.h index 41138144e..e17e13d13 100644 --- a/lib/nfc/protocols/mf_classic/mf_classic_poller_i.h +++ b/lib/nfc/protocols/mf_classic/mf_classic_poller_i.h @@ -52,7 +52,6 @@ typedef enum { MfClassicNestedPhaseAnalyzePRNG, MfClassicNestedPhaseDictAttack, MfClassicNestedPhaseDictAttackResume, - MfClassicNestedPhaseAnalyzeBackdoor, MfClassicNestedPhaseCalibrate, MfClassicNestedPhaseCollectNtEnc, MfClassicNestedPhaseFinished,