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

nfc: add debug PCAP output, refactor Mifare DESFire following #1095 (#1294)

* nfc: refactor nfc_worker_read_mifare_desfire to use furi_hal_nfc_tx_rx
  Renames furi_hal_nfc_exchange_full to furi_hal_nfc_tx_rx_full, and
  rewrites it to use furi_hal_nfc_tx_rx.  This eliminates the final
  remaining use of furi_hal_nfc_exchange, so remove that.
* nfc: write debug.pcap when debug mode enabled
  Limited to NFC protocols that use furi_hal_nfc_tx_rx to communicate.
* switch to Doxygen style comment

Co-authored-by: Kevin Wallace <git+flipperzero@kevin.wallace.seattle.wa.us>
Co-authored-by: あく <alleteam@gmail.com>
Kevin Wallace 3 лет назад
Родитель
Сommit
9c9f66a30f

+ 99 - 0
applications/nfc/helpers/nfc_debug_pcap.c

@@ -0,0 +1,99 @@
+#include "nfc_debug_pcap.h"
+
+#include <furi_hal_rtc.h>
+
+#define TAG "NfcDebugPcap"
+
+#define PCAP_MAGIC 0xa1b2c3d4
+#define PCAP_MAJOR 2
+#define PCAP_MINOR 4
+#define DLT_ISO_14443 264
+
+#define DATA_PICC_TO_PCD 0xFF
+#define DATA_PCD_TO_PICC 0xFE
+#define DATA_PICC_TO_PCD_CRC_DROPPED 0xFB
+#define DATA_PCD_TO_PICC_CRC_DROPPED 0xFA
+
+File* nfc_debug_pcap_open(Storage* storage) {
+    File* file = storage_file_alloc(storage);
+    if(!storage_file_open(file, "/ext/nfc/debug.pcap", 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");
+        }
+    }
+    return file;
+}
+
+void nfc_debug_pcap_write(Storage* storage, uint8_t event, uint8_t* data, uint16_t len) {
+    File* file = nfc_debug_pcap_open(storage);
+    if(!file) return;
+
+    FuriHalRtcDateTime datetime;
+    furi_hal_rtc_get_datetime(&datetime);
+
+    struct {
+        // https://wiki.wireshark.org/Development/LibpcapFileFormat#record-packet-header
+        uint32_t ts_sec;
+        uint32_t ts_usec;
+        uint32_t incl_len;
+        uint32_t orig_len;
+        // https://www.kaiser.cx/posts/pcap-iso14443/#_packet_data
+        uint8_t version;
+        uint8_t event;
+        uint16_t len;
+    } __attribute__((__packed__)) pkt_hdr = {
+        .ts_sec = furi_hal_rtc_datetime_to_timestamp(&datetime),
+        .ts_usec = 0,
+        .incl_len = len + 4,
+        .orig_len = len + 4,
+        .version = 0,
+        .event = event,
+        .len = len << 8 | len >> 8,
+    };
+    if(storage_file_write(file, &pkt_hdr, sizeof(pkt_hdr)) != sizeof(pkt_hdr)) {
+        FURI_LOG_E(TAG, "Failed to write pcap packet header");
+    } else if(storage_file_write(file, data, len) != len) {
+        FURI_LOG_E(TAG, "Failed to write pcap packet data");
+    }
+    storage_file_free(file);
+}
+
+void nfc_debug_pcap_write_tx(uint8_t* data, uint16_t bits, bool crc_dropped, void* context) {
+    uint8_t event = crc_dropped ? DATA_PCD_TO_PICC_CRC_DROPPED : DATA_PCD_TO_PICC;
+    nfc_debug_pcap_write(context, event, data, bits / 8);
+}
+
+void nfc_debug_pcap_write_rx(uint8_t* data, uint16_t bits, bool crc_dropped, void* context) {
+    uint8_t event = crc_dropped ? DATA_PICC_TO_PCD_CRC_DROPPED : DATA_PICC_TO_PCD;
+    nfc_debug_pcap_write(context, event, data, bits / 8);
+}
+
+void nfc_debug_pcap_prepare_tx_rx(FuriHalNfcTxRxContext* tx_rx, Storage* storage, bool is_picc) {
+    if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
+        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 = storage;
+    }
+}

