diff --git a/applications/nfc/nfc.c b/applications/nfc/nfc.c index 3422e91a..57e25f81 100644 --- a/applications/nfc/nfc.c +++ b/applications/nfc/nfc.c @@ -94,6 +94,11 @@ Nfc* nfc_alloc() { view_dispatcher_add_view( nfc->view_dispatcher, NfcViewDictAttack, dict_attack_get_view(nfc->dict_attack)); + // Detect Reader + nfc->detect_reader = detect_reader_alloc(); + view_dispatcher_add_view( + nfc->view_dispatcher, NfcViewDetectReader, detect_reader_get_view(nfc->detect_reader)); + // Generator nfc->generator = NULL; @@ -158,6 +163,10 @@ void nfc_free(Nfc* nfc) { view_dispatcher_remove_view(nfc->view_dispatcher, NfcViewDictAttack); dict_attack_free(nfc->dict_attack); + // Detect Reader + view_dispatcher_remove_view(nfc->view_dispatcher, NfcViewDetectReader); + detect_reader_free(nfc->detect_reader); + // Worker nfc_worker_stop(nfc->worker); nfc_worker_free(nfc->worker); diff --git a/applications/nfc/nfc_i.h b/applications/nfc/nfc_i.h index bcfe4a21..c9ba7fff 100644 --- a/applications/nfc/nfc_i.h +++ b/applications/nfc/nfc_i.h @@ -28,6 +28,7 @@ #include #include "views/dict_attack.h" +#include "views/detect_reader.h" #include #include @@ -71,6 +72,7 @@ struct Nfc { TextBox* text_box; Widget* widget; DictAttack* dict_attack; + DetectReader* detect_reader; const NfcGenerator* generator; }; @@ -85,6 +87,7 @@ typedef enum { NfcViewTextBox, NfcViewWidget, NfcViewDictAttack, + NfcViewDetectReader, } NfcView; Nfc* nfc_alloc(); diff --git a/applications/nfc/scenes/nfc_scene_config.h b/applications/nfc/scenes/nfc_scene_config.h index ff34a11d..540fe109 100644 --- a/applications/nfc/scenes/nfc_scene_config.h +++ b/applications/nfc/scenes/nfc_scene_config.h @@ -48,4 +48,6 @@ ADD_SCENE(nfc, rpc, Rpc) ADD_SCENE(nfc, exit_confirm, ExitConfirm) ADD_SCENE(nfc, retry_confirm, RetryConfirm) ADD_SCENE(nfc, detect_reader, DetectReader) +ADD_SCENE(nfc, mfkey_nonces_info, MfkeyNoncesInfo) +ADD_SCENE(nfc, mfkey_complete, MfkeyComplete) ADD_SCENE(nfc, nfc_data_info, NfcDataInfo) diff --git a/applications/nfc/scenes/nfc_scene_detect_reader.c b/applications/nfc/scenes/nfc_scene_detect_reader.c index f734f04c..8945feba 100644 --- a/applications/nfc/scenes/nfc_scene_detect_reader.c +++ b/applications/nfc/scenes/nfc_scene_detect_reader.c @@ -1,126 +1,48 @@ #include "../nfc_i.h" #include -#define NFC_SCENE_DETECT_READER_LOG_SIZE_MAX (200) - -enum { - NfcSceneDetectReaderStateWidget, - NfcSceneDetectReaderStateTextBox, -}; - bool nfc_detect_reader_worker_callback(NfcWorkerEvent event, void* context) { UNUSED(event); furi_assert(context); Nfc* nfc = context; - view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventWorkerExit); + view_dispatcher_send_custom_event(nfc->view_dispatcher, event); return true; } -void nfc_scene_detect_reader_widget_callback(GuiButtonType result, InputType type, void* context) { - furi_assert(context); - Nfc* nfc = context; - if(type == InputTypeShort) { - view_dispatcher_send_custom_event(nfc->view_dispatcher, result); - } -} - -void nfc_detect_reader_textbox_callback(void* context) { +void nfc_scene_detect_reader_callback(void* context) { furi_assert(context); Nfc* nfc = context; view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); } -// Add widget with device name or inform that data received -static void nfc_scene_detect_reader_widget_config(Nfc* nfc, bool data_received) { - Widget* widget = nfc->widget; - widget_reset(widget); - - widget_add_icon_element(widget, 0, 14, &I_Reader_detect); - widget_add_string_element( - widget, 64, 3, AlignCenter, AlignTop, FontSecondary, "Hold Near Reader"); - widget_add_string_element(widget, 55, 22, AlignLeft, AlignTop, FontPrimary, "Emulating..."); - - if(data_received) { - widget_add_button_element( - widget, GuiButtonTypeCenter, "Log", nfc_scene_detect_reader_widget_callback, nfc); - } -} - void nfc_scene_detect_reader_on_enter(void* context) { Nfc* nfc = context; DOLPHIN_DEED(DolphinDeedNfcEmulate); - FuriHalNfcDevData nfc_params = { - .uid = {0x36, 0x9C, 0xe7, 0xb1, 0x0A, 0xC1, 0x34}, - .uid_len = 7, - .atqa = {0x44, 0x00}, - .sak = 0x08, - .type = FuriHalNfcTypeA, - }; - nfc->dev->dev_data.nfc_data = nfc_params; - // Setup Widget - nfc_scene_detect_reader_widget_config(nfc, false); - // Setup TextBox - 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); - - // Set Widget state and view - scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneDetectReader, NfcSceneDetectReaderStateWidget); - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); - // Start worker - memset(&nfc->dev->dev_data.reader_data, 0, sizeof(NfcReaderRequestData)); + detect_reader_set_callback(nfc->detect_reader, nfc_scene_detect_reader_callback, nfc); nfc_worker_start( nfc->worker, - NfcWorkerStateUidEmulate, + NfcWorkerStateAnalyzeReader, &nfc->dev->dev_data, nfc_detect_reader_worker_callback, nfc); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDetectReader); + nfc_blink_start(nfc); } bool nfc_scene_detect_reader_on_event(void* context, SceneManagerEvent event) { Nfc* nfc = context; - NfcReaderRequestData* reader_data = &nfc->dev->dev_data.reader_data; - uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneDetectReader); bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { - if(event.event == NfcCustomEventWorkerExit) { - // Add data button to widget if data is received for the first time - if(!string_size(nfc->text_box_store)) { - nfc_scene_detect_reader_widget_config(nfc, true); - } - // Update TextBox data - if(string_size(nfc->text_box_store) < NFC_SCENE_DETECT_READER_LOG_SIZE_MAX) { - string_cat_printf(nfc->text_box_store, "R:"); - for(uint16_t i = 0; i < reader_data->size; i++) { - string_cat_printf(nfc->text_box_store, " %02X", reader_data->data[i]); - } - string_push_back(nfc->text_box_store, '\n'); - text_box_set_text(nfc->text_box, string_get_cstr(nfc->text_box_store)); - } - memset(reader_data, 0, sizeof(NfcReaderRequestData)); + if(event.event == NfcCustomEventViewExit) { + nfc_worker_stop(nfc->worker); + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfkeyNoncesInfo); consumed = true; - } else if(event.event == GuiButtonTypeCenter && state == NfcSceneDetectReaderStateWidget) { - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox); - scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneDetectReader, NfcSceneDetectReaderStateTextBox); - consumed = true; - } else if(event.event == NfcCustomEventViewExit && state == NfcSceneDetectReaderStateTextBox) { - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); - scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneDetectReader, NfcSceneDetectReaderStateWidget); - consumed = true; - } - } else if(event.type == SceneManagerEventTypeBack) { - if(state == NfcSceneDetectReaderStateTextBox) { - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); - scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneDetectReader, NfcSceneDetectReaderStateWidget); + } else if(event.event == NfcWorkerEventDetectReaderMfkeyCollected) { + detect_reader_inc_nonce_cnt(nfc->detect_reader); consumed = true; } } @@ -135,9 +57,7 @@ void nfc_scene_detect_reader_on_exit(void* context) { nfc_worker_stop(nfc->worker); // Clear view - widget_reset(nfc->widget); - text_box_reset(nfc->text_box); - string_reset(nfc->text_box_store); + detect_reader_reset(nfc->detect_reader); nfc_blink_stop(nfc); } diff --git a/applications/nfc/scenes/nfc_scene_mfkey_complete.c b/applications/nfc/scenes/nfc_scene_mfkey_complete.c new file mode 100644 index 00000000..3c4f9dba --- /dev/null +++ b/applications/nfc/scenes/nfc_scene_mfkey_complete.c @@ -0,0 +1,49 @@ +#include "../nfc_i.h" + +void nfc_scene_mfkey_complete_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_mfkey_complete_on_enter(void* context) { + Nfc* nfc = context; + + widget_add_string_element(nfc->widget, 64, 0, AlignCenter, AlignTop, FontPrimary, "Complete!"); + widget_add_string_multiline_element( + nfc->widget, + 64, + 32, + AlignCenter, + AlignCenter, + FontSecondary, + "Now use mfkey32v2\nto extract keys"); + widget_add_button_element( + nfc->widget, GuiButtonTypeCenter, "OK", nfc_scene_mfkey_complete_callback, nfc); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); +} + +bool nfc_scene_mfkey_complete_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeCenter) { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneStart); + } + } else if(event.event == SceneManagerEventTypeBack) { + consumed = + scene_manager_search_and_switch_to_previous_scene(nfc->scene_manager, NfcSceneStart); + } + + return consumed; +} + +void nfc_scene_mfkey_complete_on_exit(void* context) { + Nfc* nfc = context; + + widget_reset(nfc->widget); +} \ No newline at end of file diff --git a/applications/nfc/scenes/nfc_scene_mfkey_nonces_info.c b/applications/nfc/scenes/nfc_scene_mfkey_nonces_info.c new file mode 100644 index 00000000..b45b690d --- /dev/null +++ b/applications/nfc/scenes/nfc_scene_mfkey_nonces_info.c @@ -0,0 +1,55 @@ +#include "../nfc_i.h" +#include + +void nfc_scene_mfkey_nonces_info_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_mfkey_nonces_info_on_enter(void* context) { + Nfc* nfc = context; + + string_t temp_str; + string_init(temp_str); + + uint16_t nonces_saved = mfkey32_get_auth_sectors(temp_str); + widget_add_text_scroll_element(nfc->widget, 0, 22, 128, 42, string_get_cstr(temp_str)); + string_printf(temp_str, "Nonces saved %d", nonces_saved); + widget_add_string_element( + nfc->widget, 0, 0, AlignLeft, AlignTop, FontPrimary, string_get_cstr(temp_str)); + widget_add_string_element( + nfc->widget, 0, 12, AlignLeft, AlignTop, FontSecondary, "Authenticated sectors:"); + + widget_add_button_element( + nfc->widget, GuiButtonTypeRight, "Next", nfc_scene_mfkey_nonces_info_callback, nfc); + + string_clear(temp_str); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); +} + +bool nfc_scene_mfkey_nonces_info_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeRight) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfkeyComplete); + consumed = true; + } + } else if(event.type == SceneManagerEventTypeBack) { + consumed = + scene_manager_search_and_switch_to_previous_scene(nfc->scene_manager, NfcSceneStart); + } + + return consumed; +} + +void nfc_scene_mfkey_nonces_info_on_exit(void* context) { + Nfc* nfc = context; + + // Clear view + widget_reset(nfc->widget); +} diff --git a/applications/nfc/scenes/nfc_scene_start.c b/applications/nfc/scenes/nfc_scene_start.c index 01ffb46b..1a9051df 100644 --- a/applications/nfc/scenes/nfc_scene_start.c +++ b/applications/nfc/scenes/nfc_scene_start.c @@ -49,7 +49,12 @@ bool nfc_scene_start_on_event(void* context, SceneManagerEvent event) { scene_manager_next_scene(nfc->scene_manager, NfcSceneRead); consumed = true; } else if(event.event == SubmenuIndexDetectReader) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneDetectReader); + bool sd_exist = storage_sd_status(nfc->dev->storage) == FSE_OK; + if(sd_exist) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneDetectReader); + } else { + scene_manager_next_scene(nfc->scene_manager, NfcSceneDictNotFound); + } consumed = true; } else if(event.event == SubmenuIndexSaved) { scene_manager_next_scene(nfc->scene_manager, NfcSceneFileSelect); @@ -61,7 +66,6 @@ bool nfc_scene_start_on_event(void* context, SceneManagerEvent event) { scene_manager_next_scene(nfc->scene_manager, NfcSceneSetType); consumed = true; } else if(event.event == SubmenuIndexDebug) { - scene_manager_set_scene_state(nfc->scene_manager, NfcSceneStart, SubmenuIndexDebug); scene_manager_next_scene(nfc->scene_manager, NfcSceneDebug); consumed = true; } diff --git a/applications/nfc/views/detect_reader.c b/applications/nfc/views/detect_reader.c new file mode 100644 index 00000000..177c13f7 --- /dev/null +++ b/applications/nfc/views/detect_reader.c @@ -0,0 +1,115 @@ +#include "detect_reader.h" + +#include + +struct DetectReader { + View* view; + DetectReaderDoneCallback callback; + void* context; +}; + +typedef struct { + uint16_t nonces; +} DetectReaderViewModel; + +static void detect_reader_draw_callback(Canvas* canvas, void* model) { + DetectReaderViewModel* m = model; + char text[32] = {}; + + snprintf(text, sizeof(text), "Tap the reader several times"); + canvas_draw_str_aligned(canvas, 64, 0, AlignCenter, AlignTop, "Tap the reader several times"); + + if(m->nonces == 0) { + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 52, 22, AlignLeft, AlignTop, "Emulating..."); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned(canvas, 52, 35, AlignLeft, AlignTop, "MIFARE Classic"); + canvas_draw_icon(canvas, 0, 13, &I_Tap_reader_36x38); + } else { + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 54, 22, AlignLeft, AlignTop, "Collecting..."); + canvas_set_font(canvas, FontSecondary); + snprintf(text, sizeof(text), "Nonces: %d", m->nonces); + canvas_draw_str_aligned(canvas, 54, 35, AlignLeft, AlignTop, text); + elements_button_right(canvas, "Next"); + canvas_draw_icon(canvas, 6, 15, &I_ArrowC_1_36x36); + } +} + +static bool detect_reader_input_callback(InputEvent* event, void* context) { + DetectReader* detect_reader = context; + furi_assert(detect_reader->callback); + bool consumed = false; + + uint8_t nonces = 0; + with_view_model( + detect_reader->view, (DetectReaderViewModel * model) { + nonces = model->nonces; + return false; + }); + + if(event->type == InputTypeShort) { + if(event->key == InputKeyRight) { + if(nonces > 0) { + detect_reader->callback(detect_reader->context); + consumed = true; + } + } + } + + return consumed; +} + +DetectReader* detect_reader_alloc() { + DetectReader* detect_reader = malloc(sizeof(DetectReader)); + detect_reader->view = view_alloc(); + view_allocate_model(detect_reader->view, ViewModelTypeLocking, sizeof(DetectReaderViewModel)); + view_set_draw_callback(detect_reader->view, detect_reader_draw_callback); + view_set_input_callback(detect_reader->view, detect_reader_input_callback); + view_set_context(detect_reader->view, detect_reader); + + return detect_reader; +} + +void detect_reader_free(DetectReader* detect_reader) { + furi_assert(detect_reader); + + view_free(detect_reader->view); + free(detect_reader); +} + +void detect_reader_reset(DetectReader* detect_reader) { + furi_assert(detect_reader); + + with_view_model( + detect_reader->view, (DetectReaderViewModel * model) { + model->nonces = 0; + return false; + }); +} + +View* detect_reader_get_view(DetectReader* detect_reader) { + furi_assert(detect_reader); + + return detect_reader->view; +} + +void detect_reader_set_callback( + DetectReader* detect_reader, + DetectReaderDoneCallback callback, + void* context) { + furi_assert(detect_reader); + furi_assert(callback); + + detect_reader->callback = callback; + detect_reader->context = context; +} + +void detect_reader_inc_nonce_cnt(DetectReader* detect_reader) { + furi_assert(detect_reader); + with_view_model( + detect_reader->view, (DetectReaderViewModel * model) { + model->nonces++; + return false; + }); +} diff --git a/applications/nfc/views/detect_reader.h b/applications/nfc/views/detect_reader.h new file mode 100644 index 00000000..12cd03db --- /dev/null +++ b/applications/nfc/views/detect_reader.h @@ -0,0 +1,23 @@ +#pragma once +#include +#include +#include + +typedef struct DetectReader DetectReader; + +typedef void (*DetectReaderDoneCallback)(void* context); + +DetectReader* detect_reader_alloc(); + +void detect_reader_free(DetectReader* detect_reader); + +void detect_reader_reset(DetectReader* detect_reader); + +View* detect_reader_get_view(DetectReader* detect_reader); + +void detect_reader_set_callback( + DetectReader* detect_reader, + DetectReaderDoneCallback callback, + void* context); + +void detect_reader_inc_nonce_cnt(DetectReader* detect_reader); diff --git a/assets/icons/NFC/Reader_detect.png b/assets/icons/NFC/ArrowC_1_36x36.png similarity index 84% rename from assets/icons/NFC/Reader_detect.png rename to assets/icons/NFC/ArrowC_1_36x36.png index 56d3663e..3a0c6dd0 100644 Binary files a/assets/icons/NFC/Reader_detect.png and b/assets/icons/NFC/ArrowC_1_36x36.png differ diff --git a/assets/icons/NFC/Tap_reader_36x38.png b/assets/icons/NFC/Tap_reader_36x38.png new file mode 100644 index 00000000..4e0ba8f0 Binary files /dev/null and b/assets/icons/NFC/Tap_reader_36x38.png differ diff --git a/firmware/targets/f7/furi_hal/furi_hal_nfc.c b/firmware/targets/f7/furi_hal/furi_hal_nfc.c index bd7fbc3f..94677481 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_nfc.c +++ b/firmware/targets/f7/furi_hal/furi_hal_nfc.c @@ -217,7 +217,6 @@ bool furi_hal_nfc_listen( } rfalLowPowerModeStop(); rfalNfcDiscoverParam params = { - .compMode = RFAL_COMPLIANCE_MODE_NFC, .techs2Find = RFAL_NFC_LISTEN_TECH_A, .totalDuration = 1000, .devLimit = 1, @@ -230,6 +229,11 @@ bool furi_hal_nfc_listen( .notifyCb = NULL, .activate_after_sak = activate_after_sak, }; + if(FURI_BIT(sak, 5)) { + params.compMode = RFAL_COMPLIANCE_MODE_EMV; + } else { + params.compMode = RFAL_COMPLIANCE_MODE_NFC; + } params.lmConfigPA.nfcidLen = uid_len; memcpy(params.lmConfigPA.nfcid, uid, uid_len); params.lmConfigPA.SENS_RES[0] = atqa[0]; @@ -271,6 +275,10 @@ void furi_hal_nfc_listen_sleep() { st25r3916ExecuteCommand(ST25R3916_CMD_GOTO_SLEEP); } +void furi_hal_nfc_stop_cmd() { + st25r3916ExecuteCommand(ST25R3916_CMD_STOP); +} + bool furi_hal_nfc_listen_rx(FuriHalNfcTxRxContext* tx_rx, uint32_t timeout_ms) { furi_assert(tx_rx); @@ -283,6 +291,9 @@ bool furi_hal_nfc_listen_rx(FuriHalNfcTxRxContext* tx_rx, uint32_t timeout_ms) { if(st25r3916GetInterrupt(ST25R3916_IRQ_MASK_RXE)) { furi_hal_nfc_read_fifo(tx_rx->rx_data, &tx_rx->rx_bits); data_received = true; + if(tx_rx->sniff_rx) { + tx_rx->sniff_rx(tx_rx->rx_data, tx_rx->rx_bits, false, tx_rx->sniff_context); + } break; } continue; @@ -497,14 +508,14 @@ static bool furi_hal_nfc_transparent_tx_rx(FuriHalNfcTxRxContext* tx_rx, uint16_ furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_nfc); st25r3916ExecuteCommand(ST25R3916_CMD_UNMASK_RECEIVE_DATA); - if(tx_rx->sniff_tx) { - tx_rx->sniff_tx(tx_rx->tx_data, tx_rx->tx_bits, false, tx_rx->sniff_context); - } - // Manually wait for interrupt furi_hal_gpio_init(&gpio_nfc_irq_rfid_pull, GpioModeInput, GpioPullDown, GpioSpeedVeryHigh); st25r3916ClearAndEnableInterrupts(ST25R3916_IRQ_MASK_RXE); + if(tx_rx->sniff_tx) { + tx_rx->sniff_tx(tx_rx->tx_data, tx_rx->tx_bits, false, tx_rx->sniff_context); + } + uint32_t irq = 0; uint8_t rxe = 0; uint32_t start = DWT->CYCCNT; diff --git a/firmware/targets/furi_hal_include/furi_hal_nfc.h b/firmware/targets/furi_hal_include/furi_hal_nfc.h index 71186076..0bcb4506 100644 --- a/firmware/targets/furi_hal_include/furi_hal_nfc.h +++ b/firmware/targets/furi_hal_include/furi_hal_nfc.h @@ -120,6 +120,8 @@ void furi_hal_nfc_field_off(); */ void furi_hal_nfc_start_sleep(); +void furi_hal_nfc_stop_cmd(); + /** NFC stop sleep */ void furi_hal_nfc_exit_sleep(); diff --git a/lib/nfc/helpers/mfkey32.c b/lib/nfc/helpers/mfkey32.c new file mode 100644 index 00000000..64b06e25 --- /dev/null +++ b/lib/nfc/helpers/mfkey32.c @@ -0,0 +1,230 @@ +#include "mfkey32.h" + +#include +#include +#include +#include +#include + +#include +#include + +#define TAG "Mfkey32" + +#define MFKEY32_LOGS_PATH EXT_PATH("nfc/.mfkey32.log") + +typedef enum { + Mfkey32StateIdle, + Mfkey32StateAuthReceived, + Mfkey32StateAuthNtSent, + Mfkey32StateAuthArNrReceived, +} Mfkey32State; + +typedef struct { + uint32_t cuid; + uint8_t sector; + MfClassicKey key; + uint32_t nt0; + uint32_t nr0; + uint32_t ar0; + uint32_t nt1; + uint32_t nr1; + uint32_t ar1; +} Mfkey32Params; + +ARRAY_DEF(Mfkey32Params, Mfkey32Params, M_POD_OPLIST); + +typedef struct { + uint8_t sector; + MfClassicKey key; + uint32_t nt; + uint32_t nr; + uint32_t ar; +} Mfkey32Nonce; + +struct Mfkey32 { + Mfkey32State state; + Stream* file_stream; + Mfkey32Params_t params_arr; + Mfkey32Nonce nonce; + uint32_t cuid; + Mfkey32ParseDataCallback callback; + void* context; +}; + +Mfkey32* mfkey32_alloc(uint32_t cuid) { + Mfkey32* instance = malloc(sizeof(Mfkey32)); + instance->cuid = cuid; + instance->state = Mfkey32StateIdle; + Storage* storage = furi_record_open(RECORD_STORAGE); + instance->file_stream = buffered_file_stream_alloc(storage); + if(!buffered_file_stream_open( + instance->file_stream, MFKEY32_LOGS_PATH, FSAM_WRITE, FSOM_OPEN_APPEND)) { + buffered_file_stream_close(instance->file_stream); + stream_free(instance->file_stream); + free(instance); + instance = NULL; + } else { + Mfkey32Params_init(instance->params_arr); + } + + furi_record_close(RECORD_STORAGE); + + return instance; +} + +void mfkey32_free(Mfkey32* instance) { + furi_assert(instance != NULL); + + Mfkey32Params_clear(instance->params_arr); + buffered_file_stream_close(instance->file_stream); + stream_free(instance->file_stream); + free(instance); +} + +void mfkey32_set_callback(Mfkey32* instance, Mfkey32ParseDataCallback callback, void* context) { + furi_assert(instance); + furi_assert(callback); + + instance->callback = callback; + instance->context = context; +} + +static bool mfkey32_write_params(Mfkey32* instance, Mfkey32Params* params) { + string_t str; + string_init_printf( + str, + "Sector %d key %c cuid %08x nt0 %08x nr0 %08x ar0 %08x nt1 %08x nr1 %08x ar1 %08x\n", + params->sector, + params->key == MfClassicKeyA ? 'A' : 'B', + params->cuid, + params->nt0, + params->nr0, + params->ar0, + params->nt1, + params->nr1, + params->ar1); + bool write_success = stream_write_string(instance->file_stream, str); + string_clear(str); + return write_success; +} + +static void mfkey32_add_params(Mfkey32* instance) { + Mfkey32Nonce* nonce = &instance->nonce; + bool nonce_added = false; + // Search if we partially collected params + if(Mfkey32Params_size(instance->params_arr)) { + Mfkey32Params_it_t it; + for(Mfkey32Params_it(it, instance->params_arr); !Mfkey32Params_end_p(it); + Mfkey32Params_next(it)) { + Mfkey32Params* params = Mfkey32Params_ref(it); + if((params->sector == nonce->sector) && (params->key == nonce->key)) { + params->nt1 = nonce->nt; + params->nr1 = nonce->nr; + params->ar1 = nonce->ar; + nonce_added = true; + FURI_LOG_I( + TAG, + "Params for sector %d key %c collected", + params->sector, + params->key == MfClassicKeyA ? 'A' : 'B'); + // Write on sd card + if(mfkey32_write_params(instance, params)) { + Mfkey32Params_remove(instance->params_arr, it); + if(instance->callback) { + instance->callback(Mfkey32EventParamCollected, instance->context); + } + } + } + } + } + if(!nonce_added) { + Mfkey32Params params = { + .sector = nonce->sector, + .key = nonce->key, + .cuid = instance->cuid, + .nt0 = nonce->nt, + .nr0 = nonce->nr, + .ar0 = nonce->ar, + }; + Mfkey32Params_push_back(instance->params_arr, params); + } +} + +void mfkey32_process_data( + Mfkey32* instance, + uint8_t* data, + uint16_t len, + bool reader_to_tag, + bool crc_dropped) { + furi_assert(instance); + furi_assert(data); + + Mfkey32Nonce* nonce = &instance->nonce; + uint16_t data_len = len; + if((data_len > 3) && !crc_dropped) { + data_len -= 2; + } + + bool data_processed = false; + if(instance->state == Mfkey32StateIdle) { + if(reader_to_tag) { + if((data[0] == 0x60) || (data[0] == 0x61)) { + nonce->key = data[0] == 0x60 ? MfClassicKeyA : MfClassicKeyB; + nonce->sector = mf_classic_get_sector_by_block(data[1]); + instance->state = Mfkey32StateAuthReceived; + data_processed = true; + } + } + } else if(instance->state == Mfkey32StateAuthReceived) { + if(!reader_to_tag) { + if(len == 4) { + nonce->nt = nfc_util_bytes2num(data, 4); + instance->state = Mfkey32StateAuthNtSent; + data_processed = true; + } + } + } else if(instance->state == Mfkey32StateAuthNtSent) { + if(reader_to_tag) { + if(len == 8) { + nonce->nr = nfc_util_bytes2num(data, 4); + nonce->ar = nfc_util_bytes2num(&data[4], 4); + mfkey32_add_params(instance); + instance->state = Mfkey32StateIdle; + } + } + } + if(!data_processed) { + instance->state = Mfkey32StateIdle; + } +} + +uint16_t mfkey32_get_auth_sectors(string_t data_str) { + furi_assert(data_str); + + uint16_t nonces_num = 0; + Storage* storage = furi_record_open(RECORD_STORAGE); + Stream* file_stream = buffered_file_stream_alloc(storage); + string_t temp_str; + string_init(temp_str); + + do { + if(!buffered_file_stream_open( + file_stream, MFKEY32_LOGS_PATH, FSAM_READ, FSOM_OPEN_EXISTING)) + break; + while(true) { + if(!stream_read_line(file_stream, temp_str)) break; + size_t uid_pos = string_search_str(temp_str, "cuid"); + string_left(temp_str, uid_pos); + string_push_back(temp_str, '\n'); + string_cat(data_str, temp_str); + nonces_num++; + } + } while(false); + + buffered_file_stream_close(file_stream); + stream_free(file_stream); + string_clear(temp_str); + + return nonces_num; +} diff --git a/lib/nfc/helpers/mfkey32.h b/lib/nfc/helpers/mfkey32.h new file mode 100644 index 00000000..c4f13cc2 --- /dev/null +++ b/lib/nfc/helpers/mfkey32.h @@ -0,0 +1,27 @@ +#pragma once + +#include +#include + +typedef struct Mfkey32 Mfkey32; + +typedef enum { + Mfkey32EventParamCollected, +} Mfkey32Event; + +typedef void (*Mfkey32ParseDataCallback)(Mfkey32Event event, void* context); + +Mfkey32* mfkey32_alloc(uint32_t cuid); + +void mfkey32_free(Mfkey32* instance); + +void mfkey32_process_data( + Mfkey32* instance, + uint8_t* data, + uint16_t len, + bool reader_to_tag, + bool crc_dropped); + +void mfkey32_set_callback(Mfkey32* instance, Mfkey32ParseDataCallback callback, void* context); + +uint16_t mfkey32_get_auth_sectors(string_t string); diff --git a/lib/nfc/helpers/nfc_debug_log.c b/lib/nfc/helpers/nfc_debug_log.c new file mode 100644 index 00000000..1cbc5224 --- /dev/null +++ b/lib/nfc/helpers/nfc_debug_log.c @@ -0,0 +1,72 @@ +#include "nfc_debug_log.h" + +#include +#include +#include + +#define TAG "NfcDebugLog" + +#define NFC_DEBUG_PCAP_FILENAME EXT_PATH("nfc/debug.txt") + +struct NfcDebugLog { + Stream* file_stream; + string_t data_str; +}; + +NfcDebugLog* nfc_debug_log_alloc() { + NfcDebugLog* instance = malloc(sizeof(NfcDebugLog)); + + Storage* storage = furi_record_open(RECORD_STORAGE); + instance->file_stream = buffered_file_stream_alloc(storage); + + if(!buffered_file_stream_open( + instance->file_stream, NFC_DEBUG_PCAP_FILENAME, FSAM_WRITE, FSOM_OPEN_APPEND)) { + buffered_file_stream_close(instance->file_stream); + stream_free(instance->file_stream); + instance->file_stream = NULL; + } + + if(!instance->file_stream) { + free(instance); + instance = NULL; + } else { + string_init(instance->data_str); + } + furi_record_close(RECORD_STORAGE); + + return instance; +} + +void nfc_debug_log_free(NfcDebugLog* instance) { + furi_assert(instance); + furi_assert(instance->file_stream); + furi_assert(instance->data_str); + + buffered_file_stream_close(instance->file_stream); + stream_free(instance->file_stream); + string_clear(instance->data_str); + + free(instance); +} + +void nfc_debug_log_process_data( + NfcDebugLog* instance, + uint8_t* data, + uint16_t len, + bool reader_to_tag, + bool crc_dropped) { + furi_assert(instance); + furi_assert(instance->file_stream); + furi_assert(instance->data_str); + furi_assert(data); + UNUSED(crc_dropped); + + string_printf(instance->data_str, "%lu %c:", furi_get_tick(), reader_to_tag ? 'R' : 'T'); + uint16_t data_len = len; + for(size_t i = 0; i < data_len; i++) { + string_cat_printf(instance->data_str, " %02x", data[i]); + } + string_push_back(instance->data_str, '\n'); + + stream_write_string(instance->file_stream, instance->data_str); +} diff --git a/lib/nfc/helpers/nfc_debug_log.h b/lib/nfc/helpers/nfc_debug_log.h new file mode 100644 index 00000000..261b8008 --- /dev/null +++ b/lib/nfc/helpers/nfc_debug_log.h @@ -0,0 +1,17 @@ +#pragma once + +#include +#include + +typedef struct NfcDebugLog NfcDebugLog; + +NfcDebugLog* nfc_debug_log_alloc(); + +void nfc_debug_log_free(NfcDebugLog* instance); + +void nfc_debug_log_process_data( + NfcDebugLog* instance, + uint8_t* data, + uint16_t len, + bool reader_to_tag, + bool crc_dropped); diff --git a/lib/nfc/helpers/nfc_debug_pcap.c b/lib/nfc/helpers/nfc_debug_pcap.c index 48d72bfb..6c7654ad 100644 --- a/lib/nfc/helpers/nfc_debug_pcap.c +++ b/lib/nfc/helpers/nfc_debug_pcap.c @@ -1,7 +1,9 @@ #include "nfc_debug_pcap.h" +#include +#include +#include #include -#include #define TAG "NfcDebugPcap" @@ -16,48 +18,94 @@ #define DATA_PCD_TO_PICC_CRC_DROPPED 0xFA #define NFC_DEBUG_PCAP_FILENAME EXT_PATH("nfc/debug.pcap") -#define NFC_DEBUG_PCAP_BUFFER_SIZE 64 -struct NfcDebugPcapWorker { - bool alive; - Storage* storage; - File* file; - StreamBufferHandle_t stream; - FuriThread* thread; +struct NfcDebugPcap { + Stream* file_stream; }; -static File* nfc_debug_pcap_open(Storage* storage) { - File* file = storage_file_alloc(storage); - if(!storage_file_open(file, NFC_DEBUG_PCAP_FILENAME, FSAM_WRITE, FSOM_OPEN_APPEND)) { - storage_file_free(file); - return NULL; - } - if(!storage_file_tell(file)) { - struct { - uint32_t magic; - uint16_t major, minor; - uint32_t reserved[2]; - uint32_t snaplen; - uint32_t link_type; - } __attribute__((__packed__)) pcap_hdr = { - .magic = PCAP_MAGIC, - .major = PCAP_MAJOR, - .minor = PCAP_MINOR, - .snaplen = FURI_HAL_NFC_DATA_BUFF_SIZE, - .link_type = DLT_ISO_14443, - }; - if(storage_file_write(file, &pcap_hdr, sizeof(pcap_hdr)) != sizeof(pcap_hdr)) { - FURI_LOG_E(TAG, "Failed to write pcap header"); +static Stream* nfc_debug_pcap_open(Storage* storage) { + Stream* stream = NULL; + stream = buffered_file_stream_alloc(storage); + if(!buffered_file_stream_open(stream, NFC_DEBUG_PCAP_FILENAME, FSAM_WRITE, FSOM_OPEN_APPEND)) { + buffered_file_stream_close(stream); + stream_free(stream); + stream = NULL; + } else { + if(!stream_tell(stream)) { + struct { + uint32_t magic; + uint16_t major, minor; + uint32_t reserved[2]; + uint32_t snaplen; + uint32_t link_type; + } __attribute__((__packed__)) pcap_hdr = { + .magic = PCAP_MAGIC, + .major = PCAP_MAJOR, + .minor = PCAP_MINOR, + .snaplen = FURI_HAL_NFC_DATA_BUFF_SIZE, + .link_type = DLT_ISO_14443, + }; + if(stream_write(stream, (uint8_t*)&pcap_hdr, sizeof(pcap_hdr)) != sizeof(pcap_hdr)) { + FURI_LOG_E(TAG, "Failed to write pcap header"); + buffered_file_stream_close(stream); + stream_free(stream); + stream = NULL; + } } } - return file; + return stream; } -static void - nfc_debug_pcap_write(NfcDebugPcapWorker* instance, uint8_t event, uint8_t* data, uint16_t len) { +NfcDebugPcap* nfc_debug_pcap_alloc() { + NfcDebugPcap* instance = malloc(sizeof(NfcDebugPcap)); + + Storage* storage = furi_record_open(RECORD_STORAGE); + instance->file_stream = nfc_debug_pcap_open(storage); + if(!instance->file_stream) { + free(instance); + instance = NULL; + } + furi_record_close(RECORD_STORAGE); + + return instance; +} + +void nfc_debug_pcap_free(NfcDebugPcap* instance) { + furi_assert(instance); + furi_assert(instance->file_stream); + + buffered_file_stream_close(instance->file_stream); + stream_free(instance->file_stream); + + free(instance); +} + +void nfc_debug_pcap_process_data( + NfcDebugPcap* instance, + uint8_t* data, + uint16_t len, + bool reader_to_tag, + bool crc_dropped) { + furi_assert(instance); + furi_assert(data); FuriHalRtcDateTime datetime; furi_hal_rtc_get_datetime(&datetime); + uint8_t event = 0; + if(reader_to_tag) { + if(crc_dropped) { + event = DATA_PCD_TO_PICC_CRC_DROPPED; + } else { + event = DATA_PCD_TO_PICC; + } + } else { + if(crc_dropped) { + event = DATA_PICC_TO_PCD_CRC_DROPPED; + } else { + event = DATA_PICC_TO_PCD; + } + } + struct { // https://wiki.wireshark.org/Development/LibpcapFileFormat#record-packet-header uint32_t ts_sec; @@ -77,90 +125,6 @@ static void .event = event, .len = len << 8 | len >> 8, }; - xStreamBufferSend(instance->stream, &pkt_hdr, sizeof(pkt_hdr), FuriWaitForever); - xStreamBufferSend(instance->stream, data, len, FuriWaitForever); -} - -static void - nfc_debug_pcap_write_tx(uint8_t* data, uint16_t bits, bool crc_dropped, void* context) { - NfcDebugPcapWorker* instance = context; - uint8_t event = crc_dropped ? DATA_PCD_TO_PICC_CRC_DROPPED : DATA_PCD_TO_PICC; - nfc_debug_pcap_write(instance, event, data, bits / 8); -} - -static void - nfc_debug_pcap_write_rx(uint8_t* data, uint16_t bits, bool crc_dropped, void* context) { - NfcDebugPcapWorker* instance = context; - uint8_t event = crc_dropped ? DATA_PICC_TO_PCD_CRC_DROPPED : DATA_PICC_TO_PCD; - nfc_debug_pcap_write(instance, event, data, bits / 8); -} - -int32_t nfc_debug_pcap_thread(void* context) { - NfcDebugPcapWorker* instance = context; - uint8_t buffer[NFC_DEBUG_PCAP_BUFFER_SIZE]; - - while(instance->alive) { - size_t ret = - xStreamBufferReceive(instance->stream, buffer, NFC_DEBUG_PCAP_BUFFER_SIZE, 50); - if(storage_file_write(instance->file, buffer, ret) != ret) { - FURI_LOG_E(TAG, "Failed to write pcap data"); - } - } - - return 0; -} - -NfcDebugPcapWorker* nfc_debug_pcap_alloc(Storage* storage) { - NfcDebugPcapWorker* instance = malloc(sizeof(NfcDebugPcapWorker)); - - instance->alive = true; - - instance->storage = storage; - - instance->file = nfc_debug_pcap_open(storage); - - instance->stream = xStreamBufferCreate(4096, 1); - - instance->thread = furi_thread_alloc(); - furi_thread_set_name(instance->thread, "PcapWorker"); - furi_thread_set_stack_size(instance->thread, 1024); - furi_thread_set_callback(instance->thread, nfc_debug_pcap_thread); - furi_thread_set_context(instance->thread, instance); - furi_thread_start(instance->thread); - - return instance; -} - -void nfc_debug_pcap_free(NfcDebugPcapWorker* instance) { - furi_assert(instance); - - instance->alive = false; - - furi_thread_join(instance->thread); - furi_thread_free(instance->thread); - - vStreamBufferDelete(instance->stream); - - if(instance->file) storage_file_free(instance->file); - - instance->storage = NULL; - - free(instance); -} - -void nfc_debug_pcap_prepare_tx_rx( - NfcDebugPcapWorker* instance, - FuriHalNfcTxRxContext* tx_rx, - bool is_picc) { - if(!instance || !instance->file) return; - - if(is_picc) { - tx_rx->sniff_tx = nfc_debug_pcap_write_rx; - tx_rx->sniff_rx = nfc_debug_pcap_write_tx; - } else { - tx_rx->sniff_tx = nfc_debug_pcap_write_tx; - tx_rx->sniff_rx = nfc_debug_pcap_write_rx; - } - - tx_rx->sniff_context = instance; + stream_write(instance->file_stream, (uint8_t*)&pkt_hdr, sizeof(pkt_hdr)); + stream_write(instance->file_stream, data, len); } diff --git a/lib/nfc/helpers/nfc_debug_pcap.h b/lib/nfc/helpers/nfc_debug_pcap.h index 6d2a449a..eeddc561 100644 --- a/lib/nfc/helpers/nfc_debug_pcap.h +++ b/lib/nfc/helpers/nfc_debug_pcap.h @@ -1,21 +1,17 @@ #pragma once -#include -#include +#include +#include -typedef struct NfcDebugPcapWorker NfcDebugPcapWorker; +typedef struct NfcDebugPcap NfcDebugPcap; -NfcDebugPcapWorker* nfc_debug_pcap_alloc(Storage* storage); +NfcDebugPcap* nfc_debug_pcap_alloc(); -void nfc_debug_pcap_free(NfcDebugPcapWorker* instance); +void nfc_debug_pcap_free(NfcDebugPcap* instance); -/** Prepare tx/rx context for debug pcap logging, if enabled. - * - * @param instance NfcDebugPcapWorker* instance, can be NULL - * @param tx_rx TX/RX context to log - * @param is_picc if true, record Flipper as PICC, else PCD. - */ -void nfc_debug_pcap_prepare_tx_rx( - NfcDebugPcapWorker* instance, - FuriHalNfcTxRxContext* tx_rx, - bool is_picc); +void nfc_debug_pcap_process_data( + NfcDebugPcap* instance, + uint8_t* data, + uint16_t len, + bool reader_to_tag, + bool crc_dropped); diff --git a/lib/nfc/helpers/reader_analyzer.c b/lib/nfc/helpers/reader_analyzer.c new file mode 100644 index 00000000..680b8cef --- /dev/null +++ b/lib/nfc/helpers/reader_analyzer.c @@ -0,0 +1,261 @@ +#include "reader_analyzer.h" +#include +#include +#include +#include + +#include "mfkey32.h" +#include "nfc_debug_pcap.h" +#include "nfc_debug_log.h" + +#define TAG "ReaderAnalyzer" + +#define READER_ANALYZER_MAX_BUFF_SIZE (1024) + +typedef struct { + bool reader_to_tag; + bool crc_dropped; + uint16_t len; +} ReaderAnalyzerHeader; + +typedef enum { + ReaderAnalyzerNfcDataMfClassic, +} ReaderAnalyzerNfcData; + +struct ReaderAnalyzer { + FuriHalNfcDevData nfc_data; + + bool alive; + StreamBufferHandle_t stream; + FuriThread* thread; + + ReaderAnalyzerParseDataCallback callback; + void* context; + + ReaderAnalyzerMode mode; + Mfkey32* mfkey32; + NfcDebugLog* debug_log; + NfcDebugPcap* pcap; +}; + +const FuriHalNfcDevData reader_analyzer_nfc_data[] = { + [ReaderAnalyzerNfcDataMfClassic] = + {.sak = 0x08, + .atqa = {0x44, 0x00}, + .interface = FuriHalNfcInterfaceRf, + .type = FuriHalNfcTypeA, + .uid_len = 7, + .uid = {0x04, 0x77, 0x70, 0x2A, 0x23, 0x4F, 0x80}, + .cuid = 0x2A234F80}, +}; + +void reader_analyzer_parse(ReaderAnalyzer* instance, uint8_t* buffer, size_t size) { + if(size < sizeof(ReaderAnalyzerHeader)) return; + + size_t bytes_i = 0; + while(bytes_i < size) { + ReaderAnalyzerHeader* header = (ReaderAnalyzerHeader*)&buffer[bytes_i]; + uint16_t len = header->len; + if(bytes_i + len > size) break; + bytes_i += sizeof(ReaderAnalyzerHeader); + if(instance->mfkey32) { + mfkey32_process_data( + instance->mfkey32, + &buffer[bytes_i], + len, + header->reader_to_tag, + header->crc_dropped); + } + if(instance->pcap) { + nfc_debug_pcap_process_data( + instance->pcap, &buffer[bytes_i], len, header->reader_to_tag, header->crc_dropped); + } + if(instance->debug_log) { + nfc_debug_log_process_data( + instance->debug_log, + &buffer[bytes_i], + len, + header->reader_to_tag, + header->crc_dropped); + } + bytes_i += len; + } +} + +int32_t reader_analyzer_thread(void* context) { + ReaderAnalyzer* reader_analyzer = context; + uint8_t buffer[READER_ANALYZER_MAX_BUFF_SIZE] = {}; + + while(reader_analyzer->alive || !xStreamBufferIsEmpty(reader_analyzer->stream)) { + size_t ret = xStreamBufferReceive( + reader_analyzer->stream, buffer, READER_ANALYZER_MAX_BUFF_SIZE, 50); + if(ret) { + reader_analyzer_parse(reader_analyzer, buffer, ret); + } + } + + return 0; +} + +ReaderAnalyzer* reader_analyzer_alloc() { + ReaderAnalyzer* instance = malloc(sizeof(ReaderAnalyzer)); + + instance->nfc_data = reader_analyzer_nfc_data[ReaderAnalyzerNfcDataMfClassic]; + instance->alive = false; + instance->stream = + xStreamBufferCreate(READER_ANALYZER_MAX_BUFF_SIZE, sizeof(ReaderAnalyzerHeader)); + + instance->thread = furi_thread_alloc(); + furi_thread_set_name(instance->thread, "ReaderAnalyzerWorker"); + furi_thread_set_stack_size(instance->thread, 2048); + furi_thread_set_callback(instance->thread, reader_analyzer_thread); + furi_thread_set_context(instance->thread, instance); + furi_thread_set_priority(instance->thread, FuriThreadPriorityLow); + + return instance; +} + +static void reader_analyzer_mfkey_callback(Mfkey32Event event, void* context) { + furi_assert(context); + ReaderAnalyzer* instance = context; + + if(event == Mfkey32EventParamCollected) { + if(instance->callback) { + instance->callback(ReaderAnalyzerEventMfkeyCollected, instance->context); + } + } +} + +void reader_analyzer_start(ReaderAnalyzer* instance, ReaderAnalyzerMode mode) { + furi_assert(instance); + + xStreamBufferReset(instance->stream); + if(mode & ReaderAnalyzerModeDebugLog) { + instance->debug_log = nfc_debug_log_alloc(); + } + if(mode & ReaderAnalyzerModeMfkey) { + instance->mfkey32 = mfkey32_alloc(instance->nfc_data.cuid); + if(instance->mfkey32) { + mfkey32_set_callback(instance->mfkey32, reader_analyzer_mfkey_callback, instance); + } + } + if(mode & ReaderAnalyzerModeDebugPcap) { + instance->pcap = nfc_debug_pcap_alloc(); + } + + instance->alive = true; + furi_thread_start(instance->thread); +} + +void reader_analyzer_stop(ReaderAnalyzer* instance) { + furi_assert(instance); + + instance->alive = false; + furi_thread_join(instance->thread); + + if(instance->debug_log) { + nfc_debug_log_free(instance->debug_log); + instance->debug_log = NULL; + } + if(instance->mfkey32) { + mfkey32_free(instance->mfkey32); + instance->mfkey32 = NULL; + } + if(instance->pcap) { + nfc_debug_pcap_free(instance->pcap); + } +} + +void reader_analyzer_free(ReaderAnalyzer* instance) { + furi_assert(instance); + + reader_analyzer_stop(instance); + furi_thread_free(instance->thread); + vStreamBufferDelete(instance->stream); + free(instance); +} + +void reader_analyzer_set_callback( + ReaderAnalyzer* instance, + ReaderAnalyzerParseDataCallback callback, + void* context) { + furi_assert(instance); + furi_assert(callback); + + instance->callback = callback; + instance->context = context; +} + +NfcProtocol + reader_analyzer_guess_protocol(ReaderAnalyzer* instance, uint8_t* buff_rx, uint16_t len) { + furi_assert(instance); + furi_assert(buff_rx); + UNUSED(len); + NfcProtocol protocol = NfcDeviceProtocolUnknown; + + if((buff_rx[0] == 0x60) || (buff_rx[0] == 0x61)) { + protocol = NfcDeviceProtocolMifareClassic; + } + + return protocol; +} + +FuriHalNfcDevData* reader_analyzer_get_nfc_data(ReaderAnalyzer* instance) { + furi_assert(instance); + + return &instance->nfc_data; +} + +static void reader_analyzer_write( + ReaderAnalyzer* instance, + uint8_t* data, + uint16_t len, + bool reader_to_tag, + bool crc_dropped) { + ReaderAnalyzerHeader header = { + .reader_to_tag = reader_to_tag, .crc_dropped = crc_dropped, .len = len}; + size_t data_sent = 0; + data_sent = xStreamBufferSend( + instance->stream, &header, sizeof(ReaderAnalyzerHeader), FuriWaitForever); + if(data_sent != sizeof(ReaderAnalyzerHeader)) { + FURI_LOG_W(TAG, "Sent %d out of %d bytes", data_sent, sizeof(ReaderAnalyzerHeader)); + } + data_sent = xStreamBufferSend(instance->stream, data, len, FuriWaitForever); + if(data_sent != len) { + FURI_LOG_W(TAG, "Sent %d out of %d bytes", data_sent, len); + } +} + +static void + reader_analyzer_write_rx(uint8_t* data, uint16_t bits, bool crc_dropped, void* context) { + UNUSED(crc_dropped); + ReaderAnalyzer* reader_analyzer = context; + uint16_t bytes = bits < 8 ? 1 : bits / 8; + reader_analyzer_write(reader_analyzer, data, bytes, false, crc_dropped); +} + +static void + reader_analyzer_write_tx(uint8_t* data, uint16_t bits, bool crc_dropped, void* context) { + UNUSED(crc_dropped); + ReaderAnalyzer* reader_analyzer = context; + uint16_t bytes = bits < 8 ? 1 : bits / 8; + reader_analyzer_write(reader_analyzer, data, bytes, true, crc_dropped); +} + +void reader_analyzer_prepare_tx_rx( + ReaderAnalyzer* instance, + FuriHalNfcTxRxContext* tx_rx, + bool is_picc) { + furi_assert(instance); + furi_assert(tx_rx); + + if(is_picc) { + tx_rx->sniff_tx = reader_analyzer_write_rx; + tx_rx->sniff_rx = reader_analyzer_write_tx; + } else { + tx_rx->sniff_rx = reader_analyzer_write_rx; + tx_rx->sniff_tx = reader_analyzer_write_tx; + } + + tx_rx->sniff_context = instance; +} diff --git a/lib/nfc/helpers/reader_analyzer.h b/lib/nfc/helpers/reader_analyzer.h new file mode 100644 index 00000000..cc501f5a --- /dev/null +++ b/lib/nfc/helpers/reader_analyzer.h @@ -0,0 +1,41 @@ +#pragma once + +#include +#include + +typedef enum { + ReaderAnalyzerModeDebugLog = 0x01, + ReaderAnalyzerModeMfkey = 0x02, + ReaderAnalyzerModeDebugPcap = 0x04, +} ReaderAnalyzerMode; + +typedef enum { + ReaderAnalyzerEventMfkeyCollected, +} ReaderAnalyzerEvent; + +typedef struct ReaderAnalyzer ReaderAnalyzer; + +typedef void (*ReaderAnalyzerParseDataCallback)(ReaderAnalyzerEvent event, void* context); + +ReaderAnalyzer* reader_analyzer_alloc(); + +void reader_analyzer_free(ReaderAnalyzer* instance); + +void reader_analyzer_set_callback( + ReaderAnalyzer* instance, + ReaderAnalyzerParseDataCallback callback, + void* context); + +void reader_analyzer_start(ReaderAnalyzer* instance, ReaderAnalyzerMode mode); + +void reader_analyzer_stop(ReaderAnalyzer* instance); + +NfcProtocol + reader_analyzer_guess_protocol(ReaderAnalyzer* instance, uint8_t* buff_rx, uint16_t len); + +FuriHalNfcDevData* reader_analyzer_get_nfc_data(ReaderAnalyzer* instance); + +void reader_analyzer_prepare_tx_rx( + ReaderAnalyzer* instance, + FuriHalNfcTxRxContext* tx_rx, + bool is_picc); diff --git a/lib/nfc/nfc_worker.c b/lib/nfc/nfc_worker.c index f07ea616..6355f8d1 100644 --- a/lib/nfc/nfc_worker.c +++ b/lib/nfc/nfc_worker.c @@ -28,9 +28,7 @@ NfcWorker* nfc_worker_alloc() { } nfc_worker_change_state(nfc_worker, NfcWorkerStateReady); - if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { - nfc_worker->debug_pcap_worker = nfc_debug_pcap_alloc(nfc_worker->storage); - } + nfc_worker->reader_analyzer = reader_analyzer_alloc(nfc_worker->storage); return nfc_worker; } @@ -42,7 +40,7 @@ void nfc_worker_free(NfcWorker* nfc_worker) { furi_record_close(RECORD_STORAGE); - if(nfc_worker->debug_pcap_worker) nfc_debug_pcap_free(nfc_worker->debug_pcap_worker); + reader_analyzer_free(nfc_worker->reader_analyzer); free(nfc_worker); } @@ -105,6 +103,8 @@ int32_t nfc_worker_task(void* context) { nfc_worker_mf_ultralight_read_auth(nfc_worker); } else if(nfc_worker->state == NfcWorkerStateMfClassicDictAttack) { nfc_worker_mf_classic_dict_attack(nfc_worker); + } else if(nfc_worker->state == NfcWorkerStateAnalyzeReader) { + nfc_worker_analyze_reader(nfc_worker); } furi_hal_nfc_sleep(); nfc_worker_change_state(nfc_worker, NfcWorkerStateReady); @@ -117,7 +117,11 @@ static bool nfc_worker_read_mf_ultralight(NfcWorker* nfc_worker, FuriHalNfcTxRxC MfUltralightReader reader = {}; MfUltralightData data = {}; - nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, tx_rx, false); + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + reader_analyzer_prepare_tx_rx(nfc_worker->reader_analyzer, tx_rx, false); + reader_analyzer_start(nfc_worker->reader_analyzer, ReaderAnalyzerModeDebugLog); + } + do { // Read card if(!furi_hal_nfc_detect(&nfc_worker->dev_data->nfc_data, 200)) break; @@ -127,6 +131,10 @@ static bool nfc_worker_read_mf_ultralight(NfcWorker* nfc_worker, FuriHalNfcTxRxC read_success = true; } while(false); + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + reader_analyzer_stop(nfc_worker->reader_analyzer); + } + return read_success; } @@ -134,7 +142,11 @@ static bool nfc_worker_read_mf_classic(NfcWorker* nfc_worker, FuriHalNfcTxRxCont furi_assert(nfc_worker->callback); bool read_success = false; - nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, tx_rx, false); + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + reader_analyzer_prepare_tx_rx(nfc_worker->reader_analyzer, tx_rx, false); + reader_analyzer_start(nfc_worker->reader_analyzer, ReaderAnalyzerModeDebugLog); + } + do { // Try to read supported card FURI_LOG_I(TAG, "Try read supported card ..."); @@ -162,6 +174,9 @@ static bool nfc_worker_read_mf_classic(NfcWorker* nfc_worker, FuriHalNfcTxRxCont } } while(false); + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + reader_analyzer_stop(nfc_worker->reader_analyzer); + } return read_success; } @@ -169,13 +184,21 @@ static bool nfc_worker_read_mf_desfire(NfcWorker* nfc_worker, FuriHalNfcTxRxCont bool read_success = false; MifareDesfireData* data = &nfc_worker->dev_data->mf_df_data; - nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, tx_rx, false); + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + reader_analyzer_prepare_tx_rx(nfc_worker->reader_analyzer, tx_rx, false); + reader_analyzer_start(nfc_worker->reader_analyzer, ReaderAnalyzerModeDebugLog); + } + do { if(!furi_hal_nfc_detect(&nfc_worker->dev_data->nfc_data, 300)) break; if(!mf_df_read_card(tx_rx, data)) break; read_success = true; } while(false); + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + reader_analyzer_stop(nfc_worker->reader_analyzer); + } + return read_success; } @@ -184,7 +207,11 @@ static bool nfc_worker_read_bank_card(NfcWorker* nfc_worker, FuriHalNfcTxRxConte EmvApplication emv_app = {}; EmvData* result = &nfc_worker->dev_data->emv_data; - nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, tx_rx, false); + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + reader_analyzer_prepare_tx_rx(nfc_worker->reader_analyzer, tx_rx, false); + reader_analyzer_start(nfc_worker->reader_analyzer, ReaderAnalyzerModeDebugLog); + } + do { // Read card if(!furi_hal_nfc_detect(&nfc_worker->dev_data->nfc_data, 300)) break; @@ -211,6 +238,10 @@ static bool nfc_worker_read_bank_card(NfcWorker* nfc_worker, FuriHalNfcTxRxConte read_success = true; } while(false); + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + reader_analyzer_stop(nfc_worker->reader_analyzer); + } + return read_success; } @@ -320,18 +351,14 @@ void nfc_worker_read(NfcWorker* nfc_worker) { void nfc_worker_emulate_uid(NfcWorker* nfc_worker) { FuriHalNfcTxRxContext tx_rx = {}; - nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, &tx_rx, true); FuriHalNfcDevData* data = &nfc_worker->dev_data->nfc_data; NfcReaderRequestData* reader_data = &nfc_worker->dev_data->reader_data; // TODO add support for RATS - // Now remove bit 6 in SAK to support ISO-14443A-3 emulation // Need to save ATS to support ISO-14443A-4 emulation - uint8_t sak = data->sak; - FURI_BIT_CLEAR(sak, 5); while(nfc_worker->state == NfcWorkerStateUidEmulate) { - if(furi_hal_nfc_listen(data->uid, data->uid_len, data->atqa, sak, true, 100)) { + if(furi_hal_nfc_listen(data->uid, data->uid_len, data->atqa, data->sak, false, 100)) { if(furi_hal_nfc_tx_rx(&tx_rx, 100)) { reader_data->size = tx_rx.rx_bits / 8; if(reader_data->size > 0) { @@ -349,7 +376,6 @@ void nfc_worker_emulate_uid(NfcWorker* nfc_worker) { void nfc_worker_emulate_apdu(NfcWorker* nfc_worker) { FuriHalNfcTxRxContext tx_rx = {}; - nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, &tx_rx, true); FuriHalNfcDevData params = { .uid = {0xCF, 0x72, 0xd4, 0x40}, .uid_len = 4, @@ -358,6 +384,11 @@ void nfc_worker_emulate_apdu(NfcWorker* nfc_worker) { .type = FuriHalNfcTypeA, }; + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + reader_analyzer_prepare_tx_rx(nfc_worker->reader_analyzer, &tx_rx, true); + reader_analyzer_start(nfc_worker->reader_analyzer, ReaderAnalyzerModeDebugLog); + } + while(nfc_worker->state == NfcWorkerStateEmulateApdu) { if(furi_hal_nfc_listen(params.uid, params.uid_len, params.atqa, params.sak, false, 300)) { FURI_LOG_D(TAG, "POS terminal detected"); @@ -370,6 +401,10 @@ void nfc_worker_emulate_apdu(NfcWorker* nfc_worker) { furi_hal_nfc_sleep(); furi_delay_ms(20); } + + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + reader_analyzer_stop(nfc_worker->reader_analyzer); + } } void nfc_worker_emulate_mf_ultralight(NfcWorker* nfc_worker) { @@ -484,7 +519,6 @@ void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker) { 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; MfClassicEmulator emulator = { .cuid = nfc_util_bytes2num(&nfc_data->uid[nfc_data->uid_len - 4], 4), @@ -525,6 +559,11 @@ void nfc_worker_mf_ultralight_read_auth(NfcWorker* nfc_worker) { MfUltralightReader reader = {}; mf_ul_reset(data); + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + reader_analyzer_prepare_tx_rx(nfc_worker->reader_analyzer, &tx_rx, true); + reader_analyzer_start(nfc_worker->reader_analyzer, ReaderAnalyzerModeDebugLog); + } + uint32_t key = 0; uint16_t pack = 0; while(nfc_worker->state == NfcWorkerStateReadMfUltralightReadAuth) { @@ -577,4 +616,61 @@ void nfc_worker_mf_ultralight_read_auth(NfcWorker* nfc_worker) { furi_delay_ms(10); } } + + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + reader_analyzer_stop(nfc_worker->reader_analyzer); + } +} + +static void nfc_worker_reader_analyzer_callback(ReaderAnalyzerEvent event, void* context) { + furi_assert(context); + NfcWorker* nfc_worker = context; + + if(event == ReaderAnalyzerEventMfkeyCollected) { + if(nfc_worker->callback) { + nfc_worker->callback(NfcWorkerEventDetectReaderMfkeyCollected, nfc_worker->context); + } + } +} + +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); + MfClassicEmulator emulator = { + .cuid = nfc_util_bytes2num(&nfc_data->uid[nfc_data->uid_len - 4], 4), + .data = nfc_worker->dev_data->mf_classic_data, + .data_changed = false, + }; + NfcaSignal* nfca_signal = nfca_signal_alloc(); + tx_rx.nfca_signal = nfca_signal; + reader_analyzer_prepare_tx_rx(reader_analyzer, &tx_rx, true); + reader_analyzer_start(nfc_worker->reader_analyzer, ReaderAnalyzerModeMfkey); + reader_analyzer_set_callback(reader_analyzer, nfc_worker_reader_analyzer_callback, nfc_worker); + + rfal_platform_spi_acquire(); + + FURI_LOG_D(TAG, "Start reader analyzer"); + while(nfc_worker->state == NfcWorkerStateAnalyzeReader) { + furi_hal_nfc_stop_cmd(); + furi_delay_ms(5); + furi_hal_nfc_listen_start(nfc_data); + if(furi_hal_nfc_listen_rx(&tx_rx, 300)) { + NfcProtocol protocol = + reader_analyzer_guess_protocol(reader_analyzer, tx_rx.rx_data, tx_rx.rx_bits / 8); + if(protocol == NfcDeviceProtocolMifareClassic) { + mf_classic_emulator(&emulator, &tx_rx); + } + } else { + FURI_LOG_D(TAG, "No data from reader"); + continue; + } + } + + rfal_platform_spi_release(); + + reader_analyzer_stop(nfc_worker->reader_analyzer); + + nfca_signal_free(nfca_signal); } diff --git a/lib/nfc/nfc_worker.h b/lib/nfc/nfc_worker.h index 22cbc3dc..b7bf4da9 100644 --- a/lib/nfc/nfc_worker.h +++ b/lib/nfc/nfc_worker.h @@ -16,6 +16,7 @@ typedef enum { NfcWorkerStateMfClassicEmulate, NfcWorkerStateReadMfUltralightReadAuth, NfcWorkerStateMfClassicDictAttack, + NfcWorkerStateAnalyzeReader, // Debug NfcWorkerStateEmulateApdu, NfcWorkerStateField, @@ -54,8 +55,12 @@ typedef enum { NfcWorkerEventFoundKeyA, NfcWorkerEventFoundKeyB, + // Detect Reader events + NfcWorkerEventDetectReaderMfkeyCollected, + // 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 1e98879a..526182f9 100644 --- a/lib/nfc/nfc_worker_i.h +++ b/lib/nfc/nfc_worker_i.h @@ -12,8 +12,7 @@ #include #include #include - -#include "helpers/nfc_debug_pcap.h" +#include struct NfcWorker { FuriThread* thread; @@ -27,7 +26,7 @@ struct NfcWorker { NfcWorkerState state; - NfcDebugPcapWorker* debug_pcap_worker; + ReaderAnalyzer* reader_analyzer; }; void nfc_worker_change_state(NfcWorker* nfc_worker, NfcWorkerState state); @@ -49,3 +48,5 @@ 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); + +void nfc_worker_analyze_reader(NfcWorker* nfc_worker);