diff --git a/applications/external/nfc_magic/nfc_magic_worker.c b/applications/external/nfc_magic/nfc_magic_worker.c index 9ee7fd3eecdf..894137c86f80 100644 --- a/applications/external/nfc_magic/nfc_magic_worker.c +++ b/applications/external/nfc_magic/nfc_magic_worker.c @@ -93,7 +93,7 @@ void nfc_magic_worker_write(NfcMagicWorker* nfc_magic_worker) { while(nfc_magic_worker->state == NfcMagicWorkerStateWrite) { do { if(magic_dev->type == MagicTypeClassicGen1) { - if(furi_hal_nfc_detect(&nfc_data, 200)) { + if(furi_hal_nfc_detect(&nfc_data, 200, false)) { magic_deactivate(); magic_activate(); if(!magic_gen1_wupa()) { @@ -134,7 +134,7 @@ void nfc_magic_worker_write(NfcMagicWorker* nfc_magic_worker) { break; } } else if(magic_dev->type == MagicTypeGen4) { - if(furi_hal_nfc_detect(&nfc_data, 200)) { + if(furi_hal_nfc_detect(&nfc_data, 200, false)) { uint8_t gen4_config[28]; uint32_t password = magic_dev->password; diff --git a/applications/main/nfc/nfc_cli.c b/applications/main/nfc/nfc_cli.c index 6e6e04ca928b..f400fb1eeb69 100644 --- a/applications/main/nfc/nfc_cli.c +++ b/applications/main/nfc/nfc_cli.c @@ -33,7 +33,8 @@ static void nfc_cli_detect(Cli* cli, FuriString* args) { printf("Detecting nfc...\r\nPress Ctrl+C to abort\r\n"); while(!cmd_exit) { cmd_exit |= cli_cmd_interrupt_received(cli); - if(furi_hal_nfc_detect(&dev_data, 400)) { + if(furi_hal_nfc_detect(&dev_data, 400, true) || + furi_hal_nfc_detect(&dev_data, 400, false)) { printf("Found: %s ", nfc_get_dev_type(dev_data.type)); printf("UID length: %d, UID:", dev_data.uid_len); for(size_t i = 0; i < dev_data.uid_len; i++) { @@ -125,7 +126,8 @@ static void nfc_cli_apdu(Cli* cli, FuriString* args) { } printf("detecting tag\r\n"); - if(!furi_hal_nfc_detect(&dev_data, 300)) { + if(!furi_hal_nfc_detect(&dev_data, 300, true) && + !furi_hal_nfc_detect(&dev_data, 300, false)) { printf("Failed to detect tag\r\n"); break; } diff --git a/applications/main/nfc/scenes/nfc_scene_config.h b/applications/main/nfc/scenes/nfc_scene_config.h index f11d1479838e..deec73141ecb 100644 --- a/applications/main/nfc/scenes/nfc_scene_config.h +++ b/applications/main/nfc/scenes/nfc_scene_config.h @@ -51,6 +51,9 @@ ADD_SCENE(nfc, mf_classic_write_fail, MfClassicWriteFail) ADD_SCENE(nfc, mf_classic_update, MfClassicUpdate) ADD_SCENE(nfc, mf_classic_update_success, MfClassicUpdateSuccess) ADD_SCENE(nfc, mf_classic_wrong_card, MfClassicWrongCard) +ADD_SCENE(nfc, topaz_read_success, TopazReadSuccess) +ADD_SCENE(nfc, topaz_menu, TopazMenu) +ADD_SCENE(nfc, topaz_data, TopazData) ADD_SCENE(nfc, emv_read_success, EmvReadSuccess) ADD_SCENE(nfc, emv_menu, EmvMenu) ADD_SCENE(nfc, emulate_apdu_sequence, EmulateApduSequence) diff --git a/applications/main/nfc/scenes/nfc_scene_delete.c b/applications/main/nfc/scenes/nfc_scene_delete.c index 0808db45a321..f99327c371cf 100644 --- a/applications/main/nfc/scenes/nfc_scene_delete.c +++ b/applications/main/nfc/scenes/nfc_scene_delete.c @@ -44,6 +44,8 @@ void nfc_scene_delete_on_enter(void* context) { } else if(protocol == NfcDeviceProtocolNfcV) { furi_string_set(temp_str, "ISO15693 tag"); nfc_type = "NFC-V"; + } else if(protocol == NfcDeviceProtocolTopaz) { + furi_string_set(temp_str, nfc_topaz_type(nfc->dev->dev_data.topaz_data.type)); } else { furi_string_set(temp_str, "Unknown ISO tag"); } 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 eb2f939c60f1..cb129bd36c11 100644 --- a/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c +++ b/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c @@ -15,7 +15,7 @@ void nfc_scene_nfc_data_info_on_enter(void* context) { NfcProtocol protocol = dev_data->protocol; uint8_t text_scroll_height = 0; if((protocol == NfcDeviceProtocolMifareDesfire) || (protocol == NfcDeviceProtocolMifareUl) || - (protocol == NfcDeviceProtocolMifareClassic)) { + (protocol == NfcDeviceProtocolMifareClassic) || (protocol == NfcDeviceProtocolTopaz)) { widget_add_button_element( widget, GuiButtonTypeRight, "More", nfc_scene_nfc_data_info_widget_callback, nfc); text_scroll_height = 52; @@ -62,6 +62,8 @@ void nfc_scene_nfc_data_info_on_enter(void* context) { furi_string_cat_printf(temp_str, "\e#ISO15693 (unknown)\n"); break; } + } else if(protocol == NfcDeviceProtocolTopaz) { + furi_string_cat_printf(temp_str, "\e#%s\n", nfc_topaz_type(dev_data->topaz_data.type)); } else { furi_string_cat_printf(temp_str, "\e#Unknown ISO tag\n"); } @@ -260,6 +262,9 @@ void nfc_scene_nfc_data_info_on_enter(void* context) { mf_classic_get_read_sectors_and_keys(data, §ors_read, &keys_found); furi_string_cat_printf(temp_str, "\nKeys Found %d/%d", keys_found, keys_total); furi_string_cat_printf(temp_str, "\nSectors Read %d/%d", sectors_read, sectors_total); + } else if(protocol == NfcDeviceProtocolTopaz) { + TopazData* data = &dev_data->topaz_data; + furi_string_cat_printf(temp_str, "\nHR0: %02X\nHR1: %02X", data->hr[0], data->hr[1]); } // Add text scroll widget @@ -288,6 +293,9 @@ bool nfc_scene_nfc_data_info_on_event(void* context, SceneManagerEvent event) { } else if(protocol == NfcDeviceProtocolNfcV) { scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVMenu); consumed = true; + } else if(protocol == NfcDeviceProtocolTopaz) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneTopazData); + consumed = true; } } } diff --git a/applications/main/nfc/scenes/nfc_scene_read.c b/applications/main/nfc/scenes/nfc_scene_read.c index 1690a9557572..b16f5eeb20e3 100644 --- a/applications/main/nfc/scenes/nfc_scene_read.c +++ b/applications/main/nfc/scenes/nfc_scene_read.c @@ -90,6 +90,11 @@ bool nfc_scene_read_on_event(void* context, SceneManagerEvent event) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfDesfireReadSuccess); dolphin_deed(DolphinDeedNfcReadSuccess); consumed = true; + } else if(event.event == NfcWorkerEventReadTopaz) { + notification_message(nfc->notifications, &sequence_success); + scene_manager_next_scene(nfc->scene_manager, NfcSceneTopazReadSuccess); + dolphin_deed(DolphinDeedNfcReadSuccess); + consumed = true; } else if(event.event == NfcWorkerEventReadMfClassicDictAttackRequired) { if(mf_classic_dict_check_presence(MfClassicDictTypeSystem)) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicDictAttack); diff --git a/applications/main/nfc/scenes/nfc_scene_read_card_type.c b/applications/main/nfc/scenes/nfc_scene_read_card_type.c index 8023026c3da8..de3ff7517c27 100644 --- a/applications/main/nfc/scenes/nfc_scene_read_card_type.c +++ b/applications/main/nfc/scenes/nfc_scene_read_card_type.c @@ -5,6 +5,7 @@ enum SubmenuIndex { SubmenuIndexReadMifareClassic, SubmenuIndexReadMifareDesfire, SubmenuIndexReadMfUltralight, + SubmenuIndexReadTopaz, SubmenuIndexReadNFCA, }; @@ -36,6 +37,12 @@ void nfc_scene_read_card_type_on_enter(void* context) { SubmenuIndexReadMfUltralight, nfc_scene_read_card_type_submenu_callback, nfc); + submenu_add_item( + submenu, + "Read Topaz", + SubmenuIndexReadTopaz, + nfc_scene_read_card_type_submenu_callback, + nfc); submenu_add_item( submenu, "Read NFC-A data", @@ -68,6 +75,11 @@ bool nfc_scene_read_card_type_on_event(void* context, SceneManagerEvent event) { scene_manager_next_scene(nfc->scene_manager, NfcSceneRead); consumed = true; } + if(event.event == SubmenuIndexReadTopaz) { + nfc->dev->dev_data.read_mode = NfcReadModeTopaz; + scene_manager_next_scene(nfc->scene_manager, NfcSceneRead); + consumed = true; + } if(event.event == SubmenuIndexReadNFCA) { nfc->dev->dev_data.read_mode = NfcReadModeNFCA; scene_manager_next_scene(nfc->scene_manager, NfcSceneRead); diff --git a/applications/main/nfc/scenes/nfc_scene_topaz_data.c b/applications/main/nfc/scenes/nfc_scene_topaz_data.c new file mode 100644 index 000000000000..20b85140577c --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_topaz_data.c @@ -0,0 +1,32 @@ +#include "../nfc_i.h" + +void nfc_scene_topaz_data_on_enter(void* context) { + Nfc* nfc = context; + TopazData* data = &nfc->dev->dev_data.topaz_data; + TextBox* text_box = nfc->text_box; + + text_box_set_font(text_box, TextBoxFontHex); + for(uint16_t i = 0; i < data->size; i += 2) { + if(!(i % 8) && i) { + furi_string_push_back(nfc->text_box_store, '\n'); + } + furi_string_cat_printf(nfc->text_box_store, "%02X%02X ", data->data[i], data->data[i + 1]); + } + text_box_set_text(text_box, furi_string_get_cstr(nfc->text_box_store)); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox); +} + +bool nfc_scene_topaz_data_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} + +void nfc_scene_topaz_data_on_exit(void* context) { + Nfc* nfc = context; + + // Clean view + text_box_reset(nfc->text_box); + furi_string_reset(nfc->text_box_store); +} diff --git a/applications/main/nfc/scenes/nfc_scene_topaz_menu.c b/applications/main/nfc/scenes/nfc_scene_topaz_menu.c new file mode 100644 index 000000000000..2f0c5f847774 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_topaz_menu.c @@ -0,0 +1,59 @@ +#include "../nfc_i.h" +#include + +enum SubmenuIndex { + SubmenuIndexSave, + SubmenuIndexInfo, +}; + +void nfc_scene_topaz_menu_submenu_callback(void* context, uint32_t index) { + Nfc* nfc = context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, index); +} + +void nfc_scene_topaz_menu_on_enter(void* context) { + Nfc* nfc = context; + Submenu* submenu = nfc->submenu; + + submenu_add_item( + submenu, "Save", SubmenuIndexSave, nfc_scene_topaz_menu_submenu_callback, nfc); + submenu_add_item( + submenu, "Info", SubmenuIndexInfo, nfc_scene_topaz_menu_submenu_callback, nfc); + + submenu_set_selected_item( + nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneTopazMenu)); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); +} + +bool nfc_scene_topaz_menu_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuIndexSave) { + nfc->dev->format = NfcDeviceSaveFormatTopaz; + // Clear device name + nfc_device_set_name(nfc->dev, ""); + scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); + consumed = true; + } else if(event.event == SubmenuIndexInfo) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcDataInfo); + consumed = true; + } + scene_manager_set_scene_state(nfc->scene_manager, NfcSceneTopazMenu, event.event); + + } else if(event.type == SceneManagerEventTypeBack) { + consumed = scene_manager_previous_scene(nfc->scene_manager); + } + + return consumed; +} + +void nfc_scene_topaz_menu_on_exit(void* context) { + Nfc* nfc = context; + + // Clear view + submenu_reset(nfc->submenu); +} diff --git a/applications/main/nfc/scenes/nfc_scene_topaz_read_success.c b/applications/main/nfc/scenes/nfc_scene_topaz_read_success.c new file mode 100644 index 000000000000..dc2940ac62be --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_topaz_read_success.c @@ -0,0 +1,73 @@ +#include "../nfc_i.h" + +void nfc_scene_topaz_read_success_widget_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_topaz_read_success_on_enter(void* context) { + Nfc* nfc = context; + + // Setup widget view + FuriHalNfcDevData* data = &nfc->dev->dev_data.nfc_data; + TopazData* topaz_data = &nfc->dev->dev_data.topaz_data; + Widget* widget = nfc->widget; + widget_add_button_element( + widget, GuiButtonTypeLeft, "Retry", nfc_scene_topaz_read_success_widget_callback, nfc); + widget_add_button_element( + widget, GuiButtonTypeRight, "More", nfc_scene_topaz_read_success_widget_callback, nfc); + + FuriString* temp_str = NULL; + if(furi_string_size(nfc->dev->dev_data.parsed_data)) { + temp_str = furi_string_alloc_set(nfc->dev->dev_data.parsed_data); + } else { + temp_str = furi_string_alloc_printf("\e#%s\n", nfc_topaz_type(topaz_data->type)); + furi_string_cat_printf(temp_str, "UID:"); + for(size_t i = 0; i < data->uid_len; i++) { + furi_string_cat_printf(temp_str, " %02X", data->uid[i]); + } + furi_string_cat_printf( + temp_str, "\nHR0: %02X\nHR1: %02X", topaz_data->hr[0], topaz_data->hr[1]); + } + widget_add_text_scroll_element(widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str)); + furi_string_free(temp_str); + + notification_message_block(nfc->notifications, &sequence_set_green_255); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); +} + +bool nfc_scene_topaz_read_success_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeLeft) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneRetryConfirm); + consumed = true; + } else if(event.event == GuiButtonTypeRight) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneTopazMenu); + consumed = true; + } + } else if(event.type == SceneManagerEventTypeBack) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneExitConfirm); + consumed = true; + } + + return consumed; +} + +void nfc_scene_topaz_read_success_on_exit(void* context) { + Nfc* nfc = context; + + notification_message_block(nfc->notifications, &sequence_reset_green); + + // Clean view + widget_reset(nfc->widget); +} diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 5ed26f296112..07e94e34722c 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1220,7 +1220,7 @@ Function,+,furi_hal_mpu_protect_no_access,void,"FuriHalMpuRegion, uint32_t, Furi Function,+,furi_hal_mpu_protect_read_only,void,"FuriHalMpuRegion, uint32_t, FuriHalMPURegionSize" Function,+,furi_hal_nfc_activate_nfca,_Bool,"uint32_t, uint32_t*" Function,-,furi_hal_nfc_deinit,void, -Function,+,furi_hal_nfc_detect,_Bool,"FuriHalNfcDevData*, uint32_t" +Function,+,furi_hal_nfc_detect,_Bool,"FuriHalNfcDevData*, uint32_t, _Bool" Function,+,furi_hal_nfc_emulate_nfca,_Bool,"uint8_t*, uint8_t, uint8_t*, uint8_t, FuriHalNfcEmulateCallback, void*, uint32_t" Function,+,furi_hal_nfc_exit_sleep,void, Function,+,furi_hal_nfc_field_off,void, @@ -2845,6 +2845,10 @@ Function,-,toascii,int,int Function,-,toascii_l,int,"int, locale_t" Function,-,tolower,int,int Function,-,tolower_l,int,"int, locale_t" +Function,-,topaz_check_card_type,_Bool,"uint8_t, uint8_t" +Function,-,topaz_get_size_by_type,size_t,TopazType +Function,-,topaz_get_type_from_hr0,TopazType,uint8_t +Function,-,topaz_read_card,_Bool,"FuriHalNfcTxRxContext*, TopazData*, uint8_t*" Function,-,toupper,int,int Function,-,toupper_l,int,"int, locale_t" Function,-,trunc,double,double diff --git a/firmware/targets/f7/furi_hal/furi_hal_nfc.c b/firmware/targets/f7/furi_hal/furi_hal_nfc.c index c4e7ad9f9842..375f0309b617 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_nfc.c +++ b/firmware/targets/f7/furi_hal/furi_hal_nfc.c @@ -76,7 +76,7 @@ void furi_hal_nfc_exit_sleep() { rfalLowPowerModeStop(); } -bool furi_hal_nfc_detect(FuriHalNfcDevData* nfc_data, uint32_t timeout) { +bool furi_hal_nfc_detect(FuriHalNfcDevData* nfc_data, uint32_t timeout, bool emv_compliance) { furi_assert(nfc_data); rfalNfcDevice* dev_list = NULL; @@ -90,7 +90,7 @@ bool furi_hal_nfc_detect(FuriHalNfcDevData* nfc_data, uint32_t timeout) { rfalNfcInitialize(); } rfalNfcDiscoverParam params; - params.compMode = RFAL_COMPLIANCE_MODE_EMV; + params.compMode = emv_compliance ? RFAL_COMPLIANCE_MODE_EMV : RFAL_COMPLIANCE_MODE_NFC; params.techs2Find = RFAL_NFC_POLL_TECH_A | RFAL_NFC_POLL_TECH_B | RFAL_NFC_POLL_TECH_F | RFAL_NFC_POLL_TECH_V | RFAL_NFC_POLL_TECH_AP2P | RFAL_NFC_POLL_TECH_ST25TB; params.totalDuration = 1000; @@ -124,7 +124,7 @@ bool furi_hal_nfc_detect(FuriHalNfcDevData* nfc_data, uint32_t timeout) { rfalNfcSelect(0); } if(DWT->CYCCNT - start > timeout * clocks_in_ms) { - rfalNfcDeactivate(true); + rfalNfcDeactivate(false); FURI_LOG_T(TAG, "Timeout"); break; } diff --git a/firmware/targets/f7/furi_hal/furi_hal_nfc.h b/firmware/targets/f7/furi_hal/furi_hal_nfc.h index dc3f873f3460..fb8df7f79a28 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_nfc.h +++ b/firmware/targets/f7/furi_hal/furi_hal_nfc.h @@ -135,16 +135,15 @@ void furi_hal_nfc_stop_cmd(); */ void furi_hal_nfc_exit_sleep(); -/** NFC poll +/** NFC detect * - * @param dev_list pointer to rfalNfcDevice buffer - * @param dev_cnt pointer device count - * @param timeout timeout in ms - * @param deactivate deactivate flag + * @param nfc_data pointer to FuriHalNfcDevData + * @param timeout timeout in ms + * @param emv_compliance whether to use EMVCo compliance mode * * @return true on success */ -bool furi_hal_nfc_detect(FuriHalNfcDevData* nfc_data, uint32_t timeout); +bool furi_hal_nfc_detect(FuriHalNfcDevData* nfc_data, uint32_t timeout, bool emv_compliance); /** Activate NFC-A tag * diff --git a/lib/ST25RFAL002/platform.h b/lib/ST25RFAL002/platform.h index d86bba73e36e..a20e4b415255 100644 --- a/lib/ST25RFAL002/platform.h +++ b/lib/ST25RFAL002/platform.h @@ -124,7 +124,8 @@ void rfal_platform_spi_release(); #define platformI2CSlaveAddrWR(add) /*!< I2C Slave address for Write operation */ #define platformI2CSlaveAddrRD(add) /*!< I2C Slave address for Read operation */ -#define platformLog(...) /*!< Log method */ +#define platformLog(...) \ + FURI_LOG_D("ST25RFAL002", __VA_ARGS__) /*!< Log method */ /* ****************************************************************************** diff --git a/lib/nfc/nfc_device.c b/lib/nfc/nfc_device.c index 952fca254bd8..4861437f547b 100644 --- a/lib/nfc/nfc_device.c +++ b/lib/nfc/nfc_device.c @@ -60,6 +60,8 @@ static void nfc_device_prepare_format_string(NfcDevice* dev, FuriString* format_ furi_string_set(format_string, "Mifare DESFire"); } else if(dev->format == NfcDeviceSaveFormatNfcV) { furi_string_set(format_string, "ISO15693"); + } else if(dev->format == NfcDeviceSaveFormatTopaz) { + furi_string_set(format_string, "Topaz"); } else { furi_string_set(format_string, "Unknown"); } @@ -100,6 +102,10 @@ static bool nfc_device_parse_format_string(NfcDevice* dev, FuriString* format_st dev->dev_data.protocol = NfcDeviceProtocolNfcV; return true; } + if(furi_string_start_with_str(format_string, "Topaz")) { + dev->format = NfcDeviceSaveFormatTopaz; + dev->dev_data.protocol = NfcDeviceProtocolTopaz; + } return false; } @@ -1339,6 +1345,58 @@ bool nfc_device_load_key_cache(NfcDevice* dev) { return load_success; } +static bool nfc_device_save_topaz_data(FlipperFormat* file, NfcDevice* dev) { + bool saved = false; + TopazData* data = &dev->dev_data.topaz_data; + FuriString* temp_str = furi_string_alloc(); + + do { + if(!flipper_format_write_comment_cstr(file, "Topaz-specific data")) break; + if(!flipper_format_write_hex(file, "HR0", &data->hr[0], 1)) break; + if(!flipper_format_write_hex(file, "HR1", &data->hr[1], 1)) break; + + for(uint16_t i = 0; i < data->size; i += 8) { + furi_string_printf(temp_str, "Block %02X", i / 8); + if(!flipper_format_write_hex(file, furi_string_get_cstr(temp_str), &data->data[i], 8)) + break; + } + + saved = true; + } while(false); + + furi_string_free(temp_str); + return saved; +} + +static bool nfc_device_load_topaz_data(FlipperFormat* file, NfcDevice* dev) { + bool parsed = false; + TopazData* data = &dev->dev_data.topaz_data; + FuriString* temp_str = furi_string_alloc(); + + do { + if(!flipper_format_read_hex(file, "HR0", &data->hr[0], 1)) break; + if(!flipper_format_read_hex(file, "HR1", &data->hr[1], 1)) break; + + if((data->hr[0] & 0xf0) != 0x10) { + FURI_LOG_D(TAG, "Non-Topaz HR0 detected: %02X", data->hr[0]); + break; + } + + data->type = topaz_get_type_from_hr0(data->hr[0]); + data->size = topaz_get_size_by_type(data->type); + + for(uint16_t i = 0; i < data->size; i += 8) { + furi_string_printf(temp_str, "Block %02X", i / 8); + if(!flipper_format_read_hex(file, furi_string_get_cstr(temp_str), &data->data[i], 8)) + break; + } + + parsed = true; + } while(false); + + return parsed; +} + void nfc_device_set_name(NfcDevice* dev, const char* name) { furi_assert(dev); @@ -1427,6 +1485,8 @@ bool nfc_device_save(NfcDevice* dev, const char* dev_name) { if(!nfc_device_save_mifare_classic_data(file, dev)) break; // Save keys cache if(!nfc_device_save_mifare_classic_keys(dev)) break; + } else if(dev->format == NfcDeviceSaveFormatTopaz) { + if(!nfc_device_save_topaz_data(file, dev)) break; } saved = true; } while(0); @@ -1526,6 +1586,8 @@ static bool nfc_device_load_data(NfcDevice* dev, FuriString* path, bool show_dia if(!nfc_device_load_mifare_df_data(file, dev)) break; } else if(dev->format == NfcDeviceSaveFormatNfcV) { if(!nfc_device_load_nfcv_data(file, dev)) break; + } else if(dev->format == NfcDeviceSaveFormatTopaz) { + if(!nfc_device_load_topaz_data(file, dev)) break; } else if(dev->format == NfcDeviceSaveFormatBankCard) { if(!nfc_device_load_bank_card_data(file, dev)) break; } diff --git a/lib/nfc/nfc_device.h b/lib/nfc/nfc_device.h index 20df4f89181d..e5faa178d505 100644 --- a/lib/nfc/nfc_device.h +++ b/lib/nfc/nfc_device.h @@ -12,6 +12,7 @@ #include #include #include +#include #ifdef __cplusplus extern "C" { @@ -32,7 +33,8 @@ typedef enum { NfcDeviceProtocolMifareUl, NfcDeviceProtocolMifareClassic, NfcDeviceProtocolMifareDesfire, - NfcDeviceProtocolNfcV + NfcDeviceProtocolNfcV, + NfcDeviceProtocolTopaz, } NfcProtocol; typedef enum { @@ -42,6 +44,7 @@ typedef enum { NfcDeviceSaveFormatMifareClassic, NfcDeviceSaveFormatMifareDesfire, NfcDeviceSaveFormatNfcV, + NfcDeviceSaveFormatTopaz, } NfcDeviceSaveFormat; typedef struct { @@ -59,6 +62,7 @@ typedef enum { NfcReadModeMfClassic, NfcReadModeMfUltralight, NfcReadModeMfDesfire, + NfcReadModeTopaz, NfcReadModeNFCA, } NfcReadMode; @@ -77,6 +81,7 @@ typedef struct { MfClassicData mf_classic_data; MifareDesfireData mf_df_data; NfcVData nfcv_data; + TopazData topaz_data; }; FuriString* parsed_data; } NfcDeviceData; diff --git a/lib/nfc/nfc_types.c b/lib/nfc/nfc_types.c index 96b92640f0ec..61a13691b74d 100644 --- a/lib/nfc/nfc_types.c +++ b/lib/nfc/nfc_types.c @@ -23,6 +23,8 @@ const char* nfc_guess_protocol(NfcProtocol protocol) { return "Mifare Classic"; } else if(protocol == NfcDeviceProtocolMifareDesfire) { return "Mifare DESFire"; + } else if(protocol == NfcDeviceProtocolTopaz) { + return "Topaz"; } else { return "Unrecognized"; } @@ -67,3 +69,13 @@ const char* nfc_mf_classic_type(MfClassicType type) { return "Mifare Classic"; } } + +const char* nfc_topaz_type(TopazType type) { + if(type == TopazType96) { + return "Topaz 96"; + } else if(type == TopazType512) { + return "Topaz 512"; + } else { + return "Topaz"; + } +} diff --git a/lib/nfc/nfc_types.h b/lib/nfc/nfc_types.h index fb53ce7c25be..8f1d168f7168 100644 --- a/lib/nfc/nfc_types.h +++ b/lib/nfc/nfc_types.h @@ -9,3 +9,5 @@ const char* nfc_guess_protocol(NfcProtocol protocol); const char* nfc_mf_ul_type(MfUltralightType type, bool full_name); const char* nfc_mf_classic_type(MfClassicType type); + +const char* nfc_topaz_type(TopazType type); diff --git a/lib/nfc/nfc_worker.c b/lib/nfc/nfc_worker.c index a6bb93f59ef9..8cde94fd3d5e 100644 --- a/lib/nfc/nfc_worker.c +++ b/lib/nfc/nfc_worker.c @@ -141,7 +141,9 @@ static bool nfc_worker_read_nfcv(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* t } do { - 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, true) && + !furi_hal_nfc_detect(&nfc_worker->dev_data->nfc_data, 200, false)) + break; if(!nfcv_read_card(&reader, nfc_data, nfcv_data)) break; read_success = true; @@ -386,7 +388,7 @@ static bool nfc_worker_read_mf_ultralight(NfcWorker* nfc_worker, FuriHalNfcTxRxC furi_hal_nfc_sleep(); // Otherwise, try to read as usual - 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, false)) break; if(!mf_ul_read_card(tx_rx, &reader, &data)) break; // Copy data nfc_worker->dev_data->mf_ul_data = data; @@ -455,7 +457,7 @@ static bool nfc_worker_read_mf_desfire(NfcWorker* nfc_worker, FuriHalNfcTxRxCont } 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, false)) break; if(!mf_df_read_card(tx_rx, data)) break; FURI_LOG_I(TAG, "Trying to parse a supported card ..."); @@ -480,6 +482,34 @@ static bool nfc_worker_read_mf_desfire(NfcWorker* nfc_worker, FuriHalNfcTxRxCont return read_success; } +static bool nfc_worker_read_topaz(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { + bool read_success = false; + TopazData* data = &nfc_worker->dev_data->topaz_data; + + 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 { + if(!furi_hal_nfc_detect(&nfc_worker->dev_data->nfc_data, 300, false)) break; + if(!topaz_read_card(tx_rx, data, nfc_worker->dev_data->nfc_data.uid)) break; + read_success = true; + } while(false); + + if(read_success) { + // Update full UID from data + memcpy(nfc_worker->dev_data->nfc_data.uid, data->data, TOPAZ_UID_FULL_LENGTH); + nfc_worker->dev_data->nfc_data.uid_len = TOPAZ_UID_FULL_LENGTH; + } + + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + reader_analyzer_stop(nfc_worker->reader_analyzer); + } + + return read_success; +} + static bool nfc_worker_read_nfca(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; @@ -503,6 +533,10 @@ static bool nfc_worker_read_nfca(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* t nfc_worker->dev_data->protocol = NfcDeviceProtocolUnknown; } card_read = true; + } else if(topaz_check_card_type(nfc_data->atqa[0], nfc_data->atqa[1])) { + FURI_LOG_I(TAG, "Topaz detected"); + nfc_worker->dev_data->protocol = NfcDeviceProtocolTopaz; + card_read = nfc_worker_read_topaz(nfc_worker, tx_rx); } else { nfc_worker->dev_data->protocol = NfcDeviceProtocolUnknown; card_read = true; @@ -523,7 +557,7 @@ void nfc_worker_read(NfcWorker* nfc_worker) { bool card_not_detected_notified = false; while(nfc_worker->state == NfcWorkerStateRead) { - if(furi_hal_nfc_detect(nfc_data, 300)) { + if(furi_hal_nfc_detect(nfc_data, 300, true) || furi_hal_nfc_detect(nfc_data, 300, false)) { // Process first found device nfc_worker->callback(NfcWorkerEventCardDetected, nfc_worker->context); card_not_detected_notified = false; @@ -538,6 +572,9 @@ void nfc_worker_read(NfcWorker* nfc_worker) { } else if(dev_data->protocol == NfcDeviceProtocolMifareDesfire) { event = NfcWorkerEventReadMfDesfire; break; + } else if(dev_data->protocol == NfcDeviceProtocolTopaz) { + event = NfcWorkerEventReadTopaz; + break; } else if(dev_data->protocol == NfcDeviceProtocolUnknown) { event = NfcWorkerEventReadUidNfcA; break; @@ -591,7 +628,7 @@ void nfc_worker_read_type(NfcWorker* nfc_worker) { bool card_not_detected_notified = false; while(nfc_worker->state == NfcWorkerStateRead) { - if(furi_hal_nfc_detect(nfc_data, 300)) { + if(furi_hal_nfc_detect(nfc_data, 300, true) || furi_hal_nfc_detect(nfc_data, 300, false)) { FURI_LOG_D(TAG, "Card detected"); furi_hal_nfc_sleep(); // Process first found device @@ -626,6 +663,12 @@ void nfc_worker_read_type(NfcWorker* nfc_worker) { event = NfcWorkerEventReadMfDesfire; break; } + } else if(read_mode == NfcReadModeTopaz) { + nfc_worker->dev_data->protocol = NfcDeviceProtocolTopaz; + if(nfc_worker_read_topaz(nfc_worker, &tx_rx)) { + event = NfcWorkerEventReadTopaz; + break; + } } else if(read_mode == NfcReadModeNFCA) { nfc_worker->dev_data->protocol = NfcDeviceProtocolUnknown; event = NfcWorkerEventReadUidNfcA; @@ -1042,7 +1085,7 @@ void nfc_worker_write_mf_classic(NfcWorker* nfc_worker) { MfClassicData dest_data = *src_data; while(nfc_worker->state == NfcWorkerStateMfClassicWrite) { - if(furi_hal_nfc_detect(&nfc_data, 200)) { + if(furi_hal_nfc_detect(&nfc_data, 200, false)) { if(!card_found_notified) { nfc_worker->callback(NfcWorkerEventCardDetected, nfc_worker->context); card_found_notified = true; @@ -1115,7 +1158,7 @@ void nfc_worker_update_mf_classic(NfcWorker* nfc_worker) { MfClassicData new_data = *old_data; while(nfc_worker->state == NfcWorkerStateMfClassicUpdate) { - if(furi_hal_nfc_detect(&nfc_data, 200)) { + if(furi_hal_nfc_detect(&nfc_data, 200, false)) { if(!card_found_notified) { nfc_worker->callback(NfcWorkerEventCardDetected, nfc_worker->context); card_found_notified = true; @@ -1193,7 +1236,7 @@ void nfc_worker_mf_ultralight_read_auth(NfcWorker* nfc_worker) { uint16_t pack = 0; while(nfc_worker->state == NfcWorkerStateReadMfUltralightReadAuth) { furi_hal_nfc_sleep(); - if(furi_hal_nfc_detect(nfc_data, 300) && nfc_data->type == FuriHalNfcTypeA) { + if(furi_hal_nfc_detect(nfc_data, 300, false) && nfc_data->type == FuriHalNfcTypeA) { if(mf_ul_check_card_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak)) { nfc_worker->callback(NfcWorkerEventCardDetected, nfc_worker->context); if(data->auth_method == MfUltralightAuthMethodManual || diff --git a/lib/nfc/nfc_worker.h b/lib/nfc/nfc_worker.h index 722f148574f2..32a5b1778ecc 100644 --- a/lib/nfc/nfc_worker.h +++ b/lib/nfc/nfc_worker.h @@ -44,6 +44,7 @@ typedef enum { NfcWorkerEventReadMfClassicLoadKeyCache, NfcWorkerEventReadMfClassicDictAttackRequired, NfcWorkerEventReadNfcV, + NfcWorkerEventReadTopaz, // Nfc worker common events NfcWorkerEventSuccess, diff --git a/lib/nfc/protocols/topaz.c b/lib/nfc/protocols/topaz.c new file mode 100644 index 000000000000..71cdf877be53 --- /dev/null +++ b/lib/nfc/protocols/topaz.c @@ -0,0 +1,107 @@ +#include +#include "topaz.h" + +#define TAG "Topaz" + +bool topaz_check_card_type(uint8_t ATQA0, uint8_t ATQA1) { + return ATQA0 == 0x00 && ATQA1 == 0x0C; +} + +TopazType topaz_get_type_from_hr0(uint8_t hr0) { + if(hr0 == TOPAZ_96_HR0) { + return TopazType96; + } else if(hr0 == TOPAZ_512_HR0) { + return TopazType512; + } else { + return TopazTypeUnknown; + } +} + +size_t topaz_get_size_by_type(TopazType type) { + if(type == TopazType96) { + return TOPAZ_96_SIZE; + } else if(type == TopazType512) { + return TOPAZ_512_SIZE; + } else { + return TOPAZ_96_SIZE; // Probably safe fallback size; What's the size of Jewel? + } +} + +bool topaz_read_card(FuriHalNfcTxRxContext* tx_rx, TopazData* data, uint8_t* uid) { + size_t data_read = 0; + bool read_success = false; + + do { + if(rfalT1TPollerInitialize() != ERR_NONE) { + FURI_LOG_D(TAG, "Failed to initialize poller"); + break; + } + + // Perform RALL: HR0, HR1, and block 00-0E + tx_rx->tx_data[0] = TOPAZ_CMD_RALL; + tx_rx->tx_data[1] = 0; + tx_rx->tx_data[2] = 0; + memcpy(&tx_rx->tx_data[3], uid, 4); + tx_rx->tx_bits = 7 * 8; + tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault; + + if(!furi_hal_nfc_tx_rx(tx_rx, 50) || tx_rx->rx_bits != 122 * 8) { + FURI_LOG_D(TAG, "Failed to RALL"); + break; + } + + memcpy(data->hr, tx_rx->rx_data, sizeof(data->hr)); + + data->type = topaz_get_type_from_hr0(data->hr[0]); + if(data->type == TopazTypeUnknown) { + FURI_LOG_D(TAG, "Unknown type, HR0=%#04x", data->hr[0]); + break; + } + data->size = topaz_get_size_by_type(data->type); + + memcpy(data->data, &tx_rx->rx_data[2], TOPAZ_BYTES_RALL); + data_read += TOPAZ_BYTES_RALL; + + if(data->size > TOPAZ_96_SIZE) { + // Perform READ8 on block 0F + tx_rx->tx_data[0] = TOPAZ_CMD_READ8; + tx_rx->tx_data[1] = 0x0F; + memset(&tx_rx->tx_data[2], 0, TOPAZ_BYTES_PER_BLOCK); + memcpy(&tx_rx->tx_data[10], uid, 4); + tx_rx->tx_bits = 14 * 8; + tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault; + + if(!furi_hal_nfc_tx_rx(tx_rx, 50) || tx_rx->rx_bits != 9 * 8 || + tx_rx->rx_data[0] != tx_rx->tx_data[1]) { + FURI_LOG_D(TAG, "Failed to read block 0F"); + break; + } + + memcpy(&data->data[data_read], &tx_rx->rx_data[1], TOPAZ_BYTES_PER_BLOCK); + data_read += TOPAZ_BYTES_PER_BLOCK; + + // Perform RSEG for rest of tag + for(uint8_t i = 1; data_read < data->size; ++i) { + tx_rx->tx_data[0] = TOPAZ_CMD_RSEG; + tx_rx->tx_data[1] = i << 4; + memset(&tx_rx->tx_data[2], 0, TOPAZ_BYTES_PER_BLOCK); + memcpy(&tx_rx->tx_data[10], uid, 4); + tx_rx->tx_bits = 14 * 8; + tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault; + + if(!furi_hal_nfc_tx_rx(tx_rx, 50) || tx_rx->rx_bits != 129 * 8 || + tx_rx->rx_data[0] != tx_rx->tx_data[1]) { + FURI_LOG_D(TAG, "Failed to read segment %d", i); + break; + } + + memcpy(&data->data[data_read], &tx_rx->rx_data[1], TOPAZ_BYTES_PER_SECTOR); + data_read += TOPAZ_BYTES_PER_SECTOR; + } + } + + read_success = true; + } while(false); + + return read_success; +} diff --git a/lib/nfc/protocols/topaz.h b/lib/nfc/protocols/topaz.h new file mode 100644 index 000000000000..7a074b7d9254 --- /dev/null +++ b/lib/nfc/protocols/topaz.h @@ -0,0 +1,42 @@ +#pragma once + +#include + +#define TOPAZ_MAX_SIZE (512) +#define TOPAZ_UID_FULL_LENGTH (7) +#define TOPAZ_BYTES_PER_BLOCK (8) +#define TOPAZ_BYTES_PER_SECTOR (TOPAZ_BYTES_PER_BLOCK * 16) +#define TOPAZ_BYTES_RALL (120) + +#define TOPAZ_CMD_RID (0x78) +#define TOPAZ_CMD_RALL (0x00) +#define TOPAZ_CMD_READ (0x01) +#define TOPAZ_CMD_WRITE_E (0x53) +#define TOPAZ_CMD_WRITE_NE (0x1A) +#define TOPAZ_CMD_RSEG (0x10) +#define TOPAZ_CMD_READ8 (0x02) +#define TOPAZ_CMD_WRITE_E8 (0x54) +#define TOPAZ_CMD_WRITE_NE8 (0x1B) + +#define TOPAZ_96_HR0 (0x11) +#define TOPAZ_96_SIZE (120) +#define TOPAZ_512_HR0 (0x12) +#define TOPAZ_512_SIZE (512) + +typedef enum { + TopazTypeUnknown, + TopazType96, + TopazType512, +} TopazType; + +typedef struct { + TopazType type; + size_t size; + uint8_t hr[2]; + uint8_t data[TOPAZ_MAX_SIZE]; +} TopazData; + +bool topaz_check_card_type(uint8_t ATQA0, uint8_t ATQA1); +TopazType topaz_get_type_from_hr0(uint8_t hr0); +size_t topaz_get_size_by_type(TopazType type); +bool topaz_read_card(FuriHalNfcTxRxContext* tx_rx, TopazData* data, uint8_t* uid);