From 6b47bc1af42f2925606f5e201cb8db65eef29f25 Mon Sep 17 00:00:00 2001 From: Yukai Li Date: Mon, 28 Nov 2022 11:16:22 -0700 Subject: [PATCH] Nfc: NTAG password auto capture (and other password-related changes) (#1843) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * nfc: MFUL minor cleanup * nfc: Add mechanism to pass event data * nfc: Add NTAG authentication event to emulation * nfc: Rename enum member to align with existing convention * nfc: Add function to determine whether MFUL is fully captured * nfc: Fix emulation of incompletely-read password-protected MFUL * nfc: Add reader password capture scene * nfc: Set default MFUL password input to 0xFFFFFFFF * nfc: Fix MFUL auth counter loading * nfc: Be explicit about using manual auth method when using auto unlock * nfc: Fill in MFUL has_auth when loading file * nfc: Fix MFUL auth success usage, remove unused variable * nfc: Display PWD and PACK in MFUL info if available * nfc: Remove unnecessary include * nfc: Add unlock options to loaded MFUL menu * nfc: Move set default MFUL password. This way it can be edited if needed instead of reentered * nfc: Fix unlock menu not maintaining selection index * nfc: Move captured MFUL auth data from worker to device data * nfc: Attempt to authenticate with default PWD when possible when reading NTAG * nfc: Don't try to auth NTAG on read if we already authed * nfc: Add title for all pages read but failed auth for NTAG auth * nfc: Add faster auth callback patch * lib: Remove scons submodule from index * nfc: Revise MFUL unlock UI flow * nfc: Disallow MFUL unlock with reader if card not read yet. Trying to read first results in either needing to make a new scene or badly jury rigging other scenes, so let's just not do that * f7: Bump API symbols * Format code Co-authored-by: gornekich Co-authored-by: あく --- .../main/nfc/scenes/nfc_scene_config.h | 1 + .../nfc/scenes/nfc_scene_mf_ultralight_menu.c | 4 +- .../nfc_scene_mf_ultralight_read_auth.c | 25 +++++-- ...nfc_scene_mf_ultralight_read_auth_result.c | 32 +++++++-- .../nfc_scene_mf_ultralight_unlock_auto.c | 64 +++++++++++++++++ .../nfc_scene_mf_ultralight_unlock_menu.c | 31 +++++--- .../nfc_scene_mf_ultralight_unlock_warn.c | 71 ++++++++++++++++--- .../main/nfc/scenes/nfc_scene_nfc_data_info.c | 14 ++++ applications/main/nfc/scenes/nfc_scene_read.c | 2 + .../main/nfc/scenes/nfc_scene_saved_menu.c | 23 ++++++ firmware/targets/f7/api_symbols.csv | 3 +- lib/nfc/nfc_device.c | 3 + lib/nfc/nfc_device.h | 1 + lib/nfc/nfc_worker.c | 18 ++++- lib/nfc/nfc_worker.h | 4 +- lib/nfc/protocols/mifare_ultralight.c | 57 ++++++++++++++- lib/nfc/protocols/mifare_ultralight.h | 15 +++- 17 files changed, 326 insertions(+), 42 deletions(-) create mode 100644 applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_auto.c diff --git a/applications/main/nfc/scenes/nfc_scene_config.h b/applications/main/nfc/scenes/nfc_scene_config.h index 49c8412c..ce51d000 100644 --- a/applications/main/nfc/scenes/nfc_scene_config.h +++ b/applications/main/nfc/scenes/nfc_scene_config.h @@ -21,6 +21,7 @@ ADD_SCENE(nfc, mf_ultralight_emulate, MfUltralightEmulate) ADD_SCENE(nfc, mf_ultralight_read_auth, MfUltralightReadAuth) ADD_SCENE(nfc, mf_ultralight_read_auth_result, MfUltralightReadAuthResult) ADD_SCENE(nfc, mf_ultralight_key_input, MfUltralightKeyInput) +ADD_SCENE(nfc, mf_ultralight_unlock_auto, MfUltralightUnlockAuto) ADD_SCENE(nfc, mf_ultralight_unlock_menu, MfUltralightUnlockMenu) ADD_SCENE(nfc, mf_ultralight_unlock_warn, MfUltralightUnlockWarn) ADD_SCENE(nfc, mf_desfire_read_success, MfDesfireReadSuccess) diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_menu.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_menu.c index ab4d37b0..c511e9dc 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_menu.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_menu.c @@ -19,10 +19,10 @@ void nfc_scene_mf_ultralight_menu_on_enter(void* context) { Submenu* submenu = nfc->submenu; MfUltralightData* data = &nfc->dev->dev_data.mf_ul_data; - if(data->data_read != data->data_size) { + if(!mf_ul_is_full_capture(data)) { submenu_add_item( submenu, - "Unlock With Password", + "Unlock", SubmenuIndexUnlock, nfc_scene_mf_ultralight_menu_submenu_callback, nfc); diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_auth.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_auth.c index 5dbb0c18..2ab5e3f3 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_auth.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_auth.c @@ -24,25 +24,29 @@ void nfc_scene_mf_ultralight_read_auth_set_state(Nfc* nfc, NfcSceneMfUlReadState if(curr_state != state) { if(state == NfcSceneMfUlReadStateDetecting) { popup_reset(nfc->popup); - popup_set_text( - nfc->popup, "Apply card to\nFlipper's back", 97, 24, AlignCenter, AlignTop); + popup_set_text(nfc->popup, "Apply the\ntarget card", 97, 24, AlignCenter, AlignTop); popup_set_icon(nfc->popup, 0, 8, &I_NFC_manual_60x50); + nfc_blink_read_start(nfc); } else if(state == NfcSceneMfUlReadStateReading) { popup_reset(nfc->popup); popup_set_header( nfc->popup, "Reading card\nDon't move...", 85, 24, AlignCenter, AlignTop); popup_set_icon(nfc->popup, 12, 23, &A_Loading_24); + nfc_blink_detect_start(nfc); } else if(state == NfcSceneMfUlReadStateNotSupportedCard) { popup_reset(nfc->popup); popup_set_header(nfc->popup, "Wrong type of card!", 64, 3, AlignCenter, AlignTop); popup_set_text( nfc->popup, - "Only MIFARE\nUltralight & NTAG\n are supported", + "Only MIFARE\nUltralight & NTAG\nare supported", 4, 22, AlignLeft, AlignTop); popup_set_icon(nfc->popup, 73, 20, &I_DolphinCommon_56x48); + nfc_blink_stop(nfc); + notification_message(nfc->notifications, &sequence_error); + notification_message(nfc->notifications, &sequence_set_red_255); } scene_manager_set_scene_state(nfc->scene_manager, NfcSceneMfUltralightReadAuth, state); } @@ -62,8 +66,6 @@ void nfc_scene_mf_ultralight_read_auth_on_enter(void* context) { &nfc->dev->dev_data, nfc_scene_mf_ultralight_read_auth_worker_callback, nfc); - - nfc_blink_read_start(nfc); } bool nfc_scene_mf_ultralight_read_auth_on_event(void* context, SceneManagerEvent event) { @@ -86,8 +88,17 @@ bool nfc_scene_mf_ultralight_read_auth_on_event(void* context, SceneManagerEvent nfc, NfcSceneMfUlReadStateNotSupportedCard); } } else if(event.type == SceneManagerEventTypeBack) { - consumed = scene_manager_search_and_switch_to_previous_scene( - nfc->scene_manager, NfcSceneMfUltralightUnlockMenu); + MfUltralightData* mf_ul_data = &nfc->dev->dev_data.mf_ul_data; + NfcScene next_scene; + if(mf_ul_data->auth_method == MfUltralightAuthMethodManual) { + next_scene = NfcSceneMfUltralightKeyInput; + } else if(mf_ul_data->auth_method == MfUltralightAuthMethodAuto) { + next_scene = NfcSceneMfUltralightUnlockAuto; + } else { + next_scene = NfcSceneMfUltralightUnlockMenu; + } + consumed = + scene_manager_search_and_switch_to_previous_scene(nfc->scene_manager, next_scene); } return consumed; } diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_auth_result.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_auth_result.c index 178d0335..b125e999 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_auth_result.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_auth_result.c @@ -19,16 +19,20 @@ void nfc_scene_mf_ultralight_read_auth_result_on_enter(void* context) { MfUltralightData* mf_ul_data = &nfc->dev->dev_data.mf_ul_data; MfUltralightConfigPages* config_pages = mf_ultralight_get_config_pages(mf_ul_data); Widget* widget = nfc->widget; + const char* title; FuriString* temp_str; temp_str = furi_string_alloc(); if((mf_ul_data->data_read == mf_ul_data->data_size) && (mf_ul_data->data_read > 0)) { - widget_add_string_element( - widget, 64, 0, AlignCenter, AlignTop, FontPrimary, "All pages are unlocked!"); + if(mf_ul_data->auth_success) { + title = "All pages are unlocked!"; + } else { + title = "All unlocked but failed auth!"; + } } else { - widget_add_string_element( - widget, 64, 0, AlignCenter, AlignTop, FontPrimary, "Not all pages unlocked!"); + title = "Not all pages unlocked!"; } + widget_add_string_element(widget, 64, 0, AlignCenter, AlignTop, FontPrimary, title); furi_string_set(temp_str, "UID:"); for(size_t i = 0; i < nfc_data->uid_len; i++) { furi_string_cat_printf(temp_str, " %02X", nfc_data->uid[i]); @@ -65,6 +69,7 @@ void nfc_scene_mf_ultralight_read_auth_result_on_enter(void* context) { nfc); furi_string_free(temp_str); + notification_message(nfc->notifications, &sequence_set_green_255); view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); } @@ -81,8 +86,21 @@ bool nfc_scene_mf_ultralight_read_auth_result_on_event(void* context, SceneManag consumed = true; } } else if(event.type == SceneManagerEventTypeBack) { - consumed = scene_manager_search_and_switch_to_previous_scene( - nfc->scene_manager, NfcSceneMfUltralightUnlockMenu); + MfUltralightData* mf_ul_data = &nfc->dev->dev_data.mf_ul_data; + if(mf_ul_data->auth_method == MfUltralightAuthMethodManual || + mf_ul_data->auth_method == MfUltralightAuthMethodAuto) { + consumed = scene_manager_previous_scene(nfc->scene_manager); + } else { + NfcScene next_scene; + if((mf_ul_data->data_read == mf_ul_data->data_size) && (mf_ul_data->data_read > 0)) { + next_scene = NfcSceneMfUltralightMenu; + } else { + next_scene = NfcSceneMfUltralightUnlockMenu; + } + + consumed = + scene_manager_search_and_switch_to_previous_scene(nfc->scene_manager, next_scene); + } } return consumed; @@ -93,4 +111,6 @@ void nfc_scene_mf_ultralight_read_auth_result_on_exit(void* context) { // Clean views widget_reset(nfc->widget); + + notification_message_block(nfc->notifications, &sequence_reset_green); } diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_auto.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_auto.c new file mode 100644 index 00000000..c59fe3a7 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_auto.c @@ -0,0 +1,64 @@ +#include "../nfc_i.h" + +bool nfc_scene_mf_ultralight_unlock_auto_worker_callback(NfcWorkerEvent event, void* context) { + Nfc* nfc = context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, event); + return true; +} + +void nfc_scene_mf_ultralight_unlock_auto_on_enter(void* context) { + Nfc* nfc = context; + + // Setup view + widget_add_string_multiline_element( + nfc->widget, + 54, + 30, + AlignLeft, + AlignCenter, + FontPrimary, + "Touch the\nreader to get\npassword..."); + widget_add_icon_element(nfc->widget, 0, 15, &I_Modern_reader_18x34); + widget_add_icon_element(nfc->widget, 20, 12, &I_Move_flipper_26x39); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); + + // Start worker + nfc_worker_start( + nfc->worker, + NfcWorkerStateMfUltralightEmulate, + &nfc->dev->dev_data, + nfc_scene_mf_ultralight_unlock_auto_worker_callback, + nfc); + + nfc_blink_read_start(nfc); +} + +bool nfc_scene_mf_ultralight_unlock_auto_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if((event.event == NfcWorkerEventMfUltralightPwdAuth)) { + MfUltralightAuth* auth = &nfc->dev->dev_data.mf_ul_auth; + memcpy(nfc->byte_input_store, auth->pwd.raw, sizeof(auth->pwd.raw)); + nfc->dev->dev_data.mf_ul_data.auth_method = MfUltralightAuthMethodAuto; + nfc_worker_stop(nfc->worker); + notification_message(nfc->notifications, &sequence_success); + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockWarn); + consumed = true; + } + } + return consumed; +} + +void nfc_scene_mf_ultralight_unlock_auto_on_exit(void* context) { + Nfc* nfc = context; + + // Stop worker + nfc_worker_stop(nfc->worker); + // Clear view + widget_reset(nfc->widget); + + nfc_blink_stop(nfc); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_menu.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_menu.c index 648aa31d..484629b0 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_menu.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_menu.c @@ -1,9 +1,10 @@ #include "../nfc_i.h" enum SubmenuIndex { - SubmenuIndexMfUlUnlockMenuManual, + SubmenuIndexMfUlUnlockMenuAuto, SubmenuIndexMfUlUnlockMenuAmeebo, SubmenuIndexMfUlUnlockMenuXiaomi, + SubmenuIndexMfUlUnlockMenuManual, }; void nfc_scene_mf_ultralight_unlock_menu_submenu_callback(void* context, uint32_t index) { @@ -18,12 +19,14 @@ void nfc_scene_mf_ultralight_unlock_menu_on_enter(void* context) { uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfUltralightUnlockMenu); - submenu_add_item( - submenu, - "Enter Password Manually", - SubmenuIndexMfUlUnlockMenuManual, - nfc_scene_mf_ultralight_unlock_menu_submenu_callback, - nfc); + if(nfc->dev->dev_data.protocol == NfcDeviceProtocolMifareUl) { + submenu_add_item( + submenu, + "Unlock With Reader", + SubmenuIndexMfUlUnlockMenuAuto, + nfc_scene_mf_ultralight_unlock_menu_submenu_callback, + nfc); + } submenu_add_item( submenu, "Auth As Ameebo", @@ -32,10 +35,16 @@ void nfc_scene_mf_ultralight_unlock_menu_on_enter(void* context) { nfc); submenu_add_item( submenu, - "Auth As Xiaomi", + "Auth As Xiaomi Air Purifier", SubmenuIndexMfUlUnlockMenuXiaomi, nfc_scene_mf_ultralight_unlock_menu_submenu_callback, nfc); + submenu_add_item( + submenu, + "Enter Password Manually", + SubmenuIndexMfUlUnlockMenuManual, + nfc_scene_mf_ultralight_unlock_menu_submenu_callback, + nfc); submenu_set_selected_item(submenu, state); view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); } @@ -57,8 +66,12 @@ bool nfc_scene_mf_ultralight_unlock_menu_on_event(void* context, SceneManagerEve nfc->dev->dev_data.mf_ul_data.auth_method = MfUltralightAuthMethodXiaomi; scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockWarn); consumed = true; + } else if(event.event == SubmenuIndexMfUlUnlockMenuAuto) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockAuto); + consumed = true; } - scene_manager_set_scene_state(nfc->scene_manager, NfcSceneExtraActions, event.event); + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneMfUltralightUnlockMenu, event.event); } return consumed; } diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_warn.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_warn.c index 514cd4e9..16efae9d 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_warn.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_warn.c @@ -10,15 +10,43 @@ void nfc_scene_mf_ultralight_unlock_warn_dialog_callback(DialogExResult result, void nfc_scene_mf_ultralight_unlock_warn_on_enter(void* context) { Nfc* nfc = context; DialogEx* dialog_ex = nfc->dialog_ex; + MfUltralightAuthMethod auth_method = nfc->dev->dev_data.mf_ul_data.auth_method; dialog_ex_set_context(dialog_ex, nfc); dialog_ex_set_result_callback(dialog_ex, nfc_scene_mf_ultralight_unlock_warn_dialog_callback); - dialog_ex_set_header(dialog_ex, "Risky function!", 64, 4, AlignCenter, AlignTop); - dialog_ex_set_text( - dialog_ex, "Wrong password\ncan block your\ncard.", 4, 18, AlignLeft, AlignTop); - dialog_ex_set_icon(dialog_ex, 73, 20, &I_DolphinCommon_56x48); - dialog_ex_set_center_button_text(dialog_ex, "OK"); + if(auth_method == MfUltralightAuthMethodManual || auth_method == MfUltralightAuthMethodAuto) { + // Build dialog text + MfUltralightAuth* auth = &nfc->dev->dev_data.mf_ul_auth; + FuriString* password_str = + furi_string_alloc_set_str("Try to unlock the card with\npassword: "); + for(size_t i = 0; i < sizeof(auth->pwd.raw); ++i) { + furi_string_cat_printf(password_str, "%02X ", nfc->byte_input_store[i]); + } + furi_string_cat_str(password_str, "?\nCaution, a wrong password\ncan block the card!"); + nfc_text_store_set(nfc, furi_string_get_cstr(password_str)); + furi_string_free(password_str); + + dialog_ex_set_header( + dialog_ex, + auth_method == MfUltralightAuthMethodAuto ? "Password captured!" : "Risky function!", + 64, + 0, + AlignCenter, + AlignTop); + dialog_ex_set_text(dialog_ex, nfc->text_store, 64, 12, AlignCenter, AlignTop); + dialog_ex_set_left_button_text(dialog_ex, "Cancel"); + dialog_ex_set_right_button_text(dialog_ex, "Continue"); + + if(auth_method == MfUltralightAuthMethodAuto) + notification_message(nfc->notifications, &sequence_set_green_255); + } else { + dialog_ex_set_header(dialog_ex, "Risky function!", 64, 4, AlignCenter, AlignTop); + dialog_ex_set_text( + dialog_ex, "Wrong password\ncan block your\ncard.", 4, 18, AlignLeft, AlignTop); + dialog_ex_set_icon(dialog_ex, 73, 20, &I_DolphinCommon_56x48); + dialog_ex_set_center_button_text(dialog_ex, "OK"); + } view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx); } @@ -28,12 +56,33 @@ bool nfc_scene_mf_ultralight_unlock_warn_on_event(void* context, SceneManagerEve bool consumed = false; - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == DialogExResultCenter) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightReadAuth); - DOLPHIN_DEED(DolphinDeedNfcRead); + MfUltralightAuthMethod auth_method = nfc->dev->dev_data.mf_ul_data.auth_method; + if(auth_method == MfUltralightAuthMethodManual || auth_method == MfUltralightAuthMethodAuto) { + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == DialogExResultRight) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightReadAuth); + DOLPHIN_DEED(DolphinDeedNfcRead); + consumed = true; + } else if(event.event == DialogExResultLeft) { + if(auth_method == MfUltralightAuthMethodAuto) { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneMfUltralightUnlockMenu); + } else { + consumed = scene_manager_previous_scene(nfc->scene_manager); + } + } + } else if(event.type == SceneManagerEventTypeBack) { + // Cannot press back consumed = true; } + } else { + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == DialogExResultCenter) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightReadAuth); + DOLPHIN_DEED(DolphinDeedNfcRead); + consumed = true; + } + } } return consumed; @@ -43,5 +92,7 @@ void nfc_scene_mf_ultralight_unlock_warn_on_exit(void* context) { Nfc* nfc = context; dialog_ex_reset(nfc->dialog_ex); - submenu_reset(nfc->submenu); + nfc_text_store_clear(nfc); + + notification_message_block(nfc->notifications, &sequence_reset_green); } diff --git a/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c b/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c index bd6f5e2e..7318fd00 100644 --- a/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c +++ b/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c @@ -87,6 +87,20 @@ void nfc_scene_nfc_data_info_on_enter(void* context) { temp_str, "\nPages Read %d/%d", data->data_read / 4, data->data_size / 4); if(data->data_size > data->data_read) { furi_string_cat_printf(temp_str, "\nPassword-protected"); + } else if(data->auth_success) { + MfUltralightConfigPages* config_pages = mf_ultralight_get_config_pages(data); + furi_string_cat_printf( + temp_str, + "\nPassword: %02X %02X %02X %02X", + config_pages->auth_data.pwd.raw[0], + config_pages->auth_data.pwd.raw[1], + config_pages->auth_data.pwd.raw[2], + config_pages->auth_data.pwd.raw[3]); + furi_string_cat_printf( + temp_str, + "\nPACK: %02X %02X", + config_pages->auth_data.pack.raw[0], + config_pages->auth_data.pack.raw[1]); } } else if(protocol == NfcDeviceProtocolMifareClassic) { MfClassicData* data = &dev_data->mf_classic_data; diff --git a/applications/main/nfc/scenes/nfc_scene_read.c b/applications/main/nfc/scenes/nfc_scene_read.c index 1f82aef0..a64d4d00 100644 --- a/applications/main/nfc/scenes/nfc_scene_read.c +++ b/applications/main/nfc/scenes/nfc_scene_read.c @@ -70,6 +70,8 @@ bool nfc_scene_read_on_event(void* context, SceneManagerEvent event) { consumed = true; } else if(event.event == NfcWorkerEventReadMfUltralight) { notification_message(nfc->notifications, &sequence_success); + // Set unlock password input to 0xFFFFFFFF only on fresh read + memset(nfc->byte_input_store, 0xFF, 4); scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightReadSuccess); DOLPHIN_DEED(DolphinDeedNfcReadSuccess); consumed = true; diff --git a/applications/main/nfc/scenes/nfc_scene_saved_menu.c b/applications/main/nfc/scenes/nfc_scene_saved_menu.c index 231f1208..e0839d66 100644 --- a/applications/main/nfc/scenes/nfc_scene_saved_menu.c +++ b/applications/main/nfc/scenes/nfc_scene_saved_menu.c @@ -11,6 +11,8 @@ enum SubmenuIndex { SubmenuIndexDelete, SubmenuIndexInfo, SubmenuIndexRestoreOriginal, + SubmenuIndexMfUlUnlockByReader, + SubmenuIndexMfUlUnlockByPassword, }; void nfc_scene_saved_menu_submenu_callback(void* context, uint32_t index) { @@ -69,6 +71,21 @@ void nfc_scene_saved_menu_on_enter(void* context) { } submenu_add_item( submenu, "Info", SubmenuIndexInfo, nfc_scene_saved_menu_submenu_callback, nfc); + if(nfc->dev->format == NfcDeviceSaveFormatMifareUl && + !mf_ul_is_full_capture(&nfc->dev->dev_data.mf_ul_data)) { + submenu_add_item( + submenu, + "Unlock With Reader", + SubmenuIndexMfUlUnlockByReader, + nfc_scene_saved_menu_submenu_callback, + nfc); + submenu_add_item( + submenu, + "Unlock With Password", + SubmenuIndexMfUlUnlockByPassword, + nfc_scene_saved_menu_submenu_callback, + nfc); + } if(nfc->dev->shadow_file_exist) { submenu_add_item( submenu, @@ -141,6 +158,12 @@ bool nfc_scene_saved_menu_on_event(void* context, SceneManagerEvent event) { } else if(event.event == SubmenuIndexRestoreOriginal) { scene_manager_next_scene(nfc->scene_manager, NfcSceneRestoreOriginalConfirm); consumed = true; + } else if(event.event == SubmenuIndexMfUlUnlockByReader) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockAuto); + consumed = true; + } else if(event.event == SubmenuIndexMfUlUnlockByPassword) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockMenu); + consumed = true; } } diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 5ccb9d43..f3ed374b 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,7.5,, +Version,+,7.6,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -1900,6 +1900,7 @@ Function,-,mf_df_prepare_read_records,uint16_t,"uint8_t*, uint8_t, uint32_t, uin Function,-,mf_df_prepare_select_application,uint16_t,"uint8_t*, uint8_t[3]" Function,-,mf_df_read_card,_Bool,"FuriHalNfcTxRxContext*, MifareDesfireData*" Function,-,mf_ul_check_card_type,_Bool,"uint8_t, uint8_t, uint8_t" +Function,-,mf_ul_is_full_capture,_Bool,MfUltralightData* Function,-,mf_ul_prepare_emulation,void,"MfUltralightEmulator*, MfUltralightData*" Function,-,mf_ul_prepare_emulation_response,_Bool,"uint8_t*, uint16_t, uint8_t*, uint16_t*, uint32_t*, void*" Function,-,mf_ul_pwdgen_amiibo,uint32_t,FuriHalNfcDevData* diff --git a/lib/nfc/nfc_device.c b/lib/nfc/nfc_device.c index 5d86da0c..dc1faa34 100644 --- a/lib/nfc/nfc_device.c +++ b/lib/nfc/nfc_device.c @@ -214,6 +214,9 @@ bool nfc_device_load_mifare_ul_data(FlipperFormat* file, NfcDevice* dev) { uint32_t auth_counter; if(!flipper_format_read_uint32(file, "Failed authentication attempts", &auth_counter, 1)) auth_counter = 0; + data->curr_authlim = auth_counter; + + data->auth_success = mf_ul_is_full_capture(data); parsed = true; } while(false); diff --git a/lib/nfc/nfc_device.h b/lib/nfc/nfc_device.h index 3d302c18..4be07f01 100644 --- a/lib/nfc/nfc_device.h +++ b/lib/nfc/nfc_device.h @@ -67,6 +67,7 @@ typedef struct { union { NfcReaderRequestData reader_data; NfcMfClassicDictAttackData mf_classic_dict_attack_data; + MfUltralightAuth mf_ul_auth; }; union { EmvData emv_data; diff --git a/lib/nfc/nfc_worker.c b/lib/nfc/nfc_worker.c index c701132c..450428a1 100644 --- a/lib/nfc/nfc_worker.c +++ b/lib/nfc/nfc_worker.c @@ -527,10 +527,25 @@ void nfc_worker_emulate_apdu(NfcWorker* nfc_worker) { } } +void nfc_worker_mf_ultralight_auth_received_callback(MfUltralightAuth auth, void* context) { + furi_assert(context); + + NfcWorker* nfc_worker = context; + nfc_worker->dev_data->mf_ul_auth = auth; + if(nfc_worker->callback) { + nfc_worker->callback(NfcWorkerEventMfUltralightPwdAuth, nfc_worker->context); + } +} + void nfc_worker_emulate_mf_ultralight(NfcWorker* nfc_worker) { FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; MfUltralightEmulator emulator = {}; mf_ul_prepare_emulation(&emulator, &nfc_worker->dev_data->mf_ul_data); + + // TODO rework with reader analyzer + emulator.auth_received_callback = nfc_worker_mf_ultralight_auth_received_callback; + emulator.context = nfc_worker; + while(nfc_worker->state == NfcWorkerStateMfUltralightEmulate) { mf_ul_reset_emulation(&emulator, true); furi_hal_nfc_emulate_nfca( @@ -905,7 +920,8 @@ void nfc_worker_mf_ultralight_read_auth(NfcWorker* nfc_worker) { if(furi_hal_nfc_detect(nfc_data, 300) && nfc_data->type == FuriHalNfcTypeA) { if(mf_ul_check_card_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak)) { nfc_worker->callback(NfcWorkerEventCardDetected, nfc_worker->context); - if(data->auth_method == MfUltralightAuthMethodManual) { + if(data->auth_method == MfUltralightAuthMethodManual || + data->auth_method == MfUltralightAuthMethodAuto) { nfc_worker->callback(NfcWorkerEventMfUltralightPassKey, nfc_worker->context); key = nfc_util_bytes2num(data->auth_key, 4); } else if(data->auth_method == MfUltralightAuthMethodAmeebo) { diff --git a/lib/nfc/nfc_worker.h b/lib/nfc/nfc_worker.h index ddee34c9..fdcaa72f 100644 --- a/lib/nfc/nfc_worker.h +++ b/lib/nfc/nfc_worker.h @@ -65,8 +65,8 @@ typedef enum { NfcWorkerEventDetectReaderMfkeyCollected, // Mifare Ultralight events - NfcWorkerEventMfUltralightPassKey, - + NfcWorkerEventMfUltralightPassKey, // NFC worker requesting manual key + NfcWorkerEventMfUltralightPwdAuth, // Reader sent auth command } NfcWorkerEvent; typedef bool (*NfcWorkerCallback)(NfcWorkerEvent event, void* context); diff --git a/lib/nfc/protocols/mifare_ultralight.c b/lib/nfc/protocols/mifare_ultralight.c index a8d1f554..85e234bd 100644 --- a/lib/nfc/protocols/mifare_ultralight.c +++ b/lib/nfc/protocols/mifare_ultralight.c @@ -51,7 +51,7 @@ void mf_ul_reset(MfUltralightData* data) { data->data_size = 0; data->data_read = 0; data->curr_authlim = 0; - data->has_auth = false; + data->auth_success = false; } static MfUltralightFeatures mf_ul_get_features(MfUltralightType type) { @@ -756,6 +756,34 @@ bool mf_ul_read_card( mf_ultralight_read_tearing_flags(tx_rx, data); } data->curr_authlim = 0; + + if(reader->pages_read == reader->pages_to_read && + reader->supported_features & MfUltralightSupportAuth && !data->auth_success) { + MfUltralightConfigPages* config = mf_ultralight_get_config_pages(data); + if(config->access.authlim == 0) { + // Attempt to auth with default PWD + uint16_t pack; + data->auth_success = mf_ultralight_authenticate(tx_rx, MF_UL_DEFAULT_PWD, &pack); + if(data->auth_success) { + config->auth_data.pwd.value = MF_UL_DEFAULT_PWD; + config->auth_data.pack.value = pack; + } else { + furi_hal_nfc_sleep(); + furi_hal_nfc_activate_nfca(300, NULL); + } + } + } + } + + if(reader->pages_read != reader->pages_to_read) { + if(reader->supported_features & MfUltralightSupportAuth) { + // Probably password protected, fix AUTH0 and PROT so before AUTH0 + // can be written and since AUTH0 won't be readable, like on the + // original card + MfUltralightConfigPages* config = mf_ultralight_get_config_pages(data); + config->auth0 = reader->pages_read; + config->access.prot = true; + } } return card_read; @@ -1201,6 +1229,8 @@ static void mf_ul_emulate_write( } void mf_ul_reset_emulation(MfUltralightEmulator* emulator, bool is_power_cycle) { + emulator->comp_write_cmd_started = false; + emulator->sector_select_cmd_started = false; emulator->curr_sector = 0; emulator->ntag_i2c_plus_sector3_lockout = false; emulator->auth_success = false; @@ -1244,8 +1274,7 @@ void mf_ul_prepare_emulation(MfUltralightEmulator* emulator, MfUltralightData* d emulator->config = mf_ultralight_get_config_pages(&emulator->data); emulator->page_num = emulator->data.data_size / 4; emulator->data_changed = false; - emulator->comp_write_cmd_started = false; - emulator->sector_select_cmd_started = false; + memset(&emulator->auth_attempt, 0, sizeof(MfUltralightAuth)); mf_ul_reset_emulation(emulator, true); } @@ -1706,6 +1735,17 @@ bool mf_ul_prepare_emulation_response( } else if(cmd == MF_UL_AUTH) { if(emulator->supported_features & MfUltralightSupportAuth) { if(buff_rx_len == (1 + 4) * 8) { + // Record password sent by PCD + memcpy( + emulator->auth_attempt.pwd.raw, + &buff_rx[1], + sizeof(emulator->auth_attempt.pwd.raw)); + emulator->auth_attempted = true; + if(emulator->auth_received_callback) { + emulator->auth_received_callback( + emulator->auth_attempt, emulator->context); + } + uint16_t scaled_authlim = mf_ultralight_calc_auth_count(&emulator->data); if(scaled_authlim != 0 && emulator->data.curr_authlim >= scaled_authlim) { if(emulator->data.curr_authlim != UINT16_MAX) { @@ -1863,3 +1903,14 @@ bool mf_ul_prepare_emulation_response( return tx_bits > 0; } + +bool mf_ul_is_full_capture(MfUltralightData* data) { + if(data->data_read != data->data_size) return false; + + // Having read all the pages doesn't mean that we've got everything. + // By default PWD is 0xFFFFFFFF, but if read back it is always 0x00000000, + // so a default read on an auth-supported NTAG is never complete. + if(!(mf_ul_get_features(data->type) & MfUltralightSupportAuth)) return true; + MfUltralightConfigPages* config = mf_ultralight_get_config_pages(data); + return config->auth_data.pwd.value != 0 || config->auth_data.pack.value != 0; +} diff --git a/lib/nfc/protocols/mifare_ultralight.h b/lib/nfc/protocols/mifare_ultralight.h index 9642824f..4ab22e89 100644 --- a/lib/nfc/protocols/mifare_ultralight.h +++ b/lib/nfc/protocols/mifare_ultralight.h @@ -28,10 +28,13 @@ #define MF_UL_NTAG203_COUNTER_PAGE (41) +#define MF_UL_DEFAULT_PWD (0xFFFFFFFF) + typedef enum { MfUltralightAuthMethodManual, MfUltralightAuthMethodAmeebo, MfUltralightAuthMethodXiaomi, + MfUltralightAuthMethodAuto, } MfUltralightAuthMethod; // Important: order matters; some features are based on positioning in this enum @@ -110,7 +113,6 @@ typedef struct { uint8_t signature[32]; uint32_t counter[3]; uint8_t tearing[3]; - bool has_auth; MfUltralightAuthMethod auth_method; uint8_t auth_key[4]; bool auth_success; @@ -169,6 +171,9 @@ typedef struct { MfUltralightFeatures supported_features; } MfUltralightReader; +// TODO rework with reader analyzer +typedef void (*MfUltralightAuthReceivedCallback)(MfUltralightAuth auth, void* context); + typedef struct { MfUltralightData data; MfUltralightConfigPages* config; @@ -185,6 +190,12 @@ typedef struct { bool sector_select_cmd_started; bool ntag_i2c_plus_sector3_lockout; bool read_counter_incremented; + bool auth_attempted; + MfUltralightAuth auth_attempt; + + // TODO rework with reader analyzer + MfUltralightAuthReceivedCallback auth_received_callback; + void* context; } MfUltralightEmulator; void mf_ul_reset(MfUltralightData* data); @@ -241,3 +252,5 @@ bool mf_ul_prepare_emulation_response( uint32_t mf_ul_pwdgen_amiibo(FuriHalNfcDevData* data); uint32_t mf_ul_pwdgen_xiaomi(FuriHalNfcDevData* data); + +bool mf_ul_is_full_capture(MfUltralightData* data);