added NfcV emulation

This commit is contained in:
g3gg0
2022-11-14 21:12:18 +01:00
parent fe216b4ddd
commit bcd33ca125
18 changed files with 1150 additions and 48 deletions

View File

@@ -1,11 +1,20 @@
#include <furi.h> #include <furi.h>
#include <furi_hal.h> #include <furi_hal.h>
#include <furi_hal_nfc.h>
#include <cli/cli.h> #include <cli/cli.h>
#include <lib/toolbox/args.h> #include <lib/toolbox/args.h>
#include <st25r3916.h>
#include <st25r3916_irq.h>
#include <furi_hal_spi.h>
#include <furi_hal_gpio.h>
#include <furi_hal_cortex.h>
#include <furi_hal_resources.h>
#include <lib/nfc/nfc_types.h> #include <lib/nfc/nfc_types.h>
#include <lib/nfc/nfc_device.h> #include <lib/nfc/nfc_device.h>
static void nfc_cli_print_usage() { static void nfc_cli_print_usage() {
printf("Usage:\r\n"); printf("Usage:\r\n");
printf("nfc <cmd>\r\n"); printf("nfc <cmd>\r\n");
@@ -14,6 +23,10 @@ static void nfc_cli_print_usage() {
printf("\temulate\t - emulate predefined nfca card\r\n"); printf("\temulate\t - emulate predefined nfca card\r\n");
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
printf("\tfield\t - turn field on\r\n"); 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(); 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 <reg> and <value>\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 <reg> and <value> 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 <reg>\r\n");
nfc_cli_print_usage();
return;
}
if(furi_string_utf8_length(reg) != 2) {
printf("You didn't specify <reg> 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 <cmd>\r\n");
nfc_cli_print_usage();
return;
}
if(furi_string_utf8_length(cmd) != 2) {
printf("You didn't specify <cmd> 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) { static void nfc_cli(Cli* cli, FuriString* args, void* context) {
UNUSED(context); UNUSED(context);
FuriString* cmd; FuriString* cmd;
@@ -122,6 +326,26 @@ static void nfc_cli(Cli* cli, FuriString* args, void* context) {
nfc_cli_field(cli, args); nfc_cli_field(cli, args);
break; 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(); nfc_cli_print_usage();

View File

@@ -12,6 +12,7 @@ ADD_SCENE(nfc, save_name, SaveName)
ADD_SCENE(nfc, save_success, SaveSuccess) ADD_SCENE(nfc, save_success, SaveSuccess)
ADD_SCENE(nfc, file_select, FileSelect) ADD_SCENE(nfc, file_select, FileSelect)
ADD_SCENE(nfc, emulate_uid, EmulateUid) ADD_SCENE(nfc, emulate_uid, EmulateUid)
ADD_SCENE(nfc, emulate_nfcv, EmulateNfcV)
ADD_SCENE(nfc, nfca_read_success, NfcaReadSuccess) ADD_SCENE(nfc, nfca_read_success, NfcaReadSuccess)
ADD_SCENE(nfc, nfca_menu, NfcaMenu) ADD_SCENE(nfc, nfca_menu, NfcaMenu)
ADD_SCENE(nfc, nfcv_menu, NfcVMenu) ADD_SCENE(nfc, nfcv_menu, NfcVMenu)

View File

@@ -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);
}

View File

@@ -14,7 +14,8 @@ void nfc_scene_nfc_data_info_on_enter(void* context) {
NfcDeviceData* dev_data = &nfc->dev->dev_data; NfcDeviceData* dev_data = &nfc->dev->dev_data;
NfcProtocol protocol = dev_data->protocol; NfcProtocol protocol = dev_data->protocol;
uint8_t text_scroll_height = 0; 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_add_button_element(
widget, GuiButtonTypeRight, "More", nfc_scene_nfc_data_info_widget_callback, nfc); widget, GuiButtonTypeRight, "More", nfc_scene_nfc_data_info_widget_callback, nfc);
text_scroll_height = 52; 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)); temp_str, "\e#%s\n", nfc_mf_classic_type(dev_data->mf_classic_data.type));
} else if(protocol == NfcDeviceProtocolMifareDesfire) { } else if(protocol == NfcDeviceProtocolMifareDesfire) {
furi_string_cat_printf(temp_str, "\e#MIFARE DESfire\n"); 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) { } else if(protocol == NfcDeviceProtocolSlixL) {
furi_string_cat_printf(temp_str, "\e#ISO15693 SLIX-L\n"); furi_string_cat_printf(temp_str, "\e#ISO15693 SLIX-L\n");
} else { } else {
@@ -47,7 +50,7 @@ void nfc_scene_nfc_data_info_on_enter(void* context) {
} }
// Set tag iso data // Set tag iso data
if(protocol == NfcDeviceProtocolSlixL) { if((protocol == NfcDeviceProtocolSlixL) || (protocol == NfcDeviceProtocolNfcV)) {
NfcVData* nfcv_data = &nfc->dev->dev_data.nfcv_data; NfcVData* nfcv_data = &nfc->dev->dev_data.nfcv_data;
furi_string_cat_printf(temp_str, "UID:\n"); 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) { } else if(protocol == NfcDeviceProtocolSlixL) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVMenu); scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVMenu);
consumed = true; consumed = true;
} else if(protocol == NfcDeviceProtocolNfcV) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVMenu);
consumed = true;
} }
} }
} }

