From 2c2228a4b91af0b88360b8e3ac7d55144ccb77e6 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sun, 21 Sep 2025 16:45:31 +0300 Subject: [PATCH] merge ofw pr 4271 also thanks WillyJL link to PR https://github.com/flipperdevices/flipperzero-firmware/pull/4271/files --- .../main/nfc/helpers/mf_ultralight_auth.c | 6 +- .../mf_ultralight/mf_ultralight.c | 51 ++- .../protocol_support/nfc_protocol_support.c | 1 + applications/main/nfc/nfc_app_i.h | 16 +- .../nfc/assets/mf_ultralight_c_dict.nfc | 55 ++++ .../main/nfc/scenes/nfc_scene_config.h | 7 + .../nfc/scenes/nfc_scene_delete_success.c | 4 + .../main/nfc/scenes/nfc_scene_extra_actions.c | 10 + .../nfc/scenes/nfc_scene_mf_classic_keys.c | 2 - .../nfc_scene_mf_ultralight_c_dict_attack.c | 238 ++++++++++++++ .../scenes/nfc_scene_mf_ultralight_c_keys.c | 96 ++++++ .../nfc_scene_mf_ultralight_c_keys_add.c | 63 ++++ .../nfc_scene_mf_ultralight_c_keys_delete.c | 108 +++++++ .../nfc_scene_mf_ultralight_c_keys_list.c | 66 ++++ ...cene_mf_ultralight_c_keys_warn_duplicate.c | 49 +++ .../main/nfc/scenes/nfc_scene_save_success.c | 4 + applications/main/nfc/views/dict_attack.c | 299 +++++++++++------- applications/main/nfc/views/dict_attack.h | 13 + .../protocols/mf_ultralight/mf_ultralight.h | 6 - .../mf_ultralight/mf_ultralight_poller.c | 170 ++++++---- .../mf_ultralight/mf_ultralight_poller.h | 34 +- .../mf_ultralight/mf_ultralight_poller_i.c | 33 -- .../mf_ultralight/mf_ultralight_poller_i.h | 3 +- lib/toolbox/keys_dict.c | 19 +- targets/f7/api_symbols.csv | 1 - 25 files changed, 1091 insertions(+), 263 deletions(-) create mode 100644 applications/main/nfc/resources/nfc/assets/mf_ultralight_c_dict.nfc create mode 100644 applications/main/nfc/scenes/nfc_scene_mf_ultralight_c_dict_attack.c create mode 100644 applications/main/nfc/scenes/nfc_scene_mf_ultralight_c_keys.c create mode 100644 applications/main/nfc/scenes/nfc_scene_mf_ultralight_c_keys_add.c create mode 100644 applications/main/nfc/scenes/nfc_scene_mf_ultralight_c_keys_delete.c create mode 100644 applications/main/nfc/scenes/nfc_scene_mf_ultralight_c_keys_list.c create mode 100644 applications/main/nfc/scenes/nfc_scene_mf_ultralight_c_keys_warn_duplicate.c diff --git a/applications/main/nfc/helpers/mf_ultralight_auth.c b/applications/main/nfc/helpers/mf_ultralight_auth.c index fccf50cdd..e97649cb3 100644 --- a/applications/main/nfc/helpers/mf_ultralight_auth.c +++ b/applications/main/nfc/helpers/mf_ultralight_auth.c @@ -18,11 +18,9 @@ void mf_ultralight_auth_free(MfUltralightAuth* instance) { void mf_ultralight_auth_reset(MfUltralightAuth* instance) { furi_assert(instance); - uint32_t default_password = MF_ULTRALIGHT_DEFAULT_PASSWORD; - instance->type = MfUltralightAuthTypeNone; - memcpy(&instance->password, &default_password, sizeof(MfUltralightAuthPassword)); - memcpy(&instance->tdes_key, MF_ULTRALIGHT_C_DEFAULT_KEY, sizeof(MfUltralightC3DesAuthKey)); + memset(&instance->password, 0, sizeof(MfUltralightAuthPassword)); + memset(&instance->tdes_key, 0, sizeof(MfUltralightC3DesAuthKey)); memset(&instance->pack, 0, sizeof(MfUltralightAuthPack)); } diff --git a/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c b/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c index 8eb42b89b..aa3cc2e75 100644 --- a/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c +++ b/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c @@ -14,6 +14,7 @@ enum { SubmenuIndexUnlock = SubmenuIndexCommonMax, SubmenuIndexUnlockByReader, SubmenuIndexUnlockByPassword, + SubmenuIndexDictAttack }; enum { @@ -149,7 +150,15 @@ static NfcCommand } if(!mf_ultralight_event->data->auth_context.skip_auth) { mf_ultralight_event->data->auth_context.password = instance->mf_ul_auth->password; - mf_ultralight_event->data->auth_context.tdes_key = instance->mf_ul_auth->tdes_key; + + // Only set tdes_key for Manual/Reader auth types, not for dictionary attacks + if(instance->mf_ul_auth->type == MfUltralightAuthTypeManual || + instance->mf_ul_auth->type == MfUltralightAuthTypeReader) { + mf_ultralight_event->data->key_request_data.key = instance->mf_ul_auth->tdes_key; + mf_ultralight_event->data->key_request_data.key_provided = true; + } else { + mf_ultralight_event->data->key_request_data.key_provided = false; + } } } else if(mf_ultralight_event->type == MfUltralightPollerEventTypeAuthSuccess) { instance->mf_ul_auth->pack = mf_ultralight_event->data->auth_context.pack; @@ -165,15 +174,31 @@ static void nfc_scene_read_on_enter_mf_ultralight(NfcApp* instance) { bool nfc_scene_read_on_event_mf_ultralight(NfcApp* instance, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { - if(event.event == NfcCustomEventCardDetected) { - nfc_unlock_helper_card_detected_handler(instance); - } else if(event.event == NfcCustomEventPollerIncomplete) { - notification_message(instance->notifications, &sequence_semi_success); + if(event.event == NfcCustomEventPollerSuccess) { + notification_message(instance->notifications, &sequence_success); scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess); dolphin_deed(DolphinDeedNfcReadSuccess); + return true; + } else if(event.event == NfcCustomEventPollerIncomplete) { + const MfUltralightData* data = + nfc_device_get_data(instance->nfc_device, NfcProtocolMfUltralight); + if(data->type == MfUltralightTypeMfulC && + instance->mf_ul_auth->type == MfUltralightAuthTypeNone) { + // Start dict attack for MFUL C cards only if no specific auth was attempted + scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightCDictAttack); + } else { + if(data->pages_read == data->pages_total) { + notification_message(instance->notifications, &sequence_success); + } else { + notification_message(instance->notifications, &sequence_semi_success); + } + scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess); + dolphin_deed(DolphinDeedNfcReadSuccess); + } + return true; } } - return true; + return false; } static void nfc_scene_read_and_saved_menu_on_enter_mf_ultralight(NfcApp* instance) { @@ -197,6 +222,14 @@ static void nfc_scene_read_and_saved_menu_on_enter_mf_ultralight(NfcApp* instanc SubmenuIndexUnlock, nfc_protocol_support_common_submenu_callback, instance); + if(data->type == MfUltralightTypeMfulC) { + submenu_add_item( + submenu, + "Unlock with Dictionary", + SubmenuIndexDictAttack, + nfc_protocol_support_common_submenu_callback, + instance); + } } } @@ -249,6 +282,12 @@ static bool nfc_scene_read_and_saved_menu_on_event_mf_ultralight( NfcSceneMfUltralightUnlockMenu; scene_manager_next_scene(instance->scene_manager, next_scene); consumed = true; + } else if(event.event == SubmenuIndexDictAttack) { + if(!scene_manager_search_and_switch_to_previous_scene( + instance->scene_manager, NfcSceneMfUltralightCDictAttack)) { + scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightCDictAttack); + } + consumed = true; } } return consumed; diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c index 008f9c317..2ee29668c 100644 --- a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c @@ -680,6 +680,7 @@ static void nfc_protocol_support_scene_save_name_on_enter(NfcApp* instance) { furi_string_replace(prefix, " Plus", "+"); // NTAG I2C+ furi_string_replace(prefix, " (Unknown)", ""); furi_string_replace_all(prefix, " ", "_"); + furi_string_replace_all(prefix, "/", "_"); name_generator_make_auto( instance->text_store, NFC_TEXT_STORE_SIZE, furi_string_get_cstr(prefix)); furi_string_free(prefix); diff --git a/applications/main/nfc/nfc_app_i.h b/applications/main/nfc/nfc_app_i.h index 4336d25e0..a142e47da 100644 --- a/applications/main/nfc/nfc_app_i.h +++ b/applications/main/nfc/nfc_app_i.h @@ -50,6 +50,7 @@ #include #include #include +#include #include #include @@ -67,7 +68,7 @@ #define NFC_NAME_SIZE 22 #define NFC_TEXT_STORE_SIZE 128 -#define NFC_BYTE_INPUT_STORE_SIZE 10 +#define NFC_BYTE_INPUT_STORE_SIZE 16 #define NFC_LOG_SIZE_MAX (1024) #define NFC_APP_FOLDER EXT_PATH("nfc") #define NFC_APP_EXTENSION ".nfc" @@ -83,6 +84,10 @@ #define NFC_APP_MF_CLASSIC_DICT_SYSTEM_PATH (NFC_APP_FOLDER "/assets/mf_classic_dict.nfc") #define NFC_APP_MF_CLASSIC_DICT_SYSTEM_NESTED_PATH \ (NFC_APP_FOLDER "/assets/mf_classic_dict_nested.nfc") +#define NFC_APP_MF_ULTRALIGHT_C_DICT_USER_PATH \ + (NFC_APP_FOLDER "/assets/mf_ultralight_c_dict_user.nfc") +#define NFC_APP_MF_ULTRALIGHT_C_DICT_SYSTEM_PATH \ + (NFC_APP_FOLDER "/assets/mf_ultralight_c_dict.nfc") #define NFC_MFKEY32_APP_PATH (EXT_PATH("apps/NFC/mfkey.fap")) @@ -110,6 +115,14 @@ typedef struct { bool enhanced_dict; } NfcMfClassicDictAttackContext; +typedef struct { + KeysDict* dict; + bool auth_success; + bool is_card_present; + size_t dict_keys_total; + size_t dict_keys_current; +} NfcMfUltralightCDictContext; + struct NfcApp { DialogsApp* dialogs; Storage* storage; @@ -148,6 +161,7 @@ struct NfcApp { MfUltralightAuth* mf_ul_auth; SlixUnlock* slix_unlock; NfcMfClassicDictAttackContext nfc_dict_context; + NfcMfUltralightCDictContext mf_ultralight_c_dict_context; Mfkey32Logger* mfkey32_logger; MfUserDict* mf_user_dict; MfClassicKeyCache* mfc_key_cache; diff --git a/applications/main/nfc/resources/nfc/assets/mf_ultralight_c_dict.nfc b/applications/main/nfc/resources/nfc/assets/mf_ultralight_c_dict.nfc new file mode 100644 index 000000000..fa5dbb1fb --- /dev/null +++ b/applications/main/nfc/resources/nfc/assets/mf_ultralight_c_dict.nfc @@ -0,0 +1,55 @@ +# Sample Key (BREAKMEIFYOUCAN!) +425245414B4D454946594F5543414E21 +# Hexadecimal-Reversed Sample Key +12E4143455F495649454D4B414542524 +# Byte-Reversed Sample Key (!NACUOYFIEMKAERB) +214E4143554F594649454D4B41455242 +# Semnox Key (IEMKAERB!NACUOY ) +49454D4B41455242214E4143554F5900 +# Modified Semnox Key (IEMKAERB!NACUOYF) +49454D4B41455242214E4143554F5946 + +# Mix of Proxmark and ChameleonMiniLiveDebugger +00000000000000000000000000000000 +000102030405060708090A0B0C0D0E0F +01010101010101010101010101010101 +FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +00112233445566778899AABBCCDDEEFF +47454D5850524553534F53414D504C45 +79702553797025537970255379702553 +4E617468616E2E4C6920546564647920 +43464F494D48504E4C4359454E528841 +6AC292FAA1315B4D858AB3A3D7D5933A +404142434445464748494A4B4C4D4E4F +2B7E151628AED2A6ABF7158809CF4F3C +FBEED618357133667C85E08F7236A8DE +F7DDAC306AE266CCF90BC11EE46D513B +54686973206973206D79206B65792020 +A0A1A2A3A4A5A6A7A0A1A2A3A4A5A6A7 +B0B1B2B3B4B5B6B7B0B1B2B3B4B5B6B7 +B0B1B2B3B4B5B6B7B8B9BABBBCBDBEBF +D3F7D3F7D3F7D3F7D3F7D3F7D3F7D3F7 +11111111111111111111111111111111 +22222222222222222222222222222222 +33333333333333333333333333333333 +44444444444444444444444444444444 +55555555555555555555555555555555 +66666666666666666666666666666666 +77777777777777777777777777777777 +88888888888888888888888888888888 +99999999999999999999999999999999 +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB +CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC +DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD +EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE +0102030405060708090A0B0C0D0E0F10 +00010203040506070809101112131415 +01020304050607080910111213141516 +16151413121110090807060504030201 +15141312111009080706050403020100 +0F0E0D0C0B0A09080706050403020100 +100F0E0D0C0B0A090807060504030201 +303132333435363738393A3B3C3D3E3F +9CABF398358405AE2F0E2B3D31C99A8A +605F5E5D5C5B5A59605F5E5D5C5B5A59 diff --git a/applications/main/nfc/scenes/nfc_scene_config.h b/applications/main/nfc/scenes/nfc_scene_config.h index b902bc940..d498eb90c 100644 --- a/applications/main/nfc/scenes/nfc_scene_config.h +++ b/applications/main/nfc/scenes/nfc_scene_config.h @@ -26,6 +26,7 @@ ADD_SCENE(nfc, retry_confirm, RetryConfirm) ADD_SCENE(nfc, exit_confirm, ExitConfirm) ADD_SCENE(nfc, save_confirm, SaveConfirm) +ADD_SCENE(nfc, mf_ultralight_c_dict_attack, MfUltralightCDictAttack) ADD_SCENE(nfc, mf_ultralight_unlock_menu, MfUltralightUnlockMenu) ADD_SCENE(nfc, mf_ultralight_unlock_warn, MfUltralightUnlockWarn) ADD_SCENE(nfc, mf_ultralight_key_input, MfUltralightKeyInput) @@ -52,6 +53,12 @@ ADD_SCENE(nfc, mf_classic_keys_delete, MfClassicKeysDelete) ADD_SCENE(nfc, mf_classic_keys_add, MfClassicKeysAdd) ADD_SCENE(nfc, mf_classic_keys_warn_duplicate, MfClassicKeysWarnDuplicate) +ADD_SCENE(nfc, mf_ultralight_c_keys, MfUltralightCKeys) +ADD_SCENE(nfc, mf_ultralight_c_keys_list, MfUltralightCKeysList) +ADD_SCENE(nfc, mf_ultralight_c_keys_delete, MfUltralightCKeysDelete) +ADD_SCENE(nfc, mf_ultralight_c_keys_add, MfUltralightCKeysAdd) +ADD_SCENE(nfc, mf_ultralight_c_keys_warn_duplicate, MfUltralightCKeysWarnDuplicate) + ADD_SCENE(nfc, set_type, SetType) ADD_SCENE(nfc, set_sak, SetSak) ADD_SCENE(nfc, set_atqa, SetAtqa) diff --git a/applications/main/nfc/scenes/nfc_scene_delete_success.c b/applications/main/nfc/scenes/nfc_scene_delete_success.c index d41e52549..d8308addd 100644 --- a/applications/main/nfc/scenes/nfc_scene_delete_success.c +++ b/applications/main/nfc/scenes/nfc_scene_delete_success.c @@ -28,6 +28,10 @@ bool nfc_scene_delete_success_on_event(void* context, SceneManagerEvent event) { if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneMfClassicKeys)) { consumed = scene_manager_search_and_switch_to_previous_scene( nfc->scene_manager, NfcSceneMfClassicKeys); + } else if(scene_manager_has_previous_scene( + nfc->scene_manager, NfcSceneMfUltralightCKeys)) { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneMfUltralightCKeys); } else { consumed = scene_manager_search_and_switch_to_previous_scene( nfc->scene_manager, NfcSceneFileSelect); diff --git a/applications/main/nfc/scenes/nfc_scene_extra_actions.c b/applications/main/nfc/scenes/nfc_scene_extra_actions.c index 2943c0c55..6720b2d7b 100644 --- a/applications/main/nfc/scenes/nfc_scene_extra_actions.c +++ b/applications/main/nfc/scenes/nfc_scene_extra_actions.c @@ -3,6 +3,7 @@ enum SubmenuIndex { SubmenuIndexReadCardType, SubmenuIndexMfClassicKeys, + SubmenuIndexMfUltralightCKeys, SubmenuIndexMfUltralightUnlock, SubmenuIndexSlixUnlock, }; @@ -29,6 +30,12 @@ void nfc_scene_extra_actions_on_enter(void* context) { SubmenuIndexMfClassicKeys, nfc_scene_extra_actions_submenu_callback, instance); + submenu_add_item( + submenu, + "MIFARE Ultralight C Keys", + SubmenuIndexMfUltralightCKeys, + nfc_scene_extra_actions_submenu_callback, + instance); submenu_add_item( submenu, "Unlock NTAG/Ultralight", @@ -54,6 +61,9 @@ bool nfc_scene_extra_actions_on_event(void* context, SceneManagerEvent event) { if(event.event == SubmenuIndexMfClassicKeys) { scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicKeys); consumed = true; + } else if(event.event == SubmenuIndexMfUltralightCKeys) { + scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightCKeys); + consumed = true; } else if(event.event == SubmenuIndexMfUltralightUnlock) { mf_ultralight_auth_reset(instance->mf_ul_auth); scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightUnlockMenu); diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_keys.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys.c index eaa054149..7ee203285 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_keys.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys.c @@ -1,7 +1,5 @@ #include "../nfc_app_i.h" -#define NFC_SCENE_MF_CLASSIC_KEYS_MAX (100) - void nfc_scene_mf_classic_keys_widget_callback(GuiButtonType result, InputType type, void* context) { NfcApp* instance = context; if(type == InputTypeShort) { diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_c_dict_attack.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_c_dict_attack.c new file mode 100644 index 000000000..b1e24def2 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_c_dict_attack.c @@ -0,0 +1,238 @@ +#include "../nfc_app_i.h" +#include + +#define TAG "NfcMfUlCDictAttack" + +// TODO: Support card_detected properly + +enum { + DictAttackStateUserDictInProgress, + DictAttackStateSystemDictInProgress, +}; + +NfcCommand nfc_mf_ultralight_c_dict_attack_worker_callback(NfcGenericEvent event, void* context) { + furi_assert(context); + furi_assert(event.event_data); + furi_assert(event.protocol == NfcProtocolMfUltralight); + NfcCommand command = NfcCommandContinue; + NfcApp* instance = context; + MfUltralightPollerEvent* poller_event = event.event_data; + + if(poller_event->type == MfUltralightPollerEventTypeRequestMode) { + poller_event->data->poller_mode = MfUltralightPollerModeDictAttack; + command = NfcCommandContinue; + } else if(poller_event->type == MfUltralightPollerEventTypeRequestKey) { + MfUltralightC3DesAuthKey key = {}; + if(keys_dict_get_next_key( + instance->mf_ultralight_c_dict_context.dict, + key.data, + sizeof(MfUltralightC3DesAuthKey))) { + poller_event->data->key_request_data.key = key; + poller_event->data->key_request_data.key_provided = true; + instance->mf_ultralight_c_dict_context.dict_keys_current++; + + if(instance->mf_ultralight_c_dict_context.dict_keys_current % 10 == 0) { + view_dispatcher_send_custom_event( + instance->view_dispatcher, NfcCustomEventDictAttackDataUpdate); + } + } else { + poller_event->data->key_request_data.key_provided = false; + } + } else if(poller_event->type == MfUltralightPollerEventTypeReadSuccess) { + nfc_device_set_data( + instance->nfc_device, NfcProtocolMfUltralight, nfc_poller_get_data(instance->poller)); + // Check if this is a successful authentication by looking at the poller's auth context + const MfUltralightData* data = nfc_poller_get_data(instance->poller); + + // Update page information + dict_attack_set_pages_read(instance->dict_attack, data->pages_read); + dict_attack_set_pages_total(instance->dict_attack, data->pages_total); + + if(data->pages_read == data->pages_total) { + // Full read indicates successful authentication in dict attack mode + instance->mf_ultralight_c_dict_context.auth_success = true; + dict_attack_set_key_found(instance->dict_attack, true); + } + view_dispatcher_send_custom_event( + instance->view_dispatcher, NfcCustomEventDictAttackComplete); + command = NfcCommandStop; + } + return command; +} + +void nfc_scene_mf_ultralight_c_dict_attack_dict_attack_result_callback( + DictAttackEvent event, + void* context) { + furi_assert(context); + NfcApp* instance = context; + if(event == DictAttackEventSkipPressed) { + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventDictAttackSkip); + } +} + +void nfc_scene_mf_ultralight_c_dict_attack_prepare_view(NfcApp* instance) { + uint32_t state = + scene_manager_get_scene_state(instance->scene_manager, NfcSceneMfUltralightCDictAttack); + + // Set attack type to Ultralight C + dict_attack_set_type(instance->dict_attack, DictAttackTypeMfUltralightC); + + if(state == DictAttackStateUserDictInProgress) { + do { + if(!keys_dict_check_presence(NFC_APP_MF_ULTRALIGHT_C_DICT_USER_PATH)) { + state = DictAttackStateSystemDictInProgress; + break; + } + instance->mf_ultralight_c_dict_context.dict = keys_dict_alloc( + NFC_APP_MF_ULTRALIGHT_C_DICT_USER_PATH, + KeysDictModeOpenAlways, + sizeof(MfUltralightC3DesAuthKey)); + if(keys_dict_get_total_keys(instance->mf_ultralight_c_dict_context.dict) == 0) { + keys_dict_free(instance->mf_ultralight_c_dict_context.dict); + state = DictAttackStateSystemDictInProgress; + break; + } + dict_attack_set_header(instance->dict_attack, "MFUL C User Dictionary"); + } while(false); + } + if(state == DictAttackStateSystemDictInProgress) { + instance->mf_ultralight_c_dict_context.dict = keys_dict_alloc( + NFC_APP_MF_ULTRALIGHT_C_DICT_SYSTEM_PATH, + KeysDictModeOpenExisting, + sizeof(MfUltralightC3DesAuthKey)); + dict_attack_set_header(instance->dict_attack, "MFUL C System Dictionary"); + } + + instance->mf_ultralight_c_dict_context.dict_keys_total = + keys_dict_get_total_keys(instance->mf_ultralight_c_dict_context.dict); + dict_attack_set_total_dict_keys( + instance->dict_attack, instance->mf_ultralight_c_dict_context.dict_keys_total); + instance->mf_ultralight_c_dict_context.dict_keys_current = 0; + dict_attack_set_current_dict_key( + instance->dict_attack, instance->mf_ultralight_c_dict_context.dict_keys_current); + + // Set initial Ultralight C specific values + dict_attack_set_key_found(instance->dict_attack, false); + dict_attack_set_pages_total(instance->dict_attack, 48); // Ultralight C page count + dict_attack_set_pages_read(instance->dict_attack, 0); + + dict_attack_set_callback( + instance->dict_attack, + nfc_scene_mf_ultralight_c_dict_attack_dict_attack_result_callback, + instance); + scene_manager_set_scene_state(instance->scene_manager, NfcSceneMfUltralightCDictAttack, state); +} + +void nfc_scene_mf_ultralight_c_dict_attack_on_enter(void* context) { + NfcApp* instance = context; + + scene_manager_set_scene_state( + instance->scene_manager, + NfcSceneMfUltralightCDictAttack, + DictAttackStateUserDictInProgress); + nfc_scene_mf_ultralight_c_dict_attack_prepare_view(instance); + + // Setup and start worker + instance->poller = nfc_poller_alloc(instance->nfc, NfcProtocolMfUltralight); + nfc_poller_start(instance->poller, nfc_mf_ultralight_c_dict_attack_worker_callback, instance); + + dict_attack_set_card_state(instance->dict_attack, true); + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewDictAttack); + nfc_blink_read_start(instance); +} + +void nfc_scene_mf_ul_c_dict_attack_update_view(NfcApp* instance) { + dict_attack_set_card_state( + instance->dict_attack, instance->mf_ultralight_c_dict_context.is_card_present); + dict_attack_set_current_dict_key( + instance->dict_attack, instance->mf_ultralight_c_dict_context.dict_keys_current); +} + +bool nfc_scene_mf_ultralight_c_dict_attack_on_event(void* context, SceneManagerEvent event) { + NfcApp* instance = context; + bool consumed = false; + + uint32_t state = + scene_manager_get_scene_state(instance->scene_manager, NfcSceneMfUltralightCDictAttack); + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventDictAttackComplete) { + if(state == DictAttackStateUserDictInProgress) { + if(instance->mf_ultralight_c_dict_context.auth_success) { + notification_message(instance->notifications, &sequence_success); + scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess); + dolphin_deed(DolphinDeedNfcReadSuccess); + consumed = true; + } else { + nfc_poller_stop(instance->poller); + nfc_poller_free(instance->poller); + keys_dict_free(instance->mf_ultralight_c_dict_context.dict); + scene_manager_set_scene_state( + instance->scene_manager, + NfcSceneMfUltralightCDictAttack, + DictAttackStateSystemDictInProgress); + nfc_scene_mf_ultralight_c_dict_attack_prepare_view(instance); + instance->poller = nfc_poller_alloc(instance->nfc, NfcProtocolMfUltralight); + nfc_poller_start( + instance->poller, + nfc_mf_ultralight_c_dict_attack_worker_callback, + instance); + consumed = true; + } + } else { + // Could check if card is fully read here like MFC dict attack, but found key means fully read + if(instance->mf_ultralight_c_dict_context.auth_success) { + notification_message(instance->notifications, &sequence_success); + } else { + notification_message(instance->notifications, &sequence_semi_success); + } + scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess); + dolphin_deed(DolphinDeedNfcReadSuccess); + consumed = true; + } + } else if(event.event == NfcCustomEventDictAttackDataUpdate) { + dict_attack_set_current_dict_key( + instance->dict_attack, instance->mf_ultralight_c_dict_context.dict_keys_current); + consumed = true; + } else if(event.event == NfcCustomEventDictAttackSkip) { + if(state == DictAttackStateUserDictInProgress) { + nfc_poller_stop(instance->poller); + nfc_poller_free(instance->poller); + keys_dict_free(instance->mf_ultralight_c_dict_context.dict); + scene_manager_set_scene_state( + instance->scene_manager, + NfcSceneMfUltralightCDictAttack, + DictAttackStateSystemDictInProgress); + nfc_scene_mf_ultralight_c_dict_attack_prepare_view(instance); + instance->poller = nfc_poller_alloc(instance->nfc, NfcProtocolMfUltralight); + nfc_poller_start( + instance->poller, nfc_mf_ultralight_c_dict_attack_worker_callback, instance); + } else { + notification_message(instance->notifications, &sequence_semi_success); + scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess); + dolphin_deed(DolphinDeedNfcReadSuccess); + } + consumed = true; + } + } else if(event.type == SceneManagerEventTypeBack) { + scene_manager_next_scene(instance->scene_manager, NfcSceneExitConfirm); + consumed = true; + } + return consumed; +} + +void nfc_scene_mf_ultralight_c_dict_attack_on_exit(void* context) { + NfcApp* instance = context; + nfc_poller_stop(instance->poller); + nfc_poller_free(instance->poller); + scene_manager_set_scene_state( + instance->scene_manager, + NfcSceneMfUltralightCDictAttack, + DictAttackStateUserDictInProgress); + keys_dict_free(instance->mf_ultralight_c_dict_context.dict); + instance->mf_ultralight_c_dict_context.dict_keys_total = 0; + instance->mf_ultralight_c_dict_context.dict_keys_current = 0; + instance->mf_ultralight_c_dict_context.auth_success = false; + instance->mf_ultralight_c_dict_context.is_card_present = false; + nfc_blink_stop(instance); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_c_keys.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_c_keys.c new file mode 100644 index 000000000..9bf96f0b4 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_c_keys.c @@ -0,0 +1,96 @@ +#include "../nfc_app_i.h" + +void nfc_scene_mf_ultralight_c_keys_widget_callback( + GuiButtonType result, + InputType type, + void* context) { + NfcApp* instance = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(instance->view_dispatcher, result); + } +} + +void nfc_scene_mf_ultralight_c_keys_on_enter(void* context) { + NfcApp* instance = context; + + // Load flipper dict keys total + uint32_t flipper_dict_keys_total = 0; + KeysDict* dict = keys_dict_alloc( + NFC_APP_MF_ULTRALIGHT_C_DICT_SYSTEM_PATH, + KeysDictModeOpenExisting, + sizeof(MfUltralightC3DesAuthKey)); + flipper_dict_keys_total = keys_dict_get_total_keys(dict); + keys_dict_free(dict); + + // Load user dict keys total + uint32_t user_dict_keys_total = 0; + dict = keys_dict_alloc( + NFC_APP_MF_ULTRALIGHT_C_DICT_USER_PATH, + KeysDictModeOpenAlways, + sizeof(MfUltralightC3DesAuthKey)); + user_dict_keys_total = keys_dict_get_total_keys(dict); + keys_dict_free(dict); + + FuriString* temp_str = furi_string_alloc(); + widget_add_string_element( + instance->widget, 0, 0, AlignLeft, AlignTop, FontPrimary, "MIFARE Ultralight C Keys"); + furi_string_printf(temp_str, "System dict: %lu", flipper_dict_keys_total); + widget_add_string_element( + instance->widget, + 0, + 20, + AlignLeft, + AlignTop, + FontSecondary, + furi_string_get_cstr(temp_str)); + furi_string_printf(temp_str, "User dict: %lu", user_dict_keys_total); + widget_add_string_element( + instance->widget, + 0, + 32, + AlignLeft, + AlignTop, + FontSecondary, + furi_string_get_cstr(temp_str)); + widget_add_icon_element(instance->widget, 87, 13, &I_Keychain_39x36); + widget_add_button_element( + instance->widget, + GuiButtonTypeCenter, + "Add", + nfc_scene_mf_ultralight_c_keys_widget_callback, + instance); + if(user_dict_keys_total > 0) { + widget_add_button_element( + instance->widget, + GuiButtonTypeRight, + "List", + nfc_scene_mf_ultralight_c_keys_widget_callback, + instance); + } + furi_string_free(temp_str); + + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewWidget); +} + +bool nfc_scene_mf_ultralight_c_keys_on_event(void* context, SceneManagerEvent event) { + NfcApp* instance = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeCenter) { + scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightCKeysAdd); + consumed = true; + } else if(event.event == GuiButtonTypeRight) { + scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightCKeysList); + consumed = true; + } + } + + return consumed; +} + +void nfc_scene_mf_ultralight_c_keys_on_exit(void* context) { + NfcApp* instance = context; + + widget_reset(instance->widget); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_c_keys_add.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_c_keys_add.c new file mode 100644 index 000000000..aa4c2fe1c --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_c_keys_add.c @@ -0,0 +1,63 @@ +#include "../nfc_app_i.h" + +void nfc_scene_mf_ultralight_c_keys_add_byte_input_callback(void* context) { + NfcApp* instance = context; + + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventByteInputDone); +} + +void nfc_scene_mf_ultralight_c_keys_add_on_enter(void* context) { + NfcApp* instance = context; + + // Setup view + ByteInput* byte_input = instance->byte_input; + byte_input_set_header_text(byte_input, "Enter the key in hex"); + byte_input_set_result_callback( + byte_input, + nfc_scene_mf_ultralight_c_keys_add_byte_input_callback, + NULL, + instance, + instance->byte_input_store, + sizeof(MfUltralightC3DesAuthKey)); + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewByteInput); +} + +bool nfc_scene_mf_ultralight_c_keys_add_on_event(void* context, SceneManagerEvent event) { + NfcApp* instance = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventByteInputDone) { + // Add key to dict + KeysDict* dict = keys_dict_alloc( + NFC_APP_MF_ULTRALIGHT_C_DICT_USER_PATH, + KeysDictModeOpenAlways, + sizeof(MfUltralightC3DesAuthKey)); + + MfUltralightC3DesAuthKey key = {}; + memcpy(key.data, instance->byte_input_store, sizeof(MfUltralightC3DesAuthKey)); + if(keys_dict_is_key_present(dict, key.data, sizeof(MfUltralightC3DesAuthKey))) { + scene_manager_next_scene( + instance->scene_manager, NfcSceneMfUltralightCKeysWarnDuplicate); + } else if(keys_dict_add_key(dict, key.data, sizeof(MfUltralightC3DesAuthKey))) { + scene_manager_next_scene(instance->scene_manager, NfcSceneSaveSuccess); + dolphin_deed(DolphinDeedNfcMfcAdd); + } else { + scene_manager_previous_scene(instance->scene_manager); + } + + keys_dict_free(dict); + consumed = true; + } + } + + return consumed; +} + +void nfc_scene_mf_ultralight_c_keys_add_on_exit(void* context) { + NfcApp* instance = context; + + // Clear view + byte_input_set_result_callback(instance->byte_input, NULL, NULL, NULL, NULL, 0); + byte_input_set_header_text(instance->byte_input, ""); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_c_keys_delete.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_c_keys_delete.c new file mode 100644 index 000000000..db2903939 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_c_keys_delete.c @@ -0,0 +1,108 @@ +#include "../nfc_app_i.h" + +void nfc_scene_mf_ultralight_c_keys_delete_widget_callback( + GuiButtonType result, + InputType type, + void* context) { + NfcApp* instance = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(instance->view_dispatcher, result); + } +} + +void nfc_scene_mf_ultralight_c_keys_delete_on_enter(void* context) { + NfcApp* instance = context; + + uint32_t key_index = + scene_manager_get_scene_state(instance->scene_manager, NfcSceneMfUltralightCKeysDelete); + FuriString* key_str = furi_string_alloc(); + + widget_add_string_element( + instance->widget, 64, 0, AlignCenter, AlignTop, FontPrimary, "Delete this key?"); + widget_add_button_element( + instance->widget, + GuiButtonTypeLeft, + "Cancel", + nfc_scene_mf_ultralight_c_keys_delete_widget_callback, + instance); + widget_add_button_element( + instance->widget, + GuiButtonTypeRight, + "Delete", + nfc_scene_mf_ultralight_c_keys_delete_widget_callback, + instance); + + KeysDict* mf_ultralight_c_user_dict = keys_dict_alloc( + NFC_APP_MF_ULTRALIGHT_C_DICT_USER_PATH, + KeysDictModeOpenAlways, + sizeof(MfUltralightC3DesAuthKey)); + size_t dict_keys_num = keys_dict_get_total_keys(mf_ultralight_c_user_dict); + furi_assert(key_index < dict_keys_num); + MfUltralightC3DesAuthKey stack_key; + for(size_t i = 0; i < (key_index + 1); i++) { + bool key_loaded = keys_dict_get_next_key( + mf_ultralight_c_user_dict, stack_key.data, sizeof(MfUltralightC3DesAuthKey)); + furi_assert(key_loaded); + } + furi_string_reset(key_str); + for(size_t i = 0; i < sizeof(MfUltralightC3DesAuthKey); i++) { + furi_string_cat_printf(key_str, "%02X", stack_key.data[i]); + } + + widget_add_string_element( + instance->widget, + 64, + 32, + AlignCenter, + AlignCenter, + FontSecondary, + furi_string_get_cstr(key_str)); + + keys_dict_free(mf_ultralight_c_user_dict); + furi_string_free(key_str); + + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewWidget); +} + +bool nfc_scene_mf_ultralight_c_keys_delete_on_event(void* context, SceneManagerEvent event) { + NfcApp* instance = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeRight) { + uint32_t key_index = scene_manager_get_scene_state( + instance->scene_manager, NfcSceneMfUltralightCKeysDelete); + KeysDict* mf_ultralight_c_user_dict = keys_dict_alloc( + NFC_APP_MF_ULTRALIGHT_C_DICT_USER_PATH, + KeysDictModeOpenAlways, + sizeof(MfUltralightC3DesAuthKey)); + size_t dict_keys_num = keys_dict_get_total_keys(mf_ultralight_c_user_dict); + furi_assert(key_index < dict_keys_num); + MfUltralightC3DesAuthKey stack_key; + for(size_t i = 0; i < (key_index + 1); i++) { + bool key_loaded = keys_dict_get_next_key( + mf_ultralight_c_user_dict, stack_key.data, sizeof(MfUltralightC3DesAuthKey)); + furi_assert(key_loaded); + } + bool key_delete_success = keys_dict_delete_key( + mf_ultralight_c_user_dict, stack_key.data, sizeof(MfUltralightC3DesAuthKey)); + keys_dict_free(mf_ultralight_c_user_dict); + if(key_delete_success) { + scene_manager_next_scene(instance->scene_manager, NfcSceneDeleteSuccess); + } else { + scene_manager_previous_scene(instance->scene_manager); + } + } else if(event.event == GuiButtonTypeLeft) { + scene_manager_previous_scene(instance->scene_manager); + } + consumed = true; + } + + return consumed; +} + +void nfc_scene_mf_ultralight_c_keys_delete_on_exit(void* context) { + NfcApp* instance = context; + + widget_reset(instance->widget); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_c_keys_list.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_c_keys_list.c new file mode 100644 index 000000000..e2fda3aea --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_c_keys_list.c @@ -0,0 +1,66 @@ +#include "../nfc_app_i.h" + +#define NFC_SCENE_MF_ULTRALIGHT_C_KEYS_LIST_MAX (100) + +void nfc_scene_mf_ultralight_c_keys_list_submenu_callback(void* context, uint32_t index) { + NfcApp* instance = context; + + view_dispatcher_send_custom_event(instance->view_dispatcher, index); +} + +void nfc_scene_mf_ultralight_c_keys_list_on_enter(void* context) { + NfcApp* instance = context; + + KeysDict* mf_ultralight_c_user_dict = keys_dict_alloc( + NFC_APP_MF_ULTRALIGHT_C_DICT_USER_PATH, + KeysDictModeOpenAlways, + sizeof(MfUltralightC3DesAuthKey)); + + submenu_set_header(instance->submenu, "Select key to delete:"); + FuriString* temp_str = furi_string_alloc(); + + size_t dict_keys_num = keys_dict_get_total_keys(mf_ultralight_c_user_dict); + size_t keys_num = MIN((size_t)NFC_SCENE_MF_ULTRALIGHT_C_KEYS_LIST_MAX, dict_keys_num); + MfUltralightC3DesAuthKey stack_key; + + if(keys_num > 0) { + for(size_t i = 0; i < keys_num; i++) { + bool key_loaded = keys_dict_get_next_key( + mf_ultralight_c_user_dict, stack_key.data, sizeof(MfUltralightC3DesAuthKey)); + furi_assert(key_loaded); + furi_string_reset(temp_str); + for(size_t i = 0; i < sizeof(MfUltralightC3DesAuthKey); i++) { + furi_string_cat_printf(temp_str, "%02X", stack_key.data[i]); + } + submenu_add_item( + instance->submenu, + furi_string_get_cstr(temp_str), + i, + nfc_scene_mf_ultralight_c_keys_list_submenu_callback, + instance); + } + } + keys_dict_free(mf_ultralight_c_user_dict); + furi_string_free(temp_str); + + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewMenu); +} + +bool nfc_scene_mf_ultralight_c_keys_list_on_event(void* context, SceneManagerEvent event) { + NfcApp* instance = context; + + bool consumed = false; + if(event.type == SceneManagerEventTypeCustom) { + scene_manager_set_scene_state( + instance->scene_manager, NfcSceneMfUltralightCKeysDelete, event.event); + scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightCKeysDelete); + } + + return consumed; +} + +void nfc_scene_mf_ultralight_c_keys_list_on_exit(void* context) { + NfcApp* instance = context; + + submenu_reset(instance->submenu); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_c_keys_warn_duplicate.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_c_keys_warn_duplicate.c new file mode 100644 index 000000000..c8881e5d4 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_c_keys_warn_duplicate.c @@ -0,0 +1,49 @@ +#include "../nfc_app_i.h" + +void nfc_scene_mf_ultralight_c_keys_warn_duplicate_popup_callback(void* context) { + NfcApp* instance = context; + + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventViewExit); +} + +void nfc_scene_mf_ultralight_c_keys_warn_duplicate_on_enter(void* context) { + NfcApp* instance = context; + + // Setup view + Popup* popup = instance->popup; + popup_set_icon(popup, 83, 22, &I_WarningDolphinFlip_45x42); + popup_set_header(popup, "Key Already Exists!", 64, 3, AlignCenter, AlignTop); + popup_set_text( + popup, + "Please enter a\n" + "different key.", + 4, + 24, + AlignLeft, + AlignTop); + popup_set_timeout(popup, 1500); + popup_set_context(popup, instance); + popup_set_callback(popup, nfc_scene_mf_ultralight_c_keys_warn_duplicate_popup_callback); + popup_enable_timeout(popup); + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup); +} + +bool nfc_scene_mf_ultralight_c_keys_warn_duplicate_on_event(void* context, SceneManagerEvent event) { + NfcApp* instance = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventViewExit) { + consumed = scene_manager_search_and_switch_to_previous_scene( + instance->scene_manager, NfcSceneMfUltralightCKeysAdd); + } + } + + return consumed; +} + +void nfc_scene_mf_ultralight_c_keys_warn_duplicate_on_exit(void* context) { + NfcApp* instance = context; + + popup_reset(instance->popup); +} diff --git a/applications/main/nfc/scenes/nfc_scene_save_success.c b/applications/main/nfc/scenes/nfc_scene_save_success.c index 5f812ba9c..06999fe9f 100644 --- a/applications/main/nfc/scenes/nfc_scene_save_success.c +++ b/applications/main/nfc/scenes/nfc_scene_save_success.c @@ -28,6 +28,10 @@ bool nfc_scene_save_success_on_event(void* context, SceneManagerEvent event) { if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneMfClassicKeys)) { consumed = scene_manager_search_and_switch_to_previous_scene( nfc->scene_manager, NfcSceneMfClassicKeys); + } else if(scene_manager_has_previous_scene( + nfc->scene_manager, NfcSceneMfUltralightCKeys)) { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneMfUltralightCKeys); } else if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSaveConfirm)) { NfcSceneSaveConfirmState scene_state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneSaveConfirm); diff --git a/applications/main/nfc/views/dict_attack.c b/applications/main/nfc/views/dict_attack.c index 726076972..a71e466f8 100644 --- a/applications/main/nfc/views/dict_attack.c +++ b/applications/main/nfc/views/dict_attack.c @@ -13,12 +13,13 @@ struct DictAttack { typedef struct { FuriString* header; bool card_detected; + DictAttackType attack_type; + + // MIFARE Classic specific uint8_t sectors_total; uint8_t sectors_read; uint8_t current_sector; uint8_t keys_found; - size_t dict_keys_total; - size_t dict_keys_current; bool is_key_attack; uint8_t key_attack_current_sector; MfClassicNestedPhase nested_phase; @@ -26,8 +27,150 @@ typedef struct { MfClassicBackdoor backdoor; uint16_t nested_target_key; uint16_t msb_count; + + // Ultralight C specific + uint8_t pages_total; + uint8_t pages_read; + bool key_found; + + // Common + size_t dict_keys_total; + size_t dict_keys_current; } DictAttackViewModel; +static void dict_attack_draw_mf_classic(Canvas* canvas, DictAttackViewModel* m) { + 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 MfClassicNestedPhaseDictAttackVerify: + case MfClassicNestedPhaseDictAttackResume: + furi_string_set(m->header, "Nested Dictionary"); + break; + case MfClassicNestedPhaseCalibrate: + case MfClassicNestedPhaseRecalibrate: + 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, 0, 0, AlignLeft, AlignTop, furi_string_get_cstr(m->header)); + if(m->nested_phase == MfClassicNestedPhaseCollectNtEnc) { + uint8_t nonce_sector = + m->nested_target_key / (m->prng_type == MfClassicPrngTypeWeak ? 4 : 2); + snprintf(draw_str, sizeof(draw_str), "Collecting from sector: %d", nonce_sector); + canvas_draw_str_aligned(canvas, 0, 10, AlignLeft, AlignTop, draw_str); + } else 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->current_sector); + } + canvas_draw_str_aligned(canvas, 0, 10, AlignLeft, AlignTop, draw_str); + float dict_progress = 0; + if(m->nested_phase == MfClassicNestedPhaseAnalyzePRNG || + m->nested_phase == MfClassicNestedPhaseDictAttack || + m->nested_phase == MfClassicNestedPhaseDictAttackVerify || + m->nested_phase == MfClassicNestedPhaseDictAttackResume) { + // Phase: Nested dictionary attack + uint8_t target_sector = + m->nested_target_key / (m->prng_type == MfClassicPrngTypeWeak ? 2 : 16); + dict_progress = (float)(target_sector) / (float)(m->sectors_total); + snprintf(draw_str, sizeof(draw_str), "%d/%d", target_sector, m->sectors_total); + } else if( + m->nested_phase == MfClassicNestedPhaseCalibrate || + m->nested_phase == MfClassicNestedPhaseRecalibrate || + m->nested_phase == MfClassicNestedPhaseCollectNtEnc) { + // Phase: Nonce collection + if(m->prng_type == MfClassicPrngTypeWeak) { + uint8_t target_sector = m->nested_target_key / 4; + dict_progress = (float)(target_sector) / (float)(m->sectors_total); + snprintf(draw_str, sizeof(draw_str), "%d/%d", target_sector, m->sectors_total); + } else { + uint16_t max_msb = UINT8_MAX + 1; + dict_progress = (float)(m->msb_count) / (float)(max_msb); + snprintf(draw_str, sizeof(draw_str), "%d/%d", m->msb_count, max_msb); + } + } else { + dict_progress = m->dict_keys_total == 0 ? + 0 : + (float)(m->dict_keys_current) / (float)(m->dict_keys_total); + if(m->dict_keys_current == 0) { + // Cause when people see 0 they think it's broken + snprintf(draw_str, sizeof(draw_str), "%d/%zu", 1, m->dict_keys_total); + } else { + snprintf( + draw_str, sizeof(draw_str), "%zu/%zu", m->dict_keys_current, m->dict_keys_total); + } + } + if(dict_progress > 1.0f) { + dict_progress = 1.0f; + } + 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->sectors_total * NFC_CLASSIC_KEYS_PER_SECTOR); + 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); +} + +static void dict_attack_draw_mf_ultralight_c(Canvas* canvas, DictAttackViewModel* m) { + char draw_str[32] = {}; + canvas_set_font(canvas, FontSecondary); + + canvas_draw_str_aligned(canvas, 0, 0, AlignLeft, AlignTop, furi_string_get_cstr(m->header)); + + snprintf(draw_str, sizeof(draw_str), "Trying keys"); + 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); + if(m->dict_keys_current == 0) { + snprintf(draw_str, sizeof(draw_str), "%d/%zu", 1, m->dict_keys_total); + } else { + snprintf(draw_str, sizeof(draw_str), "%zu/%zu", m->dict_keys_current, m->dict_keys_total); + } + if(dict_progress > 1.0f) { + dict_progress = 1.0f; + } + elements_progress_bar_with_text(canvas, 0, 20, 128, dict_progress, draw_str); + + canvas_set_font(canvas, FontSecondary); + snprintf(draw_str, sizeof(draw_str), "Key found: %s", m->key_found ? "Yes" : "No"); + canvas_draw_str_aligned(canvas, 0, 33, AlignLeft, AlignTop, draw_str); + + snprintf(draw_str, sizeof(draw_str), "Pages read: %d/%d", m->pages_read, m->pages_total); + canvas_draw_str_aligned(canvas, 0, 43, AlignLeft, AlignTop, draw_str); +} + static void dict_attack_draw_callback(Canvas* canvas, void* model) { DictAttackViewModel* m = model; if(!m->card_detected) { @@ -37,113 +180,11 @@ static void dict_attack_draw_callback(Canvas* canvas, void* model) { elements_multiline_text_aligned( canvas, 64, 23, AlignCenter, AlignTop, "Make sure the tag is\npositioned correctly."); } 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 MfClassicNestedPhaseDictAttackVerify: - case MfClassicNestedPhaseDictAttackResume: - furi_string_set(m->header, "Nested Dictionary"); - break; - case MfClassicNestedPhaseCalibrate: - case MfClassicNestedPhaseRecalibrate: - furi_string_set(m->header, "Calibration"); - break; - case MfClassicNestedPhaseCollectNtEnc: - furi_string_set(m->header, "Nonce Collection"); - break; - default: - break; + if(m->attack_type == DictAttackTypeMfClassic) { + dict_attack_draw_mf_classic(canvas, m); + } else if(m->attack_type == DictAttackTypeMfUltralightC) { + dict_attack_draw_mf_ultralight_c(canvas, m); } - - 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, 0, 0, AlignLeft, AlignTop, furi_string_get_cstr(m->header)); - if(m->nested_phase == MfClassicNestedPhaseCollectNtEnc) { - uint8_t nonce_sector = - m->nested_target_key / (m->prng_type == MfClassicPrngTypeWeak ? 4 : 2); - snprintf(draw_str, sizeof(draw_str), "Collecting from sector: %d", nonce_sector); - canvas_draw_str_aligned(canvas, 0, 10, AlignLeft, AlignTop, draw_str); - } else 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->current_sector); - } - canvas_draw_str_aligned(canvas, 0, 10, AlignLeft, AlignTop, draw_str); - float dict_progress = 0; - if(m->nested_phase == MfClassicNestedPhaseAnalyzePRNG || - m->nested_phase == MfClassicNestedPhaseDictAttack || - m->nested_phase == MfClassicNestedPhaseDictAttackVerify || - m->nested_phase == MfClassicNestedPhaseDictAttackResume) { - // Phase: Nested dictionary attack - uint8_t target_sector = - m->nested_target_key / (m->prng_type == MfClassicPrngTypeWeak ? 2 : 16); - dict_progress = (float)(target_sector) / (float)(m->sectors_total); - snprintf(draw_str, sizeof(draw_str), "%d/%d", target_sector, m->sectors_total); - } else if( - m->nested_phase == MfClassicNestedPhaseCalibrate || - m->nested_phase == MfClassicNestedPhaseRecalibrate || - m->nested_phase == MfClassicNestedPhaseCollectNtEnc) { - // Phase: Nonce collection - if(m->prng_type == MfClassicPrngTypeWeak) { - uint8_t target_sector = m->nested_target_key / 4; - dict_progress = (float)(target_sector) / (float)(m->sectors_total); - snprintf(draw_str, sizeof(draw_str), "%d/%d", target_sector, m->sectors_total); - } else { - uint16_t max_msb = UINT8_MAX + 1; - dict_progress = (float)(m->msb_count) / (float)(max_msb); - snprintf(draw_str, sizeof(draw_str), "%d/%d", m->msb_count, max_msb); - } - } else { - dict_progress = m->dict_keys_total == 0 ? - 0 : - (float)(m->dict_keys_current) / (float)(m->dict_keys_total); - if(m->dict_keys_current == 0) { - // Cause when people see 0 they think it's broken - snprintf(draw_str, sizeof(draw_str), "%d/%zu", 1, m->dict_keys_total); - } else { - snprintf( - draw_str, - sizeof(draw_str), - "%zu/%zu", - m->dict_keys_current, - m->dict_keys_total); - } - } - if(dict_progress > 1.0f) { - dict_progress = 1.0f; - } - 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->sectors_total * NFC_CLASSIC_KEYS_PER_SECTOR); - 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"); } @@ -195,18 +236,28 @@ void dict_attack_reset(DictAttack* instance) { instance->view, DictAttackViewModel * model, { + model->attack_type = DictAttackTypeMfClassic; + + // MIFARE Classic fields model->sectors_total = 0; model->sectors_read = 0; model->current_sector = 0; model->keys_found = 0; - 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; model->nested_target_key = 0; model->msb_count = 0; + + // Ultralight C fields + model->pages_total = 0; + model->pages_read = 0; + model->key_found = false; + + // Common fields + model->dict_keys_total = 0; + model->dict_keys_current = 0; furi_string_reset(model->header); }, false); @@ -355,3 +406,31 @@ void dict_attack_set_msb_count(DictAttack* instance, uint16_t msb_count) { with_view_model( instance->view, DictAttackViewModel * model, { model->msb_count = msb_count; }, true); } + +void dict_attack_set_type(DictAttack* instance, DictAttackType type) { + furi_assert(instance); + + with_view_model( + instance->view, DictAttackViewModel * model, { model->attack_type = type; }, true); +} + +void dict_attack_set_pages_total(DictAttack* instance, uint8_t pages_total) { + furi_assert(instance); + + with_view_model( + instance->view, DictAttackViewModel * model, { model->pages_total = pages_total; }, true); +} + +void dict_attack_set_pages_read(DictAttack* instance, uint8_t pages_read) { + furi_assert(instance); + + with_view_model( + instance->view, DictAttackViewModel * model, { model->pages_read = pages_read; }, true); +} + +void dict_attack_set_key_found(DictAttack* instance, bool key_found) { + furi_assert(instance); + + with_view_model( + instance->view, DictAttackViewModel * model, { model->key_found = key_found; }, true); +} diff --git a/applications/main/nfc/views/dict_attack.h b/applications/main/nfc/views/dict_attack.h index b6c6fdbdc..70709f86e 100644 --- a/applications/main/nfc/views/dict_attack.h +++ b/applications/main/nfc/views/dict_attack.h @@ -8,6 +8,11 @@ extern "C" { #endif +typedef enum { + DictAttackTypeMfClassic, + DictAttackTypeMfUltralightC, +} DictAttackType; + typedef struct DictAttack DictAttack; typedef enum { @@ -56,6 +61,14 @@ void dict_attack_set_nested_target_key(DictAttack* instance, uint16_t target_key void dict_attack_set_msb_count(DictAttack* instance, uint16_t msb_count); +void dict_attack_set_type(DictAttack* instance, DictAttackType type); + +void dict_attack_set_pages_total(DictAttack* instance, uint8_t pages_total); + +void dict_attack_set_pages_read(DictAttack* instance, uint8_t pages_read); + +void dict_attack_set_key_found(DictAttack* instance, bool key_found); + #ifdef __cplusplus } #endif diff --git a/lib/nfc/protocols/mf_ultralight/mf_ultralight.h b/lib/nfc/protocols/mf_ultralight/mf_ultralight.h index 191deeda6..caf25ceee 100644 --- a/lib/nfc/protocols/mf_ultralight/mf_ultralight.h +++ b/lib/nfc/protocols/mf_ultralight/mf_ultralight.h @@ -38,7 +38,6 @@ extern "C" { #define MF_ULTRALIGHT_TEARING_FLAG_NUM (3) #define MF_ULTRALIGHT_AUTH_PASSWORD_SIZE (4) #define MF_ULTRALIGHT_AUTH_PACK_SIZE (2) -#define MF_ULTRALIGHT_DEFAULT_PASSWORD (0xffffffffUL) #define MF_ULTRALIGHT_C_AUTH_RESPONSE_SIZE (9) #define MF_ULTRALIGHT_C_AUTH_DES_KEY_SIZE (16) @@ -48,11 +47,6 @@ extern "C" { #define MF_ULTRALIGHT_C_AUTH_RND_A_BLOCK_OFFSET (0) #define MF_ULTRALIGHT_C_AUTH_RND_B_BLOCK_OFFSET (8) #define MF_ULTRALIGHT_C_ENCRYPTED_PACK_SIZE (MF_ULTRALIGHT_C_AUTH_DATA_SIZE + 1) -#define MF_ULTRALIGHT_C_DEFAULT_KEY \ - (uint8_t[]) { \ - 0x49, 0x45, 0x4D, 0x4B, 0x41, 0x45, 0x52, 0x42, 0x21, 0x4E, 0x41, 0x43, 0x55, 0x4F, 0x59, \ - 0x46 \ - } typedef enum { MfUltralightErrorNone, diff --git a/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller.c b/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller.c index 6c6e230f0..cf942172b 100644 --- a/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller.c +++ b/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller.c @@ -251,7 +251,7 @@ static NfcCommand mf_ultralight_poller_handler_read_version(MfUltralightPoller* instance->data->type = mf_ultralight_get_type_by_version(&instance->data->version); instance->state = MfUltralightPollerStateGetFeatureSet; } else { - FURI_LOG_D(TAG, "Didn't response. Check Ultralight C"); + FURI_LOG_D(TAG, "Didn't respond. Check Ultralight C"); iso14443_3a_poller_halt(instance->iso14443_3a_poller); instance->state = MfUltralightPollerStateDetectMfulC; } @@ -266,7 +266,7 @@ static NfcCommand mf_ultralight_poller_handler_check_ultralight_c(MfUltralightPo instance->data->type = MfUltralightTypeMfulC; instance->state = MfUltralightPollerStateGetFeatureSet; } else { - FURI_LOG_D(TAG, "Didn't response. Check NTAG 203"); + FURI_LOG_D(TAG, "Didn't respond. Check NTAG 203"); instance->state = MfUltralightPollerStateDetectNtag203; } iso14443_3a_poller_halt(instance->iso14443_3a_poller); @@ -445,36 +445,105 @@ static NfcCommand mf_ultralight_poller_handler_auth(MfUltralightPoller* instance static NfcCommand mf_ultralight_poller_handler_auth_ultralight_c(MfUltralightPoller* instance) { NfcCommand command = NfcCommandContinue; FURI_LOG_D(TAG, "MfulC auth"); + if(mf_ultralight_support_feature( + instance->feature_set, MfUltralightFeatureSupportAuthenticate)) { + instance->mfu_event.type = MfUltralightPollerEventTypeAuthRequest; - do { - if(mf_ultralight_support_feature( - instance->feature_set, MfUltralightFeatureSupportAuthenticate)) { - instance->mfu_event.type = MfUltralightPollerEventTypeAuthRequest; - - command = instance->callback(instance->general_event, instance->context); - if(!instance->mfu_event.data->auth_context.skip_auth) { - FURI_LOG_D(TAG, "Trying to authenticate with 3des key"); - instance->auth_context.tdes_key = instance->mfu_event.data->auth_context.tdes_key; - instance->error = - mf_ultralight_poller_auth_tdes(instance, &instance->auth_context); - - if(instance->error == MfUltralightErrorNone && - instance->auth_context.auth_success) { - FURI_LOG_D(TAG, "Auth success"); + command = instance->callback(instance->general_event, instance->context); + if(!instance->mfu_event.data->auth_context.skip_auth) { + FURI_LOG_D(TAG, "Trying to authenticate with 3des key"); + // Only use the key if it was actually provided + if(instance->mfu_event.data->key_request_data.key_provided) { + instance->auth_context.tdes_key = instance->mfu_event.data->key_request_data.key; + } else if(instance->mode == MfUltralightPollerModeDictAttack) { + // TODO: Can logic be rearranged to request this key before reaching mf_ultralight_poller_handler_auth_ultralight_c in poller? + FURI_LOG_D(TAG, "No initial key provided, requesting key from dictionary"); + // Trigger dictionary key request + instance->mfu_event.type = MfUltralightPollerEventTypeRequestKey; + command = instance->callback(instance->general_event, instance->context); + if(!instance->mfu_event.data->key_request_data.key_provided) { + instance->state = MfUltralightPollerStateReadPages; + return command; } else { - FURI_LOG_D(TAG, "Auth failed"); - iso14443_3a_poller_halt(instance->iso14443_3a_poller); + instance->auth_context.tdes_key = + instance->mfu_event.data->key_request_data.key; } } else { - // We assume here that it is card read without explicitly provided key - // So we try to auth with default one - instance->state = MfUltralightPollerStateTryDefaultMfulCKey; - break; + FURI_LOG_D(TAG, "No key provided, skipping auth"); + instance->state = MfUltralightPollerStateReadPages; + return command; + } + instance->auth_context.auth_success = false; + // For debugging + FURI_LOG_D( + "TAG", + "Key data: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", + instance->auth_context.tdes_key.data[0], + instance->auth_context.tdes_key.data[1], + instance->auth_context.tdes_key.data[2], + instance->auth_context.tdes_key.data[3], + instance->auth_context.tdes_key.data[4], + instance->auth_context.tdes_key.data[5], + instance->auth_context.tdes_key.data[6], + instance->auth_context.tdes_key.data[7], + instance->auth_context.tdes_key.data[8], + instance->auth_context.tdes_key.data[9], + instance->auth_context.tdes_key.data[10], + instance->auth_context.tdes_key.data[11], + instance->auth_context.tdes_key.data[12], + instance->auth_context.tdes_key.data[13], + instance->auth_context.tdes_key.data[14], + instance->auth_context.tdes_key.data[15]); + do { + uint8_t output[MF_ULTRALIGHT_C_AUTH_DATA_SIZE]; + uint8_t RndA[MF_ULTRALIGHT_C_AUTH_RND_BLOCK_SIZE] = {0}; + furi_hal_random_fill_buf(RndA, sizeof(RndA)); + instance->error = mf_ultralight_poller_authenticate_start(instance, RndA, output); + if(instance->error != MfUltralightErrorNone) break; + + uint8_t decoded_shifted_RndA[MF_ULTRALIGHT_C_AUTH_RND_BLOCK_SIZE] = {0}; + const uint8_t* RndB = output + MF_ULTRALIGHT_C_AUTH_RND_B_BLOCK_OFFSET; + instance->error = mf_ultralight_poller_authenticate_end( + instance, RndB, output, decoded_shifted_RndA); + if(instance->error != MfUltralightErrorNone) break; + + mf_ultralight_3des_shift_data(RndA); + instance->auth_context.auth_success = + (memcmp(RndA, decoded_shifted_RndA, sizeof(decoded_shifted_RndA)) == 0); + if(instance->auth_context.auth_success) { + FURI_LOG_E(TAG, "Auth success"); + if(instance->mode == MfUltralightPollerModeDictAttack) { + memcpy( + &instance->data->page[44], + instance->auth_context.tdes_key.data, + MF_ULTRALIGHT_C_AUTH_DES_KEY_SIZE); + // Continue to read pages after successful authentication + instance->state = MfUltralightPollerStateReadPages; + } + } + } while(false); + if(instance->error != MfUltralightErrorNone || !instance->auth_context.auth_success) { + FURI_LOG_E(TAG, "Auth failed"); + iso14443_3a_poller_halt(instance->iso14443_3a_poller); + if(instance->mode == MfUltralightPollerModeDictAttack) { + // Not needed? We already do a callback earlier? + instance->mfu_event.type = MfUltralightPollerEventTypeRequestKey; + command = instance->callback(instance->general_event, instance->context); + if(!instance->mfu_event.data->key_request_data.key_provided) { + instance->state = MfUltralightPollerStateReadPages; + } else { + instance->auth_context.tdes_key = + instance->mfu_event.data->key_request_data.key; + instance->state = MfUltralightPollerStateAuthMfulC; + } + } } } + } + // Regression review + if(instance->mode != MfUltralightPollerModeDictAttack) { instance->state = MfUltralightPollerStateReadPages; - } while(false); - + } return command; } @@ -497,12 +566,16 @@ static NfcCommand mf_ultralight_poller_handler_read_pages(MfUltralightPoller* in instance->error = mf_ultralight_poller_read_page(instance, start_page, &data); } + // Regression review + const uint8_t read_cnt = instance->data->type == MfUltralightTypeMfulC ? 1 : 4; if(instance->error == MfUltralightErrorNone) { - if(start_page < instance->pages_total) { - FURI_LOG_D(TAG, "Read page %d success", start_page); - instance->data->page[start_page] = data.page[0]; - instance->pages_read++; - instance->data->pages_read = instance->pages_read; + for(size_t i = 0; i < read_cnt; i++) { + if(start_page + i < instance->pages_total) { + FURI_LOG_D(TAG, "Read page %d success", start_page + i); + instance->data->page[start_page + i] = data.page[i]; + instance->pages_read++; + instance->data->pages_read = instance->pages_read; + } } if(instance->pages_read == instance->pages_total) { @@ -570,40 +643,6 @@ static NfcCommand mf_ultralight_poller_handler_try_default_pass(MfUltralightPoll return NfcCommandContinue; } -static NfcCommand - mf_ultralight_poller_handler_try_default_ultralight_c_key(MfUltralightPoller* instance) { - do { - if(!mf_ultralight_support_feature( - instance->feature_set, MfUltralightFeatureSupportAuthenticate)) { - break; - } - - if(instance->auth_context.auth_success) { - break; - } - - FURI_LOG_D(TAG, "Trying authentication with default 3DES key"); - - memcpy( - &instance->auth_context.tdes_key.data, - MF_ULTRALIGHT_C_DEFAULT_KEY, - MF_ULTRALIGHT_C_AUTH_DES_KEY_SIZE); - - instance->error = mf_ultralight_poller_auth_tdes(instance, &instance->auth_context); - - if(instance->error == MfUltralightErrorNone && instance->auth_context.auth_success) { - FURI_LOG_D(TAG, "Default 3DES key detected"); - } else { - FURI_LOG_D(TAG, "Authentication attempt with default 3DES key failed"); - iso14443_3a_poller_halt(instance->iso14443_3a_poller); - } - - } while(false); - - instance->state = MfUltralightPollerStateReadPages; - return NfcCommandContinue; -} - static NfcCommand mf_ultralight_poller_handler_check_mfuc_auth_status(MfUltralightPoller* instance) { instance->state = MfUltralightPollerStateReadSuccess; @@ -768,8 +807,6 @@ static const MfUltralightPollerReadHandler mf_ultralight_poller_handler_read_tearing_flags, [MfUltralightPollerStateAuth] = mf_ultralight_poller_handler_auth, [MfUltralightPollerStateTryDefaultPass] = mf_ultralight_poller_handler_try_default_pass, - [MfUltralightPollerStateTryDefaultMfulCKey] = - mf_ultralight_poller_handler_try_default_ultralight_c_key, [MfUltralightPollerStateCheckMfulCAuthStatus] = mf_ultralight_poller_handler_check_mfuc_auth_status, [MfUltralightPollerStateAuthMfulC] = mf_ultralight_poller_handler_auth_ultralight_c, @@ -781,7 +818,6 @@ static const MfUltralightPollerReadHandler [MfUltralightPollerStateWritePages] = mf_ultralight_poller_handler_write_pages, [MfUltralightPollerStateWriteFail] = mf_ultralight_poller_handler_write_fail, [MfUltralightPollerStateWriteSuccess] = mf_ultralight_poller_handler_write_success, - }; static NfcCommand mf_ultralight_poller_run(NfcGenericEvent event, void* context) { diff --git a/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller.h b/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller.h index 661c6db74..2552abeb5 100644 --- a/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller.h +++ b/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller.h @@ -27,6 +27,7 @@ typedef enum { MfUltralightPollerEventTypeCardLocked, /**< Presented card is locked by password, AUTH0 or lock bytes. */ MfUltralightPollerEventTypeWriteSuccess, /**< Poller wrote card successfully. */ MfUltralightPollerEventTypeWriteFail, /**< Poller failed to write card. */ + MfUltralightPollerEventTypeRequestKey, /**< Poller requests key for dict attack. */ } MfUltralightPollerEventType; /** @@ -35,6 +36,7 @@ typedef enum { typedef enum { MfUltralightPollerModeRead, /**< Poller will only read card. It's a default mode. */ MfUltralightPollerModeWrite, /**< Poller will write already saved card to another presented card. */ + MfUltralightPollerModeDictAttack, /**< Poller will perform dictionary attack against card. */ } MfUltralightPollerMode; /** @@ -42,20 +44,29 @@ typedef enum { */ typedef struct { MfUltralightAuthPassword password; /**< Password to be used for authentication. */ - MfUltralightC3DesAuthKey tdes_key; - MfUltralightAuthPack pack; /**< Pack received on successfull authentication. */ + MfUltralightC3DesAuthKey tdes_key; /**< 3DES key to be used for authentication. */ + MfUltralightAuthPack pack; /**< Pack received on successful authentication. */ bool auth_success; /**< Set to true if authentication succeeded, false otherwise. */ bool skip_auth; /**< Set to true if authentication should be skipped, false otherwise. */ } MfUltralightPollerAuthContext; +/** + * @brief MfUltralight poller key request data. + */ +typedef struct { + MfUltralightC3DesAuthKey key; /**< Key to try. */ + bool key_provided; /**< Set to true if key was provided, false to stop attack. */ +} MfUltralightPollerKeyRequestData; + /** * @brief MfUltralight poller event data. */ typedef union { MfUltralightPollerAuthContext auth_context; /**< Authentication context. */ MfUltralightError error; /**< Error code indicating reading fail reason. */ - const MfUltralightData* write_data; - MfUltralightPollerMode poller_mode; + const MfUltralightData* write_data; /**< Data to be written to card. */ + MfUltralightPollerMode poller_mode; /**< Mode to operate in. */ + MfUltralightPollerKeyRequestData key_request_data; /**< Key request data. */ } MfUltralightPollerEventData; /** @@ -64,7 +75,7 @@ typedef union { * Upon emission of an event, an instance of this struct will be passed to the callback. */ typedef struct { - MfUltralightPollerEventType type; /**< Type of emmitted event. */ + MfUltralightPollerEventType type; /**< Type of emitted event. */ MfUltralightPollerEventData* data; /**< Pointer to event specific data. */ } MfUltralightPollerEvent; @@ -81,19 +92,6 @@ MfUltralightError mf_ultralight_poller_auth_pwd( MfUltralightPoller* instance, MfUltralightPollerAuthContext* data); -/** - * @brief Perform 3DES authentication with key. - * - * Must ONLY be used inside the callback function. - * - * @param[in, out] instance pointer to the instance to be used in the transaction. - * @param[in, out] data pointer to the authentication context. - * @return MfUltralightErrorNone on success, an error code on failure. - */ -MfUltralightError mf_ultralight_poller_auth_tdes( - MfUltralightPoller* instance, - MfUltralightPollerAuthContext* data); - /** * @brief Start authentication procedure. * diff --git a/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller_i.c b/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller_i.c index fdafaf37d..82e647da8 100644 --- a/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller_i.c +++ b/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller_i.c @@ -1,7 +1,6 @@ #include "mf_ultralight_poller_i.h" #include -#include #define TAG "MfUltralightPoller" @@ -63,38 +62,6 @@ MfUltralightError mf_ultralight_poller_auth_pwd( return ret; } -MfUltralightError mf_ultralight_poller_auth_tdes( - MfUltralightPoller* instance, - MfUltralightPollerAuthContext* data) { - furi_check(instance); - furi_check(data); - - MfUltralightError ret = MfUltralightErrorNone; - - uint8_t output[MF_ULTRALIGHT_C_AUTH_DATA_SIZE]; - uint8_t RndA[MF_ULTRALIGHT_C_AUTH_RND_BLOCK_SIZE] = {0}; - furi_hal_random_fill_buf(RndA, sizeof(RndA)); - - ret = mf_ultralight_poller_authenticate_start(instance, RndA, output); - - if(ret != MfUltralightErrorNone) { - return ret; - } - - uint8_t decoded_shifted_RndA[MF_ULTRALIGHT_C_AUTH_RND_BLOCK_SIZE] = {0}; - const uint8_t* RndB = output + MF_ULTRALIGHT_C_AUTH_RND_B_BLOCK_OFFSET; - ret = mf_ultralight_poller_authenticate_end(instance, RndB, output, decoded_shifted_RndA); - - if(ret != MfUltralightErrorNone) { - return ret; - } - - mf_ultralight_3des_shift_data(RndA); - data->auth_success = (memcmp(RndA, decoded_shifted_RndA, sizeof(decoded_shifted_RndA)) == 0); - - return ret; -} - static MfUltralightError mf_ultralight_poller_send_authenticate_cmd( MfUltralightPoller* instance, const uint8_t* cmd, diff --git a/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller_i.h b/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller_i.h index 7db9a77d9..b35c49aea 100644 --- a/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller_i.h +++ b/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller_i.h @@ -11,6 +11,8 @@ extern "C" { #define MF_ULTRALIGHT_POLLER_STANDARD_FWT_FC (60000) #define MF_ULTRALIGHT_MAX_BUFF_SIZE (64) +#define MF_ULTRALIGHT_DEFAULT_PASSWORD (0xffffffffUL) + #define MF_ULTRALIGHT_IS_NTAG_I2C(type) \ (((type) == MfUltralightTypeNTAGI2C1K) || ((type) == MfUltralightTypeNTAGI2C2K) || \ ((type) == MfUltralightTypeNTAGI2CPlus1K) || ((type) == MfUltralightTypeNTAGI2CPlus2K)) @@ -59,7 +61,6 @@ typedef enum { MfUltralightPollerStateAuthMfulC, MfUltralightPollerStateReadPages, MfUltralightPollerStateTryDefaultPass, - MfUltralightPollerStateTryDefaultMfulCKey, MfUltralightPollerStateCheckMfulCAuthStatus, MfUltralightPollerStateReadFailed, MfUltralightPollerStateReadSuccess, diff --git a/lib/toolbox/keys_dict.c b/lib/toolbox/keys_dict.c index 602653e8f..c26e9c1e7 100644 --- a/lib/toolbox/keys_dict.c +++ b/lib/toolbox/keys_dict.c @@ -134,22 +134,21 @@ static void keys_dict_int_to_str(KeysDict* instance, const uint8_t* key_int, Fur furi_string_cat_printf(key_str, "%02X", key_int[i]); } -static void keys_dict_str_to_int(KeysDict* instance, FuriString* key_str, uint64_t* key_int) { +static void keys_dict_str_to_int(KeysDict* instance, FuriString* key_str, uint8_t* key_out) { furi_assert(instance); furi_assert(key_str); - furi_assert(key_int); + furi_assert(key_out); uint8_t key_byte_tmp; char h, l; - *key_int = 0ULL; - + // Process two hex characters at a time to create each byte for(size_t i = 0; i < instance->key_size_symbols - 1; i += 2) { h = furi_string_get_char(key_str, i); l = furi_string_get_char(key_str, i + 1); args_char_to_hex(h, l, &key_byte_tmp); - *key_int |= (uint64_t)key_byte_tmp << (8 * (instance->key_size - 1 - i / 2)); + key_out[i / 2] = key_byte_tmp; } } @@ -193,15 +192,7 @@ bool keys_dict_get_next_key(KeysDict* instance, uint8_t* key, size_t key_size) { bool key_read = keys_dict_get_next_key_str(instance, temp_key); if(key_read) { - size_t tmp_len = key_size; - uint64_t key_int = 0; - - keys_dict_str_to_int(instance, temp_key, &key_int); - - while(tmp_len--) { - key[tmp_len] = (uint8_t)key_int; - key_int >>= 8; - } + keys_dict_str_to_int(instance, temp_key, key); } furi_string_free(temp_key); diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index 7696a05bc..2f1b0646e 100755 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -2765,7 +2765,6 @@ Function,+,mf_ultralight_is_equal,_Bool,"const MfUltralightData*, const MfUltral Function,+,mf_ultralight_is_page_pwd_or_pack,_Bool,"MfUltralightType, uint16_t" Function,+,mf_ultralight_load,_Bool,"MfUltralightData*, FlipperFormat*, uint32_t" Function,+,mf_ultralight_poller_auth_pwd,MfUltralightError,"MfUltralightPoller*, MfUltralightPollerAuthContext*" -Function,+,mf_ultralight_poller_auth_tdes,MfUltralightError,"MfUltralightPoller*, MfUltralightPollerAuthContext*" Function,+,mf_ultralight_poller_authenticate_end,MfUltralightError,"MfUltralightPoller*, const uint8_t*, const uint8_t*, uint8_t*" Function,+,mf_ultralight_poller_authenticate_start,MfUltralightError,"MfUltralightPoller*, const uint8_t*, uint8_t*" Function,+,mf_ultralight_poller_read_counter,MfUltralightError,"MfUltralightPoller*, uint8_t, MfUltralightCounter*"