From 9a21dae29c8d28799cc31748bf825622f0b762bc Mon Sep 17 00:00:00 2001 From: gornekich Date: Wed, 7 Dec 2022 14:52:44 +0400 Subject: [PATCH] [FL-3008], [FL-2734], [FL-2766], [FL-2898] NFC bug fixes (#2098) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * nfc: rework mf classic update * nfc: rename cache folder to .cache * nfc: fix ATQA order bytes in nfc files * file browser: add hide dot files option * nfc: fix iso-14443-4 uid cards emulation * nfc: fix unit tests Co-authored-by: あく --- applications/debug/unit_tests/nfc/nfc_test.c | 2 +- .../main/archive/helpers/archive_browser.c | 10 +++-- applications/services/dialogs/dialogs.h | 2 + applications/services/dialogs/dialogs_api.c | 1 + .../services/dialogs/dialogs_message.h | 1 + .../dialogs/dialogs_module_file_browser.c | 7 +++- .../services/gui/modules/file_browser.c | 6 ++- .../services/gui/modules/file_browser.h | 1 + .../gui/modules/file_browser_worker.c | 20 +++++++-- .../gui/modules/file_browser_worker.h | 10 +++-- firmware/targets/f7/api_symbols.csv | 8 ++-- firmware/targets/f7/furi_hal/furi_hal_nfc.c | 3 ++ lib/nfc/nfc_device.c | 35 ++++++++++++---- lib/nfc/protocols/mifare_classic.c | 42 +++---------------- 14 files changed, 88 insertions(+), 60 deletions(-) diff --git a/applications/debug/unit_tests/nfc/nfc_test.c b/applications/debug/unit_tests/nfc/nfc_test.c index 07ec73a0..4218482c 100644 --- a/applications/debug/unit_tests/nfc/nfc_test.c +++ b/applications/debug/unit_tests/nfc/nfc_test.c @@ -393,7 +393,7 @@ static void mf_classic_generator_test(uint8_t uid_len, MfClassicType type) { "nfc_device_save == true assert failed\r\n"); // Verify that key cache is saved FuriString* key_cache_name = furi_string_alloc(); - furi_string_set_str(key_cache_name, "/ext/nfc/cache/"); + furi_string_set_str(key_cache_name, "/ext/nfc/.cache/"); for(size_t i = 0; i < uid_len; i++) { furi_string_cat_printf(key_cache_name, "%02X", uid[i]); } diff --git a/applications/main/archive/helpers/archive_browser.c b/applications/main/archive/helpers/archive_browser.c index 149b3908..fa705189 100644 --- a/applications/main/archive/helpers/archive_browser.c +++ b/applications/main/archive/helpers/archive_browser.c @@ -80,10 +80,11 @@ static void archive_file_browser_set_path( ArchiveBrowserView* browser, FuriString* path, const char* filter_ext, - bool skip_assets) { + bool skip_assets, + bool hide_dot_files) { furi_assert(browser); if(!browser->worker_running) { - browser->worker = file_browser_worker_alloc(path, filter_ext, skip_assets); + browser->worker = file_browser_worker_alloc(path, filter_ext, skip_assets, hide_dot_files); file_browser_worker_set_callback_context(browser->worker, browser); file_browser_worker_set_folder_callback(browser->worker, archive_folder_open_cb); file_browser_worker_set_list_callback(browser->worker, archive_list_load_cb); @@ -92,7 +93,8 @@ static void archive_file_browser_set_path( browser->worker_running = true; } else { furi_assert(browser->worker); - file_browser_worker_set_config(browser->worker, path, filter_ext, skip_assets); + file_browser_worker_set_config( + browser->worker, path, filter_ext, skip_assets, hide_dot_files); } } @@ -473,7 +475,7 @@ void archive_switch_tab(ArchiveBrowserView* browser, InputKey key) { if(archive_is_dir_exists(browser->path)) { bool skip_assets = (strcmp(archive_get_tab_ext(tab), "*") == 0) ? false : true; archive_file_browser_set_path( - browser, browser->path, archive_get_tab_ext(tab), skip_assets); + browser, browser->path, archive_get_tab_ext(tab), skip_assets, false); tab_empty = false; // Empty check will be performed later } } diff --git a/applications/services/dialogs/dialogs.h b/applications/services/dialogs/dialogs.h index 4e836e36..97783eb0 100644 --- a/applications/services/dialogs/dialogs.h +++ b/applications/services/dialogs/dialogs.h @@ -19,6 +19,7 @@ typedef struct DialogsApp DialogsApp; * File browser dialog extra options * @param extension file extension to be offered for selection * @param skip_assets true - do not show assets folders + * @param hide_dot_files true - hide dot files * @param icon file icon pointer, NULL for default icon * @param hide_ext true - hide extensions for files * @param item_loader_callback callback function for providing custom icon & entry name @@ -27,6 +28,7 @@ typedef struct DialogsApp DialogsApp; typedef struct { const char* extension; bool skip_assets; + bool hide_dot_files; const Icon* icon; bool hide_ext; FileBrowserLoadItemCallback item_loader_callback; diff --git a/applications/services/dialogs/dialogs_api.c b/applications/services/dialogs/dialogs_api.c index c1de5999..91b3a1d0 100644 --- a/applications/services/dialogs/dialogs_api.c +++ b/applications/services/dialogs/dialogs_api.c @@ -20,6 +20,7 @@ bool dialog_file_browser_show( .file_icon = options ? options->icon : NULL, .hide_ext = options ? options->hide_ext : true, .skip_assets = options ? options->skip_assets : true, + .hide_dot_files = options ? options->hide_dot_files : true, .preselected_filename = path, .item_callback = options ? options->item_loader_callback : NULL, .item_callback_context = options ? options->item_loader_context : NULL, diff --git a/applications/services/dialogs/dialogs_message.h b/applications/services/dialogs/dialogs_message.h index d01c5aa9..45d36ba1 100644 --- a/applications/services/dialogs/dialogs_message.h +++ b/applications/services/dialogs/dialogs_message.h @@ -11,6 +11,7 @@ typedef struct { const char* extension; bool skip_assets; bool hide_ext; + bool hide_dot_files; const Icon* file_icon; FuriString* result_path; FuriString* preselected_filename; diff --git a/applications/services/dialogs/dialogs_module_file_browser.c b/applications/services/dialogs/dialogs_module_file_browser.c index 7e67d6c6..e03434cf 100644 --- a/applications/services/dialogs/dialogs_module_file_browser.c +++ b/applications/services/dialogs/dialogs_module_file_browser.c @@ -38,7 +38,12 @@ bool dialogs_app_process_module_file_browser(const DialogsAppMessageDataFileBrow file_browser_set_callback( file_browser, dialogs_app_file_browser_callback, file_browser_context); file_browser_configure( - file_browser, data->extension, data->skip_assets, data->file_icon, data->hide_ext); + file_browser, + data->extension, + data->skip_assets, + data->hide_dot_files, + data->file_icon, + data->hide_ext); file_browser_set_item_callback(file_browser, data->item_callback, data->item_callback_context); file_browser_start(file_browser, data->preselected_filename); diff --git a/applications/services/gui/modules/file_browser.c b/applications/services/gui/modules/file_browser.c index 203be23e..f20f950f 100644 --- a/applications/services/gui/modules/file_browser.c +++ b/applications/services/gui/modules/file_browser.c @@ -84,6 +84,7 @@ struct FileBrowser { BrowserWorker* worker; const char* ext_filter; bool skip_assets; + bool hide_dot_files; bool hide_ext; FileBrowserCallback callback; @@ -163,6 +164,7 @@ void file_browser_configure( FileBrowser* browser, const char* extension, bool skip_assets, + bool hide_dot_files, const Icon* file_icon, bool hide_ext) { furi_assert(browser); @@ -170,6 +172,7 @@ void file_browser_configure( browser->ext_filter = extension; browser->skip_assets = skip_assets; browser->hide_ext = hide_ext; + browser->hide_dot_files = hide_dot_files; with_view_model( browser->view, @@ -183,7 +186,8 @@ void file_browser_configure( void file_browser_start(FileBrowser* browser, FuriString* path) { furi_assert(browser); - browser->worker = file_browser_worker_alloc(path, browser->ext_filter, browser->skip_assets); + browser->worker = file_browser_worker_alloc( + path, browser->ext_filter, browser->skip_assets, browser->hide_dot_files); file_browser_worker_set_callback_context(browser->worker, browser); file_browser_worker_set_folder_callback(browser->worker, browser_folder_open_cb); file_browser_worker_set_list_callback(browser->worker, browser_list_load_cb); diff --git a/applications/services/gui/modules/file_browser.h b/applications/services/gui/modules/file_browser.h index c9fdddb5..377d4d9b 100644 --- a/applications/services/gui/modules/file_browser.h +++ b/applications/services/gui/modules/file_browser.h @@ -30,6 +30,7 @@ void file_browser_configure( FileBrowser* browser, const char* extension, bool skip_assets, + bool hide_dot_files, const Icon* file_icon, bool hide_ext); diff --git a/applications/services/gui/modules/file_browser_worker.c b/applications/services/gui/modules/file_browser_worker.c index a85a14b7..fb45a384 100644 --- a/applications/services/gui/modules/file_browser_worker.c +++ b/applications/services/gui/modules/file_browser_worker.c @@ -42,6 +42,7 @@ struct BrowserWorker { uint32_t load_offset; uint32_t load_count; bool skip_assets; + bool hide_dot_files; idx_last_array_t idx_last; void* cb_ctx; @@ -76,6 +77,13 @@ static bool browser_path_trim(FuriString* path) { } static bool browser_filter_by_name(BrowserWorker* browser, FuriString* name, bool is_folder) { + // Skip dot files if enabled + if(browser->hide_dot_files) { + if(furi_string_start_with_str(name, ".")) { + return false; + } + } + if(is_folder) { // Skip assets folders (if enabled) if(browser->skip_assets) { @@ -361,14 +369,18 @@ static int32_t browser_worker(void* context) { return 0; } -BrowserWorker* - file_browser_worker_alloc(FuriString* path, const char* filter_ext, bool skip_assets) { +BrowserWorker* file_browser_worker_alloc( + FuriString* path, + const char* filter_ext, + bool skip_assets, + bool hide_dot_files) { BrowserWorker* browser = malloc(sizeof(BrowserWorker)); //-V773 idx_last_array_init(browser->idx_last); browser->filter_extension = furi_string_alloc_set(filter_ext); browser->skip_assets = skip_assets; + browser->hide_dot_files = hide_dot_files; browser->path_start = furi_string_alloc_set(path); browser->path_current = furi_string_alloc_set(path); browser->path_next = furi_string_alloc_set(path); @@ -437,11 +449,13 @@ void file_browser_worker_set_config( BrowserWorker* browser, FuriString* path, const char* filter_ext, - bool skip_assets) { + bool skip_assets, + bool hide_dot_files) { furi_assert(browser); furi_string_set(browser->path_next, path); furi_string_set(browser->filter_extension, filter_ext); browser->skip_assets = skip_assets; + browser->hide_dot_files = hide_dot_files; furi_thread_flags_set(furi_thread_get_id(browser->thread), WorkerEvtConfigChange); } diff --git a/applications/services/gui/modules/file_browser_worker.h b/applications/services/gui/modules/file_browser_worker.h index 2f815540..ca143d66 100644 --- a/applications/services/gui/modules/file_browser_worker.h +++ b/applications/services/gui/modules/file_browser_worker.h @@ -21,8 +21,11 @@ typedef void (*BrowserWorkerListItemCallback)( bool is_last); typedef void (*BrowserWorkerLongLoadCallback)(void* context); -BrowserWorker* - file_browser_worker_alloc(FuriString* path, const char* filter_ext, bool skip_assets); +BrowserWorker* file_browser_worker_alloc( + FuriString* path, + const char* filter_ext, + bool skip_assets, + bool hide_dot_files); void file_browser_worker_free(BrowserWorker* browser); @@ -48,7 +51,8 @@ void file_browser_worker_set_config( BrowserWorker* browser, FuriString* path, const char* filter_ext, - bool skip_assets); + bool skip_assets, + bool hide_dot_files); void file_browser_worker_folder_enter(BrowserWorker* browser, FuriString* path, int32_t item_idx); diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 0f00ca90..38f21902 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,8.1,, +Version,+,9.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -806,14 +806,14 @@ Function,-,fgetpos,int,"FILE*, fpos_t*" Function,-,fgets,char*,"char*, int, FILE*" Function,-,fgets_unlocked,char*,"char*, int, FILE*" Function,+,file_browser_alloc,FileBrowser*,FuriString* -Function,+,file_browser_configure,void,"FileBrowser*, const char*, _Bool, const Icon*, _Bool" +Function,+,file_browser_configure,void,"FileBrowser*, const char*, _Bool, _Bool, const Icon*, _Bool" Function,+,file_browser_free,void,FileBrowser* Function,+,file_browser_get_view,View*,FileBrowser* Function,+,file_browser_set_callback,void,"FileBrowser*, FileBrowserCallback, void*" Function,+,file_browser_set_item_callback,void,"FileBrowser*, FileBrowserLoadItemCallback, void*" Function,+,file_browser_start,void,"FileBrowser*, FuriString*" Function,+,file_browser_stop,void,FileBrowser* -Function,+,file_browser_worker_alloc,BrowserWorker*,"FuriString*, const char*, _Bool" +Function,+,file_browser_worker_alloc,BrowserWorker*,"FuriString*, const char*, _Bool, _Bool" Function,+,file_browser_worker_folder_enter,void,"BrowserWorker*, FuriString*, int32_t" Function,+,file_browser_worker_folder_exit,void,BrowserWorker* Function,+,file_browser_worker_folder_refresh,void,"BrowserWorker*, int32_t" @@ -821,7 +821,7 @@ Function,+,file_browser_worker_free,void,BrowserWorker* Function,+,file_browser_worker_is_in_start_folder,_Bool,BrowserWorker* Function,+,file_browser_worker_load,void,"BrowserWorker*, uint32_t, uint32_t" Function,+,file_browser_worker_set_callback_context,void,"BrowserWorker*, void*" -Function,+,file_browser_worker_set_config,void,"BrowserWorker*, FuriString*, const char*, _Bool" +Function,+,file_browser_worker_set_config,void,"BrowserWorker*, FuriString*, const char*, _Bool, _Bool" Function,+,file_browser_worker_set_folder_callback,void,"BrowserWorker*, BrowserWorkerFolderOpenCallback" Function,+,file_browser_worker_set_item_callback,void,"BrowserWorker*, BrowserWorkerListItemCallback" Function,+,file_browser_worker_set_list_callback,void,"BrowserWorker*, BrowserWorkerListLoadCallback" diff --git a/firmware/targets/f7/furi_hal/furi_hal_nfc.c b/firmware/targets/f7/furi_hal/furi_hal_nfc.c index 2d27313a..75c695af 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_nfc.c +++ b/firmware/targets/f7/furi_hal/furi_hal_nfc.c @@ -244,6 +244,9 @@ bool furi_hal_nfc_listen( params.lmConfigPA.SEL_RES = sak; rfalNfcDiscover(¶ms); + // Disable EMD suppression. + st25r3916ModifyRegister(ST25R3916_REG_EMD_SUP_CONF, ST25R3916_REG_EMD_SUP_CONF_emd_emv, 0); + uint32_t start = DWT->CYCCNT; while(state != RFAL_NFC_STATE_ACTIVATED) { rfalNfcWorker(); diff --git a/lib/nfc/nfc_device.c b/lib/nfc/nfc_device.c index dc1faa34..e18f0945 100644 --- a/lib/nfc/nfc_device.c +++ b/lib/nfc/nfc_device.c @@ -8,11 +8,11 @@ #include #define TAG "NfcDevice" -#define NFC_DEVICE_KEYS_FOLDER EXT_PATH("nfc/cache") +#define NFC_DEVICE_KEYS_FOLDER EXT_PATH("nfc/.cache") #define NFC_DEVICE_KEYS_EXTENSION ".keys" static const char* nfc_file_header = "Flipper NFC device"; -static const uint32_t nfc_file_version = 2; +static const uint32_t nfc_file_version = 3; static const char* nfc_keys_file_header = "Flipper NFC keys"; static const uint32_t nfc_keys_file_version = 1; @@ -27,6 +27,11 @@ NfcDevice* nfc_device_alloc() { nfc_dev->dialogs = furi_record_open(RECORD_DIALOGS); nfc_dev->load_path = furi_string_alloc(); nfc_dev->dev_data.parsed_data = furi_string_alloc(); + + // Rename cache folder name for backward compatibility + if(storage_common_stat(nfc_dev->storage, "/ext/nfc/cache", NULL) == FSE_OK) { + storage_common_rename(nfc_dev->storage, "/ext/nfc/cache", NFC_DEVICE_KEYS_FOLDER); + } return nfc_dev; } @@ -1041,7 +1046,9 @@ bool nfc_device_save(NfcDevice* dev, const char* dev_name) { if(!flipper_format_write_comment_cstr(file, "UID, ATQA and SAK are common for all formats")) break; if(!flipper_format_write_hex(file, "UID", data->uid, data->uid_len)) break; - if(!flipper_format_write_hex(file, "ATQA", data->atqa, 2)) break; + // Save ATQA in MSB order for correct companion apps display + uint8_t atqa[2] = {data->atqa[1], data->atqa[0]}; + if(!flipper_format_write_hex(file, "ATQA", atqa, 2)) break; if(!flipper_format_write_hex(file, "SAK", &data->sak, 1)) break; // Save more data if necessary if(dev->format == NfcDeviceSaveFormatMifareUl) { @@ -1089,6 +1096,9 @@ static bool nfc_device_load_data(NfcDevice* dev, FuriString* path, bool show_dia temp_str = furi_string_alloc(); bool deprecated_version = false; + // Version 2 of file format had ATQA bytes swapped + uint32_t version_with_lsb_atqa = 2; + if(dev->loading_cb) { dev->loading_cb(dev->loading_cb_ctx, true); } @@ -1107,9 +1117,12 @@ static bool nfc_device_load_data(NfcDevice* dev, FuriString* path, bool show_dia // Read and verify file header uint32_t version = 0; if(!flipper_format_read_header(file, temp_str, &version)) break; - if(furi_string_cmp_str(temp_str, nfc_file_header) || (version != nfc_file_version)) { - deprecated_version = true; - break; + if(furi_string_cmp_str(temp_str, nfc_file_header)) break; + if(version != nfc_file_version) { + if(version < version_with_lsb_atqa) { + deprecated_version = true; + break; + } } // Read Nfc device type if(!flipper_format_read_string(file, "Device type", temp_str)) break; @@ -1119,7 +1132,14 @@ static bool nfc_device_load_data(NfcDevice* dev, FuriString* path, bool show_dia if(!(data_cnt == 4 || data_cnt == 7)) break; data->uid_len = data_cnt; if(!flipper_format_read_hex(file, "UID", data->uid, data->uid_len)) break; - if(!flipper_format_read_hex(file, "ATQA", data->atqa, 2)) break; + if(version == version_with_lsb_atqa) { + if(!flipper_format_read_hex(file, "ATQA", data->atqa, 2)) break; + } else { + uint8_t atqa[2] = {}; + if(!flipper_format_read_hex(file, "ATQA", atqa, 2)) break; + data->atqa[0] = atqa[1]; + data->atqa[1] = atqa[0]; + } if(!flipper_format_read_hex(file, "SAK", &data->sak, 1)) break; // Load CUID uint8_t* cuid_start = data->uid; @@ -1187,6 +1207,7 @@ bool nfc_file_select(NfcDevice* dev) { const DialogsFileBrowserOptions browser_options = { .extension = NFC_APP_EXTENSION, .skip_assets = true, + .hide_dot_files = true, .icon = &I_Nfc_10px, .hide_ext = true, .item_loader_callback = NULL, diff --git a/lib/nfc/protocols/mifare_classic.c b/lib/nfc/protocols/mifare_classic.c index b7a52bc0..f4c7353a 100644 --- a/lib/nfc/protocols/mifare_classic.c +++ b/lib/nfc/protocols/mifare_classic.c @@ -712,46 +712,16 @@ uint8_t mf_classic_update_card(FuriHalNfcTxRxContext* tx_rx, MfClassicData* data furi_assert(tx_rx); furi_assert(data); - uint8_t sectors_read = 0; - Crypto1 crypto = {}; uint8_t total_sectors = mf_classic_get_total_sectors_num(data->type); - uint64_t key_a = 0; - uint64_t key_b = 0; - MfClassicSectorReader sec_reader = {}; - MfClassicSector temp_sector = {}; for(size_t i = 0; i < total_sectors; i++) { - MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, i); - // Load key A - if(mf_classic_is_key_found(data, i, MfClassicKeyA)) { - sec_reader.key_a = nfc_util_bytes2num(sec_tr->key_a, 6); - } else { - sec_reader.key_a = MF_CLASSIC_NO_KEY; - } - // Load key B - if(mf_classic_is_key_found(data, i, MfClassicKeyB)) { - sec_reader.key_b = nfc_util_bytes2num(sec_tr->key_b, 6); - } else { - sec_reader.key_b = MF_CLASSIC_NO_KEY; - } - if((key_a != MF_CLASSIC_NO_KEY) || (key_b != MF_CLASSIC_NO_KEY)) { - sec_reader.sector_num = i; - if(mf_classic_read_sector_with_reader(tx_rx, &crypto, &sec_reader, &temp_sector)) { - uint8_t first_block = mf_classic_get_first_block_num_of_sector(i); - for(uint8_t j = 0; j < temp_sector.total_blocks; j++) { - mf_classic_set_block_read(data, first_block + j, &temp_sector.block[j]); - } - sectors_read++; - } else { - // Invalid key, set it to not found - if(key_a != MF_CLASSIC_NO_KEY) { - mf_classic_set_key_not_found(data, i, MfClassicKeyA); - } else { - mf_classic_set_key_not_found(data, i, MfClassicKeyB); - } - } - } + mf_classic_read_sector(tx_rx, data, i); } + uint8_t sectors_read = 0; + uint8_t keys_found = 0; + mf_classic_get_read_sectors_and_keys(data, §ors_read, &keys_found); + FURI_LOG_D(TAG, "Read %d sectors and %d keys", sectors_read, keys_found); + return sectors_read; }