Просмотр исходного кода

[FL-2759], [FL-2766] NFC collect params for mfkey32 attack (#1643)

* nfc: start nfc over rpc
* nfc: add detect reader state
* nfc: add reader analyzer
* nfc: rework reader analyzer
* reader_analyzer: print collected nonces to debug
* reader analyzer: add save on SD card
* reader_analyzer: separate mfkey related part to different file
* mfkey32: add logic for collecting parameters
* nfc: rework pcap with reader analyzer
* nfc: add logger for reader
* nfc: clean up
* nfc: add detect reader view
* nfc: add detect reader and mfkey nonces scenes
* nfc: add mfkey comlplete scene
* nfc: add new assets
* nfc: fix gui
* nfc: fix iso14443-4 UID emulation
* nfc: add no sd card notification
* nfc: fix grammar

Co-authored-by: あく <alleteam@gmail.com>
gornekich 3 лет назад
Родитель
Сommit
1853359d78

+ 9 - 0
applications/nfc/nfc.c

@@ -94,6 +94,11 @@ Nfc* nfc_alloc() {
     view_dispatcher_add_view(
     view_dispatcher_add_view(
         nfc->view_dispatcher, NfcViewDictAttack, dict_attack_get_view(nfc->dict_attack));
         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
     // Generator
     nfc->generator = NULL;
     nfc->generator = NULL;
 
 
@@ -158,6 +163,10 @@ void nfc_free(Nfc* nfc) {
     view_dispatcher_remove_view(nfc->view_dispatcher, NfcViewDictAttack);
     view_dispatcher_remove_view(nfc->view_dispatcher, NfcViewDictAttack);
     dict_attack_free(nfc->dict_attack);
     dict_attack_free(nfc->dict_attack);
 
 
+    // Detect Reader
+    view_dispatcher_remove_view(nfc->view_dispatcher, NfcViewDetectReader);
+    detect_reader_free(nfc->detect_reader);
+
     // Worker
     // Worker
     nfc_worker_stop(nfc->worker);
     nfc_worker_stop(nfc->worker);
     nfc_worker_free(nfc->worker);
     nfc_worker_free(nfc->worker);

+ 3 - 0
applications/nfc/nfc_i.h

@@ -28,6 +28,7 @@
 #include <lib/nfc/parsers/nfc_supported_card.h>
 #include <lib/nfc/parsers/nfc_supported_card.h>
 
 
 #include "views/dict_attack.h"
 #include "views/dict_attack.h"
+#include "views/detect_reader.h"
 
 
 #include <nfc/scenes/nfc_scene.h>
 #include <nfc/scenes/nfc_scene.h>
 #include <nfc/helpers/nfc_custom_event.h>
 #include <nfc/helpers/nfc_custom_event.h>
@@ -71,6 +72,7 @@ struct Nfc {
     TextBox* text_box;
     TextBox* text_box;
     Widget* widget;
     Widget* widget;
     DictAttack* dict_attack;
     DictAttack* dict_attack;
+    DetectReader* detect_reader;
 
 
     const NfcGenerator* generator;
     const NfcGenerator* generator;
 };
 };
@@ -85,6 +87,7 @@ typedef enum {
     NfcViewTextBox,
     NfcViewTextBox,
     NfcViewWidget,
     NfcViewWidget,
     NfcViewDictAttack,
     NfcViewDictAttack,
+    NfcViewDetectReader,
 } NfcView;
 } NfcView;
 
 
 Nfc* nfc_alloc();
 Nfc* nfc_alloc();

+ 2 - 0
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, exit_confirm, ExitConfirm)
 ADD_SCENE(nfc, retry_confirm, RetryConfirm)
 ADD_SCENE(nfc, retry_confirm, RetryConfirm)
 ADD_SCENE(nfc, detect_reader, DetectReader)
 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)
 ADD_SCENE(nfc, nfc_data_info, NfcDataInfo)

+ 12 - 92
applications/nfc/scenes/nfc_scene_detect_reader.c

@@ -1,126 +1,48 @@
 #include "../nfc_i.h"
 #include "../nfc_i.h"
 #include <dolphin/dolphin.h>
 #include <dolphin/dolphin.h>
 
 
