From bcd33ca12589837a210225ecdc26bb98d357c860 Mon Sep 17 00:00:00 2001 From: g3gg0 Date: Mon, 14 Nov 2022 21:12:18 +0100 Subject: [PATCH] added NfcV emulation --- applications/main/nfc/nfc_cli.c | 224 +++++++ .../main/nfc/scenes/nfc_scene_config.h | 1 + .../main/nfc/scenes/nfc_scene_emulate_nfcv.c | 141 +++++ .../main/nfc/scenes/nfc_scene_nfc_data_info.c | 10 +- .../main/nfc/scenes/nfc_scene_nfcv_menu.c | 13 +- .../main/nfc/scenes/nfc_scene_nfcv_unlock.c | 3 +- firmware/targets/f7/api_symbols.csv | 7 +- firmware/targets/f7/furi_hal/furi_hal_nfc.c | 33 + .../targets/furi_hal_include/furi_hal_nfc.h | 6 + furi/core/common_defines.h | 37 +- lib/digital_signal/digital_signal.c | 11 +- lib/nfc/nfc_device.h | 1 + lib/nfc/nfc_worker.c | 37 +- lib/nfc/nfc_worker.h | 3 +- lib/nfc/protocols/nfcv.c | 587 +++++++++++++++++- lib/nfc/protocols/nfcv.h | 69 ++ lib/nfc/protocols/slix_l.c | 14 - lib/nfc/protocols/slix_l.h | 1 - 18 files changed, 1150 insertions(+), 48 deletions(-) create mode 100644 applications/main/nfc/scenes/nfc_scene_emulate_nfcv.c diff --git a/applications/main/nfc/nfc_cli.c b/applications/main/nfc/nfc_cli.c index a6475ca68..cc2770731 100644 --- a/applications/main/nfc/nfc_cli.c +++ b/applications/main/nfc/nfc_cli.c @@ -1,11 +1,20 @@ #include #include +#include #include #include +#include +#include +#include +#include +#include +#include #include #include + + static void nfc_cli_print_usage() { printf("Usage:\r\n"); printf("nfc \r\n"); @@ -14,6 +23,10 @@ static void nfc_cli_print_usage() { printf("\temulate\t - emulate predefined nfca card\r\n"); if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { printf("\tfield\t - turn field on\r\n"); + printf("\tst25r_read\t - read ST25R3916 register\r\n"); + printf("\tst25r_write\t - write ST25R3916 register\r\n"); + printf("\tst25r_cmd\t - execute ST25R3916 command\r\n"); + printf("\tst25r_fifo\t - wait for FIFO data\r\n"); } } @@ -98,6 +111,197 @@ static void nfc_cli_field(Cli* cli, FuriString* args) { furi_hal_nfc_sleep(); } +static uint8_t hexdigit(char hex) { + return (hex <= '9') ? hex - '0' : toupper(hex) - 'A' + 10 ; +} + +static uint8_t hexbyte(const char* hex) { + return (hexdigit(*hex) << 4) | hexdigit(*(hex+1)); +} + +static void nfc_cli_st25r_write(Cli* cli, FuriString* args) { + UNUSED(cli); + FuriString* reg = furi_string_alloc(); + FuriString* value = furi_string_alloc(); + + // Check if nfc worker is not busy + if(furi_hal_nfc_is_busy()) { + printf("Nfc is busy\r\n"); + return; + } + + if(!args_read_string_and_trim(args, reg) || !args_read_string_and_trim(args, value)) { + printf("You didn't specify and \r\n"); + nfc_cli_print_usage(); + return; + } + + if(furi_string_utf8_length(reg) != 2 || furi_string_utf8_length(value) != 2) { + printf("You didn't specify and as 2-character hex values\r\n"); + nfc_cli_print_usage(); + return; + } + + uint8_t reg_val = hexbyte(furi_string_get_cstr(reg)); + uint8_t value_val = hexbyte(furi_string_get_cstr(value)); + + printf("W %02X <- %02X\r\n", reg_val, value_val); + if(st25r3916WriteRegister(reg_val, value_val) != ERR_NONE) { + printf("Failed to write register\r\n"); + return; + } +} + +static void nfc_cli_st25r_read(Cli* cli, FuriString* args) { + UNUSED(cli); + FuriString* reg = furi_string_alloc(); + + // Check if nfc worker is not busy + if(furi_hal_nfc_is_busy()) { + printf("Nfc is busy\r\n"); + return; + } + + if(!args_read_string_and_trim(args, reg)) { + printf("You didn't specify \r\n"); + nfc_cli_print_usage(); + return; + } + + if(furi_string_utf8_length(reg) != 2) { + printf("You didn't specify as 2-character hex value\r\n"); + nfc_cli_print_usage(); + return; + } + + uint8_t reg_val = hexbyte(furi_string_get_cstr(reg)); + uint8_t value_val = 0; + + if(st25r3916ReadRegister(reg_val, &value_val) != ERR_NONE) { + printf("Failed to read register\r\n"); + return; + } + + printf("R %02X -> %02X\r\n", reg_val, value_val); +} + +static void nfc_cli_st25r_cmd(Cli* cli, FuriString* args) { + UNUSED(cli); + FuriString* cmd = furi_string_alloc(); + + // Check if nfc worker is not busy + if(furi_hal_nfc_is_busy()) { + printf("Nfc is busy\r\n"); + return; + } + + if(!args_read_string_and_trim(args, cmd)) { + printf("You didn't specify \r\n"); + nfc_cli_print_usage(); + return; + } + + if(furi_string_utf8_length(cmd) != 2) { + printf("You didn't specify as 2-character hex value\r\n"); + nfc_cli_print_usage(); + return; + } + + uint8_t cmd_val = hexbyte(furi_string_get_cstr(cmd)); + + if(st25r3916ExecuteCommand(cmd_val) != ERR_NONE) { + printf("Failed to execute\r\n"); + return; + } + + printf("X %02X\r\n", cmd_val); +} + +static FuriHalNfcTxRxContext tx_rx = {}; + +static void nfc_cli_st25r_fifo(Cli* cli, FuriString* args) { + UNUSED(args); + + // Check if nfc worker is not busy + if(furi_hal_nfc_is_busy()) { + printf("Nfc is busy\r\n"); + return; + } + + bool field = false; + + tx_rx.sniff_tx = NULL; + tx_rx.sniff_rx = NULL; + tx_rx.tx_bits = 0; + tx_rx.tx_rx_type = FuriHalNfcTxRxTypeRxRaw; + + rfal_platform_spi_acquire(); + + furi_hal_nfcv_listen_start(); + + printf("Reading FIFO...\r\nPress Ctrl+C to abort\r\n"); + while(!cli_cmd_interrupt_received(cli)) { + + if(st25r3916IsExtFieldOn() && !field) { + printf("Field entered\r\n"); + field = true; + } + if(!st25r3916IsExtFieldOn() && field) { + printf("Field left\r\n"); + field = false; + } + + if(furi_hal_nfc_listen_rx(&tx_rx, 50)) { + for(int pos = 0; pos < tx_rx.rx_bits / 8; pos++) { + printf(" %02X", tx_rx.rx_data[pos]); + } + printf("\r\n"); + } + + furi_delay_ms(50); + } + rfal_platform_spi_release(); +} + + +static void nfc_cli_st25r_trans(Cli* cli, FuriString* args) { + UNUSED(args); + + // Check if nfc worker is not busy + if(furi_hal_nfc_is_busy()) { + printf("Nfc is busy\r\n"); + return; + } + + printf("ISO15693 emulator...\r\nPress Ctrl+C to abort\r\n"); + + FuriHalNfcDevData nfc_data = { + .uid = { 0x36, 0x78, 0x45, 0x0E, 0x50, 0x03, 0x04, 0xE0 }, + .uid_len = 8, + .type = FuriHalNfcTypeV, + }; + NfcVData nfcv_data = { + .afi = 0, + .dsfid = 0, + .block_num = 8, + .block_size = 4, + .ic_ref = 3, + .key_privacy = { 0x0F, 0x0F, 0x0F, 0x0F }, + .privacy = false + }; + + memset(nfcv_data.data, 0xAE, 4 * 8); + + nfcv_emu_init(); + while(!cli_cmd_interrupt_received(cli)) { + if(nfcv_emu_loop(&nfc_data, &nfcv_data, 1000)) { + printf("[NfcV-Emu] %s\r\n", nfcv_data.last_command); + } + } + + nfcv_emu_deinit(); +} + static void nfc_cli(Cli* cli, FuriString* args, void* context) { UNUSED(context); FuriString* cmd; @@ -122,6 +326,26 @@ static void nfc_cli(Cli* cli, FuriString* args, void* context) { nfc_cli_field(cli, args); break; } + if(furi_string_cmp_str(cmd, "st25r_read") == 0) { + nfc_cli_st25r_read(cli, args); + break; + } + if(furi_string_cmp_str(cmd, "st25r_write") == 0) { + nfc_cli_st25r_write(cli, args); + break; + } + if(furi_string_cmp_str(cmd, "st25r_cmd") == 0) { + nfc_cli_st25r_cmd(cli, args); + break; + } + if(furi_string_cmp_str(cmd, "st25r_fifo") == 0) { + nfc_cli_st25r_fifo(cli, args); + break; + } + if(furi_string_cmp_str(cmd, "st25r_trans") == 0) { + nfc_cli_st25r_trans(cli, args); + break; + } } nfc_cli_print_usage(); diff --git a/applications/main/nfc/scenes/nfc_scene_config.h b/applications/main/nfc/scenes/nfc_scene_config.h index 1653a7edd..33f37f3e3 100644 --- a/applications/main/nfc/scenes/nfc_scene_config.h +++ b/applications/main/nfc/scenes/nfc_scene_config.h @@ -12,6 +12,7 @@ ADD_SCENE(nfc, save_name, SaveName) ADD_SCENE(nfc, save_success, SaveSuccess) ADD_SCENE(nfc, file_select, FileSelect) ADD_SCENE(nfc, emulate_uid, EmulateUid) +ADD_SCENE(nfc, emulate_nfcv, EmulateNfcV) ADD_SCENE(nfc, nfca_read_success, NfcaReadSuccess) ADD_SCENE(nfc, nfca_menu, NfcaMenu) ADD_SCENE(nfc, nfcv_menu, NfcVMenu) diff --git a/applications/main/nfc/scenes/nfc_scene_emulate_nfcv.c b/applications/main/nfc/scenes/nfc_scene_emulate_nfcv.c new file mode 100644 index 000000000..70f0c8ba0 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_emulate_nfcv.c @@ -0,0 +1,141 @@ +#include "../nfc_i.h" + +#define NFC_SCENE_EMULATE_NFCV_LOG_SIZE_MAX (200) + +enum { + NfcSceneEmulateNfcVStateWidget, + NfcSceneEmulateNfcVStateTextBox, +}; + +bool nfc_emulate_nfcv_worker_callback(NfcWorkerEvent event, void* context) { + UNUSED(event); + furi_assert(context); + Nfc* nfc = context; + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventWorkerExit); + return true; +} + +void nfc_scene_emulate_nfcv_widget_callback(GuiButtonType result, InputType type, void* context) { + furi_assert(context); + Nfc* nfc = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(nfc->view_dispatcher, result); + } +} + +void nfc_emulate_nfcv_textbox_callback(void* context) { + furi_assert(context); + Nfc* nfc = context; + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); +} + +// Add widget with device name or inform that data received +static void nfc_scene_emulate_nfcv_widget_config(Nfc* nfc, bool data_received) { + FuriHalNfcDevData* data = &nfc->dev->dev_data.nfc_data; + Widget* widget = nfc->widget; + widget_reset(widget); + FuriString* info_str; + info_str = furi_string_alloc(); + + widget_add_icon_element(widget, 0, 3, &I_RFIDDolphinSend_97x61); + widget_add_string_element(widget, 89, 32, AlignCenter, AlignTop, FontPrimary, "Emulating NfcV"); + if(strcmp(nfc->dev->dev_name, "")) { + furi_string_printf(info_str, "%s", nfc->dev->dev_name); + } else { + for(uint8_t i = 0; i < data->uid_len; i++) { + furi_string_cat_printf(info_str, "%02X ", data->uid[i]); + } + } + furi_string_trim(info_str); + widget_add_text_box_element( + widget, 56, 43, 70, 21, AlignCenter, AlignTop, furi_string_get_cstr(info_str), true); + furi_string_free(info_str); + if(data_received) { + widget_add_button_element( + widget, GuiButtonTypeCenter, "Log", nfc_scene_emulate_nfcv_widget_callback, nfc); + } +} + +void nfc_scene_emulate_nfcv_on_enter(void* context) { + Nfc* nfc = context; + + // Setup Widget + nfc_scene_emulate_nfcv_widget_config(nfc, false); + // Setup TextBox + TextBox* text_box = nfc->text_box; + text_box_set_font(text_box, TextBoxFontHex); + text_box_set_focus(text_box, TextBoxFocusEnd); + furi_string_reset(nfc->text_box_store); + + // Set Widget state and view + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneEmulateNfcV, NfcSceneEmulateNfcVStateWidget); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); + // Start worker + memset(&nfc->dev->dev_data.reader_data, 0, sizeof(NfcReaderRequestData)); + nfc_worker_start( + nfc->worker, + NfcWorkerStateNfcVEmulate, + &nfc->dev->dev_data, + nfc_emulate_nfcv_worker_callback, + nfc); + + nfc_blink_emulate_start(nfc); +} + +bool nfc_scene_emulate_nfcv_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + NfcVData* nfcv_data = &nfc->dev->dev_data.nfcv_data; + uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneEmulateNfcV); + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventWorkerExit) { + // Add data button to widget if data is received for the first time + if(!furi_string_size(nfc->text_box_store)) { + nfc_scene_emulate_nfcv_widget_config(nfc, true); + } + // Update TextBox data + if(furi_string_size(nfc->text_box_store) < NFC_SCENE_EMULATE_NFCV_LOG_SIZE_MAX) { + furi_string_cat_printf(nfc->text_box_store, "%s", nfcv_data->last_command); + furi_string_push_back(nfc->text_box_store, '\n'); + text_box_set_text(nfc->text_box, furi_string_get_cstr(nfc->text_box_store)); + strcpy(nfcv_data->last_command, ""); + } + consumed = true; + } else if(event.event == GuiButtonTypeCenter && state == NfcSceneEmulateNfcVStateWidget) { + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox); + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneEmulateNfcV, NfcSceneEmulateNfcVStateTextBox); + consumed = true; + } else if(event.event == NfcCustomEventViewExit && state == NfcSceneEmulateNfcVStateTextBox) { + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneEmulateNfcV, NfcSceneEmulateNfcVStateWidget); + consumed = true; + } + } else if(event.type == SceneManagerEventTypeBack) { + if(state == NfcSceneEmulateNfcVStateTextBox) { + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneEmulateNfcV, NfcSceneEmulateNfcVStateWidget); + consumed = true; + } + } + + return consumed; +} + +void nfc_scene_emulate_nfcv_on_exit(void* context) { + Nfc* nfc = context; + + // Stop worker + nfc_worker_stop(nfc->worker); + + // Clear view + widget_reset(nfc->widget); + text_box_reset(nfc->text_box); + furi_string_reset(nfc->text_box_store); + + nfc_blink_stop(nfc); +} 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 38289e041..98d11f2dc 100644 --- a/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c +++ b/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c @@ -14,7 +14,8 @@ void nfc_scene_nfc_data_info_on_enter(void* context) { NfcDeviceData* dev_data = &nfc->dev->dev_data; NfcProtocol protocol = dev_data->protocol; uint8_t text_scroll_height = 0; - if((protocol == NfcDeviceProtocolMifareDesfire) || (protocol == NfcDeviceProtocolMifareUl)|| (protocol == NfcDeviceProtocolSlixL)) { + if((protocol == NfcDeviceProtocolMifareDesfire) || (protocol == NfcDeviceProtocolMifareUl) + || (protocol == NfcDeviceProtocolSlixL) || (protocol == NfcDeviceProtocolNfcV)) { widget_add_button_element( widget, GuiButtonTypeRight, "More", nfc_scene_nfc_data_info_widget_callback, nfc); text_scroll_height = 52; @@ -40,6 +41,8 @@ void nfc_scene_nfc_data_info_on_enter(void* context) { temp_str, "\e#%s\n", nfc_mf_classic_type(dev_data->mf_classic_data.type)); } else if(protocol == NfcDeviceProtocolMifareDesfire) { furi_string_cat_printf(temp_str, "\e#MIFARE DESfire\n"); + } else if(protocol == NfcDeviceProtocolNfcV) { + furi_string_cat_printf(temp_str, "\e#ISO15693\n"); } else if(protocol == NfcDeviceProtocolSlixL) { furi_string_cat_printf(temp_str, "\e#ISO15693 SLIX-L\n"); } else { @@ -47,7 +50,7 @@ void nfc_scene_nfc_data_info_on_enter(void* context) { } // Set tag iso data - if(protocol == NfcDeviceProtocolSlixL) { + if((protocol == NfcDeviceProtocolSlixL) || (protocol == NfcDeviceProtocolNfcV)) { NfcVData* nfcv_data = &nfc->dev->dev_data.nfcv_data; furi_string_cat_printf(temp_str, "UID:\n"); @@ -151,6 +154,9 @@ bool nfc_scene_nfc_data_info_on_event(void* context, SceneManagerEvent event) { } else if(protocol == NfcDeviceProtocolSlixL) { scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVMenu); consumed = true; + } else if(protocol == NfcDeviceProtocolNfcV) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVMenu); + consumed = true; } } } diff --git a/applications/main/nfc/scenes/nfc_scene_nfcv_menu.c b/applications/main/nfc/scenes/nfc_scene_nfcv_menu.c index 5605c6c07..fdf7de9ff 100644 --- a/applications/main/nfc/scenes/nfc_scene_nfcv_menu.c +++ b/applications/main/nfc/scenes/nfc_scene_nfcv_menu.c @@ -3,6 +3,7 @@ enum SubmenuIndex { SubmenuIndexSave, + SubmenuIndexEmulate, }; void nfc_scene_nfcv_menu_submenu_callback(void* context, uint32_t index) { @@ -17,6 +18,8 @@ void nfc_scene_nfcv_menu_on_enter(void* context) { submenu_add_item( submenu, "Save", SubmenuIndexSave, nfc_scene_nfcv_menu_submenu_callback, nfc); + submenu_add_item( + submenu, "Emulate", SubmenuIndexEmulate, nfc_scene_nfcv_menu_submenu_callback, nfc); submenu_set_selected_item( nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneNfcVMenu)); @@ -35,7 +38,15 @@ bool nfc_scene_nfcv_menu_on_event(void* context, SceneManagerEvent event) { nfc_device_set_name(nfc->dev, ""); scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); consumed = true; - } + } else if(event.event == SubmenuIndexEmulate) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateNfcV); + if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) { + DOLPHIN_DEED(DolphinDeedNfcAddEmulate); + } else { + DOLPHIN_DEED(DolphinDeedNfcEmulate); + } + consumed = true; + } scene_manager_set_scene_state(nfc->scene_manager, NfcSceneNfcVMenu, event.event); } else if(event.type == SceneManagerEventTypeBack) { diff --git a/applications/main/nfc/scenes/nfc_scene_nfcv_unlock.c b/applications/main/nfc/scenes/nfc_scene_nfcv_unlock.c index 4b15c777c..b9050e0fc 100644 --- a/applications/main/nfc/scenes/nfc_scene_nfcv_unlock.c +++ b/applications/main/nfc/scenes/nfc_scene_nfcv_unlock.c @@ -96,7 +96,8 @@ void nfc_scene_nfcv_unlock_set_state(Nfc* nfc, NfcSceneNfcVUnlockState state) { popup_set_header(popup, "Successfully\nunlocked", 94, 3, AlignCenter, AlignTop); } - notification_message(nfc->notifications, &sequence_success); + notification_message(nfc->notifications, &sequence_single_vibro); + //notification_message(nfc->notifications, &sequence_success); popup_set_icon(popup, 0, 6, &I_RFIDDolphinSuccess_108x57); popup_set_context(popup, nfc); diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 306e2e559..2aaf7c8dc 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,+,7.5,, +Version,+,7.12,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -1181,6 +1181,7 @@ Function,+,furi_hal_nfc_stop,void, Function,+,furi_hal_nfc_stop_cmd,void, Function,+,furi_hal_nfc_tx_rx,_Bool,"FuriHalNfcTxRxContext*, uint16_t" Function,+,furi_hal_nfc_tx_rx_full,_Bool,FuriHalNfcTxRxContext* +Function,-,furi_hal_nfcv_listen_start,void, Function,-,furi_hal_os_init,void, Function,+,furi_hal_os_tick,void, Function,+,furi_hal_power_check_otg_status,void, @@ -1958,8 +1959,12 @@ Function,-,nfca_get_crc16,uint16_t,"uint8_t*, uint16_t" Function,-,nfca_signal_alloc,NfcaSignal*, Function,-,nfca_signal_encode,void,"NfcaSignal*, uint8_t*, uint16_t, uint8_t*" Function,-,nfca_signal_free,void,NfcaSignal* +Function,-,nfcv_emu_deinit,void, +Function,-,nfcv_emu_init,void, +Function,-,nfcv_emu_loop,_Bool,"FuriHalNfcDevData*, NfcVData*, uint32_t" Function,-,nfcv_inventory,ReturnCode,uint8_t* Function,-,nfcv_read_blocks,ReturnCode,"NfcVReader*, NfcVData*" +Function,-,nfcv_read_card,_Bool,"NfcVReader*, FuriHalNfcDevData*, NfcVData*" Function,-,nfcv_read_sysinfo,ReturnCode,"FuriHalNfcDevData*, NfcVData*" Function,+,notification_internal_message,void,"NotificationApp*, const NotificationSequence*" Function,+,notification_internal_message_block,void,"NotificationApp*, const NotificationSequence*" diff --git a/firmware/targets/f7/furi_hal/furi_hal_nfc.c b/firmware/targets/f7/furi_hal/furi_hal_nfc.c index 2d27313ae..b95d490af 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_nfc.c +++ b/firmware/targets/f7/furi_hal/furi_hal_nfc.c @@ -367,6 +367,39 @@ void furi_hal_nfc_listen_start(FuriHalNfcDevData* nfc_data) { st25r3916ExecuteCommand(ST25R3916_CMD_GOTO_SENSE); } +void furi_hal_nfcv_listen_start() { + + furi_hal_gpio_init(&gpio_nfc_irq_rfid_pull, GpioModeInput, GpioPullDown, GpioSpeedVeryHigh); + // Clear interrupts + st25r3916ClearInterrupts(); + // Mask all interrupts + st25r3916DisableInterrupts(ST25R3916_IRQ_MASK_ALL); + // RESET + st25r3916ExecuteCommand(ST25R3916_CMD_STOP); + // Setup registers + st25r3916WriteRegister( + ST25R3916_REG_OP_CONTROL, + ST25R3916_REG_OP_CONTROL_en | ST25R3916_REG_OP_CONTROL_rx_en | + ST25R3916_REG_OP_CONTROL_en_fd_auto_efd); + st25r3916WriteRegister( + ST25R3916_REG_MODE, + ST25R3916_REG_MODE_targ_targ | ST25R3916_REG_MODE_om3 | ST25R3916_REG_MODE_om0); + st25r3916WriteRegister( + ST25R3916_REG_PASSIVE_TARGET, + ST25R3916_REG_PASSIVE_TARGET_fdel_2 | ST25R3916_REG_PASSIVE_TARGET_fdel_0 | + ST25R3916_REG_PASSIVE_TARGET_d_ac_ap2p | ST25R3916_REG_PASSIVE_TARGET_d_212_424_1r); + st25r3916WriteRegister(ST25R3916_REG_MASK_RX_TIMER, 0x02); + + // Mask interrupts + uint32_t clear_irq_mask = + (ST25R3916_IRQ_MASK_RXE | ST25R3916_IRQ_MASK_RXE_PTA | ST25R3916_IRQ_MASK_WU_A_X | + ST25R3916_IRQ_MASK_WU_A); + st25r3916EnableInterrupts(clear_irq_mask); + + // Go to sense + st25r3916ExecuteCommand(ST25R3916_CMD_GOTO_SENSE); +} + void rfal_interrupt_callback_handler() { furi_event_flag_set(event, EVENT_FLAG_INTERRUPT); } diff --git a/firmware/targets/furi_hal_include/furi_hal_nfc.h b/firmware/targets/furi_hal_include/furi_hal_nfc.h index d3f6de602..1363a1572 100644 --- a/firmware/targets/furi_hal_include/furi_hal_nfc.h +++ b/firmware/targets/furi_hal_include/furi_hal_nfc.h @@ -177,6 +177,12 @@ bool furi_hal_nfc_listen( */ void furi_hal_nfc_listen_start(FuriHalNfcDevData* nfc_data); +/** Start Target Listen mode + * @note RFAL free implementation + * + */ +void furi_hal_nfcv_listen_start(); + /** Read data in Target Listen mode * @note Must be called only after furi_hal_nfc_listen_start() * diff --git a/furi/core/common_defines.h b/furi/core/common_defines.h index 31be7fff0..c5ffbf454 100644 --- a/furi/core/common_defines.h +++ b/furi/core/common_defines.h @@ -23,18 +23,33 @@ extern "C" { #define FURI_IS_ISR() (FURI_IS_IRQ_MODE() || FURI_IS_IRQ_MASKED()) #endif +#ifndef FURI_CRITICAL_DEFINE +#define FURI_CRITICAL_DEFINE() \ + uint32_t __isrm = 0; \ + bool __from_isr = false; \ + bool __kernel_running = false; +#endif + +#ifndef FURI_CRITICAL_ENTER_ADV +#define FURI_CRITICAL_ENTER_ADV() \ + do { \ + __isrm = 0; \ + __from_isr = FURI_IS_ISR(); \ + __kernel_running = (xTaskGetSchedulerState() == taskSCHEDULER_RUNNING); \ + if(__from_isr) { \ + __isrm = taskENTER_CRITICAL_FROM_ISR(); \ + } else if(__kernel_running) { \ + taskENTER_CRITICAL(); \ + } else { \ + __disable_irq(); \ + } \ + } while(0) +#endif + #ifndef FURI_CRITICAL_ENTER -#define FURI_CRITICAL_ENTER() \ - uint32_t __isrm = 0; \ - bool __from_isr = FURI_IS_ISR(); \ - bool __kernel_running = (xTaskGetSchedulerState() == taskSCHEDULER_RUNNING); \ - if(__from_isr) { \ - __isrm = taskENTER_CRITICAL_FROM_ISR(); \ - } else if(__kernel_running) { \ - taskENTER_CRITICAL(); \ - } else { \ - __disable_irq(); \ - } +#define FURI_CRITICAL_ENTER() \ + FURI_CRITICAL_DEFINE(); \ + FURI_CRITICAL_ENTER_ADV(); #endif #ifndef FURI_CRITICAL_EXIT diff --git a/lib/digital_signal/digital_signal.c b/lib/digital_signal/digital_signal.c index 46ca307a7..c855f188b 100644 --- a/lib/digital_signal/digital_signal.c +++ b/lib/digital_signal/digital_signal.c @@ -16,7 +16,7 @@ DigitalSignal* digital_signal_alloc(uint32_t max_edges_cnt) { signal->start_level = true; signal->edges_max_cnt = max_edges_cnt; signal->edge_timings = malloc(max_edges_cnt * sizeof(uint32_t)); - signal->reload_reg_buff = malloc(max_edges_cnt * sizeof(uint32_t)); + signal->reload_reg_buff = malloc(signal->edges_max_cnt * sizeof(uint32_t)); signal->edge_cnt = 0; return signal; @@ -37,7 +37,10 @@ bool digital_signal_append(DigitalSignal* signal_a, DigitalSignal* signal_b) { if(signal_a->edges_max_cnt < signal_a->edge_cnt + signal_b->edge_cnt) { return false; } - + /* in case there are no edges in our target signal, the signal to append makes the rules */ + if(!signal_a->edge_cnt) { + signal_a->start_level = signal_b->start_level; + } bool end_level = signal_a->start_level; if(signal_a->edge_cnt) { end_level = signal_a->start_level ^ !(signal_a->edge_cnt % 2); @@ -84,6 +87,7 @@ void digital_signal_prepare_arr(DigitalSignal* signal) { uint32_t r_count_tick_arr = 0; uint32_t r_rest_div = 0; + for(size_t i = 0; i < signal->edge_cnt - 1; i++) { r_count_tick_arr = t_signal_rest / T_TIM; r_rest_div = t_signal_rest % T_TIM; @@ -162,7 +166,8 @@ void digital_signal_send(DigitalSignal* signal, const GpioPin* gpio) { LL_TIM_EnableCounter(TIM2); while(!LL_DMA_IsActiveFlag_TC2(DMA1)) - ; + { + } LL_DMA_ClearFlag_TC1(DMA1); LL_DMA_ClearFlag_TC2(DMA1); diff --git a/lib/nfc/nfc_device.h b/lib/nfc/nfc_device.h index eab3137b3..d0299fea2 100644 --- a/lib/nfc/nfc_device.h +++ b/lib/nfc/nfc_device.h @@ -33,6 +33,7 @@ typedef enum { NfcDeviceProtocolMifareUl, NfcDeviceProtocolMifareClassic, NfcDeviceProtocolMifareDesfire, + NfcDeviceProtocolNfcV, NfcDeviceProtocolSlixL, } NfcProtocol; diff --git a/lib/nfc/nfc_worker.c b/lib/nfc/nfc_worker.c index a36ee4da3..98f88a0ab 100644 --- a/lib/nfc/nfc_worker.c +++ b/lib/nfc/nfc_worker.c @@ -93,6 +93,8 @@ int32_t nfc_worker_task(void* context) { nfc_worker_read(nfc_worker); } else if(nfc_worker->state == NfcWorkerStateUidEmulate) { nfc_worker_emulate_uid(nfc_worker); + } else if(nfc_worker->state == NfcWorkerStateNfcVEmulate) { + nfc_worker_emulate_nfcv(nfc_worker); } else if(nfc_worker->state == NfcWorkerStateEmulateApdu) { nfc_worker_emulate_apdu(nfc_worker); } else if(nfc_worker->state == NfcWorkerStateMfUltralightEmulate) { @@ -135,6 +137,7 @@ void nfc_worker_nfcv_unlock(NfcWorker* nfc_worker) { furi_hal_nfc_sleep(); + while((nfc_worker->state == NfcWorkerStateNfcVUnlock) || (nfc_worker->state == NfcWorkerStateNfcVUnlockAndSave)) { @@ -201,7 +204,7 @@ void nfc_worker_nfcv_unlock(NfcWorker* nfc_worker) { if(nfc_worker->state == NfcWorkerStateNfcVUnlockAndSave) { NfcVReader reader; - if(!slix_l_read_card(&reader, &nfc_worker->dev_data->nfc_data, nfcv_data)) { + if(!nfcv_read_card(&reader, &nfc_worker->dev_data->nfc_data, nfcv_data)) { furi_hal_console_printf(" => failed, wait for chip to disappear.\r\n"); snprintf(nfcv_data->error, sizeof(nfcv_data->error), "Read card\nfailed"); nfc_worker->callback(NfcWorkerEventWrongCardDetected, nfc_worker->context); @@ -249,7 +252,7 @@ void nfc_worker_nfcv_unlock(NfcWorker* nfc_worker) { } } -static bool nfc_worker_read_slix_l(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { +static bool nfc_worker_read_nfcv_content(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { bool read_success = false; NfcVReader reader = {}; @@ -260,7 +263,7 @@ static bool nfc_worker_read_slix_l(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* do { if(!furi_hal_nfc_detect(&nfc_worker->dev_data->nfc_data, 200)) break; - if(!slix_l_read_card(&reader, &nfc_worker->dev_data->nfc_data, &nfc_worker->dev_data->nfcv_data)) break; + if(!nfcv_read_card(&reader, &nfc_worker->dev_data->nfc_data, &nfc_worker->dev_data->nfcv_data)) break; read_success = true; } while(false); @@ -271,6 +274,7 @@ static bool nfc_worker_read_slix_l(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* return read_success; } + static bool nfc_worker_read_mf_ultralight(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { bool read_success = false; MfUltralightReader reader = {}; @@ -495,11 +499,11 @@ static bool nfc_worker_read_nfcv(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* t if(slix_l_check_card_type(nfc_data->uid[7], nfc_data->uid[6], nfc_data->uid[5])) { FURI_LOG_I(TAG, "NXP SLIX-L detected"); nfc_worker->dev_data->protocol = NfcDeviceProtocolSlixL; - card_read = nfc_worker_read_slix_l(nfc_worker, tx_rx); + card_read = nfc_worker_read_nfcv_content(nfc_worker, tx_rx); } else { FURI_LOG_I(TAG, "unknown detected"); - nfc_worker->dev_data->protocol = NfcDeviceProtocolUnknown; - card_read = true; + nfc_worker->dev_data->protocol = NfcDeviceProtocolNfcV; + card_read = nfc_worker_read_nfcv_content(nfc_worker, tx_rx); } return card_read; @@ -555,10 +559,8 @@ void nfc_worker_read(NfcWorker* nfc_worker) { FURI_LOG_I(TAG, "NfcV detected"); if(nfc_worker_read_nfcv(nfc_worker, &tx_rx)) { FURI_LOG_I(TAG, "nfc_worker_read_nfcv success"); - if(dev_data->protocol == NfcDeviceProtocolSlixL) { - event = NfcWorkerEventReadNfcV; - break; - } + event = NfcWorkerEventReadNfcV; + break; } event = NfcWorkerEventReadUidNfcV; break; @@ -603,6 +605,21 @@ void nfc_worker_emulate_uid(NfcWorker* nfc_worker) { } } +void nfc_worker_emulate_nfcv(NfcWorker* nfc_worker) { + FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; + NfcVData* nfcv_data = &nfc_worker->dev_data->nfcv_data; + + nfcv_emu_init(); + while(nfc_worker->state == NfcWorkerStateNfcVEmulate) { + if(nfcv_emu_loop(nfc_data, nfcv_data, 1000)) { + if(nfc_worker->callback) { + nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context); + } + } + } + nfcv_emu_deinit(); +} + void nfc_worker_emulate_apdu(NfcWorker* nfc_worker) { FuriHalNfcTxRxContext tx_rx = {}; FuriHalNfcDevData params = { diff --git a/lib/nfc/nfc_worker.h b/lib/nfc/nfc_worker.h index 2910cb240..6e8ef854d 100644 --- a/lib/nfc/nfc_worker.h +++ b/lib/nfc/nfc_worker.h @@ -19,6 +19,7 @@ typedef enum { NfcWorkerStateReadMfUltralightReadAuth, NfcWorkerStateMfClassicDictAttack, NfcWorkerStateAnalyzeReader, + NfcWorkerStateNfcVEmulate, NfcWorkerStateNfcVUnlock, NfcWorkerStateNfcVUnlockAndSave, // Debug @@ -91,4 +92,4 @@ void nfc_worker_start( void nfc_worker_stop(NfcWorker* nfc_worker); void nfc_worker_nfcv_unlock(NfcWorker* nfc_worker); - +void nfc_worker_emulate_nfcv(NfcWorker* nfc_worker); diff --git a/lib/nfc/protocols/nfcv.c b/lib/nfc/protocols/nfcv.c index b8933d438..11604afef 100644 --- a/lib/nfc/protocols/nfcv.c +++ b/lib/nfc/protocols/nfcv.c @@ -1,8 +1,16 @@ #include +#include +#include +#include +#include +#include +#include +#include +#include +#include + #include "nfcv.h" #include "nfc_util.h" -#include -#include "furi_hal_nfc.h" #define TAG "NfcV" @@ -82,4 +90,577 @@ ReturnCode nfcv_read_sysinfo(FuriHalNfcDevData* nfc_data, NfcVData* data) { FURI_LOG_D(TAG, "Failed: %d", ret); return ret; -} \ No newline at end of file +} + +bool nfcv_read_card( + NfcVReader* reader, + FuriHalNfcDevData* nfc_data, + NfcVData* nfcv_data) { + furi_assert(reader); + furi_assert(nfcv_data); + + if(nfcv_read_sysinfo(nfc_data, nfcv_data) != ERR_NONE) { + return false; + } + + reader->blocks_to_read = nfcv_data->block_num; + return (nfcv_read_blocks(reader, nfcv_data) == ERR_NONE); +} + +/* emulation part */ + +static const uint32_t clocks_in_ms = 64 * 1000; +static const uint32_t bit_time = 64 * 9.44f; + +DigitalSignal* nfcv_resp_pulse_32 = NULL; +DigitalSignal* nfcv_resp_unmod = NULL; +DigitalSignal* nfcv_resp_one = NULL; +DigitalSignal* nfcv_resp_zero = NULL; +DigitalSignal* nfcv_resp_sof = NULL; +DigitalSignal* nfcv_resp_eof = NULL; +DigitalSignal* nfcv_resp_unmod_256 = NULL; +DigitalSignal* nfcv_resp_unmod_768 = NULL; + +DigitalSignal* nfcv_signal = NULL; + + +void nfcv_crc(uint8_t* data, uint32_t length, uint8_t* out) { + uint32_t reg = 0xFFFF; + uint32_t i = 0; + uint32_t j = 0; + + for (i = 0; i < length; i++) { + reg = reg ^ ((uint32_t)data[i]); + for (j = 0; j < 8; j++) { + if (reg & 0x0001) { + reg = (reg >> 1) ^ 0x8408; + } else { + reg = (reg >> 1); + } + } + } + + uint16_t crc = ~(uint16_t)(reg & 0xffff); + + out[0] = crc & 0xFF; + out[1] = crc >> 8; +} + +void nfcv_emu_free() { + digital_signal_free(nfcv_signal); + digital_signal_free(nfcv_resp_unmod_256); + digital_signal_free(nfcv_resp_pulse_32); + digital_signal_free(nfcv_resp_one); + digital_signal_free(nfcv_resp_zero); + digital_signal_free(nfcv_resp_sof); + digital_signal_free(nfcv_resp_eof); + + nfcv_signal = NULL; + nfcv_resp_unmod_256 = NULL; + nfcv_resp_pulse_32 = NULL; + nfcv_resp_one = NULL; + nfcv_resp_zero = NULL; + nfcv_resp_sof = NULL; + nfcv_resp_eof = NULL; +} + +void nfcv_emu_alloc() { + + if(!nfcv_signal) { + nfcv_signal = digital_signal_alloc(8192); + } + + if(!nfcv_resp_unmod_256) { + /* unmodulated 256/fc signal as building block */ + nfcv_resp_unmod_256 = digital_signal_alloc(4); + nfcv_resp_unmod_256->start_level = false; + nfcv_resp_unmod_256->edge_timings[0] = (uint32_t)(NFCV_RESP_SUBC1_UNMOD_256 * DIGITAL_SIGNAL_UNIT_S); + nfcv_resp_unmod_256->edge_cnt = 1; + } + if(!nfcv_resp_pulse_32) { + /* modulated fc/32 pulse as building block */ + nfcv_resp_pulse_32 = digital_signal_alloc(4); + nfcv_resp_pulse_32->start_level = true; + nfcv_resp_pulse_32->edge_timings[0] = (uint32_t)(NFCV_RESP_SUBC1_PULSE_32 * DIGITAL_SIGNAL_UNIT_S); + nfcv_resp_pulse_32->edge_timings[1] = (uint32_t)(NFCV_RESP_SUBC1_PULSE_32 * DIGITAL_SIGNAL_UNIT_S); + nfcv_resp_pulse_32->edge_cnt = 2; + } + if(!nfcv_resp_one) { + /* logical one: 256/fc unmodulated then 8 pulses fc/32 */ + nfcv_resp_one = digital_signal_alloc(24); + digital_signal_append(nfcv_resp_one, nfcv_resp_unmod_256); + for(size_t i = 0; i < 8; i++) { + digital_signal_append(nfcv_resp_one, nfcv_resp_pulse_32); + } + } + if(!nfcv_resp_zero) { + /* logical zero: 8 pulses fc/32 then 256/fc unmodulated */ + nfcv_resp_zero = digital_signal_alloc(24); + for(size_t i = 0; i < 8; i++) { + digital_signal_append(nfcv_resp_zero, nfcv_resp_pulse_32); + } + digital_signal_append(nfcv_resp_zero, nfcv_resp_unmod_256); + } + if(!nfcv_resp_sof) { + /* SOF: unmodulated 768/fc, 24 pulses fc/32, logic 1 */ + nfcv_resp_sof = digital_signal_alloc(128); + digital_signal_append(nfcv_resp_sof, nfcv_resp_unmod_256); + digital_signal_append(nfcv_resp_sof, nfcv_resp_unmod_256); + digital_signal_append(nfcv_resp_sof, nfcv_resp_unmod_256); + for(size_t i = 0; i < 24; i++) { + digital_signal_append(nfcv_resp_sof, nfcv_resp_pulse_32); + } + digital_signal_append(nfcv_resp_sof, nfcv_resp_one); + } + if(!nfcv_resp_eof) { + /* EOF: logic 0, 24 pulses fc/32, unmodulated 768/fc */ + nfcv_resp_eof = digital_signal_alloc(128); + digital_signal_append(nfcv_resp_eof, nfcv_resp_zero); + for(size_t i = 0; i < 24; i++) { + digital_signal_append(nfcv_resp_eof, nfcv_resp_pulse_32); + } + digital_signal_append(nfcv_resp_eof, nfcv_resp_unmod_256); + digital_signal_append(nfcv_resp_eof, nfcv_resp_unmod_256); + digital_signal_append(nfcv_resp_eof, nfcv_resp_unmod_256); + /* add extra silence */ + digital_signal_append(nfcv_resp_eof, nfcv_resp_unmod_256); + } +} + + +void nfcv_emu_send_raw(uint8_t* data, uint8_t length) { + + int bits = length * 8; + + nfcv_signal->start_level = false; + nfcv_signal->edge_cnt = 0; + + digital_signal_append(nfcv_signal, nfcv_resp_sof); + + for(int bit_total = 0; bit_total < bits; bit_total++) { + uint32_t byte_pos = bit_total / 8; + uint32_t bit_pos = bit_total % 8; + uint8_t bit_val = 0x01 << bit_pos; + + if(data[byte_pos] & bit_val) { + digital_signal_append(nfcv_signal, nfcv_resp_one); + } else { + digital_signal_append(nfcv_signal, nfcv_resp_zero); + } + } + + digital_signal_append(nfcv_signal, nfcv_resp_eof); + + /* digital signal setup will take some time. win some time by tricking the VCD into thinking that something happens */ + furi_hal_gpio_write(&gpio_spi_r_mosi, false); + furi_delay_us(10); + furi_hal_gpio_write(&gpio_spi_r_mosi, true); + furi_delay_us(10); + furi_hal_gpio_write(&gpio_spi_r_mosi, false); + + FURI_CRITICAL_ENTER(); + digital_signal_send(nfcv_signal, &gpio_spi_r_mosi); + FURI_CRITICAL_EXIT(); + furi_hal_gpio_write(&gpio_spi_r_mosi, false); +} + +void nfcv_emu_send(uint8_t* data, uint8_t length) { + uint8_t buffer[64]; + + if(length + 2 > (uint8_t)sizeof(buffer)) { + return; + } + + memcpy(buffer, data, length); + nfcv_crc(buffer, length, &buffer[length]); + nfcv_emu_send_raw(buffer, length + 2); +} + + +void nfcv_uidcpy(uint8_t *dst, uint8_t *src) { + for(int pos = 0; pos < 8; pos++) { + dst[pos] = src[7-pos]; + } +} + +int nfcv_uidcmp(uint8_t *dst, uint8_t *src) { + for(int pos = 0; pos < 8; pos++) { + if(dst[pos] != src[7-pos]) { + return 1; + } + } + return 0; +} + + + +void nfcv_emu_handle_packet(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data, uint8_t* payload, uint32_t payload_length) { + + if(!payload_length) { + return; + } + + uint8_t flags = payload[0]; + uint8_t command = payload[1]; + bool addressed = !(flags & RFAL_NFCV_REQ_FLAG_INVENTORY) && (flags & RFAL_NFCV_REQ_FLAG_ADDRESS); + bool advanced = addressed && (command >= 0xA0); + uint8_t address_offset = 2 + (advanced ? 1 : 0); + uint8_t payload_offset = address_offset + (addressed ? 8 : 0); + uint8_t *address = &payload[address_offset]; + + if(addressed && nfcv_uidcmp(address, nfc_data->uid)) { + printf("addressed packet, but not for us:\r\n"); + printf(" destination: %02X%02X%02X%02X%02X%02X%02X%02X\r\n", address[7], address[6], address[5], address[4], address[3], address[2], address[1], address[0]); + printf(" our UID: %02X%02X%02X%02X%02X%02X%02X%02X\r\n", nfc_data->uid[7], nfc_data->uid[6], nfc_data->uid[5], nfc_data->uid[4], nfc_data->uid[3], nfc_data->uid[2], nfc_data->uid[1], nfc_data->uid[0]); + return; + } + + uint8_t response_buffer[32]; + + switch(command) { + case ISO15693_GET_SYSTEM_INFO: + { + if(nfcv_data->privacy) { + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "SYSTEMINFO (ignored, privacy)"); + } else { + response_buffer[0] = ISO15693_NOERROR; + response_buffer[1] = 0x0F; + nfcv_uidcpy(&response_buffer[2], nfc_data->uid); + response_buffer[10] = nfcv_data->dsfid; /* DSFID */ + response_buffer[11] = nfcv_data->afi; /* AFI */ + response_buffer[12] = nfcv_data->block_num - 1; /* number of blocks */ + response_buffer[13] = nfcv_data->block_size - 1; /* block size */ + response_buffer[14] = nfcv_data->ic_ref; /* IC reference */ + + nfcv_emu_send(response_buffer, 15); + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "SYSTEMINFO"); + } + + break; + } + + case ISO15693_INVENTORY: + { + if(nfcv_data->privacy) { + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "INVENTORY (ignored, privacy)"); + } else { + response_buffer[0] = ISO15693_NOERROR; + response_buffer[1] = nfcv_data->dsfid; /* DSFID */ + nfcv_uidcpy(&response_buffer[2], nfc_data->uid); + + nfcv_emu_send(response_buffer, 10); + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "INVENTORY"); + } + break; + } + + case ISO15693_READBLOCK: + { + uint8_t block = payload[payload_offset]; + + if(block >= nfcv_data->block_num) { + response_buffer[0] = ISO15693_ERROR_BLOCK_WRITE; + nfcv_emu_send(response_buffer, 1); + } else { + response_buffer[0] = ISO15693_NOERROR; + memcpy(&response_buffer[1], &nfcv_data->data[nfcv_data->block_size * block], nfcv_data->block_size); + nfcv_emu_send(response_buffer, 1 + nfcv_data->block_size); + } + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "READ BLOCK %d", block); + break; + } + + case ISO15693_WRITEBLOCK: + { + uint8_t block = payload[payload_offset]; + uint8_t *data = &payload[payload_offset + 1]; + + if(block >= nfcv_data->block_num) { + response_buffer[0] = ISO15693_ERROR_BLOCK_WRITE; + } else { + response_buffer[0] = ISO15693_NOERROR; + memcpy(&nfcv_data->data[nfcv_data->block_size * block], &response_buffer[1], nfcv_data->block_size); + } + nfcv_emu_send(response_buffer, 1); + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "WRITE BLOCK %d <- %02X %02X %02X %02X", block, data[0], data[1], data[2], data[3]); + break; + } + + case ISO15693_CMD_NXP_GET_RANDOM_NUMBER: + { + response_buffer[0] = ISO15693_NOERROR; + response_buffer[1] = 0x00; /* set number to 0x0000 so we get the key in plaintext */ + response_buffer[2] = 0x00; + + nfcv_emu_send(response_buffer, 3); + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "GET_RANDOM_NUMBER"); + break; + } + + case ISO15693_CMD_NXP_SET_PASSWORD: + { + uint32_t pass = (payload[payload_offset + 1] << 24) + | (payload[payload_offset + 2] << 16) + | (payload[payload_offset + 3] << 8) + | (payload[payload_offset + 4] << 0); + + response_buffer[0] = ISO15693_NOERROR; + + nfcv_emu_send(response_buffer, 1); + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "SET_PASSWORD #%02X: %08lX", payload[payload_offset + 0], pass); + + nfcv_data->privacy = false; + break; + } + + case ISO15693_CMD_NXP_ENABLE_PRIVACY: + { + response_buffer[0] = ISO15693_NOERROR; + + nfcv_emu_send(response_buffer, 1); + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "ISO15693_CMD_NXP_ENABLE_PRIVACY"); + + nfcv_data->privacy = true; + break; + } + + default: + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "unsupported: %02X", command); + break; + } +} + +void nfcv_emu_init() { + nfcv_emu_alloc(); + rfal_platform_spi_acquire(); + + st25r3916ExecuteCommand(ST25R3916_CMD_STOP); + st25r3916WriteRegister(ST25R3916_REG_OP_CONTROL, 0xC3); + st25r3916WriteRegister(ST25R3916_REG_MODE, 0x88); + st25r3916ExecuteCommand(ST25R3916_CMD_TRANSPARENT_MODE); + + furi_hal_spi_bus_handle_deinit(&furi_hal_spi_bus_handle_nfc); +} + +void nfcv_emu_deinit() { + furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_nfc); + rfal_platform_spi_release(); + nfcv_emu_free(); +} + +bool nfcv_emu_loop(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data, uint32_t timeout_ms) { + + bool ret = false; + uint32_t next_sleep = DWT->CYCCNT + (timeout_ms * clocks_in_ms); + uint32_t timeout = 0; + uint32_t last_change = 0; + uint32_t edges_received = 0; + + uint32_t frame_state = NFCV_FRAME_STATE_SOF1; + uint32_t periods_previous = 0; + uint8_t frame_payload[128]; + uint32_t frame_pos = 0; + uint32_t byte_value = 0; + uint32_t bits_received = 0; + char reset_reason[128]; + + bool prev = furi_hal_gpio_read(&gpio_spi_r_miso); + + bool in_critical = false; + FURI_CRITICAL_DEFINE(); + + while(true) { + + bool state = furi_hal_gpio_read(&gpio_spi_r_miso); + uint32_t cur_time = DWT->CYCCNT; + + if(state != prev) { + uint32_t delta = cur_time - last_change; + uint32_t periods = (delta + bit_time/2) / bit_time; + + last_change = cur_time; + prev = state; + next_sleep = cur_time + (timeout_ms * clocks_in_ms); + + /* start edge counting on first rising edge */ + if(state || edges_received) { + edges_received++; + + /* ignore periods which are too long, might happen on field start */ + if(periods > 1024) { + continue; + } + + switch(frame_state) { + case NFCV_FRAME_STATE_SOF1: + /* got a rising edge, was it one period? */ + if(state) { + if(periods == 1) { + FURI_CRITICAL_ENTER_ADV(); + timeout = cur_time + bit_time * 16; + in_critical = true; + frame_state = NFCV_FRAME_STATE_SOF2; + } else { + snprintf(reset_reason, sizeof(reset_reason), "SOF: Expected 1 period, got %lu", periods); + frame_state = NFCV_FRAME_STATE_RESET; + break; + } + } + break; + + case NFCV_FRAME_STATE_SOF2: + /* waiting for the second low period, telling us about coding */ + if(!state) { + timeout = cur_time + bit_time * 16; + if(periods == 6) { + frame_state = NFCV_FRAME_STATE_CODING_256; + periods_previous = 0; + } else if(periods == 4) { + frame_state = NFCV_FRAME_STATE_CODING_4; + periods_previous = 2; + } else { + snprintf(reset_reason, sizeof(reset_reason), "SOF: Expected 4/6 periods, got %lu", periods); + frame_state = NFCV_FRAME_STATE_RESET; + break; + } + } + break; + + case NFCV_FRAME_STATE_CODING_256: + if(!state) { + timeout = cur_time + bit_time * 1024; + if(periods_previous > periods) { + snprintf(reset_reason, sizeof(reset_reason), "1oo256: Missing %lu periods from previous symbol, got %lu", periods_previous, periods); + frame_state = NFCV_FRAME_STATE_RESET; + break; + } + /* previous symbol left us with some pulse periods */ + periods -= periods_previous; + + if(periods > 512) { + snprintf(reset_reason, sizeof(reset_reason), "1oo256: %lu periods is too much", periods); + frame_state = NFCV_FRAME_STATE_RESET; + break; + } + + if(periods == 2) { + frame_state = NFCV_FRAME_STATE_EOF; + break; + } + + periods_previous = 512 - (periods + 1); + byte_value = (periods - 1) / 2; + frame_payload[frame_pos++] = (uint8_t)byte_value; + + } else { + if(periods != 1) { + snprintf(reset_reason, sizeof(reset_reason), "1oo256: Expected a single low pulse"); + frame_state = NFCV_FRAME_STATE_RESET; + break; + } + } + break; + + case NFCV_FRAME_STATE_CODING_4: + /* evaluate high periods on falling edge */ + if(!state) { + timeout = cur_time + bit_time * 16; + if(periods_previous > periods) { + snprintf(reset_reason, sizeof(reset_reason), "1oo4: Missing %lu periods from previous symbol, got %lu", periods_previous, periods); + frame_state = NFCV_FRAME_STATE_RESET; + break; + } + /* previous symbol left us with some pulse periods */ + periods -= periods_previous; + periods_previous = 0; + + byte_value >>= 2; + bits_received += 2; + + if(periods == 1) { + byte_value |= 0x00 << 6; + periods_previous = 6; + } else if(periods == 3) { + byte_value |= 0x01 << 6; + periods_previous = 4; + } else if(periods == 5) { + byte_value |= 0x02 << 6; + periods_previous = 2; + } else if(periods == 7) { + byte_value |= 0x03 << 6; + periods_previous = 0; + } else if(periods == 2) { + frame_state = NFCV_FRAME_STATE_EOF; + break; + } else { + snprintf(reset_reason, sizeof(reset_reason), "1oo4: Expected 1/3/5/7 low pulses, but got %lu", periods); + frame_state = NFCV_FRAME_STATE_RESET; + break; + } + + if(bits_received >= 8) { + frame_payload[frame_pos++] = (uint8_t)byte_value; + bits_received = 0; + } + } else { + if(periods != 1) { + snprintf(reset_reason, sizeof(reset_reason), "1oo4: Expected a single low pulse"); + frame_state = NFCV_FRAME_STATE_RESET; + break; + } + } + break; + } + } + } + + /* post-state-machine cleanup and reset */ + if(frame_state == NFCV_FRAME_STATE_RESET) { + timeout = 0; + edges_received = 0; + frame_state = NFCV_FRAME_STATE_SOF1; + FURI_CRITICAL_EXIT(); + printf("Reset state machine, reason: %s\r\n", reset_reason); + in_critical = false; + furi_delay_ms(50); + } else if(frame_state == NFCV_FRAME_STATE_EOF) { + FURI_CRITICAL_EXIT(); + in_critical = false; + break; + } + + /* no edges detected */ + if(timeout && cur_time > timeout) { + break; + } + /* might exit early on overflows. guess thats okay. */ + if(cur_time > next_sleep) { + break; + } + } + + if(in_critical) { + FURI_CRITICAL_EXIT(); + in_critical = false; + } + + if(frame_state == NFCV_FRAME_STATE_EOF) { + + furi_hal_gpio_write(&gpio_spi_r_mosi, false); + furi_delay_us(10); + furi_hal_gpio_write(&gpio_spi_r_mosi, true); + furi_delay_us(10); + furi_hal_gpio_write(&gpio_spi_r_mosi, false); + furi_delay_us(10); + furi_hal_gpio_write(&gpio_spi_r_mosi, true); + furi_delay_us(10); + furi_hal_gpio_write(&gpio_spi_r_mosi, false); + + nfcv_emu_handle_packet(nfc_data, nfcv_data, frame_payload, frame_pos); + ret = true; + } + + furi_delay_ms(0); + + return ret; +} diff --git a/lib/nfc/protocols/nfcv.h b/lib/nfc/protocols/nfcv.h index 024dea42e..c2efcd1a1 100644 --- a/lib/nfc/protocols/nfcv.h +++ b/lib/nfc/protocols/nfcv.h @@ -8,10 +8,73 @@ #include +#define NFCV_FC (13560000.0f) +#define NFCV_RESP_SUBC1_PULSE_32 (1.0f / (NFCV_FC/32) / 2.0f) /* 1.1799 µs */ +#define NFCV_RESP_SUBC1_UNMOD_256 (256.0f / NFCV_FC) /* 18.8791 µs */ + +#define DIGITAL_SIGNAL_UNIT_S (100000000000.0f) +#define DIGITAL_SIGNAL_UNIT_US (100000.0f) + #define NFCV_TOTAL_BLOCKS_MAX 32 #define NFCV_BLOCK_SIZE 4 #define NFCV_MAX_DUMP_SIZE (NFCV_BLOCK_SIZE*NFCV_TOTAL_BLOCKS_MAX) + +#define NFCV_FRAME_STATE_SOF1 0 +#define NFCV_FRAME_STATE_SOF2 1 +#define NFCV_FRAME_STATE_CODING_4 2 +#define NFCV_FRAME_STATE_CODING_256 3 +#define NFCV_FRAME_STATE_EOF 4 +#define NFCV_FRAME_STATE_RESET 5 + +/* */ +#define ISO15693_INVENTORY 0x01 +#define ISO15693_STAYQUIET 0x02 +#define ISO15693_READBLOCK 0x20 +#define ISO15693_WRITEBLOCK 0x21 +#define ISO15693_LOCKBLOCK 0x22 +#define ISO15693_READ_MULTI_BLOCK 0x23 +#define ISO15693_WRITE_MULTI_BLOCK 0x24 +#define ISO15693_SELECT 0x25 +#define ISO15693_RESET_TO_READY 0x26 +#define ISO15693_WRITE_AFI 0x27 +#define ISO15693_LOCK_AFI 0x28 +#define ISO15693_WRITE_DSFID 0x29 +#define ISO15693_LOCK_DSFID 0x2A +#define ISO15693_GET_SYSTEM_INFO 0x2B +#define ISO15693_READ_MULTI_SECSTATUS 0x2C + +// ISO15693 MANUFACTURER CODES +#define ISO15693_MANUFACTURER_NXP 0x04 + +// ISO15693-3 CUSTOM NXP COMMANDS +#define ISO15693_CMD_NXP_SET_EAS 0xA2 +#define ISO15693_CMD_NXP_RESET_EAS 0xA3 +#define ISO15693_CMD_NXP_LOCK_EAS 0xA4 +#define ISO15693_CMD_NXP_EAS_ALARM 0xA5 +#define ISO15693_CMD_NXP_PASSWORD_PROTECT_EAS_AFI 0xA6 +#define ISO15693_CMD_NXP_WRITE_EAS_ID 0xA7 +#define ISO15693_CMD_NXP_INVENTORY_PAGE_READ 0xB0 +#define ISO15693_CMD_NXP_INVENTORY_PAGE_READ_FAST 0xB1 +#define ISO15693_CMD_NXP_GET_RANDOM_NUMBER 0xB2 +#define ISO15693_CMD_NXP_SET_PASSWORD 0xB3 +#define ISO15693_CMD_NXP_WRITE_PASSWORD 0xB4 +#define ISO15693_CMD_NXP_DESTROY 0xB9 +#define ISO15693_CMD_NXP_ENABLE_PRIVACY 0xBA + +// ISO15693 RESPONSE ERROR CODES +#define ISO15693_NOERROR 0x00 +#define ISO15693_ERROR_CMD_NOT_SUP 0x01 // Command not supported +#define ISO15693_ERROR_CMD_NOT_REC 0x02 // Command not recognized (eg. parameter error) +#define ISO15693_ERROR_CMD_OPTION 0x03 // Command option not supported +#define ISO15693_ERROR_GENERIC 0x0F // No additional Info about this error +#define ISO15693_ERROR_BLOCK_UNAVAILABLE 0x10 +#define ISO15693_ERROR_BLOCK_LOCKED_ALREADY 0x11 // cannot lock again +#define ISO15693_ERROR_BLOCK_LOCKED 0x12 // cannot be changed +#define ISO15693_ERROR_BLOCK_WRITE 0x13 // Writing was unsuccessful +#define ISO15693_ERROR_BLOCL_WRITELOCK 0x14 // Locking was unsuccessful + + typedef enum { NfcVAuthMethodManual, NfcVAuthMethodTonieBox, @@ -39,8 +102,10 @@ typedef struct { uint8_t block_num; uint8_t block_size; uint8_t data[NFCV_MAX_DUMP_SIZE]; + bool privacy; char error[32]; + char last_command[128]; } NfcVData; typedef struct { @@ -51,4 +116,8 @@ typedef struct { ReturnCode nfcv_read_blocks(NfcVReader* reader, NfcVData* data); ReturnCode nfcv_read_sysinfo(FuriHalNfcDevData* nfc_data, NfcVData* data); ReturnCode nfcv_inventory(uint8_t* uid); +bool nfcv_read_card(NfcVReader* reader, FuriHalNfcDevData* nfc_data, NfcVData* data); +void nfcv_emu_init(); +void nfcv_emu_deinit(); +bool nfcv_emu_loop(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data, uint32_t timeout_ms); diff --git a/lib/nfc/protocols/slix_l.c b/lib/nfc/protocols/slix_l.c index b7d1bd174..d22a8470e 100644 --- a/lib/nfc/protocols/slix_l.c +++ b/lib/nfc/protocols/slix_l.c @@ -14,20 +14,6 @@ bool slix_l_check_card_type(uint8_t UID0, uint8_t UID1, uint8_t UID2) { return false; } -bool slix_l_read_card( - NfcVReader* reader, - FuriHalNfcDevData* nfc_data, - NfcVData* nfcv_data) { - furi_assert(reader); - furi_assert(nfcv_data); - - if(nfcv_read_sysinfo(nfc_data, nfcv_data) != ERR_NONE) { - return false; - } - - reader->blocks_to_read = nfcv_data->block_num; - return (nfcv_read_blocks(reader, nfcv_data) == ERR_NONE); -} ReturnCode slix_l_get_random(uint8_t* rand) { uint16_t received = 0; diff --git a/lib/nfc/protocols/slix_l.h b/lib/nfc/protocols/slix_l.h index c40ced942..fb7a9c4c0 100644 --- a/lib/nfc/protocols/slix_l.h +++ b/lib/nfc/protocols/slix_l.h @@ -11,7 +11,6 @@ bool slix_l_check_card_type(uint8_t UID0, uint8_t UID1, uint8_t UID2); -bool slix_l_read_card(NfcVReader* reader, FuriHalNfcDevData* nfc_data, NfcVData* data); ReturnCode slix_l_get_random(uint8_t* rand); ReturnCode slix_l_unlock(uint32_t id, uint8_t* rand, uint32_t password);