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

[FL-2883] NFC: bank card rework reading (#1858)

* nfc: remove bank card save option
* nfc: remove bank card save from nfc device
* nfc: remove unused function in emv
* nfc: try several times to start emv application
* nfc: add AID display fallback for bank cards

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

+ 1 - 9
applications/main/nfc/scenes/nfc_scene_emv_menu.c

@@ -1,7 +1,6 @@
 #include "../nfc_i.h"
 
 enum SubmenuIndex {
-    SubmenuIndexSave,
     SubmenuIndexInfo,
 };
 
@@ -15,7 +14,6 @@ void nfc_scene_emv_menu_on_enter(void* context) {
     Nfc* nfc = context;
     Submenu* submenu = nfc->submenu;
 
-    submenu_add_item(submenu, "Save", SubmenuIndexSave, nfc_scene_emv_menu_submenu_callback, nfc);
     submenu_add_item(submenu, "Info", SubmenuIndexInfo, nfc_scene_emv_menu_submenu_callback, nfc);
     submenu_set_selected_item(
         nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneEmvMenu));
@@ -28,13 +26,7 @@ bool nfc_scene_emv_menu_on_event(void* context, SceneManagerEvent event) {
     bool consumed = false;
 
     if(event.type == SceneManagerEventTypeCustom) {
-        if(event.event == SubmenuIndexSave) {
-            nfc->dev->format = NfcDeviceSaveFormatBankCard;
-            // Clear device name
-            nfc_device_set_name(nfc->dev, "");
-            scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName);
-            consumed = true;
-        } else if(event.event == SubmenuIndexInfo) {
+        if(event.event == SubmenuIndexInfo) {
             scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcDataInfo);
             consumed = true;
         }

+ 26 - 5
applications/main/nfc/scenes/nfc_scene_emv_read_success.c

@@ -24,12 +24,33 @@ void nfc_scene_emv_read_success_on_enter(void* context) {
         nfc->widget, GuiButtonTypeRight, "More", nfc_scene_emv_read_success_widget_callback, nfc);
 
     FuriString* temp_str;
-    temp_str = furi_string_alloc_printf("\e#%s\n", emv_data->name);
-    for(uint8_t i = 0; i < emv_data->number_len; i += 2) {
-        furi_string_cat_printf(
-            temp_str, "%02X%02X ", emv_data->number[i], emv_data->number[i + 1]);
+    if(emv_data->name[0] != '\0') {
+        temp_str = furi_string_alloc_printf("\e#%s\n", emv_data->name);
+    } else {
+        temp_str = furi_string_alloc_printf("\e#Unknown Bank Card\n");
+    }
+    if(emv_data->number_len) {
+        for(uint8_t i = 0; i < emv_data->number_len; i += 2) {
+            furi_string_cat_printf(
+                temp_str, "%02X%02X ", emv_data->number[i], emv_data->number[i + 1]);
+        }
+        furi_string_trim(temp_str);
+    } else if(emv_data->aid_len) {
+        furi_string_cat_printf(temp_str, "Can't parse data from app\n");
+        // Parse AID name
+        FuriString* aid_name;
+        aid_name = furi_string_alloc();
+        if(nfc_emv_parser_get_aid_name(
+               nfc->dev->storage, emv_data->aid, emv_data->aid_len, aid_name)) {
+            furi_string_cat_printf(temp_str, "AID: %s", furi_string_get_cstr(aid_name));
+        } else {
+            furi_string_cat_printf(temp_str, "AID: ");
+            for(uint8_t i = 0; i < emv_data->aid_len; i++) {
+                furi_string_cat_printf(temp_str, "%02X", emv_data->aid[i]);
+            }
+        }
+        furi_string_free(aid_name);
     }
-    furi_string_trim(temp_str);
 
     // Add expiration date
     if(emv_data->exp_mon) {

+ 2 - 32
lib/nfc/nfc_device.c

@@ -636,35 +636,7 @@ bool nfc_device_load_mifare_df_data(FlipperFormat* file, NfcDevice* dev) {
     return parsed;
 }
 
-static bool nfc_device_save_bank_card_data(FlipperFormat* file, NfcDevice* dev) {
-    bool saved = false;
-    EmvData* data = &dev->dev_data.emv_data;
-    uint32_t data_temp = 0;
-
-    do {
-        // Write Bank card specific data
-        if(!flipper_format_write_comment_cstr(file, "Bank card specific data")) break;
-        if(!flipper_format_write_hex(file, "AID", data->aid, data->aid_len)) break;
-        if(!flipper_format_write_string_cstr(file, "Name", data->name)) break;
-        if(!flipper_format_write_hex(file, "Number", data->number, data->number_len)) break;
-        if(data->exp_mon) {
-            uint8_t exp_data[2] = {data->exp_mon, data->exp_year};
-            if(!flipper_format_write_hex(file, "Exp data", exp_data, sizeof(exp_data))) break;
-        }
-        if(data->country_code) {
-            data_temp = data->country_code;
-            if(!flipper_format_write_uint32(file, "Country code", &data_temp, 1)) break;
-        }
-        if(data->currency_code) {
-            data_temp = data->currency_code;
-            if(!flipper_format_write_uint32(file, "Currency code", &data_temp, 1)) break;
-        }
-        saved = true;
-    } while(false);
-
-    return saved;
-}
-
+// Leave for backward compatibility
 bool nfc_device_load_bank_card_data(FlipperFormat* file, NfcDevice* dev) {
     bool parsed = false;
     EmvData* data = &dev->dev_data.emv_data;
@@ -1068,7 +1040,7 @@ static bool nfc_device_save_file(
         if(!flipper_format_write_header_cstr(file, nfc_file_header, nfc_file_version)) break;
         // Write nfc device type
         if(!flipper_format_write_comment_cstr(
-               file, "Nfc device type can be UID, Mifare Ultralight, Mifare Classic, Bank card"))
+               file, "Nfc device type can be UID, Mifare Ultralight, Mifare Classic"))
             break;
         nfc_device_prepare_format_string(dev, temp_str);
         if(!flipper_format_write_string(file, "Device type", temp_str)) break;
@@ -1083,8 +1055,6 @@ static bool nfc_device_save_file(
             if(!nfc_device_save_mifare_ul_data(file, dev)) break;
         } else if(dev->format == NfcDeviceSaveFormatMifareDesfire) {
             if(!nfc_device_save_mifare_df_data(file, dev)) break;
-        } else if(dev->format == NfcDeviceSaveFormatBankCard) {
-            if(!nfc_device_save_bank_card_data(file, dev)) break;
         } else if(dev->format == NfcDeviceSaveFormatMifareClassic) {
             // Save data
             if(!nfc_device_save_mifare_classic_data(file, dev)) break;

+ 41 - 21
lib/nfc/nfc_worker.c

@@ -233,31 +233,51 @@ static bool nfc_worker_read_bank_card(NfcWorker* nfc_worker, FuriHalNfcTxRxConte
         reader_analyzer_start(nfc_worker->reader_analyzer, ReaderAnalyzerModeDebugLog);
     }
 
-    do {
-        // Read card
+    // Bank cards require strong field to start application. If we find AID, try at least several
+    // times to start EMV application
+    uint8_t start_application_attempts = 0;
+    while(start_application_attempts < 3) {
+        if(nfc_worker->state != NfcWorkerStateRead) break;
+        start_application_attempts++;
         if(!furi_hal_nfc_detect(&nfc_worker->dev_data->nfc_data, 300)) break;
-        if(!emv_read_bank_card(tx_rx, &emv_app)) break;
-        // Copy data
-        // TODO Set EmvData to reader or like in mifare ultralight!
-        result->number_len = emv_app.card_number_len;
-        memcpy(result->number, emv_app.card_number, result->number_len);
+        if(emv_read_bank_card(tx_rx, &emv_app)) {
+            FURI_LOG_D(TAG, "Bank card number read from %d attempt", start_application_attempts);
+            break;
+        } else if(emv_app.aid_len && !emv_app.app_started) {
+            FURI_LOG_D(
+                TAG,
+                "AID found but failed to start EMV app from %d attempt",
+                start_application_attempts);
+            furi_hal_nfc_sleep();
+            continue;
+        } else {
+            FURI_LOG_D(TAG, "Failed to find AID");
+            break;
+        }
+    }
+    // Copy data
+    if(emv_app.aid_len) {
         result->aid_len = emv_app.aid_len;
         memcpy(result->aid, emv_app.aid, result->aid_len);
-        if(emv_app.name_found) {
-            memcpy(result->name, emv_app.name, sizeof(emv_app.name));
-        }
-        if(emv_app.exp_month) {
-            result->exp_mon = emv_app.exp_month;
-            result->exp_year = emv_app.exp_year;
-        }
-        if(emv_app.country_code) {
-            result->country_code = emv_app.country_code;
-        }
-        if(emv_app.currency_code) {
-            result->currency_code = emv_app.currency_code;
-        }
         read_success = true;
-    } while(false);
+    }
+    if(emv_app.card_number_len) {
+        result->number_len = emv_app.card_number_len;
+        memcpy(result->number, emv_app.card_number, result->number_len);
+    }
+    if(emv_app.name_found) {
+        memcpy(result->name, emv_app.name, sizeof(emv_app.name));
+    }
+    if(emv_app.exp_month) {
+        result->exp_mon = emv_app.exp_month;
+        result->exp_year = emv_app.exp_year;
+    }
+    if(emv_app.country_code) {
+        result->country_code = emv_app.country_code;
+    }
+    if(emv_app.currency_code) {
+        result->currency_code = emv_app.currency_code;
+    }
 
     if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
         reader_analyzer_stop(nfc_worker->reader_analyzer);

+ 5 - 13
lib/nfc/protocols/emv.c

@@ -182,7 +182,7 @@ static bool emv_decode_response(uint8_t* buff, uint16_t len, EmvApplication* app
     return success;
 }
 
-bool emv_select_ppse(FuriHalNfcTxRxContext* tx_rx, EmvApplication* app) {
+static bool emv_select_ppse(FuriHalNfcTxRxContext* tx_rx, EmvApplication* app) {
     bool app_aid_found = false;
     const uint8_t emv_select_ppse_cmd[] = {
         0x00, 0xA4, // SELECT ppse
@@ -212,8 +212,8 @@ bool emv_select_ppse(FuriHalNfcTxRxContext* tx_rx, EmvApplication* app) {
     return app_aid_found;
 }
 
-bool emv_select_app(FuriHalNfcTxRxContext* tx_rx, EmvApplication* app) {
-    bool select_app_success = false;
+static bool emv_select_app(FuriHalNfcTxRxContext* tx_rx, EmvApplication* app) {
+    app->app_started = false;
     const uint8_t emv_select_header[] = {
         0x00,
         0xA4, // SELECT application
@@ -236,7 +236,7 @@ bool emv_select_app(FuriHalNfcTxRxContext* tx_rx, EmvApplication* app) {
     if(furi_hal_nfc_tx_rx(tx_rx, 300)) {
         emv_trace(tx_rx, "Start application answer:");
         if(emv_decode_response(tx_rx->rx_data, tx_rx->rx_bits / 8, app)) {
-            select_app_success = true;
+            app->app_started = true;
         } else {
             FURI_LOG_E(TAG, "Failed to read PAN or PDOL");
         }
@@ -244,7 +244,7 @@ bool emv_select_app(FuriHalNfcTxRxContext* tx_rx, EmvApplication* app) {
         FURI_LOG_E(TAG, "Failed to start application");
     }
 
-    return select_app_success;
+    return app->app_started;
 }
 
 static uint16_t emv_prepare_pdol(APDU* dest, APDU* src) {
@@ -367,14 +367,6 @@ static bool emv_read_files(FuriHalNfcTxRxContext* tx_rx, EmvApplication* app) {
     return card_num_read;
 }
 
-bool emv_search_application(FuriHalNfcTxRxContext* tx_rx, EmvApplication* emv_app) {
-    furi_assert(tx_rx);
-    furi_assert(emv_app);
-    memset(emv_app, 0, sizeof(EmvApplication));
-
-    return emv_select_ppse(tx_rx, emv_app);
-}
-
 bool emv_read_bank_card(FuriHalNfcTxRxContext* tx_rx, EmvApplication* emv_app) {
     furi_assert(tx_rx);
     furi_assert(emv_app);

+ 1 - 9
lib/nfc/protocols/emv.h

@@ -45,6 +45,7 @@ typedef struct {
     uint8_t priority;
     uint8_t aid[16];
     uint8_t aid_len;
+    bool app_started;
     char name[32];
     bool name_found;
     uint8_t card_number[10];
@@ -68,15 +69,6 @@ typedef struct {
  */
 bool emv_read_bank_card(FuriHalNfcTxRxContext* tx_rx, EmvApplication* emv_app);
 
-/** Search for EMV Application
- *
- * @param tx_rx     FuriHalNfcTxRxContext instance
- * @param emv_app   EmvApplication instance
- *
- * @return true on success
- */
-bool emv_search_application(FuriHalNfcTxRxContext* tx_rx, EmvApplication* emv_app);
-
 /** Emulate bank card
  * @note Answer to application selection and PDOL
  *