-#define NFC_SCENE_DETECT_READER_LOG_SIZE_MAX (200)
-
-enum {
-    NfcSceneDetectReaderStateWidget,
-    NfcSceneDetectReaderStateTextBox,
-};
-
 bool nfc_detect_reader_worker_callback(NfcWorkerEvent event, void* context) {
 bool nfc_detect_reader_worker_callback(NfcWorkerEvent event, void* context) {
     UNUSED(event);
     UNUSED(event);
     furi_assert(context);
     furi_assert(context);
     Nfc* nfc = context;
     Nfc* nfc = context;
-    view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventWorkerExit);
+    view_dispatcher_send_custom_event(nfc->view_dispatcher, event);
     return true;
     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);
     furi_assert(context);
     Nfc* nfc = context;
     Nfc* nfc = context;
     view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit);
     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) {
 void nfc_scene_detect_reader_on_enter(void* context) {
     Nfc* nfc = context;
     Nfc* nfc = context;
     DOLPHIN_DEED(DolphinDeedNfcEmulate);
     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_start(
         nfc->worker,
         nfc->worker,
-        NfcWorkerStateUidEmulate,
+        NfcWorkerStateAnalyzeReader,
         &nfc->dev->dev_data,
         &nfc->dev->dev_data,
         nfc_detect_reader_worker_callback,
         nfc_detect_reader_worker_callback,
         nfc);
         nfc);
 
 
+    view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDetectReader);
+
     nfc_blink_start(nfc);
     nfc_blink_start(nfc);
 }
 }
 
 
 bool nfc_scene_detect_reader_on_event(void* context, SceneManagerEvent event) {
 bool nfc_scene_detect_reader_on_event(void* context, SceneManagerEvent event) {
     Nfc* nfc = context;
     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;
     bool consumed = false;
 
 
     if(event.type == SceneManagerEventTypeCustom) {
     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));
-            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);
+        if(event.event == NfcCustomEventViewExit) {
+            nfc_worker_stop(nfc->worker);
+            scene_manager_next_scene(nfc->scene_manager, NfcSceneMfkeyNoncesInfo);
             consumed = true;
             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;
             consumed = true;
         }
         }
     }
     }
@@ -135,9 +57,7 @@ void nfc_scene_detect_reader_on_exit(void* context) {
     nfc_worker_stop(nfc->worker);
     nfc_worker_stop(nfc->worker);
 
 
     // Clear view
     // 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);
     nfc_blink_stop(nfc);
 }
 }

+ 49 - 0
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);
+}

+ 55 - 0
applications/nfc/scenes/nfc_scene_mfkey_nonces_info.c

@@ -0,0 +1,55 @@
+#include "../nfc_i.h"
+#include <lib/nfc/helpers/mfkey32.h>
+
+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);
+}

+ 6 - 2
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);
             scene_manager_next_scene(nfc->scene_manager, NfcSceneRead);
             consumed = true;
             consumed = true;
         } else if(event.event == SubmenuIndexDetectReader) {
         } 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;
             consumed = true;
         } else if(event.event == SubmenuIndexSaved) {
         } else if(event.event == SubmenuIndexSaved) {
             scene_manager_next_scene(nfc->scene_manager, NfcSceneFileSelect);
             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);
             scene_manager_next_scene(nfc->scene_manager, NfcSceneSetType);
             consumed = true;
             consumed = true;
         } else if(event.event == SubmenuIndexDebug) {
         } else if(event.event == SubmenuIndexDebug) {
-            scene_manager_set_scene_state(nfc->scene_manager, NfcSceneStart, SubmenuIndexDebug);
             scene_manager_next_scene(nfc->scene_manager, NfcSceneDebug);
             scene_manager_next_scene(nfc->scene_manager, NfcSceneDebug);
             consumed = true;
             consumed = true;
         }
         }

+ 115 - 0
applications/nfc/views/detect_reader.c

@@ -0,0 +1,115 @@
+#include "detect_reader.h"
+
+#include <gui/elements.h>
+
+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;
+        });
+}

+ 23 - 0
applications/nfc/views/detect_reader.h

@@ -0,0 +1,23 @@
+#pragma once
+#include <stdint.h>
+#include <gui/view.h>
+#include <gui/modules/widget.h>
+
+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);

BIN
assets/icons/NFC/Reader_detect.png → assets/icons/NFC/ArrowC_1_36x36.png


BIN
assets/icons/NFC/Tap_reader_36x38.png