+ 12 - 0
applications/nfc/helpers/nfc_debug_pcap.h

@@ -0,0 +1,12 @@
+#pragma once
+
+#include <furi_hal_nfc.h>
+#include <storage/storage.h>
+
+/** Prepare tx/rx context for debug pcap logging, if enabled.
+ *
+ * @param      tx_rx   TX/RX context to log
+ * @param      storage Storage to log to
+ * @param      is_picc if true, record Flipper as PICC, else PCD.
+ */
+void nfc_debug_pcap_prepare_tx_rx(FuriHalNfcTxRxContext* tx_rx, Storage* storage, bool is_picc);

+ 64 - 65
applications/nfc/nfc_worker.c

@@ -10,6 +10,7 @@
 #include <lib/nfc_protocols/nfca.h>
 
 #include "helpers/nfc_mf_classic_dict.h"
+#include "helpers/nfc_debug_pcap.h"
 
 #define TAG "NfcWorker"
 
@@ -153,6 +154,7 @@ void nfc_worker_detect(NfcWorker* nfc_worker) {
 
 void nfc_worker_emulate(NfcWorker* nfc_worker) {
     FuriHalNfcTxRxContext tx_rx = {};
+    nfc_debug_pcap_prepare_tx_rx(&tx_rx, nfc_worker->storage, true);
     FuriHalNfcDevData* data = &nfc_worker->dev_data->nfc_data;
     NfcReaderRequestData* reader_data = &nfc_worker->dev_data->reader_data;
 
@@ -175,6 +177,7 @@ void nfc_worker_emulate(NfcWorker* nfc_worker) {
 
 void nfc_worker_read_emv_app(NfcWorker* nfc_worker) {
     FuriHalNfcTxRxContext tx_rx = {};
+    nfc_debug_pcap_prepare_tx_rx(&tx_rx, nfc_worker->storage, false);
     EmvApplication emv_app = {};
     NfcDeviceData* result = nfc_worker->dev_data;
     FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data;
@@ -206,6 +209,7 @@ void nfc_worker_read_emv_app(NfcWorker* nfc_worker) {
 
 void nfc_worker_read_emv(NfcWorker* nfc_worker) {
     FuriHalNfcTxRxContext tx_rx = {};
+    nfc_debug_pcap_prepare_tx_rx(&tx_rx, nfc_worker->storage, false);
     EmvApplication emv_app = {};
     NfcDeviceData* result = nfc_worker->dev_data;
     FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data;
@@ -254,6 +258,7 @@ void nfc_worker_read_emv(NfcWorker* nfc_worker) {
 
 void nfc_worker_emulate_apdu(NfcWorker* nfc_worker) {
     FuriHalNfcTxRxContext tx_rx = {};
+    nfc_debug_pcap_prepare_tx_rx(&tx_rx, nfc_worker->storage, true);
     FuriHalNfcDevData params = {
         .uid = {0xCF, 0x72, 0xd4, 0x40},
         .uid_len = 4,
@@ -278,6 +283,7 @@ void nfc_worker_emulate_apdu(NfcWorker* nfc_worker) {
 
 void nfc_worker_read_mifare_ultralight(NfcWorker* nfc_worker) {
     FuriHalNfcTxRxContext tx_rx = {};
+    nfc_debug_pcap_prepare_tx_rx(&tx_rx, nfc_worker->storage, false);
     MfUltralightReader reader = {};
     MfUltralightData data = {};
     NfcDeviceData* result = nfc_worker->dev_data;
@@ -342,6 +348,7 @@ void nfc_worker_emulate_mifare_ul(NfcWorker* nfc_worker) {
 void nfc_worker_mifare_classic_dict_attack(NfcWorker* nfc_worker) {
     furi_assert(nfc_worker->callback);
     FuriHalNfcTxRxContext tx_rx_ctx = {};
+    nfc_debug_pcap_prepare_tx_rx(&tx_rx_ctx, nfc_worker->storage, false);
     MfClassicAuthContext auth_ctx = {};
     MfClassicReader reader = {};
     uint64_t curr_key = 0;
@@ -483,7 +490,8 @@ void nfc_worker_mifare_classic_dict_attack(NfcWorker* nfc_worker) {
 }
 
 void nfc_worker_emulate_mifare_classic(NfcWorker* nfc_worker) {
-    FuriHalNfcTxRxContext tx_rx;
+    FuriHalNfcTxRxContext tx_rx = {};
+    nfc_debug_pcap_prepare_tx_rx(&tx_rx, nfc_worker->storage, 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),
@@ -511,11 +519,8 @@ void nfc_worker_emulate_mifare_classic(NfcWorker* nfc_worker) {
 }
 
 void nfc_worker_read_mifare_desfire(NfcWorker* nfc_worker) {
-    ReturnCode err;
-    uint8_t tx_buff[64] = {};
-    uint16_t tx_len = 0;
-    uint8_t rx_buff[512] = {};
-    uint16_t rx_len;
+    FuriHalNfcTxRxContext tx_rx = {};
+    nfc_debug_pcap_prepare_tx_rx(&tx_rx, nfc_worker->storage, false);
     NfcDeviceData* result = nfc_worker->dev_data;
     nfc_device_data_clear(result);
     MifareDesfireData* data = &result->mf_df_data;
@@ -540,37 +545,36 @@ void nfc_worker_read_mifare_desfire(NfcWorker* nfc_worker) {
         result->protocol = NfcDeviceProtocolMifareDesfire;
 
         // Get DESFire version
-        tx_len = mf_df_prepare_get_version(tx_buff);
-        err = furi_hal_nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len);
-        if(err != ERR_NONE) {
-            FURI_LOG_W(TAG, "Bad exchange getting version, err: %d", err);
+        tx_rx.tx_bits = 8 * mf_df_prepare_get_version(tx_rx.tx_data);
+        if(!furi_hal_nfc_tx_rx_full(&tx_rx)) {
+            FURI_LOG_W(TAG, "Bad exchange getting version");
             continue;
         }
-        if(!mf_df_parse_get_version_response(rx_buff, rx_len, &data->version)) {
+        if(!mf_df_parse_get_version_response(tx_rx.rx_data, tx_rx.rx_bits / 8, &data->version)) {
             FURI_LOG_W(TAG, "Bad DESFire GET_VERSION response");
             continue;
         }
 
-        tx_len = mf_df_prepare_get_free_memory(tx_buff);
-        err = furi_hal_nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len);
-        if(err == ERR_NONE) {
+        tx_rx.tx_bits = 8 * mf_df_prepare_get_free_memory(tx_rx.tx_data);
+        if(furi_hal_nfc_tx_rx_full(&tx_rx)) {
             data->free_memory = malloc(sizeof(MifareDesfireFreeMemory));
             memset(data->free_memory, 0, sizeof(MifareDesfireFreeMemory));
-            if(!mf_df_parse_get_free_memory_response(rx_buff, rx_len, data->free_memory)) {
+            if(!mf_df_parse_get_free_memory_response(
+                   tx_rx.rx_data, tx_rx.rx_bits / 8, data->free_memory)) {
                 FURI_LOG_D(TAG, "Bad DESFire GET_FREE_MEMORY response (normal for pre-EV1 cards)");
                 free(data->free_memory);
                 data->free_memory = NULL;
             }
         }
 
-        tx_len = mf_df_prepare_get_key_settings(tx_buff);
-        err = furi_hal_nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len);
-        if(err != ERR_NONE) {
-            FURI_LOG_D(TAG, "Bad exchange getting key settings, err: %d", err);
+        tx_rx.tx_bits = 8 * mf_df_prepare_get_key_settings(tx_rx.tx_data);
+        if(!furi_hal_nfc_tx_rx_full(&tx_rx)) {
+            FURI_LOG_D(TAG, "Bad exchange getting key settings");
         } else {
             data->master_key_settings = malloc(sizeof(MifareDesfireKeySettings));
             memset(data->master_key_settings, 0, sizeof(MifareDesfireKeySettings));
-            if(!mf_df_parse_get_key_settings_response(rx_buff, rx_len, data->master_key_settings)) {
+            if(!mf_df_parse_get_key_settings_response(
+                   tx_rx.rx_data, tx_rx.rx_bits / 8, data->master_key_settings)) {
                 FURI_LOG_W(TAG, "Bad DESFire GET_KEY_SETTINGS response");
                 free(data->master_key_settings);
                 data->master_key_settings = NULL;
@@ -580,17 +584,16 @@ void nfc_worker_read_mifare_desfire(NfcWorker* nfc_worker) {
             MifareDesfireKeyVersion** key_version_head =
                 &data->master_key_settings->key_version_head;
             for(uint8_t key_id = 0; key_id < data->master_key_settings->max_keys; key_id++) {
-                tx_len = mf_df_prepare_get_key_version(tx_buff, key_id);
-                err =
-                    furi_hal_nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len);
-                if(err != ERR_NONE) {
-                    FURI_LOG_W(TAG, "Bad exchange getting key version, err: %d", err);
+                tx_rx.tx_bits = 8 * mf_df_prepare_get_key_version(tx_rx.tx_data, key_id);
+                if(!furi_hal_nfc_tx_rx_full(&tx_rx)) {
+                    FURI_LOG_W(TAG, "Bad exchange getting key version");
                     continue;
                 }
                 MifareDesfireKeyVersion* key_version = malloc(sizeof(MifareDesfireKeyVersion));
                 memset(key_version, 0, sizeof(MifareDesfireKeyVersion));
                 key_version->id = key_id;
-                if(!mf_df_parse_get_key_version_response(rx_buff, rx_len, key_version)) {
+                if(!mf_df_parse_get_key_version_response(
+                       tx_rx.rx_data, tx_rx.rx_bits / 8, key_version)) {
                     FURI_LOG_W(TAG, "Bad DESFire GET_KEY_VERSION response");
                     free(key_version);
                     continue;
@@ -600,31 +603,31 @@ void nfc_worker_read_mifare_desfire(NfcWorker* nfc_worker) {
             }
         }
 
-        tx_len = mf_df_prepare_get_application_ids(tx_buff);
-        err = furi_hal_nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len);
-        if(err != ERR_NONE) {
-            FURI_LOG_W(TAG, "Bad exchange getting application IDs, err: %d", err);
+        tx_rx.tx_bits = 8 * mf_df_prepare_get_application_ids(tx_rx.tx_data);
+        if(!furi_hal_nfc_tx_rx_full(&tx_rx)) {
+            FURI_LOG_W(TAG, "Bad exchange getting application IDs");
         } else {
-            if(!mf_df_parse_get_application_ids_response(rx_buff, rx_len, &data->app_head)) {
+            if(!mf_df_parse_get_application_ids_response(
+                   tx_rx.rx_data, tx_rx.rx_bits / 8, &data->app_head)) {
                 FURI_LOG_W(TAG, "Bad DESFire GET_APPLICATION_IDS response");
             }
         }
 
         for(MifareDesfireApplication* app = data->app_head; app; app = app->next) {
-            tx_len = mf_df_prepare_select_application(tx_buff, app->id);
-            err = furi_hal_nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len);
-            if(!mf_df_parse_select_application_response(rx_buff, rx_len)) {
-                FURI_LOG_W(TAG, "Bad exchange selecting application, err: %d", err);
+            tx_rx.tx_bits = 8 * mf_df_prepare_select_application(tx_rx.tx_data, app->id);
+            if(!furi_hal_nfc_tx_rx_full(&tx_rx) ||
+               !mf_df_parse_select_application_response(tx_rx.rx_data, tx_rx.rx_bits / 8)) {
+                FURI_LOG_W(TAG, "Bad exchange selecting application");
                 continue;
             }
-            tx_len = mf_df_prepare_get_key_settings(tx_buff);
-            err = furi_hal_nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len);
-            if(err != ERR_NONE) {
-                FURI_LOG_W(TAG, "Bad exchange getting key settings, err: %d", err);
+            tx_rx.tx_bits = 8 * mf_df_prepare_get_key_settings(tx_rx.tx_data);
+            if(!furi_hal_nfc_tx_rx_full(&tx_rx)) {
+                FURI_LOG_W(TAG, "Bad exchange getting key settings");
             } else {
                 app->key_settings = malloc(sizeof(MifareDesfireKeySettings));
                 memset(app->key_settings, 0, sizeof(MifareDesfireKeySettings));
-                if(!mf_df_parse_get_key_settings_response(rx_buff, rx_len, app->key_settings)) {
+                if(!mf_df_parse_get_key_settings_response(
+                       tx_rx.rx_data, tx_rx.rx_bits / 8, app->key_settings)) {
                     FURI_LOG_W(TAG, "Bad DESFire GET_KEY_SETTINGS response");
                     free(app->key_settings);
                     app->key_settings = NULL;
@@ -633,17 +636,16 @@ void nfc_worker_read_mifare_desfire(NfcWorker* nfc_worker) {
 
                 MifareDesfireKeyVersion** key_version_head = &app->key_settings->key_version_head;
                 for(uint8_t key_id = 0; key_id < app->key_settings->max_keys; key_id++) {
-                    tx_len = mf_df_prepare_get_key_version(tx_buff, key_id);
-                    err = furi_hal_nfc_exchange_full(
-                        tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len);
-                    if(err != ERR_NONE) {
-                        FURI_LOG_W(TAG, "Bad exchange getting key version, err: %d", err);
+                    tx_rx.tx_bits = 8 * mf_df_prepare_get_key_version(tx_rx.tx_data, key_id);
+                    if(!furi_hal_nfc_tx_rx_full(&tx_rx)) {
+                        FURI_LOG_W(TAG, "Bad exchange getting key version");
                         continue;
                     }
                     MifareDesfireKeyVersion* key_version = malloc(sizeof(MifareDesfireKeyVersion));
                     memset(key_version, 0, sizeof(MifareDesfireKeyVersion));
                     key_version->id = key_id;
-                    if(!mf_df_parse_get_key_version_response(rx_buff, rx_len, key_version)) {
+                    if(!mf_df_parse_get_key_version_response(
+                           tx_rx.rx_data, tx_rx.rx_bits / 8, key_version)) {
                         FURI_LOG_W(TAG, "Bad DESFire GET_KEY_VERSION response");
                         free(key_version);
                         continue;
@@ -653,48 +655,45 @@ void nfc_worker_read_mifare_desfire(NfcWorker* nfc_worker) {
                 }
             }
 
-            tx_len = mf_df_prepare_get_file_ids(tx_buff);
-            err = furi_hal_nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len);
-            if(err != ERR_NONE) {
-                FURI_LOG_W(TAG, "Bad exchange getting file IDs, err: %d", err);
+            tx_rx.tx_bits = 8 * mf_df_prepare_get_file_ids(tx_rx.tx_data);
+            if(!furi_hal_nfc_tx_rx_full(&tx_rx)) {
+                FURI_LOG_W(TAG, "Bad exchange getting file IDs");
             } else {
-                if(!mf_df_parse_get_file_ids_response(rx_buff, rx_len, &app->file_head)) {
+                if(!mf_df_parse_get_file_ids_response(
+                       tx_rx.rx_data, tx_rx.rx_bits / 8, &app->file_head)) {
                     FURI_LOG_W(TAG, "Bad DESFire GET_FILE_IDS response");
                 }
             }
 
             for(MifareDesfireFile* file = app->file_head; file; file = file->next) {
-                tx_len = mf_df_prepare_get_file_settings(tx_buff, file->id);
-                err =
-                    furi_hal_nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len);
-                if(err != ERR_NONE) {
-                    FURI_LOG_W(TAG, "Bad exchange getting file settings, err: %d", err);
+                tx_rx.tx_bits = 8 * mf_df_prepare_get_file_settings(tx_rx.tx_data, file->id);
+                if(!furi_hal_nfc_tx_rx_full(&tx_rx)) {
+                    FURI_LOG_W(TAG, "Bad exchange getting file settings");
                     continue;
                 }
-                if(!mf_df_parse_get_file_settings_response(rx_buff, rx_len, file)) {
+                if(!mf_df_parse_get_file_settings_response(
+                       tx_rx.rx_data, tx_rx.rx_bits / 8, file)) {
                     FURI_LOG_W(TAG, "Bad DESFire GET_FILE_SETTINGS response");
                     continue;
                 }
                 switch(file->type) {
                 case MifareDesfireFileTypeStandard:
                 case MifareDesfireFileTypeBackup:
-                    tx_len = mf_df_prepare_read_data(tx_buff, file->id, 0, 0);
+                    tx_rx.tx_bits = 8 * mf_df_prepare_read_data(tx_rx.tx_data, file->id, 0, 0);
                     break;
                 case MifareDesfireFileTypeValue:
-                    tx_len = mf_df_prepare_get_value(tx_buff, file->id);
+                    tx_rx.tx_bits = 8 * mf_df_prepare_get_value(tx_rx.tx_data, file->id);
                     break;
                 case MifareDesfireFileTypeLinearRecord:
                 case MifareDesfireFileTypeCyclicRecord:
-                    tx_len = mf_df_prepare_read_records(tx_buff, file->id, 0, 0);
+                    tx_rx.tx_bits = 8 * mf_df_prepare_read_records(tx_rx.tx_data, file->id, 0, 0);
                     break;
                 }
-                err =
-                    furi_hal_nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len);
-                if(err != ERR_NONE) {
-                    FURI_LOG_W(TAG, "Bad exchange reading file %d, err: %d", file->id, err);
+                if(!furi_hal_nfc_tx_rx_full(&tx_rx)) {
+                    FURI_LOG_W(TAG, "Bad exchange reading file %d", file->id);
                     continue;
                 }
-                if(!mf_df_parse_read_data_response(rx_buff, rx_len, file)) {
+                if(!mf_df_parse_read_data_response(tx_rx.rx_data, tx_rx.rx_bits / 8, file)) {
                     FURI_LOG_W(TAG, "Bad response reading file %d", file->id);
                     continue;
                 }

+ 31 - 63
firmware/targets/f7/furi_hal/furi_hal_nfc.c

@@ -366,44 +366,6 @@ bool furi_hal_nfc_emulate_nfca(
     return true;
 }
 
-ReturnCode furi_hal_nfc_data_exchange(
-    uint8_t* tx_buff,
-    uint16_t tx_len,
-    uint8_t** rx_buff,
-    uint16_t** rx_len,
-    bool deactivate) {
-    furi_assert(rx_buff);
-    furi_assert(rx_len);
-
-    ReturnCode ret;
-    rfalNfcState state = RFAL_NFC_STATE_ACTIVATED;
-    ret = rfalNfcDataExchangeStart(tx_buff, tx_len, rx_buff, rx_len, 0, RFAL_TXRX_FLAGS_DEFAULT);
-    if(ret != ERR_NONE) {
-        return ret;
-    }
-    uint32_t start = DWT->CYCCNT;
-    while(state != RFAL_NFC_STATE_DATAEXCHANGE_DONE) {
-        rfalNfcWorker();
-        state = rfalNfcGetState();
-        ret = rfalNfcDataExchangeGetStatus();
-        if(ret == ERR_BUSY) {
-            if(DWT->CYCCNT - start > 1000 * clocks_in_ms) {
-                ret = ERR_TIMEOUT;
-                break;
-            }
-            continue;
-        } else {
-            start = DWT->CYCCNT;
-        }
-        taskYIELD();
-    }
-    if(deactivate) {
-        rfalNfcDeactivate(false);
-        rfalLowPowerModeStart();
-    }
-    return ret;
-}
-
 static bool furi_hal_nfc_transparent_tx_rx(FuriHalNfcTxRxContext* tx_rx, uint16_t timeout_ms) {
     furi_assert(tx_rx->nfca_signal);
 
@@ -576,6 +538,12 @@ bool furi_hal_nfc_tx_rx(FuriHalNfcTxRxContext* tx_rx, uint16_t timeout_ms) {
         FURI_LOG_E(TAG, "Failed to start data exchange");
         return false;
     }
+
+    if(tx_rx->sniff_tx) {
+        bool crc_dropped = !(flags & RFAL_TXRX_FLAGS_CRC_TX_MANUAL);
+        tx_rx->sniff_tx(tx_rx->tx_data, tx_rx->tx_bits, crc_dropped, tx_rx->sniff_context);
+    }
+
     uint32_t start = DWT->CYCCNT;
     while(state != RFAL_NFC_STATE_DATAEXCHANGE_DONE) {
         rfalNfcWorker();
@@ -602,42 +570,42 @@ bool furi_hal_nfc_tx_rx(FuriHalNfcTxRxContext* tx_rx, uint16_t timeout_ms) {
         tx_rx->rx_bits = *temp_rx_bits;
     }
 
+    if(tx_rx->sniff_rx) {
+        bool crc_dropped = !(flags & RFAL_TXRX_FLAGS_CRC_RX_KEEP);
+        tx_rx->sniff_rx(tx_rx->rx_data, tx_rx->rx_bits, crc_dropped, tx_rx->sniff_context);
+    }
+
     return true;
 }
 
-ReturnCode furi_hal_nfc_exchange_full(
-    uint8_t* tx_buff,
-    uint16_t tx_len,
-    uint8_t* rx_buff,
-    uint16_t rx_cap,
-    uint16_t* rx_len) {
-    ReturnCode err;
-    uint8_t* part_buff;
-    uint16_t* part_len_bits;
+bool furi_hal_nfc_tx_rx_full(FuriHalNfcTxRxContext* tx_rx) {
     uint16_t part_len_bytes;
 
-    err = furi_hal_nfc_data_exchange(tx_buff, tx_len, &part_buff, &part_len_bits, false);
-    part_len_bytes = *part_len_bits / 8;
-    if(part_len_bytes > rx_cap) {
-        return ERR_OVERRUN;
+    if(!furi_hal_nfc_tx_rx(tx_rx, 1000)) {
+        return false;
     }
-    memcpy(rx_buff, part_buff, part_len_bytes);
-    *rx_len = part_len_bytes;
-    while(err == ERR_NONE && rx_buff[0] == 0xAF) {
-        err = furi_hal_nfc_data_exchange(rx_buff, 1, &part_buff, &part_len_bits, false);
-        part_len_bytes = *part_len_bits / 8;
-        if(part_len_bytes > rx_cap - *rx_len) {
-            return ERR_OVERRUN;
+    while(tx_rx->rx_bits && tx_rx->rx_data[0] == 0xAF) {
+        FuriHalNfcTxRxContext tmp = *tx_rx;
+        tmp.tx_data[0] = 0xAF;
+        tmp.tx_bits = 8;
+        if(!furi_hal_nfc_tx_rx(&tmp, 1000)) {
+            return false;
+        }
+        part_len_bytes = tmp.rx_bits / 8;
+        if(part_len_bytes > FURI_HAL_NFC_DATA_BUFF_SIZE - tx_rx->rx_bits / 8) {
+            FURI_LOG_W(TAG, "Overrun rx buf");
+            return false;
         }
         if(part_len_bytes == 0) {
-            return ERR_PROTO;
+            FURI_LOG_W(TAG, "Empty 0xAF response");
+            return false;
         }
-        memcpy(rx_buff + *rx_len, part_buff + 1, part_len_bytes - 1);
-        *rx_buff = *part_buff;
-        *rx_len += part_len_bytes - 1;
+        memcpy(tx_rx->rx_data + tx_rx->rx_bits / 8, tmp.rx_data + 1, part_len_bytes - 1);
+        tx_rx->rx_data[0] = tmp.rx_data[0];
+        tx_rx->rx_bits += 8 * (part_len_bytes - 1);
     }
 
-    return err;
+    return true;
 }
 
 void furi_hal_nfc_sleep() {

+ 11 - 30
firmware/targets/furi_hal_include/furi_hal_nfc.h

@@ -17,7 +17,7 @@ extern "C" {
 #endif
 
 #define FURI_HAL_NFC_UID_MAX_LEN 10
-#define FURI_HAL_NFC_DATA_BUFF_SIZE (256)
+#define FURI_HAL_NFC_DATA_BUFF_SIZE (512)
 #define FURI_HAL_NFC_PARITY_BUFF_SIZE (FURI_HAL_NFC_DATA_BUFF_SIZE / 8)
 
 #define FURI_HAL_NFC_TXRX_DEFAULT                                                    \
@@ -80,6 +80,9 @@ typedef struct {
     uint8_t sak;
 } FuriHalNfcDevData;
 
+typedef void (
+    *FuriHalNfcTxRxSniffCallback)(uint8_t* data, uint16_t bits, bool crc_dropped, void* context);
+
 typedef struct {
     uint8_t tx_data[FURI_HAL_NFC_DATA_BUFF_SIZE];
     uint8_t tx_parity[FURI_HAL_NFC_PARITY_BUFF_SIZE];
@@ -89,6 +92,10 @@ typedef struct {
     uint16_t rx_bits;
     FuriHalNfcTxRxType tx_rx_type;
     NfcaSignal* nfca_signal;
+
+    FuriHalNfcTxRxSniffCallback sniff_tx;
+    FuriHalNfcTxRxSniffCallback sniff_rx;
+    void* sniff_context;
 } FuriHalNfcTxRxContext;
 
 /** Init nfc
@@ -165,23 +172,6 @@ bool furi_hal_nfc_emulate_nfca(
     void* context,
     uint32_t timeout);
 
-/** NFC data exchange
- *
- * @param      tx_buff     transmit buffer
- * @param      tx_len      transmit buffer length
- * @param      rx_buff     receive buffer
- * @param      rx_len      receive buffer length
- * @param      deactivate  deactivate flag
- *
- * @return     ST ReturnCode
- */
-ReturnCode furi_hal_nfc_data_exchange(
-    uint8_t* tx_buff,
-    uint16_t tx_len,
-    uint8_t** rx_buff,
-    uint16_t** rx_len,
-    bool deactivate);
-
 /** NFC data exchange
  *
  * @param       tx_rx_ctx   FuriHalNfcTxRxContext instance
@@ -192,20 +182,11 @@ bool furi_hal_nfc_tx_rx(FuriHalNfcTxRxContext* tx_rx, uint16_t timeout_ms);
 
 /** NFC data full exhange
  *
- * @param      tx_buff     transmit buffer
- * @param      tx_len      transmit buffer length
- * @param      rx_buff     receive buffer
- * @param      rx_cap      receive buffer capacity
- * @param      rx_len      receive buffer length
+ * @param       tx_rx_ctx   FuriHalNfcTxRxContext instance
  *
- * @return     ST ReturnCode
+ * @return      true on success
  */
-ReturnCode furi_hal_nfc_exchange_full(
-    uint8_t* tx_buff,
-    uint16_t tx_len,
-    uint8_t* rx_buff,
-    uint16_t rx_cap,
-    uint16_t* rx_len);
+bool furi_hal_nfc_tx_rx_full(FuriHalNfcTxRxContext* tx_rx);
 
 /** NFC deactivate and start sleep
  */

+ 2 - 1
lib/nfc_protocols/emv.c

@@ -397,7 +397,8 @@ bool emv_read_bank_card(FuriHalNfcTxRxContext* tx_rx, EmvApplication* emv_app) {
 bool emv_card_emulation(FuriHalNfcTxRxContext* tx_rx) {
     furi_assert(tx_rx);
     bool emulation_complete = false;
-    memset(tx_rx, 0, sizeof(FuriHalNfcTxRxContext));
+    tx_rx->tx_bits = 0;
+    tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault;
 
     do {
         FURI_LOG_D(TAG, "Read select PPSE command");

+ 5 - 2
lib/nfc_protocols/mifare_classic.c

@@ -270,7 +270,9 @@ static bool mf_classic_auth(
     MfClassicKey key_type,
     Crypto1* crypto) {
     bool auth_success = false;
-    memset(tx_rx, 0, sizeof(FuriHalNfcTxRxContext));
+    memset(tx_rx->tx_data, 0, sizeof(tx_rx->tx_data));
+    memset(tx_rx->tx_parity, 0, sizeof(tx_rx->tx_parity));
+    tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault;
 
     do {
         if(key_type == MfClassicKeyA) {
@@ -372,7 +374,8 @@ bool mf_classic_read_block(
     bool read_block_success = false;
     uint8_t plain_cmd[4] = {MF_CLASSIC_READ_SECT_CMD, block_num, 0x00, 0x00};
     nfca_append_crc16(plain_cmd, 2);
-    memset(tx_rx, 0, sizeof(FuriHalNfcTxRxContext));
+    memset(tx_rx->tx_data, 0, sizeof(tx_rx->tx_data));
+    memset(tx_rx->tx_parity, 0, sizeof(tx_rx->tx_parity));
 
     for(uint8_t i = 0; i < 4; i++) {
         tx_rx->tx_data[i] = crypto1_byte(crypto, 0x00, 0) ^ plain_cmd[i];