View File

@@ -3,6 +3,7 @@
enum SubmenuIndex { enum SubmenuIndex {
SubmenuIndexSave, SubmenuIndexSave,
SubmenuIndexEmulate,
}; };
void nfc_scene_nfcv_menu_submenu_callback(void* context, uint32_t index) { 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_add_item(
submenu, "Save", SubmenuIndexSave, nfc_scene_nfcv_menu_submenu_callback, nfc); 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( submenu_set_selected_item(
nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneNfcVMenu)); 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, ""); nfc_device_set_name(nfc->dev, "");
scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName);
consumed = true; 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); scene_manager_set_scene_state(nfc->scene_manager, NfcSceneNfcVMenu, event.event);
} else if(event.type == SceneManagerEventTypeBack) { } else if(event.type == SceneManagerEventTypeBack) {

View File

@@ -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); 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_icon(popup, 0, 6, &I_RFIDDolphinSuccess_108x57);
popup_set_context(popup, nfc); popup_set_context(popup, nfc);

View File

@@ -1,5 +1,5 @@
entry,status,name,type,params entry,status,name,type,params
Version,+,7.5,, Version,+,7.12,,
Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/bt/bt_service/bt.h,,
Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli.h,,
Header,+,applications/services/cli/cli_vcp.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_stop_cmd,void,
Function,+,furi_hal_nfc_tx_rx,_Bool,"FuriHalNfcTxRxContext*, uint16_t" Function,+,furi_hal_nfc_tx_rx,_Bool,"FuriHalNfcTxRxContext*, uint16_t"
Function,+,furi_hal_nfc_tx_rx_full,_Bool,FuriHalNfcTxRxContext* 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_init,void,
Function,+,furi_hal_os_tick,void, Function,+,furi_hal_os_tick,void,
Function,+,furi_hal_power_check_otg_status,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_alloc,NfcaSignal*,
Function,-,nfca_signal_encode,void,"NfcaSignal*, uint8_t*, uint16_t, uint8_t*" Function,-,nfca_signal_encode,void,"NfcaSignal*, uint8_t*, uint16_t, uint8_t*"
Function,-,nfca_signal_free,void,NfcaSignal* 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_inventory,ReturnCode,uint8_t*
Function,-,nfcv_read_blocks,ReturnCode,"NfcVReader*, NfcVData*" Function,-,nfcv_read_blocks,ReturnCode,"NfcVReader*, NfcVData*"
Function,-,nfcv_read_card,_Bool,"NfcVReader*, FuriHalNfcDevData*, NfcVData*"
Function,-,nfcv_read_sysinfo,ReturnCode,"FuriHalNfcDevData*, NfcVData*" Function,-,nfcv_read_sysinfo,ReturnCode,"FuriHalNfcDevData*, NfcVData*"
Function,+,notification_internal_message,void,"NotificationApp*, const NotificationSequence*" Function,+,notification_internal_message,void,"NotificationApp*, const NotificationSequence*"
Function,+,notification_internal_message_block,void,"NotificationApp*, const NotificationSequence*" Function,+,notification_internal_message_block,void,"NotificationApp*, const NotificationSequence*"
1 entry status name type params
2 Version + 7.5 7.12
3 Header + applications/services/bt/bt_service/bt.h
4 Header + applications/services/cli/cli.h
5 Header + applications/services/cli/cli_vcp.h
1181 Function + furi_hal_nfc_stop_cmd void
1182 Function + furi_hal_nfc_tx_rx _Bool FuriHalNfcTxRxContext*, uint16_t
1183 Function + furi_hal_nfc_tx_rx_full _Bool FuriHalNfcTxRxContext*
1184 Function - furi_hal_nfcv_listen_start void
1185 Function - furi_hal_os_init void
1186 Function + furi_hal_os_tick void
1187 Function + furi_hal_power_check_otg_status void
1959 Function - nfca_signal_alloc NfcaSignal*
1960 Function - nfca_signal_encode void NfcaSignal*, uint8_t*, uint16_t, uint8_t*
1961 Function - nfca_signal_free void NfcaSignal*
1962 Function - nfcv_emu_deinit void
1963 Function - nfcv_emu_init void
1964 Function - nfcv_emu_loop _Bool FuriHalNfcDevData*, NfcVData*, uint32_t
1965 Function - nfcv_inventory ReturnCode uint8_t*
1966 Function - nfcv_read_blocks ReturnCode NfcVReader*, NfcVData*
1967 Function - nfcv_read_card _Bool NfcVReader*, FuriHalNfcDevData*, NfcVData*
1968 Function - nfcv_read_sysinfo ReturnCode FuriHalNfcDevData*, NfcVData*
1969 Function + notification_internal_message void NotificationApp*, const NotificationSequence*
1970 Function + notification_internal_message_block void NotificationApp*, const NotificationSequence*