+ 16 - 5
firmware/targets/f7/furi_hal/furi_hal_nfc.c

@@ -217,7 +217,6 @@ bool furi_hal_nfc_listen(
     }
     }
     rfalLowPowerModeStop();
     rfalLowPowerModeStop();
     rfalNfcDiscoverParam params = {
     rfalNfcDiscoverParam params = {
-        .compMode = RFAL_COMPLIANCE_MODE_NFC,
         .techs2Find = RFAL_NFC_LISTEN_TECH_A,
         .techs2Find = RFAL_NFC_LISTEN_TECH_A,
         .totalDuration = 1000,
         .totalDuration = 1000,
         .devLimit = 1,
         .devLimit = 1,
@@ -230,6 +229,11 @@ bool furi_hal_nfc_listen(
         .notifyCb = NULL,
         .notifyCb = NULL,
         .activate_after_sak = activate_after_sak,
         .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;
     params.lmConfigPA.nfcidLen = uid_len;
     memcpy(params.lmConfigPA.nfcid, uid, uid_len);
     memcpy(params.lmConfigPA.nfcid, uid, uid_len);
     params.lmConfigPA.SENS_RES[0] = atqa[0];
     params.lmConfigPA.SENS_RES[0] = atqa[0];
@@ -271,6 +275,10 @@ void furi_hal_nfc_listen_sleep() {
     st25r3916ExecuteCommand(ST25R3916_CMD_GOTO_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) {
 bool furi_hal_nfc_listen_rx(FuriHalNfcTxRxContext* tx_rx, uint32_t timeout_ms) {
     furi_assert(tx_rx);
     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)) {
             if(st25r3916GetInterrupt(ST25R3916_IRQ_MASK_RXE)) {
                 furi_hal_nfc_read_fifo(tx_rx->rx_data, &tx_rx->rx_bits);
                 furi_hal_nfc_read_fifo(tx_rx->rx_data, &tx_rx->rx_bits);
                 data_received = true;
                 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;
                 break;
             }
             }
             continue;
             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);
     furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_nfc);
     st25r3916ExecuteCommand(ST25R3916_CMD_UNMASK_RECEIVE_DATA);
     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
     // Manually wait for interrupt
     furi_hal_gpio_init(&gpio_nfc_irq_rfid_pull, GpioModeInput, GpioPullDown, GpioSpeedVeryHigh);
     furi_hal_gpio_init(&gpio_nfc_irq_rfid_pull, GpioModeInput, GpioPullDown, GpioSpeedVeryHigh);
     st25r3916ClearAndEnableInterrupts(ST25R3916_IRQ_MASK_RXE);
     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;
     uint32_t irq = 0;
     uint8_t rxe = 0;
     uint8_t rxe = 0;
     uint32_t start = DWT->CYCCNT;
     uint32_t start = DWT->CYCCNT;

+ 2 - 0
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_start_sleep();
 
 
+void furi_hal_nfc_stop_cmd();
+
 /** NFC stop sleep
 /** NFC stop sleep
  */
  */
 void furi_hal_nfc_exit_sleep();
 void furi_hal_nfc_exit_sleep();

+ 230 - 0
lib/nfc/helpers/mfkey32.c

@@ -0,0 +1,230 @@
+#include "mfkey32.h"
+
+#include <furi/furi.h>
+#include <storage/storage.h>
+#include <stream/stream.h>
+#include <stream/buffered_file_stream.h>
+#include <m-array.h>
+
+#include <lib/nfc/protocols/mifare_classic.h>
+#include <lib/nfc/protocols/nfc_util.h>
+
+#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;
+}

+ 27 - 0
lib/nfc/helpers/mfkey32.h

@@ -0,0 +1,27 @@
+#pragma once
+
+#include <lib/nfc/protocols/mifare_classic.h>
+#include <m-string.h>
+
+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);

+ 72 - 0
lib/nfc/helpers/nfc_debug_log.c

@@ -0,0 +1,72 @@
+#include "nfc_debug_log.h"
+
+#include <m-string.h>
+#include <storage/storage.h>
+#include <stream/buffered_file_stream.h>
+
+#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);
+}

+ 17 - 0
lib/nfc/helpers/nfc_debug_log.h

@@ -0,0 +1,17 @@
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+
+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);

+ 84 - 120
lib/nfc/helpers/nfc_debug_pcap.c

