diff --git a/applications/nfc/application.fam b/applications/nfc/application.fam index f43ed3144..ce21bfd27 100644 --- a/applications/nfc/application.fam +++ b/applications/nfc/application.fam @@ -10,7 +10,7 @@ App( ], provides=["nfc_start"], icon="A_NFC_14", - stack_size=4 * 1024, + stack_size=5 * 1024, order=30, ) diff --git a/applications/nfc/helpers/nfc_generators.c b/applications/nfc/helpers/nfc_generators.c index 066ac9d1b..3ec78a127 100644 --- a/applications/nfc/helpers/nfc_generators.c +++ b/applications/nfc/helpers/nfc_generators.c @@ -55,6 +55,7 @@ static void nfc_generate_mf_ul_orig(NfcDeviceData* data) { MfUltralightData* mful = &data->mf_ul_data; mful->type = MfUltralightTypeUnknown; mful->data_size = 16 * 4; + mful->data_read = mful->data_size; nfc_generate_mf_ul_copy_uid_with_bcc(data); // TODO: what's internal byte on page 2? memset(&mful->data[4 * 4], 0xFF, 4); @@ -67,6 +68,7 @@ static void nfc_generate_mf_ul_ntag203(NfcDeviceData* data) { MfUltralightData* mful = &data->mf_ul_data; mful->type = MfUltralightTypeNTAG203; mful->data_size = 42 * 4; + mful->data_read = mful->data_size; nfc_generate_mf_ul_copy_uid_with_bcc(data); mful->data[9] = 0x48; // Internal byte memcpy(&mful->data[3 * 4], default_data_ntag203, sizeof(default_data_ntag203)); @@ -78,6 +80,7 @@ static void nfc_generate_mf_ul_with_config_common(NfcDeviceData* data, uint8_t n MfUltralightData* mful = &data->mf_ul_data; mful->data_size = num_pages * 4; + mful->data_read = mful->data_size; nfc_generate_mf_ul_copy_uid_with_bcc(data); uint16_t config_index = (num_pages - 4) * 4; mful->data[config_index] = 0x04; // STRG_MOD_EN @@ -180,6 +183,7 @@ static void mful->type = type; memcpy(&mful->version, version_bytes_ntag_i2c, sizeof(version_bytes_ntag_i2c)); mful->data_size = num_pages * 4; + mful->data_read = mful->data_size; memcpy(mful->data, data->nfc_data.uid, data->nfc_data.uid_len); mful->data[7] = data->nfc_data.sak; mful->data[8] = data->nfc_data.atqa[0]; diff --git a/applications/nfc/nfc_i.h b/applications/nfc/nfc_i.h old mode 100755 new mode 100644 diff --git a/applications/nfc/scenes/nfc_scene_config.h b/applications/nfc/scenes/nfc_scene_config.h index de8de6b40..584faf309 100755 --- a/applications/nfc/scenes/nfc_scene_config.h +++ b/applications/nfc/scenes/nfc_scene_config.h @@ -15,6 +15,11 @@ ADD_SCENE(nfc, emulate_uid, EmulateUid) ADD_SCENE(nfc, mf_ultralight_read_success, MfUltralightReadSuccess) ADD_SCENE(nfc, mf_ultralight_menu, MfUltralightMenu) 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_menu, MfUltralightUnlockMenu) +ADD_SCENE(nfc, mf_ultralight_unlock_warn, MfUltralightUnlockWarn) ADD_SCENE(nfc, mf_desfire_read_success, MfDesfireReadSuccess) ADD_SCENE(nfc, mf_desfire_menu, MfDesfireMenu) ADD_SCENE(nfc, mf_desfire_data, MfDesfireData) diff --git a/applications/nfc/scenes/nfc_scene_extra_actions.c b/applications/nfc/scenes/nfc_scene_extra_actions.c index 823a4ace0..43e49e5a0 100644 --- a/applications/nfc/scenes/nfc_scene_extra_actions.c +++ b/applications/nfc/scenes/nfc_scene_extra_actions.c @@ -2,6 +2,7 @@ enum SubmenuIndex { SubmenuIndexMfClassicKeys, + SubmenuIndexMfUltralightUnlock, }; void nfc_scene_extra_actions_submenu_callback(void* context, uint32_t index) { @@ -20,6 +21,12 @@ void nfc_scene_extra_actions_on_enter(void* context) { SubmenuIndexMfClassicKeys, nfc_scene_extra_actions_submenu_callback, nfc); + submenu_add_item( + submenu, + "Unlock NTAG/Ultralight", + SubmenuIndexMfUltralightUnlock, + nfc_scene_extra_actions_submenu_callback, + nfc); view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); } @@ -35,6 +42,8 @@ bool nfc_scene_extra_actions_on_event(void* context, SceneManagerEvent event) { scene_manager_next_scene(nfc->scene_manager, NfcSceneDictNotFound); } consumed = true; + } else if(event.event == SubmenuIndexMfUltralightUnlock) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockMenu); } scene_manager_set_scene_state(nfc->scene_manager, NfcSceneExtraActions, event.event); } diff --git a/applications/nfc/scenes/nfc_scene_mf_ultralight_key_input.c b/applications/nfc/scenes/nfc_scene_mf_ultralight_key_input.c new file mode 100644 index 000000000..089187d5b --- /dev/null +++ b/applications/nfc/scenes/nfc_scene_mf_ultralight_key_input.c @@ -0,0 +1,44 @@ +#include "../nfc_i.h" + +void nfc_scene_mf_ultralight_key_input_byte_input_callback(void* context) { + Nfc* nfc = context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventByteInputDone); +} + +void nfc_scene_mf_ultralight_key_input_on_enter(void* context) { + Nfc* nfc = context; + + // Setup view + ByteInput* byte_input = nfc->byte_input; + byte_input_set_header_text(byte_input, "Enter the password in hex"); + byte_input_set_result_callback( + byte_input, + nfc_scene_mf_ultralight_key_input_byte_input_callback, + NULL, + nfc, + nfc->byte_input_store, + 4); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewByteInput); +} + +bool nfc_scene_mf_ultralight_key_input_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventByteInputDone) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockWarn); + consumed = true; + } + } + return consumed; +} + +void nfc_scene_mf_ultralight_key_input_on_exit(void* context) { + Nfc* nfc = context; + + // Clear view + byte_input_set_result_callback(nfc->byte_input, NULL, NULL, NULL, NULL, 0); + byte_input_set_header_text(nfc->byte_input, ""); +} diff --git a/applications/nfc/scenes/nfc_scene_mf_ultralight_menu.c b/applications/nfc/scenes/nfc_scene_mf_ultralight_menu.c index e3d256a55..9174a8b19 100644 --- a/applications/nfc/scenes/nfc_scene_mf_ultralight_menu.c +++ b/applications/nfc/scenes/nfc_scene_mf_ultralight_menu.c @@ -1,6 +1,7 @@ #include "../nfc_i.h" enum SubmenuIndex { + SubmenuIndexUnlock, SubmenuIndexSave, SubmenuIndexEmulate, }; @@ -14,7 +15,16 @@ void nfc_scene_mf_ultralight_menu_submenu_callback(void* context, uint32_t index void nfc_scene_mf_ultralight_menu_on_enter(void* context) { Nfc* nfc = context; Submenu* submenu = nfc->submenu; + MfUltralightData* data = &nfc->dev->dev_data.mf_ul_data; + if(data->data_read != data->data_size) { + submenu_add_item( + submenu, + "Unlock With Password", + SubmenuIndexUnlock, + nfc_scene_mf_ultralight_menu_submenu_callback, + nfc); + } submenu_add_item( submenu, "Save", SubmenuIndexSave, nfc_scene_mf_ultralight_menu_submenu_callback, nfc); submenu_add_item( @@ -35,19 +45,20 @@ bool nfc_scene_mf_ultralight_menu_on_event(void* context, SceneManagerEvent even if(event.type == SceneManagerEventTypeCustom) { if(event.event == SubmenuIndexSave) { - scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneMfUltralightMenu, SubmenuIndexSave); nfc->dev->format = NfcDeviceSaveFormatMifareUl; // Clear device name nfc_device_set_name(nfc->dev, ""); scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); consumed = true; } else if(event.event == SubmenuIndexEmulate) { - scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneMfUltralightMenu, SubmenuIndexEmulate); scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightEmulate); consumed = true; + } else if(event.event == SubmenuIndexUnlock) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockMenu); + consumed = true; } + scene_manager_set_scene_state(nfc->scene_manager, NfcSceneMfUltralightMenu, event.event); + } else if(event.type == SceneManagerEventTypeBack) { consumed = scene_manager_previous_scene(nfc->scene_manager); } diff --git a/applications/nfc/scenes/nfc_scene_mf_ultralight_read_auth.c b/applications/nfc/scenes/nfc_scene_mf_ultralight_read_auth.c new file mode 100644 index 000000000..968157bdb --- /dev/null +++ b/applications/nfc/scenes/nfc_scene_mf_ultralight_read_auth.c @@ -0,0 +1,107 @@ +#include "../nfc_i.h" +#include + +typedef enum { + NfcSceneMfUlReadStateIdle, + NfcSceneMfUlReadStateDetecting, + NfcSceneMfUlReadStateReading, + NfcSceneMfUlReadStateNotSupportedCard, +} NfcSceneMfUlReadState; + +bool nfc_scene_mf_ultralight_read_auth_worker_callback(NfcWorkerEvent event, void* context) { + Nfc* nfc = context; + + if(event == NfcWorkerEventMfUltralightPassKey) { + memcpy(nfc->dev->dev_data.mf_ul_data.auth_key, nfc->byte_input_store, 4); + } else { + view_dispatcher_send_custom_event(nfc->view_dispatcher, event); + } + return true; +} + +void nfc_scene_mf_ultralight_read_auth_set_state(Nfc* nfc, NfcSceneMfUlReadState state) { + uint32_t curr_state = + scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfUltralightReadAuth); + 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_icon(nfc->popup, 0, 8, &I_NFC_manual); + } 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); + } 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", + 4, + 22, + AlignLeft, + AlignTop); + popup_set_icon(nfc->popup, 73, 17, &I_DolphinFirstStart8_56x51); + } + scene_manager_set_scene_state(nfc->scene_manager, NfcSceneMfUltralightReadAuth, state); + } +} + +void nfc_scene_mf_ultralight_read_auth_on_enter(void* context) { + Nfc* nfc = context; + DOLPHIN_DEED(DolphinDeedNfcRead); + + nfc_device_clear(nfc->dev); + // Setup view + nfc_scene_mf_ultralight_read_auth_set_state(nfc, NfcSceneMfUlReadStateDetecting); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); + // Start worker + nfc_worker_start( + nfc->worker, + NfcWorkerStateReadMfUltralightReadAuth, + &nfc->dev->dev_data, + nfc_scene_mf_ultralight_read_auth_worker_callback, + nfc); + + nfc_blink_start(nfc); +} + +bool nfc_scene_mf_ultralight_read_auth_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if((event.event == NfcWorkerEventSuccess) || (event.event == NfcWorkerEventFail)) { + notification_message(nfc->notifications, &sequence_success); + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightReadAuthResult); + consumed = true; + } else if(event.event == NfcWorkerEventCardDetected) { + nfc_scene_mf_ultralight_read_auth_set_state(nfc, NfcSceneMfUlReadStateReading); + consumed = true; + } else if(event.event == NfcWorkerEventNoCardDetected) { + nfc_scene_mf_ultralight_read_auth_set_state(nfc, NfcSceneMfUlReadStateDetecting); + consumed = true; + } else if(event.event == NfcWorkerEventWrongCardDetected) { + nfc_scene_mf_ultralight_read_auth_set_state( + nfc, NfcSceneMfUlReadStateNotSupportedCard); + } + } else if(event.type == SceneManagerEventTypeBack) { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneMfUltralightUnlockMenu); + } + return consumed; +} + +void nfc_scene_mf_ultralight_read_auth_on_exit(void* context) { + Nfc* nfc = context; + + // Stop worker + nfc_worker_stop(nfc->worker); + // Clear view + popup_reset(nfc->popup); + nfc_blink_stop(nfc); + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneMfUltralightReadAuth, NfcSceneMfUlReadStateIdle); +} diff --git a/applications/nfc/scenes/nfc_scene_mf_ultralight_read_auth_result.c b/applications/nfc/scenes/nfc_scene_mf_ultralight_read_auth_result.c new file mode 100644 index 000000000..b94215455 --- /dev/null +++ b/applications/nfc/scenes/nfc_scene_mf_ultralight_read_auth_result.c @@ -0,0 +1,98 @@ +#include "../nfc_i.h" +#include + +void nfc_scene_mf_ultralight_read_auth_result_widget_callback( + GuiButtonType result, + InputType type, + void* context) { + Nfc* nfc = context; + + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(nfc->view_dispatcher, result); + } +} + +void nfc_scene_mf_ultralight_read_auth_result_on_enter(void* context) { + Nfc* nfc = context; + DOLPHIN_DEED(DolphinDeedNfcReadSuccess); + + // Setup dialog view + FuriHalNfcDevData* nfc_data = &nfc->dev->dev_data.nfc_data; + 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; + string_t temp_str; + string_init(temp_str); + + 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!"); + } else { + widget_add_string_element( + widget, 64, 0, AlignCenter, AlignTop, FontPrimary, "Not all pages unlocked!"); + } + string_set_str(temp_str, "UID:"); + for(size_t i = 0; i < nfc_data->uid_len; i++) { + string_cat_printf(temp_str, " %02X", nfc_data->uid[i]); + } + widget_add_string_element( + widget, 0, 17, AlignLeft, AlignTop, FontSecondary, string_get_cstr(temp_str)); + if(mf_ul_data->auth_success) { + string_printf( + temp_str, + "Password: %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]); + widget_add_string_element( + widget, 0, 28, AlignLeft, AlignTop, FontSecondary, string_get_cstr(temp_str)); + string_printf( + temp_str, + "PACK: %02X %02X", + config_pages->auth_data.pack.raw[0], + config_pages->auth_data.pack.raw[1]); + widget_add_string_element( + widget, 0, 39, AlignLeft, AlignTop, FontSecondary, string_get_cstr(temp_str)); + } + string_printf( + temp_str, "Pages Read: %d/%d", mf_ul_data->data_read / 4, mf_ul_data->data_size / 4); + widget_add_string_element( + widget, 0, 50, AlignLeft, AlignTop, FontSecondary, string_get_cstr(temp_str)); + widget_add_button_element( + widget, + GuiButtonTypeRight, + "Save", + nfc_scene_mf_ultralight_read_auth_result_widget_callback, + nfc); + + string_clear(temp_str); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); +} + +bool nfc_scene_mf_ultralight_read_auth_result_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeRight) { + nfc->dev->format = NfcDeviceSaveFormatMifareUl; + // Clear device name + nfc_device_set_name(nfc->dev, ""); + scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); + consumed = true; + } + } else if(event.type == SceneManagerEventTypeBack) { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneMfUltralightUnlockMenu); + } + + return consumed; +} + +void nfc_scene_mf_ultralight_read_auth_result_on_exit(void* context) { + Nfc* nfc = context; + + // Clean views + widget_reset(nfc->widget); +} diff --git a/applications/nfc/scenes/nfc_scene_mf_ultralight_read_success.c b/applications/nfc/scenes/nfc_scene_mf_ultralight_read_success.c old mode 100755 new mode 100644 index 8b1b6c004..65750b963 --- a/applications/nfc/scenes/nfc_scene_mf_ultralight_read_success.c +++ b/applications/nfc/scenes/nfc_scene_mf_ultralight_read_success.c @@ -1,51 +1,67 @@ #include "../nfc_i.h" #include -#define NFC_SCENE_READ_SUCCESS_SHIFT " " - enum { - ReadMifareUlStateShowUID, + ReadMifareUlStateShowInfo, ReadMifareUlStateShowData, }; -void nfc_scene_mf_ultralight_read_success_dialog_callback(DialogExResult result, void* context) { +void nfc_scene_mf_ultralight_read_success_widget_callback( + GuiButtonType result, + InputType type, + void* context) { Nfc* nfc = context; - view_dispatcher_send_custom_event(nfc->view_dispatcher, result); + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(nfc->view_dispatcher, result); + } } void nfc_scene_mf_ultralight_read_success_on_enter(void* context) { Nfc* nfc = context; DOLPHIN_DEED(DolphinDeedNfcReadSuccess); - // Setup dialog view + // Setup widget view FuriHalNfcDevData* data = &nfc->dev->dev_data.nfc_data; MfUltralightData* mf_ul_data = &nfc->dev->dev_data.mf_ul_data; - DialogEx* dialog_ex = nfc->dialog_ex; - dialog_ex_set_left_button_text(dialog_ex, "Retry"); - dialog_ex_set_right_button_text(dialog_ex, "More"); - dialog_ex_set_center_button_text(dialog_ex, "Data"); - dialog_ex_set_header( - dialog_ex, nfc_mf_ul_type(mf_ul_data->type, true), 64, 8, AlignCenter, AlignCenter); - dialog_ex_set_icon(dialog_ex, 8, 13, &I_Medium_chip_22x21); - // Display UID - nfc_text_store_set( - nfc, - NFC_SCENE_READ_SUCCESS_SHIFT "ATQA: %02X%02X\n" NFC_SCENE_READ_SUCCESS_SHIFT - "SAK: %02X\nUID: %02X %02X %02X %02X %02X %02X %02X", - data->atqa[0], - data->atqa[1], - data->sak, - data->uid[0], - data->uid[1], - data->uid[2], - data->uid[3], - data->uid[4], - data->uid[5], - data->uid[6]); - dialog_ex_set_text(dialog_ex, nfc->text_store, 8, 16, AlignLeft, AlignTop); - dialog_ex_set_context(dialog_ex, nfc); - dialog_ex_set_result_callback(dialog_ex, nfc_scene_mf_ultralight_read_success_dialog_callback); + Widget* widget = nfc->widget; + widget_add_button_element( + widget, + GuiButtonTypeLeft, + "Retry", + nfc_scene_mf_ultralight_read_success_widget_callback, + nfc); + widget_add_button_element( + widget, + GuiButtonTypeCenter, + "Data", + nfc_scene_mf_ultralight_read_success_widget_callback, + nfc); + widget_add_button_element( + widget, + GuiButtonTypeRight, + "More", + nfc_scene_mf_ultralight_read_success_widget_callback, + nfc); + + widget_add_string_element( + widget, 0, 0, AlignLeft, AlignTop, FontSecondary, nfc_mf_ul_type(mf_ul_data->type, true)); + string_t data_str; + string_init_printf(data_str, "UID:"); + for(size_t i = 0; i < data->uid_len; i++) { + string_cat_printf(data_str, " %02X", data->uid[i]); + } + widget_add_string_element( + widget, 0, 13, AlignLeft, AlignTop, FontSecondary, string_get_cstr(data_str)); + string_printf( + data_str, "Pages Read: %d/%d", mf_ul_data->data_read / 4, mf_ul_data->data_size / 4); + widget_add_string_element( + widget, 0, 24, AlignLeft, AlignTop, FontSecondary, string_get_cstr(data_str)); + if(mf_ul_data->data_read != mf_ul_data->data_size) { + widget_add_string_element( + widget, 0, 35, AlignLeft, AlignTop, FontSecondary, "Password-protected pages!"); + } + string_clear(data_str); // Setup TextBox view TextBox* text_box = nfc->text_box; @@ -60,8 +76,8 @@ void nfc_scene_mf_ultralight_read_success_on_enter(void* context) { text_box_set_text(text_box, string_get_cstr(nfc->text_box_store)); scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneMfUltralightReadSuccess, ReadMifareUlStateShowUID); - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx); + nfc->scene_manager, NfcSceneMfUltralightReadSuccess, ReadMifareUlStateShowInfo); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); } bool nfc_scene_mf_ultralight_read_success_on_event(void* context, SceneManagerEvent event) { @@ -71,13 +87,13 @@ bool nfc_scene_mf_ultralight_read_success_on_event(void* context, SceneManagerEv scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfUltralightReadSuccess); if(event.type == SceneManagerEventTypeCustom) { - if(state == ReadMifareUlStateShowUID && event.event == DialogExResultLeft) { + if(state == ReadMifareUlStateShowInfo && event.event == GuiButtonTypeLeft) { scene_manager_next_scene(nfc->scene_manager, NfcSceneRetryConfirm); consumed = true; - } else if(state == ReadMifareUlStateShowUID && event.event == DialogExResultRight) { + } else if(state == ReadMifareUlStateShowInfo && event.event == GuiButtonTypeRight) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightMenu); consumed = true; - } else if(state == ReadMifareUlStateShowUID && event.event == DialogExResultCenter) { + } else if(state == ReadMifareUlStateShowInfo && event.event == GuiButtonTypeCenter) { view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox); scene_manager_set_scene_state( nfc->scene_manager, NfcSceneMfUltralightReadSuccess, ReadMifareUlStateShowData); @@ -85,9 +101,9 @@ bool nfc_scene_mf_ultralight_read_success_on_event(void* context, SceneManagerEv } } else if(event.type == SceneManagerEventTypeBack) { if(state == ReadMifareUlStateShowData) { - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneMfUltralightReadSuccess, ReadMifareUlStateShowUID); + nfc->scene_manager, NfcSceneMfUltralightReadSuccess, ReadMifareUlStateShowInfo); consumed = true; } else { scene_manager_next_scene(nfc->scene_manager, NfcSceneExitConfirm); @@ -102,7 +118,7 @@ void nfc_scene_mf_ultralight_read_success_on_exit(void* context) { Nfc* nfc = context; // Clean views - dialog_ex_reset(nfc->dialog_ex); + widget_reset(nfc->widget); text_box_reset(nfc->text_box); string_reset(nfc->text_box_store); } diff --git a/applications/nfc/scenes/nfc_scene_mf_ultralight_unlock_menu.c b/applications/nfc/scenes/nfc_scene_mf_ultralight_unlock_menu.c new file mode 100644 index 000000000..648aa31dc --- /dev/null +++ b/applications/nfc/scenes/nfc_scene_mf_ultralight_unlock_menu.c @@ -0,0 +1,70 @@ +#include "../nfc_i.h" + +enum SubmenuIndex { + SubmenuIndexMfUlUnlockMenuManual, + SubmenuIndexMfUlUnlockMenuAmeebo, + SubmenuIndexMfUlUnlockMenuXiaomi, +}; + +void nfc_scene_mf_ultralight_unlock_menu_submenu_callback(void* context, uint32_t index) { + Nfc* nfc = context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, index); +} + +void nfc_scene_mf_ultralight_unlock_menu_on_enter(void* context) { + Nfc* nfc = context; + Submenu* submenu = nfc->submenu; + + 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); + submenu_add_item( + submenu, + "Auth As Ameebo", + SubmenuIndexMfUlUnlockMenuAmeebo, + nfc_scene_mf_ultralight_unlock_menu_submenu_callback, + nfc); + submenu_add_item( + submenu, + "Auth As Xiaomi", + SubmenuIndexMfUlUnlockMenuXiaomi, + nfc_scene_mf_ultralight_unlock_menu_submenu_callback, + nfc); + submenu_set_selected_item(submenu, state); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); +} + +bool nfc_scene_mf_ultralight_unlock_menu_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuIndexMfUlUnlockMenuManual) { + nfc->dev->dev_data.mf_ul_data.auth_method = MfUltralightAuthMethodManual; + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightKeyInput); + consumed = true; + } else if(event.event == SubmenuIndexMfUlUnlockMenuAmeebo) { + nfc->dev->dev_data.mf_ul_data.auth_method = MfUltralightAuthMethodAmeebo; + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockWarn); + consumed = true; + } else if(event.event == SubmenuIndexMfUlUnlockMenuXiaomi) { + nfc->dev->dev_data.mf_ul_data.auth_method = MfUltralightAuthMethodXiaomi; + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockWarn); + consumed = true; + } + scene_manager_set_scene_state(nfc->scene_manager, NfcSceneExtraActions, event.event); + } + return consumed; +} + +void nfc_scene_mf_ultralight_unlock_menu_on_exit(void* context) { + Nfc* nfc = context; + + submenu_reset(nfc->submenu); +} diff --git a/applications/nfc/scenes/nfc_scene_mf_ultralight_unlock_warn.c b/applications/nfc/scenes/nfc_scene_mf_ultralight_unlock_warn.c new file mode 100644 index 000000000..00df98e75 --- /dev/null +++ b/applications/nfc/scenes/nfc_scene_mf_ultralight_unlock_warn.c @@ -0,0 +1,45 @@ +#include "../nfc_i.h" + +void nfc_scene_mf_ultralight_unlock_warn_dialog_callback(DialogExResult result, void* context) { + Nfc* nfc = context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, result); +} + +void nfc_scene_mf_ultralight_unlock_warn_on_enter(void* context) { + Nfc* nfc = context; + DialogEx* dialog_ex = nfc->dialog_ex; + + 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, 17, &I_DolphinFirstStart8_56x51); + dialog_ex_set_center_button_text(dialog_ex, "OK"); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx); +} + +bool nfc_scene_mf_ultralight_unlock_warn_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == DialogExResultCenter) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightReadAuth); + consumed = true; + } + } + + return consumed; +} + +void nfc_scene_mf_ultralight_unlock_warn_on_exit(void* context) { + Nfc* nfc = context; + + dialog_ex_reset(nfc->dialog_ex); + submenu_reset(nfc->submenu); +} diff --git a/firmware.scons b/firmware.scons index 4eb76f520..064e0ddab 100644 --- a/firmware.scons +++ b/firmware.scons @@ -97,7 +97,7 @@ else: ], ) -# Invoke child SCopscripts to populate global `env` + build their own part of the code +# Invoke child SConscripts to populate global `env` + build their own part of the code lib_targets = env.BuildModules( [ "lib", diff --git a/lib/SConscript b/lib/SConscript index 459a80d62..5e5bb2eaa 100644 --- a/lib/SConscript +++ b/lib/SConscript @@ -7,7 +7,7 @@ env.Append( "lib/drivers", "lib/flipper_format", "lib/infrared", - "lib/nfc_protocols", + "lib/nfc", "lib/one_wire", "lib/ST25RFAL002", "lib/subghz", @@ -44,7 +44,7 @@ env.Append( # fnv1a-hash # micro-ecc # microtar -# nfc_protocols +# nfc # one_wire # qrcode # u8g2 @@ -71,11 +71,11 @@ libs = env.BuildModules( "flipper_format", "infrared", "littlefs", + "mbedtls", "subghz", "nfc", "appframe", "misc", - "mbedtls", "loclass", ], ) diff --git a/lib/mbedtls.scons b/lib/mbedtls.scons index 1b0bec87d..e39d9dae8 100644 --- a/lib/mbedtls.scons +++ b/lib/mbedtls.scons @@ -11,7 +11,11 @@ env.Append( libenv = env.Clone(FW_LIB_NAME="mbedtls") libenv.ApplyLibFlags() -sources = ["mbedtls/library/des.c", "mbedtls/library/platform_util.c"] +sources = [ + "mbedtls/library/des.c", + "mbedtls/library/sha1.c", + "mbedtls/library/platform_util.c", +] lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources) libenv.Install("${LIB_DIST_DIR}", lib) diff --git a/lib/nfc/nfc_device.c b/lib/nfc/nfc_device.c index 649a2c5f5..9da891327 100644 --- a/lib/nfc/nfc_device.c +++ b/lib/nfc/nfc_device.c @@ -19,6 +19,7 @@ static const uint32_t nfc_keys_file_version = 1; // Protocols format versions static const uint32_t nfc_mifare_classic_data_format_version = 2; +static const uint32_t nfc_mifare_ultralight_data_format_version = 1; NfcDevice* nfc_device_alloc() { NfcDevice* nfc_dev = malloc(sizeof(NfcDevice)); @@ -97,6 +98,9 @@ static bool nfc_device_save_mifare_ul_data(FlipperFormat* file, NfcDevice* dev) // Save Mifare Ultralight specific data do { if(!flipper_format_write_comment_cstr(file, "Mifare Ultralight specific data")) break; + if(!flipper_format_write_uint32( + file, "Data format version", &nfc_mifare_ultralight_data_format_version, 1)) + break; if(!flipper_format_write_hex(file, "Signature", data->signature, sizeof(data->signature))) break; if(!flipper_format_write_hex( @@ -121,6 +125,8 @@ static bool nfc_device_save_mifare_ul_data(FlipperFormat* file, NfcDevice* dev) // Write pages data uint32_t pages_total = data->data_size / 4; if(!flipper_format_write_uint32(file, "Pages total", &pages_total, 1)) break; + uint32_t pages_read = data->data_read / 4; + if(!flipper_format_write_uint32(file, "Pages read", &pages_read, 1)) break; bool pages_saved = true; for(uint16_t i = 0; i < data->data_size; i += 4) { string_printf(temp_str, "Page %d", i / 4); @@ -148,8 +154,14 @@ bool nfc_device_load_mifare_ul_data(FlipperFormat* file, NfcDevice* dev) { MfUltralightData* data = &dev->dev_data.mf_ul_data; string_t temp_str; string_init(temp_str); + uint32_t data_format_version = 0; do { + // Read Mifare Ultralight format version + if(!flipper_format_read_uint32(file, "Data format version", &data_format_version, 1)) { + if(!flipper_format_rewind(file)) break; + } + // Read signature if(!flipper_format_read_hex(file, "Signature", data->signature, sizeof(data->signature))) break; @@ -173,11 +185,18 @@ bool nfc_device_load_mifare_ul_data(FlipperFormat* file, NfcDevice* dev) { } if(!counters_parsed) break; // Read pages - uint32_t pages = 0; - if(!flipper_format_read_uint32(file, "Pages total", &pages, 1)) break; - data->data_size = pages * 4; + uint32_t pages_total = 0; + if(!flipper_format_read_uint32(file, "Pages total", &pages_total, 1)) break; + uint32_t pages_read = 0; + if(data_format_version < nfc_mifare_ultralight_data_format_version) { + pages_read = pages_total; + } else { + if(!flipper_format_read_uint32(file, "Pages read", &pages_read, 1)) break; + } + data->data_size = pages_total * 4; + data->data_read = pages_read * 4; bool pages_parsed = true; - for(uint16_t i = 0; i < pages; i++) { + for(uint16_t i = 0; i < pages_total; i++) { string_printf(temp_str, "Page %d", i); if(!flipper_format_read_hex(file, string_get_cstr(temp_str), &data->data[i * 4], 4)) { pages_parsed = false; @@ -1186,7 +1205,7 @@ void nfc_device_data_clear(NfcDeviceData* dev_data) { } else if(dev_data->protocol == NfcDeviceProtocolMifareClassic) { memset(&dev_data->mf_classic_data, 0, sizeof(MfClassicData)); } else if(dev_data->protocol == NfcDeviceProtocolMifareUl) { - memset(&dev_data->mf_ul_data, 0, sizeof(MfUltralightData)); + mf_ul_reset(&dev_data->mf_ul_data); } else if(dev_data->protocol == NfcDeviceProtocolEMV) { memset(&dev_data->emv_data, 0, sizeof(EmvData)); } diff --git a/lib/nfc/nfc_worker.c b/lib/nfc/nfc_worker.c index 7c3c083b1..a92f148a2 100644 --- a/lib/nfc/nfc_worker.c +++ b/lib/nfc/nfc_worker.c @@ -101,6 +101,8 @@ int32_t nfc_worker_task(void* context) { nfc_worker_emulate_mf_ultralight(nfc_worker); } else if(nfc_worker->state == NfcWorkerStateMfClassicEmulate) { nfc_worker_emulate_mf_classic(nfc_worker); + } else if(nfc_worker->state == NfcWorkerStateReadMfUltralightReadAuth) { + nfc_worker_mf_ultralight_read_auth(nfc_worker); } else if(nfc_worker->state == NfcWorkerStateMfClassicDictAttack) { nfc_worker_mf_classic_dict_attack(nfc_worker); } @@ -416,10 +418,7 @@ void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker) { return; } - FURI_LOG_D( - TAG, - "Start Dictionary attack, Key Count %d", - mf_classic_dict_get_total_keys(dict)); + FURI_LOG_D(TAG, "Start Dictionary attack, Key Count %d", mf_classic_dict_get_total_keys(dict)); for(size_t i = 0; i < total_sectors; i++) { FURI_LOG_I(TAG, "Sector %d", i); nfc_worker->callback(NfcWorkerEventNewSector, nfc_worker->context); @@ -462,20 +461,17 @@ void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker) { } } if(is_key_a_found && is_key_b_found) break; - if(nfc_worker->state != NfcWorkerStateMfClassicDictAttack) - break; + if(nfc_worker->state != NfcWorkerStateMfClassicDictAttack) break; } else { if(!card_removed_notified) { nfc_worker->callback(NfcWorkerEventNoCardDetected, nfc_worker->context); card_removed_notified = true; card_found_notified = false; } - if(nfc_worker->state != NfcWorkerStateMfClassicDictAttack) - break; + if(nfc_worker->state != NfcWorkerStateMfClassicDictAttack) break; } } - if(nfc_worker->state != NfcWorkerStateMfClassicDictAttack) - break; + if(nfc_worker->state != NfcWorkerStateMfClassicDictAttack) break; mf_classic_read_sector(&tx_rx, data, i); mf_classic_dict_rewind(dict); } @@ -518,3 +514,57 @@ void nfc_worker_emulate_mf_classic(NfcWorker* nfc_worker) { rfal_platform_spi_release(); } + +void nfc_worker_mf_ultralight_read_auth(NfcWorker* nfc_worker) { + furi_assert(nfc_worker); + furi_assert(nfc_worker->callback); + + MfUltralightData* data = &nfc_worker->dev_data->mf_ul_data; + FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; + FuriHalNfcTxRxContext tx_rx = {}; + MfUltralightReader reader = {}; + mf_ul_reset(data); + + uint32_t key = 0; + uint16_t pack = 0; + while(nfc_worker->state == NfcWorkerStateReadMfUltralightReadAuth) { + furi_hal_nfc_sleep(); + 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) { + nfc_worker->callback(NfcWorkerEventMfUltralightPassKey, nfc_worker->context); + key = nfc_util_bytes2num(data->auth_key, 4); + } else if(data->auth_method == MfUltralightAuthMethodAmeebo) { + key = mf_ul_pwdgen_amiibo(nfc_data); + } else if(data->auth_method == MfUltralightAuthMethodXiaomi) { + key = mf_ul_pwdgen_xiaomi(nfc_data); + } else { + FURI_LOG_E(TAG, "Incorrect auth method"); + break; + } + + data->auth_success = mf_ultralight_authenticate(&tx_rx, key, &pack); + mf_ul_read_card(&tx_rx, &reader, data); + if(data->auth_success) { + MfUltralightConfigPages* config_pages = mf_ultralight_get_config_pages(data); + if(config_pages != NULL) { + config_pages->auth_data.pwd.value = REVERSE_BYTES_U32(key); + config_pages->auth_data.pack.value = pack; + } + nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context); + break; + } else { + nfc_worker->callback(NfcWorkerEventFail, nfc_worker->context); + break; + } + } else { + nfc_worker->callback(NfcWorkerEventWrongCardDetected, nfc_worker->context); + furi_delay_ms(10); + } + } else { + nfc_worker->callback(NfcWorkerEventNoCardDetected, nfc_worker->context); + furi_delay_ms(10); + } + } +} diff --git a/lib/nfc/nfc_worker.h b/lib/nfc/nfc_worker.h index a3326808b..22cbc3dcc 100755 --- a/lib/nfc/nfc_worker.h +++ b/lib/nfc/nfc_worker.h @@ -14,6 +14,7 @@ typedef enum { NfcWorkerStateUidEmulate, NfcWorkerStateMfUltralightEmulate, NfcWorkerStateMfClassicEmulate, + NfcWorkerStateReadMfUltralightReadAuth, NfcWorkerStateMfClassicDictAttack, // Debug NfcWorkerStateEmulateApdu, @@ -44,6 +45,7 @@ typedef enum { NfcWorkerEventAborted, NfcWorkerEventCardDetected, NfcWorkerEventNoCardDetected, + NfcWorkerEventWrongCardDetected, // Mifare Classic events NfcWorkerEventNoDictFound, @@ -51,6 +53,9 @@ typedef enum { NfcWorkerEventNewDictKeyBatch, NfcWorkerEventFoundKeyA, NfcWorkerEventFoundKeyB, + + // Mifare Ultralight events + NfcWorkerEventMfUltralightPassKey, } NfcWorkerEvent; typedef bool (*NfcWorkerCallback)(NfcWorkerEvent event, void* context); diff --git a/lib/nfc/nfc_worker_i.h b/lib/nfc/nfc_worker_i.h index bb4c31ddb..1e98879a7 100644 --- a/lib/nfc/nfc_worker_i.h +++ b/lib/nfc/nfc_worker_i.h @@ -44,4 +44,8 @@ void nfc_worker_emulate_mf_classic(NfcWorker* nfc_worker); void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker); +void nfc_worker_mf_ultralight_read_auth(NfcWorker* nfc_worker); + +void nfc_worker_mf_ul_auth_attack(NfcWorker* nfc_worker); + void nfc_worker_emulate_apdu(NfcWorker* nfc_worker); diff --git a/lib/nfc/parsers/nfc_supported_card.c b/lib/nfc/parsers/nfc_supported_card.c index 44eee8386..59482a123 100644 --- a/lib/nfc/parsers/nfc_supported_card.c +++ b/lib/nfc/parsers/nfc_supported_card.c @@ -3,10 +3,11 @@ #include "troyka_parser.h" NfcSupportedCard nfc_supported_card[NfcSupportedCardTypeEnd] = { - [NfcSupportedCardTypeTroyka] = { - .protocol = NfcDeviceProtocolMifareClassic, - .verify = troyka_parser_verify, - .read = troyka_parser_read, - .parse = troyka_parser_parse, - }, + [NfcSupportedCardTypeTroyka] = + { + .protocol = NfcDeviceProtocolMifareClassic, + .verify = troyka_parser_verify, + .read = troyka_parser_read, + .parse = troyka_parser_parse, + }, }; diff --git a/lib/nfc/protocols/mifare_ultralight.c b/lib/nfc/protocols/mifare_ultralight.c index 9dcd1d6aa..c043f2069 100644 --- a/lib/nfc/protocols/mifare_ultralight.c +++ b/lib/nfc/protocols/mifare_ultralight.c @@ -1,10 +1,39 @@ #include +#include #include "mifare_ultralight.h" +#include "nfc_util.h" #include +#include "furi_hal_nfc.h" #include #define TAG "MfUltralight" +// Algorithms from: https://github.com/RfidResearchGroup/proxmark3/blob/0f6061c16f072372b7d4d381911f1542afbc3a69/common/generator.c#L110 +uint32_t mf_ul_pwdgen_xiaomi(FuriHalNfcDevData* data) { + uint8_t hash[20]; + mbedtls_sha1(data->uid, data->uid_len, hash); + + uint32_t pwd = 0; + pwd |= (hash[hash[0] % 20]) << 24; + pwd |= (hash[(hash[0] + 5) % 20]) << 16; + pwd |= (hash[(hash[0] + 13) % 20]) << 8; + pwd |= (hash[(hash[0] + 17) % 20]); + + return pwd; +} + +uint32_t mf_ul_pwdgen_amiibo(FuriHalNfcDevData* data) { + uint8_t* uid = data->uid; + + uint32_t pwd = 0; + pwd |= (uid[1] ^ uid[3] ^ 0xAA) << 24; + pwd |= (uid[2] ^ uid[4] ^ 0x55) << 16; + pwd |= (uid[3] ^ uid[5] ^ 0xAA) << 8; + pwd |= uid[4] ^ uid[6] ^ 0x55; + + return pwd; +} + bool mf_ul_check_card_type(uint8_t ATQA0, uint8_t ATQA1, uint8_t SAK) { if((ATQA0 == 0x44) && (ATQA1 == 0x00) && (SAK == 0x00)) { return true; @@ -12,6 +41,20 @@ bool mf_ul_check_card_type(uint8_t ATQA0, uint8_t ATQA1, uint8_t SAK) { return false; } +void mf_ul_reset(MfUltralightData* data) { + furi_assert(data); + data->type = MfUltralightTypeUnknown; + memset(&data->version, 0, sizeof(MfUltralightVersion)); + memset(data->signature, 0, sizeof(data->signature)); + memset(data->counter, 0, sizeof(data->counter)); + memset(data->tearing, 0, sizeof(data->tearing)); + memset(data->data, 0, sizeof(data->data)); + data->data_size = 0; + data->data_read = 0; + data->curr_authlim = 0; + data->has_auth = false; +} + static MfUltralightFeatures mf_ul_get_features(MfUltralightType type) { switch(type) { case MfUltralightTypeUL11: @@ -127,6 +170,37 @@ bool mf_ultralight_read_version( return version_read; } +bool mf_ultralight_authenticate(FuriHalNfcTxRxContext* tx_rx, uint32_t key, uint16_t* pack) { + bool authenticated = false; + + do { + FURI_LOG_D(TAG, "Authenticating"); + tx_rx->tx_data[0] = MF_UL_AUTH; + nfc_util_num2bytes(key, 4, &tx_rx->tx_data[1]); + tx_rx->tx_bits = 40; + tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault; + if(!furi_hal_nfc_tx_rx(tx_rx, 50)) { + FURI_LOG_D(TAG, "Tag did not respond to authentication"); + break; + } + + // PACK + if(tx_rx->rx_bits < 2 * 8) { + FURI_LOG_D(TAG, "Authentication failed"); + break; + } + + if(pack != NULL) { + *pack = (tx_rx->rx_data[0] << 8) | tx_rx->rx_data[1]; + } + + FURI_LOG_I(TAG, "Auth success. Password: %08X. PACK: %04X", key, *pack); + authenticated = true; + } while(false); + + return authenticated; +} + static int16_t mf_ultralight_page_addr_to_tag_addr(uint8_t sector, uint8_t page) { return sector * 256 + page; } @@ -413,7 +487,7 @@ static int16_t mf_ultralight_ntag_i2c_addr_tag_to_lin( } } -static MfUltralightConfigPages* mf_ultralight_get_config_pages(MfUltralightData* data) { +MfUltralightConfigPages* mf_ultralight_get_config_pages(MfUltralightData* data) { if(data->type >= MfUltralightTypeUL11 && data->type <= MfUltralightTypeNTAG216) { return (MfUltralightConfigPages*)&data->data[data->data_size - 4 * 4]; } else if( @@ -516,6 +590,7 @@ bool mf_ultralight_read_pages( tx_rx->tx_data[1] = tag_page; tx_rx->tx_bits = 16; tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault; + if(!furi_hal_nfc_tx_rx(tx_rx, 50) || tx_rx->rx_bits < 16 * 8) { FURI_LOG_D( TAG, @@ -524,17 +599,19 @@ bool mf_ultralight_read_pages( i + (valid_pages > 4 ? 4 : valid_pages) - 1); break; } + if(valid_pages > 4) { pages_read_cnt = 4; } else { pages_read_cnt = valid_pages; } reader->pages_read += pages_read_cnt; - data->data_size = reader->pages_read * 4; memcpy(&data->data[i * 4], tx_rx->rx_data, pages_read_cnt * 4); } + data->data_size = reader->pages_to_read * 4; + data->data_read = reader->pages_read * 4; - return reader->pages_read == reader->pages_to_read; + return reader->pages_read > 0; } bool mf_ultralight_fast_read_pages( @@ -620,6 +697,48 @@ bool mf_ultralight_read_counters(FuriHalNfcTxRxContext* tx_rx, MfUltralightData* return counter_read == (is_single_counter ? 1 : 3); } +int16_t mf_ultralight_get_authlim( + FuriHalNfcTxRxContext* tx_rx, + MfUltralightReader* reader, + MfUltralightData* data) { + mf_ultralight_read_version(tx_rx, reader, data); + if(!(reader->supported_features & MfUltralightSupportAuth)) { + // No authentication + return -2; + } + + uint8_t config_pages_index; + if(data->type >= MfUltralightTypeUL11 && data->type <= MfUltralightTypeNTAG216) { + config_pages_index = reader->pages_to_read - 4; + } else if( + data->type >= MfUltralightTypeNTAGI2CPlus1K && + data->type <= MfUltralightTypeNTAGI2CPlus1K) { + config_pages_index = 0xe3; + } else { + // No config pages + return -2; + } + + if(!mf_ultralight_read_pages_direct(tx_rx, config_pages_index, data->data)) { + // Config pages are not readable due to protection + return -1; + } + + MfUltralightConfigPages* config_pages = (MfUltralightConfigPages*)&data->data; + if(config_pages->auth0 >= reader->pages_to_read) { + // Authentication is not configured + return -2; + } + + int16_t authlim = config_pages->access.authlim; + if(authlim > 0 && data->type >= MfUltralightTypeNTAGI2CPlus1K && + data->type <= MfUltralightTypeNTAGI2CPlus2K) { + authlim = 1 << authlim; + } + + return authlim; +} + bool mf_ultralight_read_tearing_flags(FuriHalNfcTxRxContext* tx_rx, MfUltralightData* data) { uint8_t flag_read = 0; diff --git a/lib/nfc/protocols/mifare_ultralight.h b/lib/nfc/protocols/mifare_ultralight.h index 77dbd1e4e..727bffab2 100644 --- a/lib/nfc/protocols/mifare_ultralight.h +++ b/lib/nfc/protocols/mifare_ultralight.h @@ -28,6 +28,12 @@ #define MF_UL_NTAG203_COUNTER_PAGE (41) +typedef enum { + MfUltralightAuthMethodManual, + MfUltralightAuthMethodAmeebo, + MfUltralightAuthMethodXiaomi, +} MfUltralightAuthMethod; + // Important: order matters; some features are based on positioning in this enum typedef enum { MfUltralightTypeUnknown, @@ -50,6 +56,13 @@ typedef enum { MfUltralightTypeNum, } MfUltralightType; +typedef enum { + MfUltralightAuthLimitUnknown, + MfUltralightAuthLimitNotSupported, + MfUltralightAuthLimitConfigured, + MfUltralightAuthLimitNotConfigured, +} MfUltralightAuthLimit; + typedef enum { MfUltralightSupportNone = 0, MfUltralightSupportFastRead = 1 << 0, @@ -104,9 +117,14 @@ 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; uint16_t curr_authlim; uint16_t data_size; uint8_t data[MF_UL_MAX_DUMP_SIZE]; + uint16_t data_read; } MfUltralightData; typedef struct __attribute__((packed)) { @@ -176,6 +194,8 @@ typedef struct { bool read_counter_incremented; } MfUltralightEmulator; +void mf_ul_reset(MfUltralightData* data); + bool mf_ul_check_card_type(uint8_t ATQA0, uint8_t ATQA1, uint8_t SAK); bool mf_ultralight_read_version( @@ -204,6 +224,10 @@ bool mf_ultralight_read_counters(FuriHalNfcTxRxContext* tx_rx, MfUltralightData* bool mf_ultralight_read_tearing_flags(FuriHalNfcTxRxContext* tx_rx, MfUltralightData* data); +bool mf_ultralight_authenticate(FuriHalNfcTxRxContext* tx_rx, uint32_t key, uint16_t* pack); + +MfUltralightConfigPages* mf_ultralight_get_config_pages(MfUltralightData* data); + bool mf_ul_read_card( FuriHalNfcTxRxContext* tx_rx, MfUltralightReader* reader, @@ -220,3 +244,12 @@ bool mf_ul_prepare_emulation_response( uint16_t* buff_tx_len, uint32_t* data_type, void* context); + +int16_t mf_ultralight_get_authlim( + FuriHalNfcTxRxContext* tx_rx, + MfUltralightReader* reader, + MfUltralightData* data); + +uint32_t mf_ul_pwdgen_amiibo(FuriHalNfcDevData* data); + +uint32_t mf_ul_pwdgen_xiaomi(FuriHalNfcDevData* data);