From 2bf17c2fcf35762afe7a6331bc74c4fedf05f196 Mon Sep 17 00:00:00 2001 From: Fedor Indutny <238531+indutny@users.noreply.github.com> Date: Tue, 26 Jul 2022 14:31:58 -0700 Subject: [PATCH] nfc: collect nonces during mf classic emulation --- applications/nfc/nfc_i.h | 1 + .../nfc/scenes/nfc_scene_mf_classic_emulate.c | 140 +++++++++++++++--- applications/nfc/scenes/nfc_scene_save_name.c | 33 ++++- .../nfc/scenes/nfc_scene_save_success.c | 3 + lib/nfc/nfc_device.c | 44 ++++++ lib/nfc/nfc_device.h | 17 ++- lib/nfc/nfc_worker.c | 46 +++++- lib/nfc/protocols/mifare_classic.c | 13 ++ lib/nfc/protocols/mifare_classic.h | 13 ++ 9 files changed, 278 insertions(+), 32 deletions(-) diff --git a/applications/nfc/nfc_i.h b/applications/nfc/nfc_i.h index 84c0e7f0c8a..d3d8a273cd7 100755 --- a/applications/nfc/nfc_i.h +++ b/applications/nfc/nfc_i.h @@ -53,6 +53,7 @@ struct Nfc { SceneManager* scene_manager; NfcDevice* dev; FuriHalNfcDevData dev_edit_data; + bool card_data_changed; char text_store[NFC_TEXT_STORE_SIZE + 1]; string_t text_box_store; diff --git a/applications/nfc/scenes/nfc_scene_mf_classic_emulate.c b/applications/nfc/scenes/nfc_scene_mf_classic_emulate.c index 044388b8b57..8d5297319fd 100644 --- a/applications/nfc/scenes/nfc_scene_mf_classic_emulate.c +++ b/applications/nfc/scenes/nfc_scene_mf_classic_emulate.c @@ -1,34 +1,80 @@ #include "../nfc_i.h" #include -#define NFC_MF_CLASSIC_DATA_NOT_CHANGED (0UL) -#define NFC_MF_CLASSIC_DATA_CHANGED (1UL) +enum { + NfcSceneMfClassicEmulateStateWidget, + NfcSceneMfClassicEmulateStateTextBox, +}; bool nfc_mf_classic_emulate_worker_callback(NfcWorkerEvent event, void* context) { UNUSED(event); Nfc* nfc = context; - scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneMfClassicEmulate, NFC_MF_CLASSIC_DATA_CHANGED); + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventWorkerExit); return true; } -void nfc_scene_mf_classic_emulate_on_enter(void* context) { +static void nfc_scene_mf_classic_emulate_widget_callback( + GuiButtonType result, + InputType type, + void* context +) { + furi_assert(context); Nfc* nfc = context; - DOLPHIN_DEED(DolphinDeedNfcEmulate); + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(nfc->view_dispatcher, result); + } +} - // Setup view - Popup* popup = nfc->popup; +// Add widget with device name or inform that data received +static void nfc_scene_mf_classic_emulate_widget_config( + Nfc* nfc, + bool has_data +) { + Widget* widget = nfc->widget; + widget_reset(widget); + + widget_add_icon_element(widget, 0, 3, &I_RFIDDolphinSend_97x61); if(strcmp(nfc->dev->dev_name, "")) { nfc_text_store_set(nfc, "Emulating\n%s", nfc->dev->dev_name); } else { - nfc_text_store_set(nfc, "Emulating\nMf Classic", nfc->dev->dev_name); + nfc_text_store_set(nfc, "Emulating\nMf Classic"); + } + widget_add_string_multiline_element( + widget, + 56, + 31, + AlignLeft, + AlignTop, + FontPrimary, + nfc->text_store); + + if(has_data) { + widget_add_button_element( + widget, GuiButtonTypeLeft, "Log", nfc_scene_mf_classic_emulate_widget_callback, nfc); + widget_add_button_element( + widget, GuiButtonTypeRight, "Save", nfc_scene_mf_classic_emulate_widget_callback, nfc); } - popup_set_icon(popup, 0, 3, &I_RFIDDolphinSend_97x61); - popup_set_header(popup, nfc->text_store, 56, 31, AlignLeft, AlignTop); +} + +void nfc_scene_mf_classic_emulate_on_enter(void* context) { + Nfc* nfc = context; + DOLPHIN_DEED(DolphinDeedNfcEmulate); + + // Setup view + nfc_scene_mf_classic_emulate_widget_config(nfc, false); // Setup and start worker - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); + + TextBox* text_box = nfc->text_box; + text_box_set_font(text_box, TextBoxFontHex); + text_box_set_focus(text_box, TextBoxFocusEnd); + string_reset(nfc->text_box_store); + + string_init(nfc->dev->dev_data.mf_classic_emulator_output.nonce_log); + nfc->dev->dev_data.mf_classic_emulator_output.data_changed = false; + nfc_worker_start( nfc->worker, NfcWorkerStateMfClassicEmulate, @@ -41,19 +87,68 @@ void nfc_scene_mf_classic_emulate_on_enter(void* context) { bool nfc_scene_mf_classic_emulate_on_event(void* context, SceneManagerEvent event) { Nfc* nfc = context; bool consumed = false; + NfcMfClassicEmulatorOutput* emulator_output = + &nfc->dev->dev_data.mf_classic_emulator_output; + uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfClassicEmulate); - if(event.type == SceneManagerEventTypeBack) { - // Stop worker - nfc_worker_stop(nfc->worker); - // Check if data changed and save in shadow file - if(scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfClassicEmulate) == - NFC_MF_CLASSIC_DATA_CHANGED) { + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventWorkerExit) { + consumed = true; + if(string_size(emulator_output->nonce_log)) { + // Add data button to widget if data is received for the first time + if(!string_size(nfc->text_box_store)) { + nfc_scene_mf_classic_emulate_widget_config(nfc, true); + } + + // TODO(indutny): worry about concurrency? + + // Copy and display log + string_set(nfc->text_box_store, emulator_output->nonce_log); + string_strim(nfc->text_box_store); + text_box_set_text(nfc->text_box, string_get_cstr(nfc->text_box_store)); + } + } else if(event.event == GuiButtonTypeLeft && state == NfcSceneMfClassicEmulateStateWidget) { + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox); scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneMfClassicEmulate, NFC_MF_CLASSIC_DATA_NOT_CHANGED); - nfc_device_save_shadow(nfc->dev, nfc->dev->dev_name); + nfc->scene_manager, NfcSceneMfClassicEmulate, NfcSceneMfClassicEmulateStateTextBox); + consumed = true; + } else if(event.event == GuiButtonTypeRight && state == NfcSceneMfClassicEmulateStateWidget) { + // Stop worker without deallocating the nonce log + nfc_worker_stop(nfc->worker); + + // Check if data changed and save in shadow file + if(emulator_output->data_changed) { + nfc_device_save_shadow(nfc->dev, nfc->dev->dev_name); + } + + // Enter next scene + scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); + consumed = true; + } else if(event.event == NfcCustomEventViewExit && state == NfcSceneMfClassicEmulateStateTextBox) { + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneMfClassicEmulate, NfcSceneMfClassicEmulateStateWidget); + consumed = true; + } + } else if(event.type == SceneManagerEventTypeBack) { + // Close TextBox + if(state == NfcSceneMfClassicEmulateStateTextBox) { + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneMfClassicEmulate, NfcSceneMfClassicEmulateStateWidget); + consumed = true; + } else { + // Stop worker + nfc_worker_stop(nfc->worker); + string_clear(emulator_output->nonce_log); + + // Check if data changed and save in shadow file + if(emulator_output->data_changed) { + nfc_device_save_shadow(nfc->dev, nfc->dev->dev_name); + } } - consumed = false; } + return consumed; } @@ -61,7 +156,8 @@ void nfc_scene_mf_classic_emulate_on_exit(void* context) { Nfc* nfc = context; // Clear view - popup_reset(nfc->popup); + widget_reset(nfc->widget); + string_reset(nfc->text_box_store); nfc_blink_stop(nfc); } diff --git a/applications/nfc/scenes/nfc_scene_save_name.c b/applications/nfc/scenes/nfc_scene_save_name.c index d5e05472ad7..c865c3cff1b 100644 --- a/applications/nfc/scenes/nfc_scene_save_name.c +++ b/applications/nfc/scenes/nfc_scene_save_name.c @@ -22,7 +22,14 @@ void nfc_scene_save_name_on_enter(void* context) { } else { nfc_text_store_set(nfc, nfc->dev->dev_name); } - text_input_set_header_text(text_input, "Name the card"); + const char* extension; + if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneMfClassicEmulate)) { + extension = NFC_APP_LOG_EXTENSION; + text_input_set_header_text(text_input, "Name the nonce file"); + } else { + extension = NFC_APP_EXTENSION; + text_input_set_header_text(text_input, "Name the card"); + } text_input_set_result_callback( text_input, nfc_scene_save_name_text_input_callback, @@ -34,14 +41,14 @@ void nfc_scene_save_name_on_enter(void* context) { string_t folder_path; string_init(folder_path); - if(string_end_with_str_p(nfc->dev->load_path, NFC_APP_EXTENSION)) { + if(string_end_with_str_p(nfc->dev->load_path, extension)) { path_extract_dirname(string_get_cstr(nfc->dev->load_path), folder_path); } else { string_set_str(folder_path, NFC_APP_FOLDER); } ValidatorIsFile* validator_is_file = validator_is_file_alloc_init( - string_get_cstr(folder_path), NFC_APP_EXTENSION, nfc->dev->dev_name); + string_get_cstr(folder_path), extension, nfc->dev->dev_name); text_input_set_validator(text_input, validator_is_file_callback, validator_is_file); view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextInput); @@ -53,8 +60,24 @@ bool nfc_scene_save_name_on_event(void* context, SceneManagerEvent event) { Nfc* nfc = context; bool consumed = false; - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == NfcCustomEventTextInputDone) { + if( + event.type == SceneManagerEventTypeCustom && + event.event == NfcCustomEventTextInputDone + ) { + if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneMfClassicEmulate)) { + NfcMfClassicEmulatorOutput* mf_classic_emulate = + &nfc->dev->dev_data.mf_classic_emulator_output; + string_t nonce_log; + string_move(nonce_log, mf_classic_emulate->nonce_log); + if(nfc_device_save_mf_classic_nonces(nfc->dev, nfc->text_store, nonce_log)) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveSuccess); + consumed = true; + } else { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneStart); + } + string_reset(nonce_log); + } else { if(strcmp(nfc->dev->dev_name, "")) { nfc_device_delete(nfc->dev, true); } diff --git a/applications/nfc/scenes/nfc_scene_save_success.c b/applications/nfc/scenes/nfc_scene_save_success.c index a3b17451f01..e995608fe5a 100644 --- a/applications/nfc/scenes/nfc_scene_save_success.c +++ b/applications/nfc/scenes/nfc_scene_save_success.c @@ -30,6 +30,9 @@ bool nfc_scene_save_success_on_event(void* context, SceneManagerEvent event) { if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSavedMenu)) { consumed = scene_manager_search_and_switch_to_previous_scene( nfc->scene_manager, NfcSceneSavedMenu); + } else if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneMfClassicEmulate)){ + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneMfClassicEmulate); } else { consumed = scene_manager_search_and_switch_to_another_scene( nfc->scene_manager, NfcSceneFileSelect); diff --git a/lib/nfc/nfc_device.c b/lib/nfc/nfc_device.c index 649a2c5f56c..a0ce4efdb3b 100644 --- a/lib/nfc/nfc_device.c +++ b/lib/nfc/nfc_device.c @@ -1066,6 +1066,50 @@ bool nfc_device_save_shadow(NfcDevice* dev, const char* dev_name) { return nfc_device_save_file(dev, dev_name, NFC_APP_FOLDER, NFC_APP_SHADOW_EXTENSION, true); } +bool nfc_device_save_mf_classic_nonces( + NfcDevice* dev, + const char* name, + string_t nonces +) { + furi_assert(dev); + furi_assert(dev->format == NfcDeviceSaveFormatMifareClassic); + + bool saved = false; + File* file = storage_file_alloc(dev->storage); + if(!file) { + return false; + } + string_t temp_str; + string_init(temp_str); + + do { + if(!string_empty_p(dev->load_path)) { + // Get directory name + path_extract_dirname(string_get_cstr(dev->load_path), temp_str); + } else { + string_set_str(temp_str, NFC_APP_FOLDER); + } + + // Create nfc directory if necessary + if(!storage_simply_mkdir(dev->storage, string_get_cstr(temp_str))) break; + // Make path to file to save + string_cat_printf(temp_str, "/%s%s", name, NFC_APP_LOG_EXTENSION); + // Open file + if(!storage_file_open(file, string_get_cstr(temp_str), FSAM_READ_WRITE, FSOM_CREATE_ALWAYS)) break; + // Write nonces + uint32_t line_len = string_size(nonces); + if(storage_file_write(file, string_get_cstr(nonces), line_len) != line_len) break; + saved = true; + } while(0); + + if(!saved) { + dialog_message_show_storage_error(dev->dialogs, "Can not save\nnonces file"); + } + string_clear(temp_str); + storage_file_free(file); + return saved; +} + static bool nfc_device_load_data(NfcDevice* dev, string_t path, bool show_dialog) { bool parsed = false; FlipperFormat* file = flipper_format_file_alloc(dev->storage); diff --git a/lib/nfc/nfc_device.h b/lib/nfc/nfc_device.h index e1ff6d42f27..4bab6b6793f 100644 --- a/lib/nfc/nfc_device.h +++ b/lib/nfc/nfc_device.h @@ -13,10 +13,12 @@ #define NFC_DEV_NAME_MAX_LEN 22 #define NFC_READER_DATA_MAX_SIZE 64 +#define NFC_MAX_NONCE_COUNT 64 #define NFC_APP_FOLDER ANY_PATH("nfc") #define NFC_APP_EXTENSION ".nfc" #define NFC_APP_SHADOW_EXTENSION ".shd" +#define NFC_APP_LOG_EXTENSION ".txt" typedef void (*NfcLoadingCallback)(void* context, bool state); @@ -41,10 +43,18 @@ typedef struct { uint16_t size; } NfcReaderRequestData; +typedef struct { + string_t nonce_log; + bool data_changed; +} NfcMfClassicEmulatorOutput; + typedef struct { FuriHalNfcDevData nfc_data; NfcProtocol protocol; - NfcReaderRequestData reader_data; + union { + NfcReaderRequestData reader_data; + NfcMfClassicEmulatorOutput mf_classic_emulator_output; + }; union { EmvData emv_data; MfUltralightData mf_ul_data; @@ -77,6 +87,11 @@ bool nfc_device_save(NfcDevice* dev, const char* dev_name); bool nfc_device_save_shadow(NfcDevice* dev, const char* dev_name); +bool nfc_device_save_mf_classic_nonces( + NfcDevice* dev, + const char* name, + string_t nonces); + bool nfc_device_load(NfcDevice* dev, const char* file_path, bool show_dialog); bool nfc_device_load_key_cache(NfcDevice* dev); diff --git a/lib/nfc/nfc_worker.c b/lib/nfc/nfc_worker.c index 3a45c3634fb..550479b2241 100644 --- a/lib/nfc/nfc_worker.c +++ b/lib/nfc/nfc_worker.c @@ -5,6 +5,7 @@ #include "parsers/nfc_supported_card.h" #define TAG "NfcWorker" +#define NFC_MAX_NONCE_LOG_SIZE (8000) /***************************** NFC Worker API *******************************/ @@ -485,14 +486,46 @@ void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker, MfClassicDictType } } +static void nfc_worker_emulate_mf_classic_nonce_callback( + MfClassicEmulatorNonce* nonce, + void* context +) { + NfcWorker* nfc_worker = context; + NfcMfClassicEmulatorOutput* output = + &nfc_worker->dev_data->mf_classic_emulator_output; + + string_cat_printf( + output->nonce_log, + "%08x %d%c %08x %08x %08x\n", + nonce->cuid, + nonce->block, + nonce->access_key == MfClassicKeyA ? 'A' : 'B', + nonce->nt, + nonce->nr, + nonce->ar); + + // Trim log string if needed + if(string_size(output->nonce_log) > NFC_MAX_NONCE_LOG_SIZE) { + string_right( + output->nonce_log, + string_size(output->nonce_log) - NFC_MAX_NONCE_LOG_SIZE); + } +} + void nfc_worker_emulate_mf_classic(NfcWorker* nfc_worker) { FuriHalNfcTxRxContext tx_rx = {}; nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, &tx_rx, true); FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; + NfcMfClassicEmulatorOutput* emulator_output = + &nfc_worker->dev_data->mf_classic_emulator_output; + + uint32_t cuid = nfc_util_bytes2num(&nfc_data->uid[nfc_data->uid_len - 4], 4); MfClassicEmulator emulator = { - .cuid = nfc_util_bytes2num(&nfc_data->uid[nfc_data->uid_len - 4], 4), + .cuid = cuid, .data = nfc_worker->dev_data->mf_classic_data, .data_changed = false, + .nonce_callback = nfc_worker_emulate_mf_classic_nonce_callback, + .context = nfc_worker, }; NfcaSignal* nfca_signal = nfca_signal_alloc(); tx_rx.nfca_signal = nfca_signal; @@ -504,12 +537,17 @@ void nfc_worker_emulate_mf_classic(NfcWorker* nfc_worker) { if(furi_hal_nfc_listen_rx(&tx_rx, 300)) { mf_classic_emulator(&emulator, &tx_rx); } - } - if(emulator.data_changed) { - nfc_worker->dev_data->mf_classic_data = emulator.data; + if(nfc_worker->state != NfcWorkerStateMfClassicEmulate) { + break; + } + if(nfc_worker->callback) { nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context); } + } + if(emulator.data_changed) { + nfc_worker->dev_data->mf_classic_data = emulator.data; + emulator_output->data_changed = true; emulator.data_changed = false; } diff --git a/lib/nfc/protocols/mifare_classic.c b/lib/nfc/protocols/mifare_classic.c index 93fe6f69468..5df0428560c 100644 --- a/lib/nfc/protocols/mifare_classic.c +++ b/lib/nfc/protocols/mifare_classic.c @@ -869,6 +869,19 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ FURI_LOG_T(TAG, "Wrong AUTH! %08X != %08X", cardRr, prng_successor(nonce, 64)); // Don't send NACK, as tag don't send it command_processed = true; + + // Collect nonce + if (emulator->nonce_callback) { + MfClassicEmulatorNonce data = { + .cuid = emulator->cuid, + .access_key = access_key, + .block = sector_trailer_block, + .nt = nonce, + .nr = nr, + .ar = ar, + }; + emulator->nonce_callback(&data, emulator->context); + } break; } diff --git a/lib/nfc/protocols/mifare_classic.h b/lib/nfc/protocols/mifare_classic.h index 85f67b118f7..6eb213c8128 100644 --- a/lib/nfc/protocols/mifare_classic.h +++ b/lib/nfc/protocols/mifare_classic.h @@ -69,11 +69,24 @@ typedef struct { MfClassicSectorReader sector_reader[MF_CLASSIC_SECTORS_MAX]; } MfClassicReader; +typedef struct { + uint32_t cuid; + MfClassicKey access_key; + uint8_t block; + uint32_t nt; + uint32_t nr; + uint32_t ar; +} MfClassicEmulatorNonce; + +typedef void (*MfClassicEmulatorKeyCallback)(MfClassicEmulatorNonce* data, void* context); + typedef struct { uint32_t cuid; Crypto1 crypto; MfClassicData data; bool data_changed; + MfClassicEmulatorKeyCallback nonce_callback; + void* context; } MfClassicEmulator; const char* mf_classic_get_type_str(MfClassicType type);