@@ -1,7 +1,9 @@
 #include "nfc_debug_pcap.h"
 #include "nfc_debug_pcap.h"
 
 
+#include <storage/storage.h>
+#include <stream/buffered_file_stream.h>
+#include <furi_hal_nfc.h>
 #include <furi_hal_rtc.h>
 #include <furi_hal_rtc.h>
-#include <stream_buffer.h>
 
 
 #define TAG "NfcDebugPcap"
 #define TAG "NfcDebugPcap"
 
 
@@ -16,48 +18,94 @@
 #define DATA_PCD_TO_PICC_CRC_DROPPED 0xFA
 #define DATA_PCD_TO_PICC_CRC_DROPPED 0xFA
 
 
 #define NFC_DEBUG_PCAP_FILENAME EXT_PATH("nfc/debug.pcap")
 #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;
+}
+
+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);
 }
 }
 
 
-static void
-    nfc_debug_pcap_write(NfcDebugPcapWorker* instance, uint8_t event, uint8_t* data, uint16_t len) {
+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;
     FuriHalRtcDateTime datetime;
     furi_hal_rtc_get_datetime(&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 {
     struct {
         // https://wiki.wireshark.org/Development/LibpcapFileFormat#record-packet-header
         // https://wiki.wireshark.org/Development/LibpcapFileFormat#record-packet-header
         uint32_t ts_sec;
         uint32_t ts_sec;
@@ -77,90 +125,6 @@ static void
         .event = event,
         .event = event,
         .len = len << 8 | len >> 8,
         .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);
 }
 }

+ 11 - 15
lib/nfc/helpers/nfc_debug_pcap.h

@@ -1,21 +1,17 @@
 #pragma once
 #pragma once
 
 
-#include <furi_hal_nfc.h>
-#include <storage/storage.h>
+#include <stdint.h>
+#include <stdbool.h>
 
 
-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);

+ 261 - 0
lib/nfc/helpers/reader_analyzer.c

@@ -0,0 +1,261 @@
+#include "reader_analyzer.h"
+#include <stream_buffer.h>
+#include <lib/nfc/protocols/nfc_util.h>
+#include <lib/nfc/protocols/mifare_classic.h>
+#include <m-array.h>
+
+#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;
+}

+ 41 - 0
lib/nfc/helpers/reader_analyzer.h

@@ -0,0 +1,41 @@
+#pragma once
+
+#include <stdint.h>
+#include <lib/nfc/nfc_device.h>
+
+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);

+ 111 - 15
lib/nfc/nfc_worker.c

@@ -28,9 +28,7 @@ NfcWorker* nfc_worker_alloc() {
     }
     }
     nfc_worker_change_state(nfc_worker, NfcWorkerStateReady);
     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;
     return nfc_worker;
 }
 }
@@ -42,7 +40,7 @@ void nfc_worker_free(NfcWorker* nfc_worker) {
 
 
     furi_record_close(RECORD_STORAGE);
     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);
     free(nfc_worker);
 }
 }
@@ -105,6 +103,8 @@ int32_t nfc_worker_task(void* context) {
         nfc_worker_mf_ultralight_read_auth(nfc_worker);
         nfc_worker_mf_ultralight_read_auth(nfc_worker);
     } else if(nfc_worker->state == NfcWorkerStateMfClassicDictAttack) {
     } else if(nfc_worker->state == NfcWorkerStateMfClassicDictAttack) {
         nfc_worker_mf_classic_dict_attack(nfc_worker);
         nfc_worker_mf_classic_dict_attack(nfc_worker);
+    } else if(nfc_worker->state == NfcWorkerStateAnalyzeReader) {
+        nfc_worker_analyze_reader(nfc_worker);
     }
     }
     furi_hal_nfc_sleep();
     furi_hal_nfc_sleep();
     nfc_worker_change_state(nfc_worker, NfcWorkerStateReady);
     nfc_worker_change_state(nfc_worker, NfcWorkerStateReady);
@@ -117,7 +117,11 @@ static bool nfc_worker_read_mf_ultralight(NfcWorker* nfc_worker, FuriHalNfcTxRxC
     MfUltralightReader reader = {};
     MfUltralightReader reader = {};
     MfUltralightData data = {};
     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 {
     do {
         // Read card
         // Read card
         if(!furi_hal_nfc_detect(&nfc_worker->dev_data->nfc_data, 200)) break;
         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;
         read_success = true;
     } while(false);
     } while(false);
 
 
+    if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
+        reader_analyzer_stop(nfc_worker->reader_analyzer);
+    }
+
     return read_success;
     return read_success;
 }
 }
 
 
