diff --git a/applications/main/nfc/scenes/nfc_scene_config.h b/applications/main/nfc/scenes/nfc_scene_config.h index cb28a07f9..206bab0cf 100644 --- a/applications/main/nfc/scenes/nfc_scene_config.h +++ b/applications/main/nfc/scenes/nfc_scene_config.h @@ -21,6 +21,7 @@ ADD_SCENE(nfc, mf_ultralight_emulate, MfUltralightEmulate) ADD_SCENE(nfc, mf_ultralight_read_auth, MfUltralightReadAuth) ADD_SCENE(nfc, mf_ultralight_read_auth_result, MfUltralightReadAuthResult) ADD_SCENE(nfc, mf_ultralight_key_input, MfUltralightKeyInput) +ADD_SCENE(nfc, mf_ultralight_unlock_auto, MfUltralightUnlockAuto) ADD_SCENE(nfc, mf_ultralight_unlock_menu, MfUltralightUnlockMenu) ADD_SCENE(nfc, mf_ultralight_unlock_warn, MfUltralightUnlockWarn) ADD_SCENE(nfc, mf_desfire_read_success, MfDesfireReadSuccess) diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_menu.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_menu.c index ba9f22338..ee56fa1cf 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_menu.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_menu.c @@ -1,7 +1,8 @@ #include "../nfc_i.h" enum SubmenuIndex { - SubmenuIndexUnlock, + SubmenuIndexUnlockByReader, + SubmenuIndexUnlockByPassword, SubmenuIndexSave, SubmenuIndexEmulate, SubmenuIndexInfo, @@ -18,11 +19,17 @@ void nfc_scene_mf_ultralight_menu_on_enter(void* context) { Submenu* submenu = nfc->submenu; MfUltralightData* data = &nfc->dev->dev_data.mf_ul_data; - if(data->data_read != data->data_size) { + if(!mf_ul_is_full_capture(data)) { + submenu_add_item( + submenu, + "Unlock With Reader", + SubmenuIndexUnlockByReader, + nfc_scene_mf_ultralight_menu_submenu_callback, + nfc); submenu_add_item( submenu, "Unlock With Password", - SubmenuIndexUnlock, + SubmenuIndexUnlockByPassword, nfc_scene_mf_ultralight_menu_submenu_callback, nfc); } @@ -57,7 +64,10 @@ bool nfc_scene_mf_ultralight_menu_on_event(void* context, SceneManagerEvent even } else if(event.event == SubmenuIndexEmulate) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightEmulate); consumed = true; - } else if(event.event == SubmenuIndexUnlock) { + } else if(event.event == SubmenuIndexUnlockByReader) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockAuto); + consumed = true; + } else if(event.event == SubmenuIndexUnlockByPassword) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockMenu); consumed = true; } else if(event.event == SubmenuIndexInfo) { diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_auth_result.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_auth_result.c index 5a690a213..564c536dd 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_auth_result.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_auth_result.c @@ -20,16 +20,19 @@ void nfc_scene_mf_ultralight_read_auth_result_on_enter(void* context) { MfUltralightData* mf_ul_data = &nfc->dev->dev_data.mf_ul_data; MfUltralightConfigPages* config_pages = mf_ultralight_get_config_pages(mf_ul_data); Widget* widget = nfc->widget; + const char* title; FuriString* temp_str; temp_str = furi_string_alloc(); if((mf_ul_data->data_read == mf_ul_data->data_size) && (mf_ul_data->data_read > 0)) { - widget_add_string_element( - widget, 64, 0, AlignCenter, AlignTop, FontPrimary, "All pages are unlocked!"); + if(mf_ul_data->auth_success) + title = "All pages are unlocked!"; + else + title = "All unlocked but failed auth!"; } else { - widget_add_string_element( - widget, 64, 0, AlignCenter, AlignTop, FontPrimary, "Not all pages unlocked!"); + title = "Not all pages unlocked!"; } + widget_add_string_element(widget, 64, 0, AlignCenter, AlignTop, FontPrimary, title); furi_string_set(temp_str, "UID:"); for(size_t i = 0; i < nfc_data->uid_len; i++) { furi_string_cat_printf(temp_str, " %02X", nfc_data->uid[i]); diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_auto.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_auto.c new file mode 100644 index 000000000..70a06091f --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_auto.c @@ -0,0 +1,56 @@ +#include "../nfc_i.h" + +bool nfc_scene_mf_ultralight_unlock_auto_worker_callback(NfcWorkerEvent event, void* context) { + Nfc* nfc = context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, event); + return true; +} + +void nfc_scene_mf_ultralight_unlock_auto_on_enter(void* context) { + Nfc* nfc = context; + + // Setup view + popup_set_text(nfc->popup, "Touch the reader", 44, 31, AlignLeft, AlignCenter); + popup_set_icon(nfc->popup, 0, 16, &I_Tap_reader_36x38); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); + + // Start worker + nfc_worker_start( + nfc->worker, + NfcWorkerStateMfUltralightEmulate, + &nfc->dev->dev_data, + nfc_scene_mf_ultralight_unlock_auto_worker_callback, + nfc); + + nfc_blink_emulate_start(nfc); +} + +bool nfc_scene_mf_ultralight_unlock_auto_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if((event.event == NfcWorkerEventMfUltralightPwdAuth)) { + MfUltralightAuth* auth = &nfc->dev->dev_data.mf_ul_auth; + memcpy(nfc->byte_input_store, auth->pwd.raw, sizeof(auth->pwd.raw)); + nfc->dev->dev_data.mf_ul_data.auth_method = MfUltralightAuthMethodManual; + nfc_worker_stop(nfc->worker); + notification_message(nfc->notifications, &sequence_success); + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockWarn); + consumed = true; + } + } + return consumed; +} + +void nfc_scene_mf_ultralight_unlock_auto_on_exit(void* context) { + Nfc* nfc = context; + + // Stop worker + nfc_worker_stop(nfc->worker); + // Clear view + popup_reset(nfc->popup); + + nfc_blink_stop(nfc); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_menu.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_menu.c index 95f1edb62..979aa8eda 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_menu.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_menu.c @@ -58,7 +58,8 @@ bool nfc_scene_mf_ultralight_unlock_menu_on_event(void* context, SceneManagerEve scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockWarn); consumed = true; } - scene_manager_set_scene_state(nfc->scene_manager, NfcSceneExtraActions, event.event); + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneMfUltralightUnlockMenu, event.event); } return consumed; } diff --git a/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c b/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c index a7ed8eb37..fa73201d0 100644 --- a/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c +++ b/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c @@ -89,6 +89,20 @@ void nfc_scene_nfc_data_info_on_enter(void* context) { temp_str, "\nPages Read %d/%d", data->data_read / 4, data->data_size / 4); if(data->data_size > data->data_read) { furi_string_cat_printf(temp_str, "\nPassword-protected"); + } else if(data->auth_success) { + MfUltralightConfigPages* config_pages = mf_ultralight_get_config_pages(data); + furi_string_cat_printf( + temp_str, + "\nPassword: %02X %02X %02X %02X", + config_pages->auth_data.pwd.raw[0], + config_pages->auth_data.pwd.raw[1], + config_pages->auth_data.pwd.raw[2], + config_pages->auth_data.pwd.raw[3]); + furi_string_cat_printf( + temp_str, + "\nPACK: %02X %02X", + config_pages->auth_data.pack.raw[0], + config_pages->auth_data.pack.raw[1]); } } else if(protocol == NfcDeviceProtocolMifareClassic) { MfClassicData* data = &dev_data->mf_classic_data; diff --git a/applications/main/nfc/scenes/nfc_scene_read.c b/applications/main/nfc/scenes/nfc_scene_read.c index c8305d898..8d39560aa 100644 --- a/applications/main/nfc/scenes/nfc_scene_read.c +++ b/applications/main/nfc/scenes/nfc_scene_read.c @@ -69,6 +69,8 @@ bool nfc_scene_read_on_event(void* context, SceneManagerEvent event) { consumed = true; } else if(event.event == NfcWorkerEventReadMfUltralight) { notification_message(nfc->notifications, &sequence_success); + // Set unlock password input to 0xFFFFFFFF only on fresh read + memset(nfc->byte_input_store, 0xFF, 4); scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightReadSuccess); consumed = true; } else if(event.event == NfcWorkerEventReadMfClassicDone) { diff --git a/applications/main/nfc/scenes/nfc_scene_saved_menu.c b/applications/main/nfc/scenes/nfc_scene_saved_menu.c index fe65b5b8a..fdc9fd5f3 100644 --- a/applications/main/nfc/scenes/nfc_scene_saved_menu.c +++ b/applications/main/nfc/scenes/nfc_scene_saved_menu.c @@ -7,6 +7,8 @@ enum SubmenuIndex { SubmenuIndexDelete, SubmenuIndexInfo, SubmenuIndexRestoreOriginal, + SubmenuIndexMfUlUnlockByReader, + SubmenuIndexMfUlUnlockByPassword, }; void nfc_scene_saved_menu_submenu_callback(void* context, uint32_t index) { @@ -43,6 +45,21 @@ void nfc_scene_saved_menu_on_enter(void* context) { } submenu_add_item( submenu, "Info", SubmenuIndexInfo, nfc_scene_saved_menu_submenu_callback, nfc); + if(nfc->dev->format == NfcDeviceSaveFormatMifareUl && + !mf_ul_is_full_capture(&nfc->dev->dev_data.mf_ul_data)) { + submenu_add_item( + submenu, + "Unlock With Reader", + SubmenuIndexMfUlUnlockByReader, + nfc_scene_saved_menu_submenu_callback, + nfc); + submenu_add_item( + submenu, + "Unlock With Password", + SubmenuIndexMfUlUnlockByPassword, + nfc_scene_saved_menu_submenu_callback, + nfc); + } if(nfc->dev->shadow_file_exist) { submenu_add_item( submenu, @@ -105,6 +122,12 @@ bool nfc_scene_saved_menu_on_event(void* context, SceneManagerEvent event) { } else if(event.event == SubmenuIndexRestoreOriginal) { scene_manager_next_scene(nfc->scene_manager, NfcSceneRestoreOriginalConfirm); consumed = true; + } else if(event.event == SubmenuIndexMfUlUnlockByReader) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockAuto); + consumed = true; + } else if(event.event == SubmenuIndexMfUlUnlockByPassword) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockMenu); + consumed = true; } } diff --git a/assets/resources/nfc/assets/mf_classic_dict.nfc b/assets/resources/nfc/assets/mf_classic_dict.nfc index 53abb2718..50996000f 100644 --- a/assets/resources/nfc/assets/mf_classic_dict.nfc +++ b/assets/resources/nfc/assets/mf_classic_dict.nfc @@ -3706,6 +3706,25 @@ AFC984A3576E # data from http://www.proxmark.org/forum/viewtopic.php?pid=45100#p45100 7CB033257498 1153AABAFF6C +# iGuard Simple and Reverse Keys +D537320FF90E +36E1765CE3E8 +C608E13ADD50 +ED0EC56EEFDD +9716D5241E28 +2E52ABE0CE95 +61D030C0D7A8 +# BadgeMaker Leaked from https://github.com/UberGuidoZ +E167EC67C7FF +# Schlage 9691T Keyfob from seasnaill Added by VideoMan. +3111A3A303EB +# Transport cards +E954024EE754 +0CD464CDC100 +BC305FE2DA65 +CF0EC6ACF2F9 +F7A545095C49 +6862FD600F78 # RENFE MADRID (TRAIN) Extracted with detect reader 701AA491A4A5 12BA20088ED3 diff --git a/lib/nfc/nfc_device.c b/lib/nfc/nfc_device.c index 740cfae5e..a9c2385e1 100644 --- a/lib/nfc/nfc_device.c +++ b/lib/nfc/nfc_device.c @@ -213,6 +213,9 @@ bool nfc_device_load_mifare_ul_data(FlipperFormat* file, NfcDevice* dev) { uint32_t auth_counter; if(!flipper_format_read_uint32(file, "Failed authentication attempts", &auth_counter, 1)) auth_counter = 0; + data->curr_authlim = auth_counter; + + data->auth_success = mf_ul_is_full_capture(data); parsed = true; } while(false); diff --git a/lib/nfc/nfc_device.h b/lib/nfc/nfc_device.h index 309f9c999..328eaccd3 100644 --- a/lib/nfc/nfc_device.h +++ b/lib/nfc/nfc_device.h @@ -55,6 +55,7 @@ typedef struct { union { NfcReaderRequestData reader_data; NfcMfClassicDictAttackData mf_classic_dict_attack_data; + MfUltralightAuth mf_ul_auth; }; union { EmvData emv_data; diff --git a/lib/nfc/nfc_worker.c b/lib/nfc/nfc_worker.c index d78b7a0f1..1fd776b8f 100644 --- a/lib/nfc/nfc_worker.c +++ b/lib/nfc/nfc_worker.c @@ -502,10 +502,25 @@ void nfc_worker_emulate_apdu(NfcWorker* nfc_worker) { } } +void nfc_worker_mf_ultralight_auth_received_callback(MfUltralightAuth auth, void* context) { + furi_assert(context); + + NfcWorker* nfc_worker = context; + nfc_worker->dev_data->mf_ul_auth = auth; + if(nfc_worker->callback) { + nfc_worker->callback(NfcWorkerEventMfUltralightPwdAuth, nfc_worker->context); + } +} + void nfc_worker_emulate_mf_ultralight(NfcWorker* nfc_worker) { FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; MfUltralightEmulator emulator = {}; mf_ul_prepare_emulation(&emulator, &nfc_worker->dev_data->mf_ul_data); + + // TODO rework with reader analyzer + emulator.auth_received_callback = nfc_worker_mf_ultralight_auth_received_callback; + emulator.context = nfc_worker; + while(nfc_worker->state == NfcWorkerStateMfUltralightEmulate) { mf_ul_reset_emulation(&emulator, true); furi_hal_nfc_emulate_nfca( diff --git a/lib/nfc/nfc_worker.h b/lib/nfc/nfc_worker.h index 2ec123357..3c987b06e 100644 --- a/lib/nfc/nfc_worker.h +++ b/lib/nfc/nfc_worker.h @@ -55,16 +55,16 @@ typedef enum { NfcWorkerEventNewDictKeyBatch, NfcWorkerEventFoundKeyA, NfcWorkerEventFoundKeyB, - - // Mifare Ultralight/NTAG events - NfcWorkerEventMfUltralightPassKey, // NFC worker requesting manual key - NfcWorkerEventMfUltralightPwdAuth, // Reader sent auth command - + // Detect Reader events NfcWorkerEventDetectReaderDetected, NfcWorkerEventDetectReaderLost, NfcWorkerEventDetectReaderMfkeyCollected, + // Mifare Ultralight events + NfcWorkerEventMfUltralightPassKey, // NFC worker requesting manual key + NfcWorkerEventMfUltralightPwdAuth, // Reader sent auth command + } NfcWorkerEvent; typedef bool (*NfcWorkerCallback)(NfcWorkerEvent event, void* context); diff --git a/lib/nfc/protocols/mifare_ultralight.c b/lib/nfc/protocols/mifare_ultralight.c index a8d1f5548..85e234bd9 100644 --- a/lib/nfc/protocols/mifare_ultralight.c +++ b/lib/nfc/protocols/mifare_ultralight.c @@ -51,7 +51,7 @@ void mf_ul_reset(MfUltralightData* data) { data->data_size = 0; data->data_read = 0; data->curr_authlim = 0; - data->has_auth = false; + data->auth_success = false; } static MfUltralightFeatures mf_ul_get_features(MfUltralightType type) { @@ -756,6 +756,34 @@ bool mf_ul_read_card( mf_ultralight_read_tearing_flags(tx_rx, data); } data->curr_authlim = 0; + + if(reader->pages_read == reader->pages_to_read && + reader->supported_features & MfUltralightSupportAuth && !data->auth_success) { + MfUltralightConfigPages* config = mf_ultralight_get_config_pages(data); + if(config->access.authlim == 0) { + // Attempt to auth with default PWD + uint16_t pack; + data->auth_success = mf_ultralight_authenticate(tx_rx, MF_UL_DEFAULT_PWD, &pack); + if(data->auth_success) { + config->auth_data.pwd.value = MF_UL_DEFAULT_PWD; + config->auth_data.pack.value = pack; + } else { + furi_hal_nfc_sleep(); + furi_hal_nfc_activate_nfca(300, NULL); + } + } + } + } + + if(reader->pages_read != reader->pages_to_read) { + if(reader->supported_features & MfUltralightSupportAuth) { + // Probably password protected, fix AUTH0 and PROT so before AUTH0 + // can be written and since AUTH0 won't be readable, like on the + // original card + MfUltralightConfigPages* config = mf_ultralight_get_config_pages(data); + config->auth0 = reader->pages_read; + config->access.prot = true; + } } return card_read; @@ -1201,6 +1229,8 @@ static void mf_ul_emulate_write( } void mf_ul_reset_emulation(MfUltralightEmulator* emulator, bool is_power_cycle) { + emulator->comp_write_cmd_started = false; + emulator->sector_select_cmd_started = false; emulator->curr_sector = 0; emulator->ntag_i2c_plus_sector3_lockout = false; emulator->auth_success = false; @@ -1244,8 +1274,7 @@ void mf_ul_prepare_emulation(MfUltralightEmulator* emulator, MfUltralightData* d emulator->config = mf_ultralight_get_config_pages(&emulator->data); emulator->page_num = emulator->data.data_size / 4; emulator->data_changed = false; - emulator->comp_write_cmd_started = false; - emulator->sector_select_cmd_started = false; + memset(&emulator->auth_attempt, 0, sizeof(MfUltralightAuth)); mf_ul_reset_emulation(emulator, true); } @@ -1706,6 +1735,17 @@ bool mf_ul_prepare_emulation_response( } else if(cmd == MF_UL_AUTH) { if(emulator->supported_features & MfUltralightSupportAuth) { if(buff_rx_len == (1 + 4) * 8) { + // Record password sent by PCD + memcpy( + emulator->auth_attempt.pwd.raw, + &buff_rx[1], + sizeof(emulator->auth_attempt.pwd.raw)); + emulator->auth_attempted = true; + if(emulator->auth_received_callback) { + emulator->auth_received_callback( + emulator->auth_attempt, emulator->context); + } + uint16_t scaled_authlim = mf_ultralight_calc_auth_count(&emulator->data); if(scaled_authlim != 0 && emulator->data.curr_authlim >= scaled_authlim) { if(emulator->data.curr_authlim != UINT16_MAX) { @@ -1863,3 +1903,14 @@ bool mf_ul_prepare_emulation_response( return tx_bits > 0; } + +bool mf_ul_is_full_capture(MfUltralightData* data) { + if(data->data_read != data->data_size) return false; + + // Having read all the pages doesn't mean that we've got everything. + // By default PWD is 0xFFFFFFFF, but if read back it is always 0x00000000, + // so a default read on an auth-supported NTAG is never complete. + if(!(mf_ul_get_features(data->type) & MfUltralightSupportAuth)) return true; + MfUltralightConfigPages* config = mf_ultralight_get_config_pages(data); + return config->auth_data.pwd.value != 0 || config->auth_data.pack.value != 0; +} diff --git a/lib/nfc/protocols/mifare_ultralight.h b/lib/nfc/protocols/mifare_ultralight.h index 957d5f9d5..3c8807433 100644 --- a/lib/nfc/protocols/mifare_ultralight.h +++ b/lib/nfc/protocols/mifare_ultralight.h @@ -28,6 +28,8 @@ #define MF_UL_NTAG203_COUNTER_PAGE (41) +#define MF_UL_DEFAULT_PWD (0xFFFFFFFF) + typedef enum { MfUltralightAuthMethodManual, MfUltralightAuthMethodAmeebo, @@ -110,7 +112,6 @@ typedef struct { uint8_t signature[32]; uint32_t counter[3]; uint8_t tearing[3]; - bool has_auth; MfUltralightAuthMethod auth_method; uint8_t auth_key[4]; bool auth_success; @@ -169,6 +170,9 @@ typedef struct { MfUltralightFeatures supported_features; } MfUltralightReader; +// TODO rework with reader analyzer +// typedef void (*MfUltralightAuthReceivedCallback)(MfUltralightAuth auth, void* context); + typedef struct { MfUltralightData data; MfUltralightConfigPages* config; @@ -187,6 +191,10 @@ typedef struct { bool read_counter_incremented; bool auth_attempted; MfUltralightAuth auth_attempt; + + // TODO rework with reader analyzer + MfUltralightAuthReceivedCallback auth_received_callback; + void* context; } MfUltralightEmulator; void mf_ul_reset(MfUltralightData* data); @@ -243,3 +251,5 @@ bool mf_ul_prepare_emulation_response( uint32_t mf_ul_pwdgen_amiibo(FuriHalNfcDevData* data); uint32_t mf_ul_pwdgen_xiaomi(FuriHalNfcDevData* data); + +bool mf_ul_is_full_capture(MfUltralightData* data);