View File

@@ -367,6 +367,39 @@ void furi_hal_nfc_listen_start(FuriHalNfcDevData* nfc_data) {
st25r3916ExecuteCommand(ST25R3916_CMD_GOTO_SENSE); 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() { void rfal_interrupt_callback_handler() {
furi_event_flag_set(event, EVENT_FLAG_INTERRUPT); furi_event_flag_set(event, EVENT_FLAG_INTERRUPT);
} }

View File

@@ -177,6 +177,12 @@ bool furi_hal_nfc_listen(
*/ */
void furi_hal_nfc_listen_start(FuriHalNfcDevData* nfc_data); 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 /** Read data in Target Listen mode
* @note Must be called only after furi_hal_nfc_listen_start() * @note Must be called only after furi_hal_nfc_listen_start()
* *

View File

@@ -23,18 +23,33 @@ extern "C" {
#define FURI_IS_ISR() (FURI_IS_IRQ_MODE() || FURI_IS_IRQ_MASKED()) #define FURI_IS_ISR() (FURI_IS_IRQ_MODE() || FURI_IS_IRQ_MASKED())
#endif #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 #ifndef FURI_CRITICAL_ENTER
#define FURI_CRITICAL_ENTER() \ #define FURI_CRITICAL_ENTER() \
uint32_t __isrm = 0; \ FURI_CRITICAL_DEFINE(); \
bool __from_isr = FURI_IS_ISR(); \ FURI_CRITICAL_ENTER_ADV();
bool __kernel_running = (xTaskGetSchedulerState() == taskSCHEDULER_RUNNING); \
if(__from_isr) { \
__isrm = taskENTER_CRITICAL_FROM_ISR(); \
} else if(__kernel_running) { \
taskENTER_CRITICAL(); \
} else { \
__disable_irq(); \
}
#endif #endif
#ifndef FURI_CRITICAL_EXIT #ifndef FURI_CRITICAL_EXIT

View File

@@ -16,7 +16,7 @@ DigitalSignal* digital_signal_alloc(uint32_t max_edges_cnt) {
signal->start_level = true; signal->start_level = true;
signal->edges_max_cnt = max_edges_cnt; signal->edges_max_cnt = max_edges_cnt;
signal->edge_timings = malloc(max_edges_cnt * sizeof(uint32_t)); 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; signal->edge_cnt = 0;
return signal; 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) { if(signal_a->edges_max_cnt < signal_a->edge_cnt + signal_b->edge_cnt) {
return false; 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; bool end_level = signal_a->start_level;
if(signal_a->edge_cnt) { if(signal_a->edge_cnt) {
end_level = signal_a->start_level ^ !(signal_a->edge_cnt % 2); 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_count_tick_arr = 0;
uint32_t r_rest_div = 0; uint32_t r_rest_div = 0;
for(size_t i = 0; i < signal->edge_cnt - 1; i++) { for(size_t i = 0; i < signal->edge_cnt - 1; i++) {
r_count_tick_arr = t_signal_rest / T_TIM; r_count_tick_arr = t_signal_rest / T_TIM;
r_rest_div = 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); LL_TIM_EnableCounter(TIM2);
while(!LL_DMA_IsActiveFlag_TC2(DMA1)) while(!LL_DMA_IsActiveFlag_TC2(DMA1))
; {
}
LL_DMA_ClearFlag_TC1(DMA1); LL_DMA_ClearFlag_TC1(DMA1);
LL_DMA_ClearFlag_TC2(DMA1); LL_DMA_ClearFlag_TC2(DMA1);

View File

@@ -33,6 +33,7 @@ typedef enum {
NfcDeviceProtocolMifareUl, NfcDeviceProtocolMifareUl,
NfcDeviceProtocolMifareClassic, NfcDeviceProtocolMifareClassic,
NfcDeviceProtocolMifareDesfire, NfcDeviceProtocolMifareDesfire,
NfcDeviceProtocolNfcV,
NfcDeviceProtocolSlixL, NfcDeviceProtocolSlixL,
} NfcProtocol; } NfcProtocol;

View File

@@ -93,6 +93,8 @@ int32_t nfc_worker_task(void* context) {
nfc_worker_read(nfc_worker); nfc_worker_read(nfc_worker);
} else if(nfc_worker->state == NfcWorkerStateUidEmulate) { } else if(nfc_worker->state == NfcWorkerStateUidEmulate) {
nfc_worker_emulate_uid(nfc_worker); nfc_worker_emulate_uid(nfc_worker);
} else if(nfc_worker->state == NfcWorkerStateNfcVEmulate) {
nfc_worker_emulate_nfcv(nfc_worker);
} else if(nfc_worker->state == NfcWorkerStateEmulateApdu) { } else if(nfc_worker->state == NfcWorkerStateEmulateApdu) {
nfc_worker_emulate_apdu(nfc_worker); nfc_worker_emulate_apdu(nfc_worker);
} else if(nfc_worker->state == NfcWorkerStateMfUltralightEmulate) { } else if(nfc_worker->state == NfcWorkerStateMfUltralightEmulate) {
@@ -135,6 +137,7 @@ void nfc_worker_nfcv_unlock(NfcWorker* nfc_worker) {
furi_hal_nfc_sleep(); furi_hal_nfc_sleep();
while((nfc_worker->state == NfcWorkerStateNfcVUnlock) || while((nfc_worker->state == NfcWorkerStateNfcVUnlock) ||
(nfc_worker->state == NfcWorkerStateNfcVUnlockAndSave)) { (nfc_worker->state == NfcWorkerStateNfcVUnlockAndSave)) {
@@ -201,7 +204,7 @@ void nfc_worker_nfcv_unlock(NfcWorker* nfc_worker) {
if(nfc_worker->state == NfcWorkerStateNfcVUnlockAndSave) { if(nfc_worker->state == NfcWorkerStateNfcVUnlockAndSave) {
NfcVReader reader; 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"); furi_hal_console_printf(" => failed, wait for chip to disappear.\r\n");
snprintf(nfcv_data->error, sizeof(nfcv_data->error), "Read card\nfailed"); snprintf(nfcv_data->error, sizeof(nfcv_data->error), "Read card\nfailed");
nfc_worker->callback(NfcWorkerEventWrongCardDetected, nfc_worker->context); 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; bool read_success = false;
NfcVReader reader = {}; NfcVReader reader = {};
@@ -260,7 +263,7 @@ static bool nfc_worker_read_slix_l(NfcWorker* nfc_worker, FuriHalNfcTxRxContext*
do { 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)) 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; read_success = true;
} while(false); } while(false);
@@ -271,6 +274,7 @@ static bool nfc_worker_read_slix_l(NfcWorker* nfc_worker, FuriHalNfcTxRxContext*
return read_success; return read_success;
} }
static bool nfc_worker_read_mf_ultralight(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { static bool nfc_worker_read_mf_ultralight(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) {
bool read_success = false; bool read_success = false;
MfUltralightReader reader = {}; 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])) { 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"); FURI_LOG_I(TAG, "NXP SLIX-L detected");
nfc_worker->dev_data->protocol = NfcDeviceProtocolSlixL; 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 { } else {
FURI_LOG_I(TAG, "unknown detected"); FURI_LOG_I(TAG, "unknown detected");
nfc_worker->dev_data->protocol = NfcDeviceProtocolUnknown; nfc_worker->dev_data->protocol = NfcDeviceProtocolNfcV;
card_read = true; card_read = nfc_worker_read_nfcv_content(nfc_worker, tx_rx);
} }
return card_read; return card_read;
@@ -555,10 +559,8 @@ void nfc_worker_read(NfcWorker* nfc_worker) {
FURI_LOG_I(TAG, "NfcV detected"); FURI_LOG_I(TAG, "NfcV detected");
if(nfc_worker_read_nfcv(nfc_worker, &tx_rx)) { if(nfc_worker_read_nfcv(nfc_worker, &tx_rx)) {
FURI_LOG_I(TAG, "nfc_worker_read_nfcv success"); FURI_LOG_I(TAG, "nfc_worker_read_nfcv success");
if(dev_data->protocol == NfcDeviceProtocolSlixL) { event = NfcWorkerEventReadNfcV;
event = NfcWorkerEventReadNfcV; break;
break;
}
} }
event = NfcWorkerEventReadUidNfcV; event = NfcWorkerEventReadUidNfcV;
break; 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) { void nfc_worker_emulate_apdu(NfcWorker* nfc_worker) {
FuriHalNfcTxRxContext tx_rx = {}; FuriHalNfcTxRxContext tx_rx = {};
FuriHalNfcDevData params = { FuriHalNfcDevData params = {

View File

@@ -19,6 +19,7 @@ typedef enum {
NfcWorkerStateReadMfUltralightReadAuth, NfcWorkerStateReadMfUltralightReadAuth,
NfcWorkerStateMfClassicDictAttack, NfcWorkerStateMfClassicDictAttack,
NfcWorkerStateAnalyzeReader, NfcWorkerStateAnalyzeReader,
NfcWorkerStateNfcVEmulate,
NfcWorkerStateNfcVUnlock, NfcWorkerStateNfcVUnlock,
NfcWorkerStateNfcVUnlockAndSave, NfcWorkerStateNfcVUnlockAndSave,
// Debug // Debug
@@ -91,4 +92,4 @@ void nfc_worker_start(
void nfc_worker_stop(NfcWorker* nfc_worker); void nfc_worker_stop(NfcWorker* nfc_worker);
void nfc_worker_nfcv_unlock(NfcWorker* nfc_worker); void nfc_worker_nfcv_unlock(NfcWorker* nfc_worker);
void nfc_worker_emulate_nfcv(NfcWorker* nfc_worker);

View File

@@ -1,8 +1,16 @@
#include <limits.h> #include <limits.h>
#include <furi.h>
#include <furi_hal.h>
#include <furi_hal_nfc.h>
#include <furi_hal_spi.h>
#include <furi_hal_gpio.h>
#include <furi_hal_cortex.h>
#include <furi_hal_resources.h>
#include <st25r3916.h>
#include <st25r3916_irq.h>
#include "nfcv.h" #include "nfcv.h"
#include "nfc_util.h" #include "nfc_util.h"
#include <furi.h>
#include "furi_hal_nfc.h"
#define TAG "NfcV" #define TAG "NfcV"
@@ -82,4 +90,577 @@ ReturnCode nfcv_read_sysinfo(FuriHalNfcDevData* nfc_data, NfcVData* data) {
FURI_LOG_D(TAG, "Failed: %d", ret); FURI_LOG_D(TAG, "Failed: %d", ret);
return ret; return ret;
} }
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;
}

View File

@@ -8,10 +8,73 @@
#include <furi_hal_nfc.h> #include <furi_hal_nfc.h>
#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_TOTAL_BLOCKS_MAX 32
#define NFCV_BLOCK_SIZE 4 #define NFCV_BLOCK_SIZE 4
#define NFCV_MAX_DUMP_SIZE (NFCV_BLOCK_SIZE*NFCV_TOTAL_BLOCKS_MAX) #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 { typedef enum {
NfcVAuthMethodManual, NfcVAuthMethodManual,
NfcVAuthMethodTonieBox, NfcVAuthMethodTonieBox,
@@ -39,8 +102,10 @@ typedef struct {
uint8_t block_num; uint8_t block_num;
uint8_t block_size; uint8_t block_size;
uint8_t data[NFCV_MAX_DUMP_SIZE]; uint8_t data[NFCV_MAX_DUMP_SIZE];
bool privacy;
char error[32]; char error[32];
char last_command[128];
} NfcVData; } NfcVData;
typedef struct { typedef struct {
@@ -51,4 +116,8 @@ typedef struct {
ReturnCode nfcv_read_blocks(NfcVReader* reader, NfcVData* data); ReturnCode nfcv_read_blocks(NfcVReader* reader, NfcVData* data);
ReturnCode nfcv_read_sysinfo(FuriHalNfcDevData* nfc_data, NfcVData* data); ReturnCode nfcv_read_sysinfo(FuriHalNfcDevData* nfc_data, NfcVData* data);
ReturnCode nfcv_inventory(uint8_t* uid); 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);

View File

@@ -14,20 +14,6 @@ bool slix_l_check_card_type(uint8_t UID0, uint8_t UID1, uint8_t UID2) {
return false; 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) { ReturnCode slix_l_get_random(uint8_t* rand) {
uint16_t received = 0; uint16_t received = 0;

View File

@@ -11,7 +11,6 @@
bool slix_l_check_card_type(uint8_t UID0, uint8_t UID1, uint8_t UID2); 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_get_random(uint8_t* rand);
ReturnCode slix_l_unlock(uint32_t id, uint8_t* rand, uint32_t password); ReturnCode slix_l_unlock(uint32_t id, uint8_t* rand, uint32_t password);