From 93a6e17ce57222fa7b92a930dd4f9aaee3a146bf Mon Sep 17 00:00:00 2001 From: gornekich Date: Fri, 28 Oct 2022 20:10:16 +0400 Subject: [PATCH] [FL-2933] Mf Classic initial write, update, detect reader (#1941) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * nfc: introduce nfc write * nfc: add write logic * nfc worker: add write state * nfc: add mfc update logic * nfc: add update success logic * nfc: add custom card for detect reader * nfc: update write logic * nfc: add halt command, add notifications * nfc: add write fail scene * nfc: fixes and clean up * nfc: fix navigation ad notifications * nfc: fix detect reader nfc data setter Co-authored-by: あく --- .../main/nfc/scenes/nfc_scene_config.h | 6 + .../main/nfc/scenes/nfc_scene_detect_reader.c | 5 + .../nfc/scenes/nfc_scene_mf_classic_update.c | 98 +++++++ .../nfc_scene_mf_classic_update_success.c | 44 +++ .../nfc/scenes/nfc_scene_mf_classic_write.c | 92 ++++++ .../scenes/nfc_scene_mf_classic_write_fail.c | 58 ++++ .../nfc_scene_mf_classic_write_success.c | 44 +++ .../scenes/nfc_scene_mf_classic_wrong_card.c | 53 ++++ .../main/nfc/scenes/nfc_scene_saved_menu.c | 34 +++ .../main/nfc/scenes/nfc_scene_start.c | 1 + applications/main/nfc/views/detect_reader.c | 36 +++ applications/main/nfc/views/detect_reader.h | 2 + firmware/targets/f7/furi_hal/furi_hal_nfc.c | 10 +- lib/nfc/helpers/reader_analyzer.c | 9 +- lib/nfc/helpers/reader_analyzer.h | 2 + lib/nfc/nfc_device.c | 7 + lib/nfc/nfc_worker.c | 150 +++++++++- lib/nfc/nfc_worker.h | 7 +- lib/nfc/nfc_worker_i.h | 4 + lib/nfc/protocols/crypto1.c | 52 ++++ lib/nfc/protocols/crypto1.h | 14 + lib/nfc/protocols/mifare_classic.c | 271 ++++++++++++------ lib/nfc/protocols/mifare_classic.h | 43 +++ 23 files changed, 949 insertions(+), 93 deletions(-) create mode 100644 applications/main/nfc/scenes/nfc_scene_mf_classic_update.c create mode 100644 applications/main/nfc/scenes/nfc_scene_mf_classic_update_success.c create mode 100644 applications/main/nfc/scenes/nfc_scene_mf_classic_write.c create mode 100644 applications/main/nfc/scenes/nfc_scene_mf_classic_write_fail.c create mode 100644 applications/main/nfc/scenes/nfc_scene_mf_classic_write_success.c create mode 100644 applications/main/nfc/scenes/nfc_scene_mf_classic_wrong_card.c diff --git a/applications/main/nfc/scenes/nfc_scene_config.h b/applications/main/nfc/scenes/nfc_scene_config.h index a25850c8..9b922add 100644 --- a/applications/main/nfc/scenes/nfc_scene_config.h +++ b/applications/main/nfc/scenes/nfc_scene_config.h @@ -36,6 +36,12 @@ ADD_SCENE(nfc, mf_classic_keys_list, MfClassicKeysList) ADD_SCENE(nfc, mf_classic_keys_delete, MfClassicKeysDelete) ADD_SCENE(nfc, mf_classic_keys_warn_duplicate, MfClassicKeysWarnDuplicate) ADD_SCENE(nfc, mf_classic_dict_attack, MfClassicDictAttack) +ADD_SCENE(nfc, mf_classic_write, MfClassicWrite) +ADD_SCENE(nfc, mf_classic_write_success, MfClassicWriteSuccess) +ADD_SCENE(nfc, mf_classic_write_fail, MfClassicWriteFail) +ADD_SCENE(nfc, mf_classic_update, MfClassicUpdate) +ADD_SCENE(nfc, mf_classic_update_success, MfClassicUpdateSuccess) +ADD_SCENE(nfc, mf_classic_wrong_card, MfClassicWrongCard) ADD_SCENE(nfc, emv_read_success, EmvReadSuccess) ADD_SCENE(nfc, emv_menu, EmvMenu) ADD_SCENE(nfc, emulate_apdu_sequence, EmulateApduSequence) diff --git a/applications/main/nfc/scenes/nfc_scene_detect_reader.c b/applications/main/nfc/scenes/nfc_scene_detect_reader.c index abf1437d..74594615 100644 --- a/applications/main/nfc/scenes/nfc_scene_detect_reader.c +++ b/applications/main/nfc/scenes/nfc_scene_detect_reader.c @@ -28,6 +28,11 @@ void nfc_scene_detect_reader_on_enter(void* context) { detect_reader_set_callback(nfc->detect_reader, nfc_scene_detect_reader_callback, nfc); detect_reader_set_nonces_max(nfc->detect_reader, NFC_SCENE_DETECT_READER_PAIR_NONCES_MAX); + NfcDeviceData* dev_data = &nfc->dev->dev_data; + if(dev_data->nfc_data.uid_len) { + detect_reader_set_uid( + nfc->detect_reader, dev_data->nfc_data.uid, dev_data->nfc_data.uid_len); + } // Store number of collected nonces in scene state scene_manager_set_scene_state(nfc->scene_manager, NfcSceneDetectReader, 0); diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_update.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_update.c new file mode 100644 index 00000000..dd3a6f7d --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_update.c @@ -0,0 +1,98 @@ +#include "../nfc_i.h" +#include + +enum { + NfcSceneMfClassicUpdateStateCardSearch, + NfcSceneMfClassicUpdateStateCardFound, +}; + +bool nfc_mf_classic_update_worker_callback(NfcWorkerEvent event, void* context) { + furi_assert(context); + + Nfc* nfc = context; + view_dispatcher_send_custom_event(nfc->view_dispatcher, event); + + return true; +} + +static void nfc_scene_mf_classic_update_setup_view(Nfc* nfc) { + Popup* popup = nfc->popup; + popup_reset(popup); + uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfClassicUpdate); + + if(state == NfcSceneMfClassicUpdateStateCardSearch) { + popup_set_text( + nfc->popup, "Apply the initial\ncard only", 128, 32, AlignRight, AlignCenter); + popup_set_icon(nfc->popup, 0, 8, &I_NFC_manual_60x50); + } else { + popup_set_header(popup, "Updating\nDon't move...", 52, 32, AlignLeft, AlignCenter); + popup_set_icon(popup, 12, 23, &A_Loading_24); + } + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); +} + +void nfc_scene_mf_classic_update_on_enter(void* context) { + Nfc* nfc = context; + DOLPHIN_DEED(DolphinDeedNfcEmulate); + + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneMfClassicUpdate, NfcSceneMfClassicUpdateStateCardSearch); + nfc_scene_mf_classic_update_setup_view(nfc); + + // Setup and start worker + nfc_worker_start( + nfc->worker, + NfcWorkerStateMfClassicUpdate, + &nfc->dev->dev_data, + nfc_mf_classic_update_worker_callback, + nfc); + nfc_blink_emulate_start(nfc); +} + +bool nfc_scene_mf_classic_update_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcWorkerEventSuccess) { + nfc_worker_stop(nfc->worker); + if(nfc_device_save_shadow(nfc->dev, nfc->dev->dev_name)) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicUpdateSuccess); + } else { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicWrongCard); + } + consumed = true; + } else if(event.event == NfcWorkerEventWrongCard) { + nfc_worker_stop(nfc->worker); + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicWrongCard); + consumed = true; + } else if(event.event == NfcWorkerEventCardDetected) { + scene_manager_set_scene_state( + nfc->scene_manager, + NfcSceneMfClassicUpdate, + NfcSceneMfClassicUpdateStateCardFound); + nfc_scene_mf_classic_update_setup_view(nfc); + consumed = true; + } else if(event.event == NfcWorkerEventNoCardDetected) { + scene_manager_set_scene_state( + nfc->scene_manager, + NfcSceneMfClassicUpdate, + NfcSceneMfClassicUpdateStateCardSearch); + nfc_scene_mf_classic_update_setup_view(nfc); + consumed = true; + } + } + return consumed; +} + +void nfc_scene_mf_classic_update_on_exit(void* context) { + Nfc* nfc = context; + nfc_worker_stop(nfc->worker); + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneMfClassicUpdate, NfcSceneMfClassicUpdateStateCardSearch); + // Clear view + popup_reset(nfc->popup); + + nfc_blink_stop(nfc); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_update_success.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_update_success.c new file mode 100644 index 00000000..fef8fd5e --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_update_success.c @@ -0,0 +1,44 @@ +#include "../nfc_i.h" +#include + +void nfc_scene_mf_classic_update_success_popup_callback(void* context) { + Nfc* nfc = context; + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); +} + +void nfc_scene_mf_classic_update_success_on_enter(void* context) { + Nfc* nfc = context; + DOLPHIN_DEED(DolphinDeedNfcSave); + + notification_message(nfc->notifications, &sequence_success); + + Popup* popup = nfc->popup; + popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); + popup_set_header(popup, "Updated!", 11, 20, AlignLeft, AlignBottom); + popup_set_timeout(popup, 1500); + popup_set_context(popup, nfc); + popup_set_callback(popup, nfc_scene_mf_classic_update_success_popup_callback); + popup_enable_timeout(popup); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); +} + +bool nfc_scene_mf_classic_update_success_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventViewExit) { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneFileSelect); + } + } + return consumed; +} + +void nfc_scene_mf_classic_update_success_on_exit(void* context) { + Nfc* nfc = context; + + // Clear view + popup_reset(nfc->popup); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_write.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_write.c new file mode 100644 index 00000000..3543cbc5 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_write.c @@ -0,0 +1,92 @@ +#include "../nfc_i.h" +#include + +enum { + NfcSceneMfClassicWriteStateCardSearch, + NfcSceneMfClassicWriteStateCardFound, +}; + +bool nfc_mf_classic_write_worker_callback(NfcWorkerEvent event, void* context) { + furi_assert(context); + + Nfc* nfc = context; + view_dispatcher_send_custom_event(nfc->view_dispatcher, event); + + return true; +} + +static void nfc_scene_mf_classic_write_setup_view(Nfc* nfc) { + Popup* popup = nfc->popup; + popup_reset(popup); + uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfClassicWrite); + + if(state == NfcSceneMfClassicWriteStateCardSearch) { + popup_set_text( + nfc->popup, "Apply the initial\ncard only", 128, 32, AlignRight, AlignCenter); + popup_set_icon(nfc->popup, 0, 8, &I_NFC_manual_60x50); + } else { + popup_set_header(popup, "Writing\nDon't move...", 52, 32, AlignLeft, AlignCenter); + popup_set_icon(popup, 12, 23, &A_Loading_24); + } + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); +} + +void nfc_scene_mf_classic_write_on_enter(void* context) { + Nfc* nfc = context; + DOLPHIN_DEED(DolphinDeedNfcEmulate); + + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneMfClassicWrite, NfcSceneMfClassicWriteStateCardSearch); + nfc_scene_mf_classic_write_setup_view(nfc); + + // Setup and start worker + nfc_worker_start( + nfc->worker, + NfcWorkerStateMfClassicWrite, + &nfc->dev->dev_data, + nfc_mf_classic_write_worker_callback, + nfc); + nfc_blink_emulate_start(nfc); +} + +bool nfc_scene_mf_classic_write_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcWorkerEventSuccess) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicWriteSuccess); + consumed = true; + } else if(event.event == NfcWorkerEventFail) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicWriteFail); + consumed = true; + } else if(event.event == NfcWorkerEventWrongCard) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicWrongCard); + consumed = true; + } else if(event.event == NfcWorkerEventCardDetected) { + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneMfClassicWrite, NfcSceneMfClassicWriteStateCardFound); + nfc_scene_mf_classic_write_setup_view(nfc); + consumed = true; + } else if(event.event == NfcWorkerEventNoCardDetected) { + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneMfClassicWrite, NfcSceneMfClassicWriteStateCardSearch); + nfc_scene_mf_classic_write_setup_view(nfc); + consumed = true; + } + } + return consumed; +} + +void nfc_scene_mf_classic_write_on_exit(void* context) { + Nfc* nfc = context; + + nfc_worker_stop(nfc->worker); + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneMfClassicWrite, NfcSceneMfClassicWriteStateCardSearch); + // Clear view + popup_reset(nfc->popup); + + nfc_blink_stop(nfc); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_write_fail.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_write_fail.c new file mode 100644 index 00000000..aeea6eef --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_write_fail.c @@ -0,0 +1,58 @@ +#include "../nfc_i.h" + +void nfc_scene_mf_classic_write_fail_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_classic_write_fail_on_enter(void* context) { + Nfc* nfc = context; + Widget* widget = nfc->widget; + + notification_message(nfc->notifications, &sequence_error); + + widget_add_icon_element(widget, 72, 17, &I_DolphinCommon_56x48); + widget_add_string_element( + widget, 7, 4, AlignLeft, AlignTop, FontPrimary, "Writing gone wrong!"); + widget_add_string_multiline_element( + widget, + 7, + 17, + AlignLeft, + AlignTop, + FontSecondary, + "Not all sectors\nwere written\ncorrectly."); + + widget_add_button_element( + widget, GuiButtonTypeLeft, "Finish", nfc_scene_mf_classic_write_fail_widget_callback, nfc); + + // Setup and start worker + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); +} + +bool nfc_scene_mf_classic_write_fail_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeLeft) { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneFileSelect); + } + } else if(event.type == SceneManagerEventTypeBack) { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneSavedMenu); + } + return consumed; +} + +void nfc_scene_mf_classic_write_fail_on_exit(void* context) { + Nfc* nfc = context; + + widget_reset(nfc->widget); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_write_success.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_write_success.c new file mode 100644 index 00000000..2f2a3beb --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_write_success.c @@ -0,0 +1,44 @@ +#include "../nfc_i.h" +#include + +void nfc_scene_mf_classic_write_success_popup_callback(void* context) { + Nfc* nfc = context; + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); +} + +void nfc_scene_mf_classic_write_success_on_enter(void* context) { + Nfc* nfc = context; + DOLPHIN_DEED(DolphinDeedNfcSave); + + notification_message(nfc->notifications, &sequence_success); + + Popup* popup = nfc->popup; + popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); + popup_set_header(popup, "Successfully\nwritten", 13, 22, AlignLeft, AlignBottom); + popup_set_timeout(popup, 1500); + popup_set_context(popup, nfc); + popup_set_callback(popup, nfc_scene_mf_classic_write_success_popup_callback); + popup_enable_timeout(popup); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); +} + +bool nfc_scene_mf_classic_write_success_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventViewExit) { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneFileSelect); + } + } + return consumed; +} + +void nfc_scene_mf_classic_write_success_on_exit(void* context) { + Nfc* nfc = context; + + // Clear view + popup_reset(nfc->popup); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_wrong_card.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_wrong_card.c new file mode 100644 index 00000000..2c56270e --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_wrong_card.c @@ -0,0 +1,53 @@ +#include "../nfc_i.h" + +void nfc_scene_mf_classic_wrong_card_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_classic_wrong_card_on_enter(void* context) { + Nfc* nfc = context; + Widget* widget = nfc->widget; + + notification_message(nfc->notifications, &sequence_error); + + widget_add_icon_element(widget, 73, 17, &I_DolphinCommon_56x48); + widget_add_string_element( + widget, 3, 4, AlignLeft, AlignTop, FontPrimary, "This is wrong card"); + widget_add_string_multiline_element( + widget, + 4, + 17, + AlignLeft, + AlignTop, + FontSecondary, + "Data management\nis only possible\nwith initial card"); + widget_add_button_element( + widget, GuiButtonTypeLeft, "Retry", nfc_scene_mf_classic_wrong_card_widget_callback, nfc); + + // Setup and start worker + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); +} + +bool nfc_scene_mf_classic_wrong_card_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeLeft) { + consumed = scene_manager_previous_scene(nfc->scene_manager); + } + } + return consumed; +} + +void nfc_scene_mf_classic_wrong_card_on_exit(void* context) { + Nfc* nfc = context; + + widget_reset(nfc->widget); +} \ No newline at end of file diff --git a/applications/main/nfc/scenes/nfc_scene_saved_menu.c b/applications/main/nfc/scenes/nfc_scene_saved_menu.c index 09d2c2d6..231f1208 100644 --- a/applications/main/nfc/scenes/nfc_scene_saved_menu.c +++ b/applications/main/nfc/scenes/nfc_scene_saved_menu.c @@ -4,6 +4,9 @@ enum SubmenuIndex { SubmenuIndexEmulate, SubmenuIndexEditUid, + SubmenuIndexDetectReader, + SubmenuIndexWrite, + SubmenuIndexUpdate, SubmenuIndexRename, SubmenuIndexDelete, SubmenuIndexInfo, @@ -42,6 +45,28 @@ void nfc_scene_saved_menu_on_enter(void* context) { submenu_add_item( submenu, "Emulate", SubmenuIndexEmulate, nfc_scene_saved_menu_submenu_callback, nfc); } + if(nfc->dev->format == NfcDeviceSaveFormatMifareClassic) { + if(!mf_classic_is_card_read(&nfc->dev->dev_data.mf_classic_data)) { + submenu_add_item( + submenu, + "Detect reader", + SubmenuIndexDetectReader, + nfc_scene_saved_menu_submenu_callback, + nfc); + } + submenu_add_item( + submenu, + "Write To Initial Card", + SubmenuIndexWrite, + nfc_scene_saved_menu_submenu_callback, + nfc); + submenu_add_item( + submenu, + "Update From Initial Card", + SubmenuIndexUpdate, + nfc_scene_saved_menu_submenu_callback, + nfc); + } submenu_add_item( submenu, "Info", SubmenuIndexInfo, nfc_scene_saved_menu_submenu_callback, nfc); if(nfc->dev->shadow_file_exist) { @@ -79,6 +104,15 @@ bool nfc_scene_saved_menu_on_event(void* context, SceneManagerEvent event) { } DOLPHIN_DEED(DolphinDeedNfcEmulate); consumed = true; + } else if(event.event == SubmenuIndexDetectReader) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneDetectReader); + consumed = true; + } else if(event.event == SubmenuIndexWrite) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicWrite); + consumed = true; + } else if(event.event == SubmenuIndexUpdate) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicUpdate); + consumed = true; } else if(event.event == SubmenuIndexRename) { scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); consumed = true; diff --git a/applications/main/nfc/scenes/nfc_scene_start.c b/applications/main/nfc/scenes/nfc_scene_start.c index 0c4ec1cf..028f85ae 100644 --- a/applications/main/nfc/scenes/nfc_scene_start.c +++ b/applications/main/nfc/scenes/nfc_scene_start.c @@ -53,6 +53,7 @@ bool nfc_scene_start_on_event(void* context, SceneManagerEvent event) { } else if(event.event == SubmenuIndexDetectReader) { bool sd_exist = storage_sd_status(nfc->dev->storage) == FSE_OK; if(sd_exist) { + nfc_device_data_clear(&nfc->dev->dev_data); scene_manager_next_scene(nfc->scene_manager, NfcSceneDetectReader); DOLPHIN_DEED(DolphinDeedNfcDetectReader); } else { diff --git a/applications/main/nfc/views/detect_reader.c b/applications/main/nfc/views/detect_reader.c index 91537868..e5951beb 100644 --- a/applications/main/nfc/views/detect_reader.c +++ b/applications/main/nfc/views/detect_reader.c @@ -2,6 +2,8 @@ #include #include +#define DETECT_READER_UID_MAX_LEN (10) + struct DetectReader { View* view; DetectReaderDoneCallback callback; @@ -12,6 +14,7 @@ typedef struct { uint16_t nonces; uint16_t nonces_max; DetectReaderState state; + FuriString* uid_str; } DetectReaderViewModel; static void detect_reader_draw_callback(Canvas* canvas, void* model) { @@ -23,6 +26,10 @@ static void detect_reader_draw_callback(Canvas* canvas, void* model) { if(m->state == DetectReaderStateStart) { snprintf(text, sizeof(text), "Touch the reader"); canvas_draw_icon(canvas, 21, 13, &I_Move_flipper_26x39); + if(furi_string_size(m->uid_str)) { + elements_multiline_text_aligned( + canvas, 64, 64, AlignCenter, AlignBottom, furi_string_get_cstr(m->uid_str)); + } } else if(m->state == DetectReaderStateReaderDetected) { snprintf(text, sizeof(text), "Move the Flipper away"); canvas_draw_icon(canvas, 24, 25, &I_Release_arrow_18x15); @@ -86,12 +93,24 @@ DetectReader* detect_reader_alloc() { view_set_input_callback(detect_reader->view, detect_reader_input_callback); view_set_context(detect_reader->view, detect_reader); + with_view_model( + detect_reader->view, + DetectReaderViewModel * model, + { model->uid_str = furi_string_alloc(); }, + false); + return detect_reader; } void detect_reader_free(DetectReader* detect_reader) { furi_assert(detect_reader); + with_view_model( + detect_reader->view, + DetectReaderViewModel * model, + { furi_string_free(model->uid_str); }, + false); + view_free(detect_reader->view); free(detect_reader); } @@ -106,6 +125,7 @@ void detect_reader_reset(DetectReader* detect_reader) { model->nonces = 0; model->nonces_max = 0; model->state = DetectReaderStateStart; + furi_string_reset(model->uid_str); }, false); } @@ -152,3 +172,19 @@ void detect_reader_set_state(DetectReader* detect_reader, DetectReaderState stat with_view_model( detect_reader->view, DetectReaderViewModel * model, { model->state = state; }, true); } + +void detect_reader_set_uid(DetectReader* detect_reader, uint8_t* uid, uint8_t uid_len) { + furi_assert(detect_reader); + furi_assert(uid); + furi_assert(uid_len < DETECT_READER_UID_MAX_LEN); + with_view_model( + detect_reader->view, + DetectReaderViewModel * model, + { + furi_string_set_str(model->uid_str, "UID:"); + for(size_t i = 0; i < uid_len; i++) { + furi_string_cat_printf(model->uid_str, " %02X", uid[i]); + } + }, + true); +} diff --git a/applications/main/nfc/views/detect_reader.h b/applications/main/nfc/views/detect_reader.h index aabdd7c8..6481216b 100644 --- a/applications/main/nfc/views/detect_reader.h +++ b/applications/main/nfc/views/detect_reader.h @@ -32,3 +32,5 @@ void detect_reader_set_nonces_max(DetectReader* detect_reader, uint16_t nonces_m void detect_reader_set_nonces_collected(DetectReader* detect_reader, uint16_t nonces_collected); void detect_reader_set_state(DetectReader* detect_reader, DetectReaderState state); + +void detect_reader_set_uid(DetectReader* detect_reader, uint8_t* uid, uint8_t uid_len); diff --git a/firmware/targets/f7/furi_hal/furi_hal_nfc.c b/firmware/targets/f7/furi_hal/furi_hal_nfc.c index 069ac4ea..3ebf4f82 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_nfc.c +++ b/firmware/targets/f7/furi_hal/furi_hal_nfc.c @@ -620,6 +620,10 @@ uint16_t furi_hal_nfc_bitstream_to_data_and_parity( uint16_t in_buff_bits, uint8_t* out_data, uint8_t* out_parity) { + if(in_buff_bits < 8) { + out_data[0] = in_buff[0]; + return in_buff_bits; + } if(in_buff_bits % 9 != 0) { return 0; } @@ -635,7 +639,7 @@ uint16_t furi_hal_nfc_bitstream_to_data_and_parity( bit_processed += 9; curr_byte++; } - return curr_byte; + return curr_byte * 8; } bool furi_hal_nfc_tx_rx(FuriHalNfcTxRxContext* tx_rx, uint16_t timeout_ms) { @@ -692,8 +696,8 @@ bool furi_hal_nfc_tx_rx(FuriHalNfcTxRxContext* tx_rx, uint16_t timeout_ms) { if(tx_rx->tx_rx_type == FuriHalNfcTxRxTypeRaw || tx_rx->tx_rx_type == FuriHalNfcTxRxTypeRxRaw) { - tx_rx->rx_bits = 8 * furi_hal_nfc_bitstream_to_data_and_parity( - temp_rx_buff, *temp_rx_bits, tx_rx->rx_data, tx_rx->rx_parity); + tx_rx->rx_bits = furi_hal_nfc_bitstream_to_data_and_parity( + temp_rx_buff, *temp_rx_bits, tx_rx->rx_data, tx_rx->rx_parity); } else { memcpy(tx_rx->rx_data, temp_rx_buff, MIN(*temp_rx_bits / 8, FURI_HAL_NFC_DATA_BUFF_SIZE)); tx_rx->rx_bits = *temp_rx_bits; diff --git a/lib/nfc/helpers/reader_analyzer.c b/lib/nfc/helpers/reader_analyzer.c index 0ba657a2..7fed9c6f 100644 --- a/lib/nfc/helpers/reader_analyzer.c +++ b/lib/nfc/helpers/reader_analyzer.c @@ -201,10 +201,17 @@ NfcProtocol FuriHalNfcDevData* reader_analyzer_get_nfc_data(ReaderAnalyzer* instance) { furi_assert(instance); - + instance->nfc_data = reader_analyzer_nfc_data[ReaderAnalyzerNfcDataMfClassic]; return &instance->nfc_data; } +void reader_analyzer_set_nfc_data(ReaderAnalyzer* instance, FuriHalNfcDevData* nfc_data) { + furi_assert(instance); + furi_assert(nfc_data); + + memcpy(&instance->nfc_data, nfc_data, sizeof(FuriHalNfcDevData)); +} + static void reader_analyzer_write( ReaderAnalyzer* instance, uint8_t* data, diff --git a/lib/nfc/helpers/reader_analyzer.h b/lib/nfc/helpers/reader_analyzer.h index cc501f5a..13bf4d77 100644 --- a/lib/nfc/helpers/reader_analyzer.h +++ b/lib/nfc/helpers/reader_analyzer.h @@ -35,6 +35,8 @@ NfcProtocol FuriHalNfcDevData* reader_analyzer_get_nfc_data(ReaderAnalyzer* instance); +void reader_analyzer_set_nfc_data(ReaderAnalyzer* instance, FuriHalNfcDevData* nfc_data); + void reader_analyzer_prepare_tx_rx( ReaderAnalyzer* instance, FuriHalNfcTxRxContext* tx_rx, diff --git a/lib/nfc/nfc_device.c b/lib/nfc/nfc_device.c index 740cfae5..a5e3fc14 100644 --- a/lib/nfc/nfc_device.c +++ b/lib/nfc/nfc_device.c @@ -1122,6 +1122,13 @@ static bool nfc_device_load_data(NfcDevice* dev, FuriString* path, bool show_dia if(!flipper_format_read_hex(file, "UID", data->uid, data->uid_len)) break; if(!flipper_format_read_hex(file, "ATQA", data->atqa, 2)) break; if(!flipper_format_read_hex(file, "SAK", &data->sak, 1)) break; + // Load CUID + uint8_t* cuid_start = data->uid; + if(data->uid_len == 7) { + cuid_start = &data->uid[3]; + } + data->cuid = (cuid_start[0] << 24) | (cuid_start[1] << 16) | (cuid_start[2] << 8) | + (cuid_start[3]); // Parse other data if(dev->format == NfcDeviceSaveFormatMifareUl) { if(!nfc_device_load_mifare_ul_data(file, dev)) break; diff --git a/lib/nfc/nfc_worker.c b/lib/nfc/nfc_worker.c index ebe20390..e1e379a0 100644 --- a/lib/nfc/nfc_worker.c +++ b/lib/nfc/nfc_worker.c @@ -99,6 +99,10 @@ 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 == NfcWorkerStateMfClassicWrite) { + nfc_worker_write_mf_classic(nfc_worker); + } else if(nfc_worker->state == NfcWorkerStateMfClassicUpdate) { + nfc_worker_update_mf_classic(nfc_worker); } else if(nfc_worker->state == NfcWorkerStateReadMfUltralightReadAuth) { nfc_worker_mf_ultralight_read_auth(nfc_worker); } else if(nfc_worker->state == NfcWorkerStateMfClassicDictAttack) { @@ -666,6 +670,144 @@ void nfc_worker_emulate_mf_classic(NfcWorker* nfc_worker) { rfal_platform_spi_release(); } +void nfc_worker_write_mf_classic(NfcWorker* nfc_worker) { + FuriHalNfcTxRxContext tx_rx = {}; + bool card_found_notified = false; + FuriHalNfcDevData nfc_data = {}; + MfClassicData* src_data = &nfc_worker->dev_data->mf_classic_data; + MfClassicData dest_data = *src_data; + + while(nfc_worker->state == NfcWorkerStateMfClassicWrite) { + if(furi_hal_nfc_detect(&nfc_data, 200)) { + if(!card_found_notified) { + nfc_worker->callback(NfcWorkerEventCardDetected, nfc_worker->context); + card_found_notified = true; + } + furi_hal_nfc_sleep(); + + FURI_LOG_I(TAG, "Check low level nfc data"); + if(memcmp(&nfc_data, &nfc_worker->dev_data->nfc_data, sizeof(FuriHalNfcDevData))) { + FURI_LOG_E(TAG, "Wrong card"); + nfc_worker->callback(NfcWorkerEventWrongCard, nfc_worker->context); + break; + } + + FURI_LOG_I(TAG, "Check mf classic type"); + MfClassicType type = + mf_classic_get_classic_type(nfc_data.atqa[0], nfc_data.atqa[1], nfc_data.sak); + if(type != nfc_worker->dev_data->mf_classic_data.type) { + FURI_LOG_E(TAG, "Wrong mf classic type"); + nfc_worker->callback(NfcWorkerEventWrongCard, nfc_worker->context); + break; + } + + // Set blocks not read + mf_classic_set_sector_data_not_read(&dest_data); + FURI_LOG_I(TAG, "Updating card sectors"); + uint8_t total_sectors = mf_classic_get_total_sectors_num(type); + bool write_success = true; + for(uint8_t i = 0; i < total_sectors; i++) { + FURI_LOG_I(TAG, "Reading sector %d", i); + mf_classic_read_sector(&tx_rx, &dest_data, i); + bool old_data_read = mf_classic_is_sector_data_read(src_data, i); + bool new_data_read = mf_classic_is_sector_data_read(&dest_data, i); + if(old_data_read != new_data_read) { + FURI_LOG_E(TAG, "Failed to update sector %d", i); + write_success = false; + break; + } + if(nfc_worker->state != NfcWorkerStateMfClassicWrite) break; + if(!mf_classic_write_sector(&tx_rx, &dest_data, src_data, i)) { + FURI_LOG_E(TAG, "Failed to write %d sector", i); + write_success = false; + break; + } + } + if(nfc_worker->state != NfcWorkerStateMfClassicWrite) break; + if(write_success) { + nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context); + break; + } else { + nfc_worker->callback(NfcWorkerEventFail, nfc_worker->context); + break; + } + + } else { + if(card_found_notified) { + nfc_worker->callback(NfcWorkerEventNoCardDetected, nfc_worker->context); + card_found_notified = false; + } + } + furi_delay_ms(300); + } +} + +void nfc_worker_update_mf_classic(NfcWorker* nfc_worker) { + FuriHalNfcTxRxContext tx_rx = {}; + bool card_found_notified = false; + FuriHalNfcDevData nfc_data = {}; + MfClassicData* old_data = &nfc_worker->dev_data->mf_classic_data; + MfClassicData new_data = *old_data; + + while(nfc_worker->state == NfcWorkerStateMfClassicUpdate) { + if(furi_hal_nfc_detect(&nfc_data, 200)) { + if(!card_found_notified) { + nfc_worker->callback(NfcWorkerEventCardDetected, nfc_worker->context); + card_found_notified = true; + } + furi_hal_nfc_sleep(); + + FURI_LOG_I(TAG, "Check low level nfc data"); + if(memcmp(&nfc_data, &nfc_worker->dev_data->nfc_data, sizeof(FuriHalNfcDevData))) { + FURI_LOG_E(TAG, "Low level nfc data mismatch"); + nfc_worker->callback(NfcWorkerEventWrongCard, nfc_worker->context); + break; + } + + FURI_LOG_I(TAG, "Check MF classic type"); + MfClassicType type = + mf_classic_get_classic_type(nfc_data.atqa[0], nfc_data.atqa[1], nfc_data.sak); + if(type != nfc_worker->dev_data->mf_classic_data.type) { + FURI_LOG_E(TAG, "MF classic type mismatch"); + nfc_worker->callback(NfcWorkerEventWrongCard, nfc_worker->context); + break; + } + + // Set blocks not read + mf_classic_set_sector_data_not_read(&new_data); + FURI_LOG_I(TAG, "Updating card sectors"); + uint8_t total_sectors = mf_classic_get_total_sectors_num(type); + bool update_success = true; + for(uint8_t i = 0; i < total_sectors; i++) { + FURI_LOG_I(TAG, "Reading sector %d", i); + mf_classic_read_sector(&tx_rx, &new_data, i); + bool old_data_read = mf_classic_is_sector_data_read(old_data, i); + bool new_data_read = mf_classic_is_sector_data_read(&new_data, i); + if(old_data_read != new_data_read) { + FURI_LOG_E(TAG, "Failed to update sector %d", i); + update_success = false; + break; + } + if(nfc_worker->state != NfcWorkerStateMfClassicUpdate) break; + } + if(nfc_worker->state != NfcWorkerStateMfClassicUpdate) break; + + // Check updated data + if(update_success) { + *old_data = new_data; + nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context); + break; + } + } else { + if(card_found_notified) { + nfc_worker->callback(NfcWorkerEventNoCardDetected, nfc_worker->context); + card_found_notified = false; + } + } + furi_delay_ms(300); + } +} + void nfc_worker_mf_ultralight_read_auth(NfcWorker* nfc_worker) { furi_assert(nfc_worker); furi_assert(nfc_worker->callback); @@ -758,7 +900,13 @@ void nfc_worker_analyze_reader(NfcWorker* nfc_worker) { FuriHalNfcTxRxContext tx_rx = {}; ReaderAnalyzer* reader_analyzer = nfc_worker->reader_analyzer; - FuriHalNfcDevData* nfc_data = reader_analyzer_get_nfc_data(reader_analyzer); + FuriHalNfcDevData* nfc_data = NULL; + if(nfc_worker->dev_data->protocol == NfcDeviceProtocolMifareClassic) { + nfc_data = &nfc_worker->dev_data->nfc_data; + reader_analyzer_set_nfc_data(reader_analyzer, nfc_data); + } else { + nfc_data = reader_analyzer_get_nfc_data(reader_analyzer); + } MfClassicEmulator emulator = { .cuid = nfc_util_bytes2num(&nfc_data->uid[nfc_data->uid_len - 4], 4), .data = nfc_worker->dev_data->mf_classic_data, diff --git a/lib/nfc/nfc_worker.h b/lib/nfc/nfc_worker.h index 84615f5d..ce3a1824 100644 --- a/lib/nfc/nfc_worker.h +++ b/lib/nfc/nfc_worker.h @@ -14,6 +14,8 @@ typedef enum { NfcWorkerStateUidEmulate, NfcWorkerStateMfUltralightEmulate, NfcWorkerStateMfClassicEmulate, + NfcWorkerStateMfClassicWrite, + NfcWorkerStateMfClassicUpdate, NfcWorkerStateReadMfUltralightReadAuth, NfcWorkerStateMfClassicDictAttack, NfcWorkerStateAnalyzeReader, @@ -48,13 +50,16 @@ typedef enum { NfcWorkerEventNoCardDetected, NfcWorkerEventWrongCardDetected, - // Mifare Classic events + // Read Mifare Classic events NfcWorkerEventNoDictFound, NfcWorkerEventNewSector, NfcWorkerEventNewDictKeyBatch, NfcWorkerEventFoundKeyA, NfcWorkerEventFoundKeyB, + // Write Mifare Classic events + NfcWorkerEventWrongCard, + // Detect Reader events NfcWorkerEventDetectReaderDetected, NfcWorkerEventDetectReaderLost, diff --git a/lib/nfc/nfc_worker_i.h b/lib/nfc/nfc_worker_i.h index 526182f9..b9f69e62 100644 --- a/lib/nfc/nfc_worker_i.h +++ b/lib/nfc/nfc_worker_i.h @@ -41,6 +41,10 @@ void nfc_worker_emulate_mf_ultralight(NfcWorker* nfc_worker); void nfc_worker_emulate_mf_classic(NfcWorker* nfc_worker); +void nfc_worker_write_mf_classic(NfcWorker* nfc_worker); + +void nfc_worker_update_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); diff --git a/lib/nfc/protocols/crypto1.c b/lib/nfc/protocols/crypto1.c index f08164ba..2ac0ff08 100644 --- a/lib/nfc/protocols/crypto1.c +++ b/lib/nfc/protocols/crypto1.c @@ -73,3 +73,55 @@ uint32_t prng_successor(uint32_t x, uint32_t n) { return SWAPENDIAN(x); } + +void crypto1_decrypt( + Crypto1* crypto, + uint8_t* encrypted_data, + uint16_t encrypted_data_bits, + uint8_t* decrypted_data) { + furi_assert(crypto); + furi_assert(encrypted_data); + furi_assert(decrypted_data); + + if(encrypted_data_bits < 8) { + uint8_t decrypted_byte = 0; + decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_data[0], 0)) << 0; + decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_data[0], 1)) << 1; + decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_data[0], 2)) << 2; + decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_data[0], 3)) << 3; + decrypted_data[0] = decrypted_byte; + } else { + for(size_t i = 0; i < encrypted_data_bits / 8; i++) { + decrypted_data[i] = crypto1_byte(crypto, 0, 0) ^ encrypted_data[i]; + } + } +} + +void crypto1_encrypt( + Crypto1* crypto, + uint8_t* keystream, + uint8_t* plain_data, + uint16_t plain_data_bits, + uint8_t* encrypted_data, + uint8_t* encrypted_parity) { + furi_assert(crypto); + furi_assert(plain_data); + furi_assert(encrypted_data); + furi_assert(encrypted_parity); + + if(plain_data_bits < 8) { + encrypted_data[0] = 0; + for(size_t i = 0; i < plain_data_bits; i++) { + encrypted_data[0] |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(plain_data[0], i)) << i; + } + } else { + memset(encrypted_parity, 0, plain_data_bits / 8 + 1); + for(uint8_t i = 0; i < plain_data_bits / 8; i++) { + encrypted_data[i] = crypto1_byte(crypto, keystream ? keystream[i] : 0, 0) ^ + plain_data[i]; + encrypted_parity[i / 8] |= + (((crypto1_filter(crypto->odd) ^ nfc_util_odd_parity8(plain_data[i])) & 0x01) + << (7 - (i & 0x0007))); + } + } +} diff --git a/lib/nfc/protocols/crypto1.h b/lib/nfc/protocols/crypto1.h index 07b39c22..450d1534 100644 --- a/lib/nfc/protocols/crypto1.h +++ b/lib/nfc/protocols/crypto1.h @@ -21,3 +21,17 @@ uint32_t crypto1_word(Crypto1* crypto1, uint32_t in, int is_encrypted); uint32_t crypto1_filter(uint32_t in); uint32_t prng_successor(uint32_t x, uint32_t n); + +void crypto1_decrypt( + Crypto1* crypto, + uint8_t* encrypted_data, + uint16_t encrypted_data_bits, + uint8_t* decrypted_data); + +void crypto1_encrypt( + Crypto1* crypto, + uint8_t* keystream, + uint8_t* plain_data, + uint16_t plain_data_bits, + uint8_t* encrypted_data, + uint8_t* encrypted_parity); diff --git a/lib/nfc/protocols/mifare_classic.c b/lib/nfc/protocols/mifare_classic.c index e879ff4e..7b0e1797 100644 --- a/lib/nfc/protocols/mifare_classic.c +++ b/lib/nfc/protocols/mifare_classic.c @@ -9,21 +9,8 @@ #define MF_CLASSIC_AUTH_KEY_A_CMD (0x60U) #define MF_CLASSIC_AUTH_KEY_B_CMD (0x61U) -#define MF_CLASSIC_READ_SECT_CMD (0x30) - -typedef enum { - MfClassicActionDataRead, - MfClassicActionDataWrite, - MfClassicActionDataInc, - MfClassicActionDataDec, - - MfClassicActionKeyARead, - MfClassicActionKeyAWrite, - MfClassicActionKeyBRead, - MfClassicActionKeyBWrite, - MfClassicActionACRead, - MfClassicActionACWrite, -} MfClassicAction; +#define MF_CLASSIC_READ_BLOCK_CMD (0x30) +#define MF_CLASSIC_WRITE_BLOCK_CMD (0xA0) const char* mf_classic_get_type_str(MfClassicType type) { if(type == MfClassicType1k) { @@ -122,6 +109,24 @@ void mf_classic_set_block_read(MfClassicData* data, uint8_t block_num, MfClassic FURI_BIT_SET(data->block_read_mask[block_num / 32], block_num % 32); } +bool mf_classic_is_sector_data_read(MfClassicData* data, uint8_t sector_num) { + furi_assert(data); + + uint8_t first_block = mf_classic_get_first_block_num_of_sector(sector_num); + uint8_t total_blocks = mf_classic_get_blocks_num_in_sector(sector_num); + bool data_read = true; + for(size_t i = first_block; i < first_block + total_blocks; i++) { + data_read &= mf_classic_is_block_read(data, i); + } + + return data_read; +} + +void mf_classic_set_sector_data_not_read(MfClassicData* data) { + furi_assert(data); + memset(data->block_read_mask, 0, sizeof(data->block_read_mask)); +} + bool mf_classic_is_key_found(MfClassicData* data, uint8_t sector_num, MfClassicKey key_type) { furi_assert(data); @@ -190,6 +195,9 @@ void mf_classic_get_read_sectors_and_keys( uint8_t* sectors_read, uint8_t* keys_found) { furi_assert(data); + furi_assert(sectors_read); + furi_assert(keys_found); + *sectors_read = 0; *keys_found = 0; uint8_t sectors_total = mf_classic_get_total_sectors_num(data->type); @@ -225,12 +233,12 @@ bool mf_classic_is_card_read(MfClassicData* data) { return card_read; } -static bool mf_classic_is_allowed_access_sector_trailer( - MfClassicEmulator* emulator, +bool mf_classic_is_allowed_access_sector_trailer( + MfClassicData* data, uint8_t block_num, MfClassicKey key, MfClassicAction action) { - uint8_t* sector_trailer = emulator->data.block[block_num].value; + uint8_t* sector_trailer = data->block[block_num].value; uint8_t AC = ((sector_trailer[7] >> 5) & 0x04) | ((sector_trailer[8] >> 2) & 0x02) | ((sector_trailer[8] >> 7) & 0x01); switch(action) { @@ -266,13 +274,13 @@ static bool mf_classic_is_allowed_access_sector_trailer( return true; } -static bool mf_classic_is_allowed_access_data_block( - MfClassicEmulator* emulator, +bool mf_classic_is_allowed_access_data_block( + MfClassicData* data, uint8_t block_num, MfClassicKey key, MfClassicAction action) { uint8_t* sector_trailer = - emulator->data.block[mf_classic_get_sector_trailer_num_by_block(block_num)].value; + data->block[mf_classic_get_sector_trailer_num_by_block(block_num)].value; uint8_t sector_block; if(block_num <= 128) { @@ -336,9 +344,10 @@ static bool mf_classic_is_allowed_access( MfClassicKey key, MfClassicAction action) { if(mf_classic_is_sector_trailer(block_num)) { - return mf_classic_is_allowed_access_sector_trailer(emulator, block_num, key, action); + return mf_classic_is_allowed_access_sector_trailer( + &emulator->data, block_num, key, action); } else { - return mf_classic_is_allowed_access_data_block(emulator, block_num, key, action); + return mf_classic_is_allowed_access_data_block(&emulator->data, block_num, key, action); } } @@ -514,25 +523,17 @@ bool mf_classic_read_block( furi_assert(block); bool read_block_success = false; - uint8_t plain_cmd[4] = {MF_CLASSIC_READ_SECT_CMD, block_num, 0x00, 0x00}; + uint8_t plain_cmd[4] = {MF_CLASSIC_READ_BLOCK_CMD, block_num, 0x00, 0x00}; nfca_append_crc16(plain_cmd, 2); - memset(tx_rx->tx_data, 0, sizeof(tx_rx->tx_data)); - memset(tx_rx->tx_parity, 0, sizeof(tx_rx->tx_parity)); - for(uint8_t i = 0; i < 4; i++) { - tx_rx->tx_data[i] = crypto1_byte(crypto, 0x00, 0) ^ plain_cmd[i]; - tx_rx->tx_parity[0] |= - ((crypto1_filter(crypto->odd) ^ nfc_util_odd_parity8(plain_cmd[i])) & 0x01) << (7 - i); - } + crypto1_encrypt(crypto, NULL, plain_cmd, 4 * 8, tx_rx->tx_data, tx_rx->tx_parity); tx_rx->tx_bits = 4 * 9; tx_rx->tx_rx_type = FuriHalNfcTxRxTypeRaw; if(furi_hal_nfc_tx_rx(tx_rx, 50)) { if(tx_rx->rx_bits == 8 * (MF_CLASSIC_BLOCK_SIZE + 2)) { uint8_t block_received[MF_CLASSIC_BLOCK_SIZE + 2]; - for(uint8_t i = 0; i < MF_CLASSIC_BLOCK_SIZE + 2; i++) { - block_received[i] = crypto1_byte(crypto, 0, 0) ^ tx_rx->rx_data[i]; - } + crypto1_decrypt(crypto, tx_rx->rx_data, tx_rx->rx_bits, block_received); uint16_t crc_calc = nfca_get_crc16(block_received, MF_CLASSIC_BLOCK_SIZE); uint16_t crc_received = (block_received[MF_CLASSIC_BLOCK_SIZE + 1] << 8) | block_received[MF_CLASSIC_BLOCK_SIZE]; @@ -754,49 +755,6 @@ uint8_t mf_classic_update_card(FuriHalNfcTxRxContext* tx_rx, MfClassicData* data return sectors_read; } -void mf_crypto1_decrypt( - Crypto1* crypto, - uint8_t* encrypted_data, - uint16_t encrypted_data_bits, - uint8_t* decrypted_data) { - if(encrypted_data_bits < 8) { - uint8_t decrypted_byte = 0; - decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_data[0], 0)) << 0; - decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_data[0], 1)) << 1; - decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_data[0], 2)) << 2; - decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_data[0], 3)) << 3; - decrypted_data[0] = decrypted_byte; - } else { - for(size_t i = 0; i < encrypted_data_bits / 8; i++) { - decrypted_data[i] = crypto1_byte(crypto, 0, 0) ^ encrypted_data[i]; - } - } -} - -void mf_crypto1_encrypt( - Crypto1* crypto, - uint8_t* keystream, - uint8_t* plain_data, - uint16_t plain_data_bits, - uint8_t* encrypted_data, - uint8_t* encrypted_parity) { - if(plain_data_bits < 8) { - encrypted_data[0] = 0; - for(size_t i = 0; i < plain_data_bits; i++) { - encrypted_data[0] |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(plain_data[0], i)) << i; - } - } else { - memset(encrypted_parity, 0, plain_data_bits / 8 + 1); - for(uint8_t i = 0; i < plain_data_bits / 8; i++) { - encrypted_data[i] = crypto1_byte(crypto, keystream ? keystream[i] : 0, 0) ^ - plain_data[i]; - encrypted_parity[i / 8] |= - (((crypto1_filter(crypto->odd) ^ nfc_util_odd_parity8(plain_data[i])) & 0x01) - << (7 - (i & 0x0007))); - } - } -} - bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_rx) { furi_assert(emulator); furi_assert(tx_rx); @@ -819,7 +777,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ tx_rx->rx_bits); break; } - mf_crypto1_decrypt(&emulator->crypto, tx_rx->rx_data, tx_rx->rx_bits, plain_data); + crypto1_decrypt(&emulator->crypto, tx_rx->rx_data, tx_rx->rx_bits, plain_data); } if(plain_data[0] == 0x50 && plain_data[1] == 0x00) { @@ -857,7 +815,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ tx_rx->tx_bits = sizeof(nt) * 8; tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent; } else { - mf_crypto1_encrypt( + crypto1_encrypt( &emulator->crypto, nt_keystream, nt, @@ -904,7 +862,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ uint32_t ans = prng_successor(nonce, 96); uint8_t responce[4] = {}; nfc_util_num2bytes(ans, 4, responce); - mf_crypto1_encrypt( + crypto1_encrypt( &emulator->crypto, NULL, responce, @@ -938,7 +896,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ // Send NACK uint8_t nack = 0x04; if(is_encrypted) { - mf_crypto1_encrypt( + crypto1_encrypt( &emulator->crypto, NULL, &nack, 4, tx_rx->tx_data, tx_rx->tx_parity); } else { tx_rx->tx_data[0] = nack; @@ -951,7 +909,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ } nfca_append_crc16(block_data, 16); - mf_crypto1_encrypt( + crypto1_encrypt( &emulator->crypto, NULL, block_data, @@ -967,14 +925,14 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ } // Send ACK uint8_t ack = 0x0A; - mf_crypto1_encrypt(&emulator->crypto, NULL, &ack, 4, tx_rx->tx_data, tx_rx->tx_parity); + crypto1_encrypt(&emulator->crypto, NULL, &ack, 4, tx_rx->tx_data, tx_rx->tx_parity); tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent; tx_rx->tx_bits = 4; if(!furi_hal_nfc_tx_rx(tx_rx, 300)) break; if(tx_rx->rx_bits != 18 * 8) break; - mf_crypto1_decrypt(&emulator->crypto, tx_rx->rx_data, tx_rx->rx_bits, plain_data); + crypto1_decrypt(&emulator->crypto, tx_rx->rx_data, tx_rx->rx_bits, plain_data); uint8_t block_data[16] = {}; memcpy(block_data, emulator->data.block[block].value, MF_CLASSIC_BLOCK_SIZE); if(mf_classic_is_sector_trailer(block)) { @@ -1002,7 +960,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ } // Send ACK ack = 0x0A; - mf_crypto1_encrypt(&emulator->crypto, NULL, &ack, 4, tx_rx->tx_data, tx_rx->tx_parity); + crypto1_encrypt(&emulator->crypto, NULL, &ack, 4, tx_rx->tx_data, tx_rx->tx_parity); tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent; tx_rx->tx_bits = 4; } else { @@ -1015,8 +973,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ // Send NACK uint8_t nack = 0x04; if(is_encrypted) { - mf_crypto1_encrypt( - &emulator->crypto, NULL, &nack, 4, tx_rx->tx_data, tx_rx->tx_parity); + crypto1_encrypt(&emulator->crypto, NULL, &nack, 4, tx_rx->tx_data, tx_rx->tx_parity); } else { tx_rx->tx_data[0] = nack; } @@ -1027,3 +984,143 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ return true; } + +bool mf_classic_write_block( + FuriHalNfcTxRxContext* tx_rx, + MfClassicBlock* src_block, + uint8_t block_num, + MfClassicKey key_type, + uint64_t key) { + furi_assert(tx_rx); + furi_assert(src_block); + + Crypto1 crypto = {}; + uint8_t plain_data[18] = {}; + uint8_t resp = 0; + bool write_success = false; + + do { + furi_hal_nfc_sleep(); + if(!mf_classic_auth(tx_rx, block_num, key, key_type, &crypto)) { + FURI_LOG_D(TAG, "Auth fail"); + break; + } + // Send write command + plain_data[0] = MF_CLASSIC_WRITE_BLOCK_CMD; + plain_data[1] = block_num; + nfca_append_crc16(plain_data, 2); + crypto1_encrypt(&crypto, NULL, plain_data, 4 * 8, tx_rx->tx_data, tx_rx->tx_parity); + tx_rx->tx_bits = 4 * 8; + tx_rx->tx_rx_type = FuriHalNfcTxRxTypeRaw; + + if(furi_hal_nfc_tx_rx(tx_rx, 50)) { + if(tx_rx->rx_bits == 4) { + crypto1_decrypt(&crypto, tx_rx->rx_data, 4, &resp); + if(resp != 0x0A) { + FURI_LOG_D(TAG, "NACK received on write cmd: %02X", resp); + break; + } + } else { + FURI_LOG_D(TAG, "Not ACK received"); + break; + } + } else { + FURI_LOG_D(TAG, "Failed to send write cmd"); + break; + } + + // Send data + memcpy(plain_data, src_block->value, MF_CLASSIC_BLOCK_SIZE); + nfca_append_crc16(plain_data, MF_CLASSIC_BLOCK_SIZE); + crypto1_encrypt( + &crypto, + NULL, + plain_data, + (MF_CLASSIC_BLOCK_SIZE + 2) * 8, + tx_rx->tx_data, + tx_rx->tx_parity); + tx_rx->tx_bits = (MF_CLASSIC_BLOCK_SIZE + 2) * 8; + tx_rx->tx_rx_type = FuriHalNfcTxRxTypeRaw; + if(furi_hal_nfc_tx_rx(tx_rx, 50)) { + if(tx_rx->rx_bits == 4) { + crypto1_decrypt(&crypto, tx_rx->rx_data, 4, &resp); + if(resp != 0x0A) { + FURI_LOG_D(TAG, "NACK received on sending data"); + break; + } + } else { + FURI_LOG_D(TAG, "Not ACK received"); + break; + } + } else { + FURI_LOG_D(TAG, "Failed to send data"); + break; + } + write_success = true; + + // Send Halt + plain_data[0] = 0x50; + plain_data[1] = 0x00; + nfca_append_crc16(plain_data, 2); + crypto1_encrypt(&crypto, NULL, plain_data, 2 * 8, tx_rx->tx_data, tx_rx->tx_parity); + tx_rx->tx_bits = 2 * 8; + tx_rx->tx_rx_type = FuriHalNfcTxRxTypeRaw; + // No response is expected + furi_hal_nfc_tx_rx(tx_rx, 50); + } while(false); + + return write_success; +} + +bool mf_classic_write_sector( + FuriHalNfcTxRxContext* tx_rx, + MfClassicData* dest_data, + MfClassicData* src_data, + uint8_t sec_num) { + furi_assert(tx_rx); + furi_assert(dest_data); + furi_assert(src_data); + + uint8_t first_block = mf_classic_get_first_block_num_of_sector(sec_num); + uint8_t total_blocks = mf_classic_get_blocks_num_in_sector(sec_num); + MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(dest_data, sec_num); + bool key_a_found = mf_classic_is_key_found(dest_data, sec_num, MfClassicKeyA); + bool key_b_found = mf_classic_is_key_found(dest_data, sec_num, MfClassicKeyB); + + bool write_success = true; + for(size_t i = first_block; i < first_block + total_blocks; i++) { + // Compare blocks + if(memcmp(dest_data->block[i].value, src_data->block[i].value, MF_CLASSIC_BLOCK_SIZE)) { + bool key_a_write_allowed = mf_classic_is_allowed_access_data_block( + dest_data, i, MfClassicKeyA, MfClassicActionDataWrite); + bool key_b_write_allowed = mf_classic_is_allowed_access_data_block( + dest_data, i, MfClassicKeyB, MfClassicActionDataWrite); + + if(key_a_found && key_a_write_allowed) { + FURI_LOG_I(TAG, "Writing block %d with key A", i); + uint64_t key = nfc_util_bytes2num(sec_tr->key_a, 6); + if(!mf_classic_write_block(tx_rx, &src_data->block[i], i, MfClassicKeyA, key)) { + FURI_LOG_E(TAG, "Failed to write block %d", i); + write_success = false; + break; + } + } else if(key_b_found && key_b_write_allowed) { + FURI_LOG_I(TAG, "Writing block %d with key A", i); + uint64_t key = nfc_util_bytes2num(sec_tr->key_b, 6); + if(!mf_classic_write_block(tx_rx, &src_data->block[i], i, MfClassicKeyB, key)) { + FURI_LOG_E(TAG, "Failed to write block %d", i); + write_success = false; + break; + } + } else { + FURI_LOG_E(TAG, "Failed to find key with write access"); + write_success = false; + break; + } + } else { + FURI_LOG_D(TAG, "Blocks %d are equal", i); + } + } + + return write_success; +} diff --git a/lib/nfc/protocols/mifare_classic.h b/lib/nfc/protocols/mifare_classic.h index ead846e4..d5467b10 100644 --- a/lib/nfc/protocols/mifare_classic.h +++ b/lib/nfc/protocols/mifare_classic.h @@ -27,6 +27,20 @@ typedef enum { MfClassicKeyB, } MfClassicKey; +typedef enum { + MfClassicActionDataRead, + MfClassicActionDataWrite, + MfClassicActionDataInc, + MfClassicActionDataDec, + + MfClassicActionKeyARead, + MfClassicActionKeyAWrite, + MfClassicActionKeyBRead, + MfClassicActionKeyBWrite, + MfClassicActionACRead, + MfClassicActionACWrite, +} MfClassicAction; + typedef struct { uint8_t value[MF_CLASSIC_BLOCK_SIZE]; } MfClassicBlock; @@ -90,6 +104,18 @@ bool mf_classic_is_sector_trailer(uint8_t block); uint8_t mf_classic_get_sector_by_block(uint8_t block); +bool mf_classic_is_allowed_access_sector_trailer( + MfClassicData* data, + uint8_t block_num, + MfClassicKey key, + MfClassicAction action); + +bool mf_classic_is_allowed_access_data_block( + MfClassicData* data, + uint8_t block_num, + MfClassicKey key, + MfClassicAction action); + bool mf_classic_is_key_found(MfClassicData* data, uint8_t sector_num, MfClassicKey key_type); void mf_classic_set_key_found( @@ -104,6 +130,10 @@ bool mf_classic_is_block_read(MfClassicData* data, uint8_t block_num); void mf_classic_set_block_read(MfClassicData* data, uint8_t block_num, MfClassicBlock* block_data); +bool mf_classic_is_sector_data_read(MfClassicData* data, uint8_t sector_num); + +void mf_classic_set_sector_data_not_read(MfClassicData* data); + bool mf_classic_is_sector_read(MfClassicData* data, uint8_t sector_num); bool mf_classic_is_card_read(MfClassicData* data); @@ -145,3 +175,16 @@ uint8_t mf_classic_read_card( uint8_t mf_classic_update_card(FuriHalNfcTxRxContext* tx_rx, MfClassicData* data); bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_rx); + +bool mf_classic_write_block( + FuriHalNfcTxRxContext* tx_rx, + MfClassicBlock* src_block, + uint8_t block_num, + MfClassicKey key_type, + uint64_t key); + +bool mf_classic_write_sector( + FuriHalNfcTxRxContext* tx_rx, + MfClassicData* dest_data, + MfClassicData* src_data, + uint8_t sec_num);