@@ -134,7 +142,11 @@ static bool nfc_worker_read_mf_classic(NfcWorker* nfc_worker, FuriHalNfcTxRxCont
     furi_assert(nfc_worker->callback);
     furi_assert(nfc_worker->callback);
     bool read_success = false;
     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 {
     do {
         // Try to read supported card
         // Try to read supported card
         FURI_LOG_I(TAG, "Try 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);
     } while(false);
 
 
+    if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
+        reader_analyzer_stop(nfc_worker->reader_analyzer);
+    }
     return read_success;
     return read_success;
 }
 }
 
 
@@ -169,13 +184,21 @@ static bool nfc_worker_read_mf_desfire(NfcWorker* nfc_worker, FuriHalNfcTxRxCont
     bool read_success = false;
     bool read_success = false;
     MifareDesfireData* data = &nfc_worker->dev_data->mf_df_data;
     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 {
     do {
         if(!furi_hal_nfc_detect(&nfc_worker->dev_data->nfc_data, 300)) break;
         if(!furi_hal_nfc_detect(&nfc_worker->dev_data->nfc_data, 300)) break;
         if(!mf_df_read_card(tx_rx, data)) break;
         if(!mf_df_read_card(tx_rx, data)) break;
         read_success = true;
         read_success = true;
     } while(false);
     } while(false);
 
 
+    if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
+        reader_analyzer_stop(nfc_worker->reader_analyzer);
+    }
+
     return read_success;
     return read_success;
 }
 }
 
 
@@ -184,7 +207,11 @@ static bool nfc_worker_read_bank_card(NfcWorker* nfc_worker, FuriHalNfcTxRxConte
     EmvApplication emv_app = {};
     EmvApplication emv_app = {};
     EmvData* result = &nfc_worker->dev_data->emv_data;
     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 {
     do {
         // Read card
         // Read card
         if(!furi_hal_nfc_detect(&nfc_worker->dev_data->nfc_data, 300)) break;
         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;
         read_success = true;
     } while(false);
     } while(false);
 
 
+    if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
+        reader_analyzer_stop(nfc_worker->reader_analyzer);
+    }
+
     return read_success;
     return read_success;
 }
 }
 
 
@@ -320,18 +351,14 @@ void nfc_worker_read(NfcWorker* nfc_worker) {
 
 
 void nfc_worker_emulate_uid(NfcWorker* nfc_worker) {
 void nfc_worker_emulate_uid(NfcWorker* nfc_worker) {
     FuriHalNfcTxRxContext tx_rx = {};
     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;
     FuriHalNfcDevData* data = &nfc_worker->dev_data->nfc_data;
     NfcReaderRequestData* reader_data = &nfc_worker->dev_data->reader_data;
     NfcReaderRequestData* reader_data = &nfc_worker->dev_data->reader_data;
 
 
     // TODO add support for RATS
     // 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
     // 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) {
     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)) {
             if(furi_hal_nfc_tx_rx(&tx_rx, 100)) {
                 reader_data->size = tx_rx.rx_bits / 8;
                 reader_data->size = tx_rx.rx_bits / 8;
                 if(reader_data->size > 0) {
                 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) {
 void nfc_worker_emulate_apdu(NfcWorker* nfc_worker) {
     FuriHalNfcTxRxContext tx_rx = {};
     FuriHalNfcTxRxContext tx_rx = {};
-    nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, &tx_rx, true);
     FuriHalNfcDevData params = {
     FuriHalNfcDevData params = {
         .uid = {0xCF, 0x72, 0xd4, 0x40},
         .uid = {0xCF, 0x72, 0xd4, 0x40},
         .uid_len = 4,
         .uid_len = 4,
@@ -358,6 +384,11 @@ void nfc_worker_emulate_apdu(NfcWorker* nfc_worker) {
         .type = FuriHalNfcTypeA,
         .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) {
     while(nfc_worker->state == NfcWorkerStateEmulateApdu) {
         if(furi_hal_nfc_listen(params.uid, params.uid_len, params.atqa, params.sak, false, 300)) {
         if(furi_hal_nfc_listen(params.uid, params.uid_len, params.atqa, params.sak, false, 300)) {
             FURI_LOG_D(TAG, "POS terminal detected");
             FURI_LOG_D(TAG, "POS terminal detected");
@@ -370,6 +401,10 @@ void nfc_worker_emulate_apdu(NfcWorker* nfc_worker) {
         furi_hal_nfc_sleep();
         furi_hal_nfc_sleep();
         furi_delay_ms(20);
         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) {
 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) {
 void nfc_worker_emulate_mf_classic(NfcWorker* nfc_worker) {
     FuriHalNfcTxRxContext tx_rx = {};
     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;
     FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data;
     MfClassicEmulator emulator = {
     MfClassicEmulator emulator = {
         .cuid = nfc_util_bytes2num(&nfc_data->uid[nfc_data->uid_len - 4], 4),
         .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 = {};
     MfUltralightReader reader = {};
     mf_ul_reset(data);
     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;
     uint32_t key = 0;
     uint16_t pack = 0;
     uint16_t pack = 0;
     while(nfc_worker->state == NfcWorkerStateReadMfUltralightReadAuth) {
     while(nfc_worker->state == NfcWorkerStateReadMfUltralightReadAuth) {
@@ -577,4 +616,61 @@ void nfc_worker_mf_ultralight_read_auth(NfcWorker* nfc_worker) {
             furi_delay_ms(10);
             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);
 }
 }

+ 5 - 0
lib/nfc/nfc_worker.h

@@ -16,6 +16,7 @@ typedef enum {
     NfcWorkerStateMfClassicEmulate,
     NfcWorkerStateMfClassicEmulate,
     NfcWorkerStateReadMfUltralightReadAuth,
     NfcWorkerStateReadMfUltralightReadAuth,
     NfcWorkerStateMfClassicDictAttack,
     NfcWorkerStateMfClassicDictAttack,
+    NfcWorkerStateAnalyzeReader,
     // Debug
     // Debug
     NfcWorkerStateEmulateApdu,
     NfcWorkerStateEmulateApdu,
     NfcWorkerStateField,
     NfcWorkerStateField,
@@ -54,8 +55,12 @@ typedef enum {
     NfcWorkerEventFoundKeyA,
     NfcWorkerEventFoundKeyA,
     NfcWorkerEventFoundKeyB,
     NfcWorkerEventFoundKeyB,
 
 
+    // Detect Reader events
+    NfcWorkerEventDetectReaderMfkeyCollected,
+
     // Mifare Ultralight events
     // Mifare Ultralight events
     NfcWorkerEventMfUltralightPassKey,
     NfcWorkerEventMfUltralightPassKey,
+
 } NfcWorkerEvent;
 } NfcWorkerEvent;
 
 
 typedef bool (*NfcWorkerCallback)(NfcWorkerEvent event, void* context);
 typedef bool (*NfcWorkerCallback)(NfcWorkerEvent event, void* context);

+ 4 - 3
lib/nfc/nfc_worker_i.h

@@ -12,8 +12,7 @@
 #include <lib/nfc/protocols/mifare_classic.h>
 #include <lib/nfc/protocols/mifare_classic.h>
 #include <lib/nfc/protocols/mifare_desfire.h>
 #include <lib/nfc/protocols/mifare_desfire.h>
 #include <lib/nfc/protocols/nfca.h>
 #include <lib/nfc/protocols/nfca.h>
-
-#include "helpers/nfc_debug_pcap.h"
+#include <lib/nfc/helpers/reader_analyzer.h>
 
 
 struct NfcWorker {
 struct NfcWorker {
     FuriThread* thread;
     FuriThread* thread;
@@ -27,7 +26,7 @@ struct NfcWorker {
 
 
     NfcWorkerState state;
     NfcWorkerState state;
 
 
-    NfcDebugPcapWorker* debug_pcap_worker;
+    ReaderAnalyzer* reader_analyzer;
 };
 };
 
 
 void nfc_worker_change_state(NfcWorker* nfc_worker, NfcWorkerState state);
 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_mf_ul_auth_attack(NfcWorker* nfc_worker);
 
 
 void nfc_worker_emulate_apdu(NfcWorker* nfc_worker);
 void nfc_worker_emulate_apdu(NfcWorker* nfc_worker);
+
+void nfc_worker_analyze_reader(NfcWorker* nfc_worker);