mirror of
https://github.com/DarkFlippers/unleashed-firmware.git
synced 2024-12-23 05:14:16 +03:00
added ISO15693 (NfcV) reading, saving, emulating and revealing from privacy mode (unlock) (#2316)
* added support for ISO15693 (NfcV) emulation, added support for reading SLIX tags * SLIX: fixed crash situation when an invalid password was requested * ISO15693: show emulate menu when opening file * rename NfcV emulate scene to match other NfcV names * optimize allocation size for signals * ISO15693: further optimizations of allocation and free code * ISO15693: reduce latency on state machine reset * respond with block security status when option flag is set * increased maximum memory size to match standard added security status handling/load/save added SELECT/QUIET handling more fine grained allocation routines and checks fix memset sizes * added "Listen NfcV Reader" to sniff traffic from reader to card * added correct description to delete menu * also added DSFID/AFI handling and locking * increase sniff log size * scale NfcV frequency a bit, add echo mode, fix signal level at the end * use symbolic modulated/unmodulated GPIO levels * honor AFI field, decrease verbosity and removed debug code * refactor defines for less namespace pollution by using NFCV_ prefixes * correct an oversight that original cards return an generic error when addressing outside block range * use inverse modulation, increasing readable range significantly * rework and better document nfc chip initialization * nfcv code review fixes * Disable accidentally left on signal debug gpio output * Improve NFCV Read/Info GUIs. Authored by @xMasterX, committed by @nvx * Fix crash that occurs when you exit from NFCV emulation and start it again. Authored by @xMasterX, committed by @nvx * Remove delay from emulation loop. This improves compatibility when the reader is Android. * Lib: digital signal debug output pin info Co-authored-by: Tiernan Messmer <tiernan.messmer@gmail.com> Co-authored-by: MX <10697207+xMasterX@users.noreply.github.com> Co-authored-by: gornekich <n.gorbadey@gmail.com> Co-authored-by: あく <alleteam@gmail.com>
This commit is contained in:
parent
436194e6c7
commit
c186d2b0cc
@ -12,4 +12,6 @@ enum NfcCustomEvent {
|
||||
NfcCustomEventDictAttackSkip,
|
||||
NfcCustomEventRpcLoad,
|
||||
NfcCustomEventRpcSessionClose,
|
||||
NfcCustomEventUpdateLog,
|
||||
NfcCustomEventSaveShadow,
|
||||
};
|
||||
|
@ -290,6 +290,9 @@ int32_t nfc_app(void* p) {
|
||||
} else if(nfc->dev->format == NfcDeviceSaveFormatMifareClassic) {
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicEmulate);
|
||||
DOLPHIN_DEED(DolphinDeedNfcEmulate);
|
||||
} else if(nfc->dev->format == NfcDeviceSaveFormatNfcV) {
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVEmulate);
|
||||
DOLPHIN_DEED(DolphinDeedNfcEmulate);
|
||||
} else if(nfc->dev->format == NfcDeviceSaveFormatBankCard) {
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneDeviceInfo);
|
||||
} else {
|
||||
|
@ -14,6 +14,13 @@ ADD_SCENE(nfc, file_select, FileSelect)
|
||||
ADD_SCENE(nfc, emulate_uid, EmulateUid)
|
||||
ADD_SCENE(nfc, nfca_read_success, NfcaReadSuccess)
|
||||
ADD_SCENE(nfc, nfca_menu, NfcaMenu)
|
||||
ADD_SCENE(nfc, nfcv_menu, NfcVMenu)
|
||||
ADD_SCENE(nfc, nfcv_unlock_menu, NfcVUnlockMenu)
|
||||
ADD_SCENE(nfc, nfcv_key_input, NfcVKeyInput)
|
||||
ADD_SCENE(nfc, nfcv_unlock, NfcVUnlock)
|
||||
ADD_SCENE(nfc, nfcv_emulate, NfcVEmulate)
|
||||
ADD_SCENE(nfc, nfcv_sniff, NfcVSniff)
|
||||
ADD_SCENE(nfc, nfcv_read_success, NfcVReadSuccess)
|
||||
ADD_SCENE(nfc, mf_ultralight_read_success, MfUltralightReadSuccess)
|
||||
ADD_SCENE(nfc, mf_ultralight_data, MfUltralightData)
|
||||
ADD_SCENE(nfc, mf_ultralight_menu, MfUltralightMenu)
|
||||
|
@ -31,6 +31,8 @@ void nfc_scene_delete_on_enter(void* context) {
|
||||
nfc->widget, 64, 24, AlignCenter, AlignTop, FontSecondary, furi_string_get_cstr(temp_str));
|
||||
|
||||
NfcProtocol protocol = nfc->dev->dev_data.protocol;
|
||||
const char* nfc_type = "NFC-A";
|
||||
|
||||
if(protocol == NfcDeviceProtocolEMV) {
|
||||
furi_string_set(temp_str, "EMV bank card");
|
||||
} else if(protocol == NfcDeviceProtocolMifareUl) {
|
||||
@ -39,12 +41,15 @@ void nfc_scene_delete_on_enter(void* context) {
|
||||
furi_string_set(temp_str, nfc_mf_classic_type(nfc->dev->dev_data.mf_classic_data.type));
|
||||
} else if(protocol == NfcDeviceProtocolMifareDesfire) {
|
||||
furi_string_set(temp_str, "MIFARE DESFire");
|
||||
} else if(protocol == NfcDeviceProtocolNfcV) {
|
||||
furi_string_set(temp_str, "ISO15693 tag");
|
||||
nfc_type = "NFC-V";
|
||||
} else {
|
||||
furi_string_set(temp_str, "Unknown ISO tag");
|
||||
}
|
||||
widget_add_string_element(
|
||||
nfc->widget, 64, 34, AlignCenter, AlignTop, FontSecondary, furi_string_get_cstr(temp_str));
|
||||
widget_add_string_element(nfc->widget, 64, 44, AlignCenter, AlignTop, FontSecondary, "NFC-A");
|
||||
widget_add_string_element(nfc->widget, 64, 44, AlignCenter, AlignTop, FontSecondary, nfc_type);
|
||||
furi_string_free(temp_str);
|
||||
|
||||
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
|
||||
|
@ -4,6 +4,8 @@ enum SubmenuIndex {
|
||||
SubmenuIndexReadCardType,
|
||||
SubmenuIndexMfClassicKeys,
|
||||
SubmenuIndexMfUltralightUnlock,
|
||||
SubmenuIndexNfcVUnlock,
|
||||
SubmenuIndexNfcVSniff,
|
||||
};
|
||||
|
||||
void nfc_scene_extra_actions_submenu_callback(void* context, uint32_t index) {
|
||||
@ -34,6 +36,18 @@ void nfc_scene_extra_actions_on_enter(void* context) {
|
||||
SubmenuIndexMfUltralightUnlock,
|
||||
nfc_scene_extra_actions_submenu_callback,
|
||||
nfc);
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
"Unlock SLIX-L",
|
||||
SubmenuIndexNfcVUnlock,
|
||||
nfc_scene_extra_actions_submenu_callback,
|
||||
nfc);
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
"Listen NfcV Reader",
|
||||
SubmenuIndexNfcVSniff,
|
||||
nfc_scene_extra_actions_submenu_callback,
|
||||
nfc);
|
||||
submenu_set_selected_item(
|
||||
submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneExtraActions));
|
||||
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu);
|
||||
@ -58,6 +72,12 @@ bool nfc_scene_extra_actions_on_event(void* context, SceneManagerEvent event) {
|
||||
scene_manager_set_scene_state(nfc->scene_manager, NfcSceneReadCardType, 0);
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneReadCardType);
|
||||
consumed = true;
|
||||
} else if(event.event == SubmenuIndexNfcVUnlock) {
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVUnlockMenu);
|
||||
consumed = true;
|
||||
} else if(event.event == SubmenuIndexNfcVSniff) {
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVSniff);
|
||||
consumed = true;
|
||||
}
|
||||
scene_manager_set_scene_state(nfc->scene_manager, NfcSceneExtraActions, event.event);
|
||||
}
|
||||
|
@ -41,19 +41,165 @@ 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) {
|
||||
switch(dev_data->nfcv_data.sub_type) {
|
||||
case NfcVTypePlain:
|
||||
furi_string_cat_printf(temp_str, "\e#ISO15693\n");
|
||||
break;
|
||||
case NfcVTypeSlix:
|
||||
furi_string_cat_printf(temp_str, "\e#ISO15693 SLIX\n");
|
||||
break;
|
||||
case NfcVTypeSlixS:
|
||||
furi_string_cat_printf(temp_str, "\e#ISO15693 SLIX-S\n");
|
||||
break;
|
||||
case NfcVTypeSlixL:
|
||||
furi_string_cat_printf(temp_str, "\e#ISO15693 SLIX-L\n");
|
||||
break;
|
||||
case NfcVTypeSlix2:
|
||||
furi_string_cat_printf(temp_str, "\e#ISO15693 SLIX2\n");
|
||||
break;
|
||||
default:
|
||||
furi_string_cat_printf(temp_str, "\e#ISO15693 (unknown)\n");
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
furi_string_cat_printf(temp_str, "\e#Unknown ISO tag\n");
|
||||
}
|
||||
|
||||
// Set tag iso data
|
||||
char iso_type = FURI_BIT(nfc_data->sak, 5) ? '4' : '3';
|
||||
furi_string_cat_printf(temp_str, "ISO 14443-%c (NFC-A)\n", iso_type);
|
||||
furi_string_cat_printf(temp_str, "UID:");
|
||||
for(size_t i = 0; i < nfc_data->uid_len; i++) {
|
||||
furi_string_cat_printf(temp_str, " %02X", nfc_data->uid[i]);
|
||||
if(protocol == NfcDeviceProtocolNfcV) {
|
||||
NfcVData* nfcv_data = &nfc->dev->dev_data.nfcv_data;
|
||||
|
||||
furi_string_cat_printf(temp_str, "UID:\n");
|
||||
for(size_t i = 0; i < nfc_data->uid_len; i++) {
|
||||
furi_string_cat_printf(temp_str, " %02X", nfc_data->uid[i]);
|
||||
}
|
||||
furi_string_cat_printf(temp_str, "\n");
|
||||
|
||||
furi_string_cat_printf(
|
||||
temp_str,
|
||||
"DSFID: %02X %s\n",
|
||||
nfcv_data->dsfid,
|
||||
(nfcv_data->security_status[0] & NfcVLockBitDsfid) ? "(locked)" : "");
|
||||
furi_string_cat_printf(
|
||||
temp_str,
|
||||
"AFI: %02X %s\n",
|
||||
nfcv_data->afi,
|
||||
(nfcv_data->security_status[0] & NfcVLockBitAfi) ? "(locked)" : "");
|
||||
furi_string_cat_printf(temp_str, "IC Ref: %02X\n", nfcv_data->ic_ref);
|
||||
furi_string_cat_printf(temp_str, "Blocks: %02X\n", nfcv_data->block_num);
|
||||
furi_string_cat_printf(temp_str, "Blocksize: %02X\n", nfcv_data->block_size);
|
||||
|
||||
switch(dev_data->nfcv_data.sub_type) {
|
||||
case NfcVTypePlain:
|
||||
furi_string_cat_printf(temp_str, "Type: Plain\n");
|
||||
break;
|
||||
case NfcVTypeSlix:
|
||||
furi_string_cat_printf(temp_str, "Type: SLIX\n");
|
||||
furi_string_cat_printf(temp_str, "Keys:\n");
|
||||
furi_string_cat_printf(
|
||||
temp_str,
|
||||
" EAS %08llX\n",
|
||||
nfc_util_bytes2num(nfcv_data->sub_data.slix.key_eas, 4));
|
||||
break;
|
||||
case NfcVTypeSlixS:
|
||||
furi_string_cat_printf(temp_str, "Type: SLIX-S\n");
|
||||
furi_string_cat_printf(temp_str, "Keys:\n");
|
||||
furi_string_cat_printf(
|
||||
temp_str,
|
||||
" Read %08llX\n",
|
||||
nfc_util_bytes2num(nfcv_data->sub_data.slix.key_read, 4));
|
||||
furi_string_cat_printf(
|
||||
temp_str,
|
||||
" Write %08llX\n",
|
||||
nfc_util_bytes2num(nfcv_data->sub_data.slix.key_write, 4));
|
||||
furi_string_cat_printf(
|
||||
temp_str,
|
||||
" Privacy %08llX\n",
|
||||
nfc_util_bytes2num(nfcv_data->sub_data.slix.key_privacy, 4));
|
||||
furi_string_cat_printf(
|
||||
temp_str,
|
||||
" Destroy %08llX\n",
|
||||
nfc_util_bytes2num(nfcv_data->sub_data.slix.key_destroy, 4));
|
||||
furi_string_cat_printf(
|
||||
temp_str,
|
||||
" EAS %08llX\n",
|
||||
nfc_util_bytes2num(nfcv_data->sub_data.slix.key_eas, 4));
|
||||
break;
|
||||
case NfcVTypeSlixL:
|
||||
furi_string_cat_printf(temp_str, "Type: SLIX-L\n");
|
||||
furi_string_cat_printf(temp_str, "Keys:\n");
|
||||
furi_string_cat_printf(
|
||||
temp_str,
|
||||
" Privacy %08llX\n",
|
||||
nfc_util_bytes2num(nfcv_data->sub_data.slix.key_privacy, 4));
|
||||
furi_string_cat_printf(
|
||||
temp_str,
|
||||
" Destroy %08llX\n",
|
||||
nfc_util_bytes2num(nfcv_data->sub_data.slix.key_destroy, 4));
|
||||
furi_string_cat_printf(
|
||||
temp_str,
|
||||
" EAS %08llX\n",
|
||||
nfc_util_bytes2num(nfcv_data->sub_data.slix.key_eas, 4));
|
||||
break;
|
||||
case NfcVTypeSlix2:
|
||||
furi_string_cat_printf(temp_str, "Type: SLIX2\n");
|
||||
furi_string_cat_printf(temp_str, "Keys:\n");
|
||||
furi_string_cat_printf(
|
||||
temp_str,
|
||||
" Read %08llX\n",
|
||||
nfc_util_bytes2num(nfcv_data->sub_data.slix.key_read, 4));
|
||||
furi_string_cat_printf(
|
||||
temp_str,
|
||||
" Write %08llX\n",
|
||||
nfc_util_bytes2num(nfcv_data->sub_data.slix.key_write, 4));
|
||||
furi_string_cat_printf(
|
||||
temp_str,
|
||||
" Privacy %08llX\n",
|
||||
nfc_util_bytes2num(nfcv_data->sub_data.slix.key_privacy, 4));
|
||||
furi_string_cat_printf(
|
||||
temp_str,
|
||||
" Destroy %08llX\n",
|
||||
nfc_util_bytes2num(nfcv_data->sub_data.slix.key_destroy, 4));
|
||||
furi_string_cat_printf(
|
||||
temp_str,
|
||||
" EAS %08llX\n",
|
||||
nfc_util_bytes2num(nfcv_data->sub_data.slix.key_eas, 4));
|
||||
break;
|
||||
default:
|
||||
furi_string_cat_printf(temp_str, "\e#ISO15693 (unknown)\n");
|
||||
break;
|
||||
}
|
||||
|
||||
furi_string_cat_printf(
|
||||
temp_str, "Data (%d byte)\n", nfcv_data->block_num * nfcv_data->block_size);
|
||||
|
||||
int maxBlocks = nfcv_data->block_num;
|
||||
if(maxBlocks > 32) {
|
||||
maxBlocks = 32;
|
||||
furi_string_cat_printf(temp_str, "(truncated to %d blocks)\n", maxBlocks);
|
||||
}
|
||||
|
||||
for(int block = 0; block < maxBlocks; block++) {
|
||||
const char* status = (nfcv_data->security_status[block] & 0x01) ? "(lck)" : "";
|
||||
for(int pos = 0; pos < nfcv_data->block_size; pos++) {
|
||||
furi_string_cat_printf(
|
||||
temp_str, " %02X", nfcv_data->data[block * nfcv_data->block_size + pos]);
|
||||
}
|
||||
furi_string_cat_printf(temp_str, " %s\n", status);
|
||||
}
|
||||
|
||||
} else {
|
||||
char iso_type = FURI_BIT(nfc_data->sak, 5) ? '4' : '3';
|
||||
furi_string_cat_printf(temp_str, "ISO 14443-%c (NFC-A)\n", iso_type);
|
||||
furi_string_cat_printf(temp_str, "UID:");
|
||||
for(size_t i = 0; i < nfc_data->uid_len; i++) {
|
||||
furi_string_cat_printf(temp_str, " %02X", nfc_data->uid[i]);
|
||||
}
|
||||
furi_string_cat_printf(
|
||||
temp_str, "\nATQA: %02X %02X ", nfc_data->atqa[1], nfc_data->atqa[0]);
|
||||
furi_string_cat_printf(temp_str, " SAK: %02X", nfc_data->sak);
|
||||
}
|
||||
furi_string_cat_printf(temp_str, "\nATQA: %02X %02X ", nfc_data->atqa[1], nfc_data->atqa[0]);
|
||||
furi_string_cat_printf(temp_str, " SAK: %02X", nfc_data->sak);
|
||||
|
||||
// Set application specific data
|
||||
if(protocol == NfcDeviceProtocolMifareDesfire) {
|
||||
@ -139,6 +285,8 @@ bool nfc_scene_nfc_data_info_on_event(void* context, SceneManagerEvent event) {
|
||||
consumed = true;
|
||||
} else if(protocol == NfcDeviceProtocolMifareClassic) {
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicData);
|
||||
} else if(protocol == NfcDeviceProtocolNfcV) {
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVMenu);
|
||||
consumed = true;
|
||||
}
|
||||
}
|
||||
|
169
applications/main/nfc/scenes/nfc_scene_nfcv_emulate.c
Normal file
169
applications/main/nfc/scenes/nfc_scene_nfcv_emulate.c
Normal file
@ -0,0 +1,169 @@
|
||||
#include "../nfc_i.h"
|
||||
|
||||
#define NFC_SCENE_EMULATE_NFCV_LOG_SIZE_MAX (200)
|
||||
|
||||
enum {
|
||||
NfcSceneNfcVEmulateStateWidget,
|
||||
NfcSceneNfcVEmulateStateTextBox,
|
||||
};
|
||||
|
||||
bool nfc_scene_nfcv_emulate_worker_callback(NfcWorkerEvent event, void* context) {
|
||||
furi_assert(context);
|
||||
Nfc* nfc = context;
|
||||
|
||||
switch(event) {
|
||||
case NfcWorkerEventNfcVCommandExecuted:
|
||||
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
|
||||
view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventUpdateLog);
|
||||
}
|
||||
break;
|
||||
case NfcWorkerEventNfcVContentChanged:
|
||||
view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventSaveShadow);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void nfc_scene_nfcv_emulate_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_scene_nfcv_emulate_textbox_callback(void* context) {
|
||||
furi_assert(context);
|
||||
Nfc* nfc = context;
|
||||
view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit);
|
||||
}
|
||||
|
||||
static void nfc_scene_nfcv_emulate_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_NFC_dolphin_emulation_47x61);
|
||||
widget_add_string_multiline_element(
|
||||
widget, 87, 13, AlignCenter, AlignTop, FontPrimary, "Emulating\nNFC V");
|
||||
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, 52, 40, 70, 21, AlignCenter, AlignTop, furi_string_get_cstr(info_str), true);
|
||||
furi_string_free(info_str);
|
||||
if(data_received) {
|
||||
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
|
||||
widget_add_button_element(
|
||||
widget, GuiButtonTypeCenter, "Log", nfc_scene_nfcv_emulate_widget_callback, nfc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void nfc_scene_nfcv_emulate_on_enter(void* context) {
|
||||
Nfc* nfc = context;
|
||||
|
||||
// Setup Widget
|
||||
nfc_scene_nfcv_emulate_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);
|
||||
text_box_set_text(text_box, "");
|
||||
furi_string_reset(nfc->text_box_store);
|
||||
|
||||
// Set Widget state and view
|
||||
scene_manager_set_scene_state(
|
||||
nfc->scene_manager, NfcSceneNfcVEmulate, NfcSceneNfcVEmulateStateWidget);
|
||||
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_scene_nfcv_emulate_worker_callback,
|
||||
nfc);
|
||||
|
||||
nfc_blink_emulate_start(nfc);
|
||||
}
|
||||
|
||||
bool nfc_scene_nfcv_emulate_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, NfcSceneNfcVEmulate);
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == NfcCustomEventUpdateLog) {
|
||||
// Add data button to widget if data is received for the first time
|
||||
if(strlen(nfcv_data->last_command) > 0) {
|
||||
if(!furi_string_size(nfc->text_box_store)) {
|
||||
nfc_scene_nfcv_emulate_widget_config(nfc, true);
|
||||
}
|
||||
/* use the last n bytes from the log so there's enough space for the new log entry */
|
||||
size_t maxSize =
|
||||
NFC_SCENE_EMULATE_NFCV_LOG_SIZE_MAX - (strlen(nfcv_data->last_command) + 1);
|
||||
if(furi_string_size(nfc->text_box_store) >= maxSize) {
|
||||
furi_string_right(nfc->text_box_store, (strlen(nfcv_data->last_command) + 1));
|
||||
}
|
||||
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));
|
||||
|
||||
/* clear previously logged command */
|
||||
strcpy(nfcv_data->last_command, "");
|
||||
}
|
||||
consumed = true;
|
||||
} else if(event.event == NfcCustomEventSaveShadow) {
|
||||
if(furi_string_size(nfc->dev->load_path)) {
|
||||
nfc_device_save_shadow(nfc->dev, furi_string_get_cstr(nfc->dev->load_path));
|
||||
}
|
||||
consumed = true;
|
||||
} else if(event.event == GuiButtonTypeCenter && state == NfcSceneNfcVEmulateStateWidget) {
|
||||
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
|
||||
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox);
|
||||
scene_manager_set_scene_state(
|
||||
nfc->scene_manager, NfcSceneNfcVEmulate, NfcSceneNfcVEmulateStateTextBox);
|
||||
}
|
||||
consumed = true;
|
||||
} else if(event.event == NfcCustomEventViewExit && state == NfcSceneNfcVEmulateStateTextBox) {
|
||||
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
|
||||
scene_manager_set_scene_state(
|
||||
nfc->scene_manager, NfcSceneNfcVEmulate, NfcSceneNfcVEmulateStateWidget);
|
||||
consumed = true;
|
||||
}
|
||||
} else if(event.type == SceneManagerEventTypeBack) {
|
||||
if(state == NfcSceneNfcVEmulateStateTextBox) {
|
||||
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
|
||||
scene_manager_set_scene_state(
|
||||
nfc->scene_manager, NfcSceneNfcVEmulate, NfcSceneNfcVEmulateStateWidget);
|
||||
consumed = true;
|
||||
}
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void nfc_scene_nfcv_emulate_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);
|
||||
}
|
48
applications/main/nfc/scenes/nfc_scene_nfcv_key_input.c
Normal file
48
applications/main/nfc/scenes/nfc_scene_nfcv_key_input.c
Normal file
@ -0,0 +1,48 @@
|
||||
#include "../nfc_i.h"
|
||||
#include <dolphin/dolphin.h>
|
||||
|
||||
void nfc_scene_nfcv_key_input_byte_input_callback(void* context) {
|
||||
Nfc* nfc = context;
|
||||
NfcVSlixData* data = &nfc->dev->dev_data.nfcv_data.sub_data.slix;
|
||||
|
||||
memcpy(data->key_privacy, nfc->byte_input_store, 4);
|
||||
view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventByteInputDone);
|
||||
}
|
||||
|
||||
void nfc_scene_nfcv_key_input_on_enter(void* context) {
|
||||
Nfc* nfc = context;
|
||||
|
||||
// Setup view
|
||||
ByteInput* byte_input = nfc->byte_input;
|
||||
byte_input_set_header_text(byte_input, "Enter The Password In Hex");
|
||||
byte_input_set_result_callback(
|
||||
byte_input,
|
||||
nfc_scene_nfcv_key_input_byte_input_callback,
|
||||
NULL,
|
||||
nfc,
|
||||
nfc->byte_input_store,
|
||||
4);
|
||||
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewByteInput);
|
||||
}
|
||||
|
||||
bool nfc_scene_nfcv_key_input_on_event(void* context, SceneManagerEvent event) {
|
||||
Nfc* nfc = context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == NfcCustomEventByteInputDone) {
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVUnlock);
|
||||
DOLPHIN_DEED(DolphinDeedNfcRead);
|
||||
consumed = true;
|
||||
}
|
||||
}
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void nfc_scene_nfcv_key_input_on_exit(void* context) {
|
||||
Nfc* nfc = context;
|
||||
|
||||
// Clear view
|
||||
byte_input_set_result_callback(nfc->byte_input, NULL, NULL, NULL, NULL, 0);
|
||||
byte_input_set_header_text(nfc->byte_input, "");
|
||||
}
|
68
applications/main/nfc/scenes/nfc_scene_nfcv_menu.c
Normal file
68
applications/main/nfc/scenes/nfc_scene_nfcv_menu.c
Normal file
@ -0,0 +1,68 @@
|
||||
#include "../nfc_i.h"
|
||||
#include <dolphin/dolphin.h>
|
||||
|
||||
enum SubmenuIndex {
|
||||
SubmenuIndexSave,
|
||||
SubmenuIndexEmulate,
|
||||
SubmenuIndexInfo,
|
||||
};
|
||||
|
||||
void nfc_scene_nfcv_menu_submenu_callback(void* context, uint32_t index) {
|
||||
Nfc* nfc = context;
|
||||
|
||||
view_dispatcher_send_custom_event(nfc->view_dispatcher, index);
|
||||
}
|
||||
|
||||
void nfc_scene_nfcv_menu_on_enter(void* context) {
|
||||
Nfc* nfc = context;
|
||||
Submenu* submenu = nfc->submenu;
|
||||
|
||||
submenu_add_item(
|
||||
submenu, "Emulate", SubmenuIndexEmulate, nfc_scene_nfcv_menu_submenu_callback, nfc);
|
||||
submenu_add_item(submenu, "Save", SubmenuIndexSave, nfc_scene_nfcv_menu_submenu_callback, nfc);
|
||||
submenu_add_item(submenu, "Info", SubmenuIndexInfo, nfc_scene_nfcv_menu_submenu_callback, nfc);
|
||||
|
||||
submenu_set_selected_item(
|
||||
nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneNfcVMenu));
|
||||
|
||||
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu);
|
||||
}
|
||||
|
||||
bool nfc_scene_nfcv_menu_on_event(void* context, SceneManagerEvent event) {
|
||||
Nfc* nfc = context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == SubmenuIndexSave) {
|
||||
nfc->dev->format = NfcDeviceSaveFormatNfcV;
|
||||
// Clear device name
|
||||
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, NfcSceneNfcVEmulate);
|
||||
if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) {
|
||||
DOLPHIN_DEED(DolphinDeedNfcAddEmulate);
|
||||
} else {
|
||||
DOLPHIN_DEED(DolphinDeedNfcEmulate);
|
||||
}
|
||||
consumed = true;
|
||||
} else if(event.event == SubmenuIndexInfo) {
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcDataInfo);
|
||||
consumed = true;
|
||||
}
|
||||
scene_manager_set_scene_state(nfc->scene_manager, NfcSceneNfcVMenu, event.event);
|
||||
|
||||
} else if(event.type == SceneManagerEventTypeBack) {
|
||||
consumed = scene_manager_previous_scene(nfc->scene_manager);
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void nfc_scene_nfcv_menu_on_exit(void* context) {
|
||||
Nfc* nfc = context;
|
||||
|
||||
// Clear view
|
||||
submenu_reset(nfc->submenu);
|
||||
}
|
94
applications/main/nfc/scenes/nfc_scene_nfcv_read_success.c
Normal file
94
applications/main/nfc/scenes/nfc_scene_nfcv_read_success.c
Normal file
@ -0,0 +1,94 @@
|
||||
#include "../nfc_i.h"
|
||||
|
||||
void nfc_scene_nfcv_read_success_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_scene_nfcv_read_success_on_enter(void* context) {
|
||||
Nfc* nfc = context;
|
||||
NfcDeviceData* dev_data = &nfc->dev->dev_data;
|
||||
FuriHalNfcDevData* nfc_data = &nfc->dev->dev_data.nfc_data;
|
||||
NfcVData* nfcv_data = &nfc->dev->dev_data.nfcv_data;
|
||||
// Setup view
|
||||
Widget* widget = nfc->widget;
|
||||
widget_add_button_element(
|
||||
widget, GuiButtonTypeLeft, "Retry", nfc_scene_nfcv_read_success_widget_callback, nfc);
|
||||
widget_add_button_element(
|
||||
widget, GuiButtonTypeRight, "More", nfc_scene_nfcv_read_success_widget_callback, nfc);
|
||||
|
||||
FuriString* temp_str = furi_string_alloc();
|
||||
|
||||
switch(dev_data->nfcv_data.sub_type) {
|
||||
case NfcVTypePlain:
|
||||
furi_string_cat_printf(temp_str, "\e#ISO15693\n");
|
||||
break;
|
||||
case NfcVTypeSlix:
|
||||
furi_string_cat_printf(temp_str, "\e#ISO15693 SLIX\n");
|
||||
break;
|
||||
case NfcVTypeSlixS:
|
||||
furi_string_cat_printf(temp_str, "\e#ISO15693 SLIX-S\n");
|
||||
break;
|
||||
case NfcVTypeSlixL:
|
||||
furi_string_cat_printf(temp_str, "\e#ISO15693 SLIX-L\n");
|
||||
break;
|
||||
case NfcVTypeSlix2:
|
||||
furi_string_cat_printf(temp_str, "\e#ISO15693 SLIX2\n");
|
||||
break;
|
||||
default:
|
||||
furi_string_cat_printf(temp_str, "\e#ISO15693 (unknown)\n");
|
||||
break;
|
||||
}
|
||||
furi_string_cat_printf(temp_str, "UID:");
|
||||
for(size_t i = 0; i < nfc_data->uid_len; i++) {
|
||||
furi_string_cat_printf(temp_str, " %02X", nfc_data->uid[i]);
|
||||
}
|
||||
furi_string_cat_printf(temp_str, "\n");
|
||||
furi_string_cat_printf(temp_str, "Blocks: %02X\n", nfcv_data->block_num);
|
||||
furi_string_cat_printf(temp_str, "Blocksize: %02X\n", nfcv_data->block_size);
|
||||
|
||||
widget_add_text_scroll_element(widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str));
|
||||
furi_string_free(temp_str);
|
||||
|
||||
notification_message_block(nfc->notifications, &sequence_set_green_255);
|
||||
|
||||
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
|
||||
}
|
||||
|
||||
bool nfc_scene_nfcv_read_success_on_event(void* context, SceneManagerEvent event) {
|
||||
Nfc* nfc = context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == GuiButtonTypeLeft) {
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneRetryConfirm);
|
||||
consumed = true;
|
||||
} else if(event.event == GuiButtonTypeRight) {
|
||||
// Clear device name
|
||||
nfc_device_set_name(nfc->dev, "");
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVMenu);
|
||||
consumed = true;
|
||||
}
|
||||
} else if(event.type == SceneManagerEventTypeBack) {
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneExitConfirm);
|
||||
consumed = true;
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void nfc_scene_nfcv_read_success_on_exit(void* context) {
|
||||
Nfc* nfc = context;
|
||||
|
||||
notification_message_block(nfc->notifications, &sequence_reset_green);
|
||||
|
||||
// Clear view
|
||||
widget_reset(nfc->widget);
|
||||
}
|
155
applications/main/nfc/scenes/nfc_scene_nfcv_sniff.c
Normal file
155
applications/main/nfc/scenes/nfc_scene_nfcv_sniff.c
Normal file
@ -0,0 +1,155 @@
|
||||
#include "../nfc_i.h"
|
||||
|
||||
#define NFC_SCENE_EMULATE_NFCV_LOG_SIZE_MAX (800)
|
||||
|
||||
enum {
|
||||
NfcSceneNfcVSniffStateWidget,
|
||||
NfcSceneNfcVSniffStateTextBox,
|
||||
};
|
||||
|
||||
bool nfc_scene_nfcv_sniff_worker_callback(NfcWorkerEvent event, void* context) {
|
||||
UNUSED(event);
|
||||
furi_assert(context);
|
||||
Nfc* nfc = context;
|
||||
|
||||
switch(event) {
|
||||
case NfcWorkerEventNfcVCommandExecuted:
|
||||
view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventUpdateLog);
|
||||
break;
|
||||
case NfcWorkerEventNfcVContentChanged:
|
||||
view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventSaveShadow);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void nfc_scene_nfcv_sniff_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_scene_nfcv_sniff_textbox_callback(void* context) {
|
||||
furi_assert(context);
|
||||
Nfc* nfc = context;
|
||||
view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit);
|
||||
}
|
||||
|
||||
static void nfc_scene_nfcv_sniff_widget_config(Nfc* nfc, bool data_received) {
|
||||
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, "Listen NfcV");
|
||||
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_nfcv_sniff_widget_callback, nfc);
|
||||
}
|
||||
}
|
||||
|
||||
void nfc_scene_nfcv_sniff_on_enter(void* context) {
|
||||
Nfc* nfc = context;
|
||||
|
||||
// Setup Widget
|
||||
nfc_scene_nfcv_sniff_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);
|
||||
text_box_set_text(text_box, "");
|
||||
furi_string_reset(nfc->text_box_store);
|
||||
|
||||
// Set Widget state and view
|
||||
scene_manager_set_scene_state(
|
||||
nfc->scene_manager, NfcSceneNfcVSniff, NfcSceneNfcVSniffStateWidget);
|
||||
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,
|
||||
NfcWorkerStateNfcVSniff,
|
||||
&nfc->dev->dev_data,
|
||||
nfc_scene_nfcv_sniff_worker_callback,
|
||||
nfc);
|
||||
|
||||
nfc_blink_emulate_start(nfc);
|
||||
}
|
||||
|
||||
bool nfc_scene_nfcv_sniff_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, NfcSceneNfcVSniff);
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == NfcCustomEventUpdateLog) {
|
||||
// Add data button to widget if data is received for the first time
|
||||
if(strlen(nfcv_data->last_command) > 0) {
|
||||
if(!furi_string_size(nfc->text_box_store)) {
|
||||
nfc_scene_nfcv_sniff_widget_config(nfc, true);
|
||||
}
|
||||
/* use the last n bytes from the log so there's enough space for the new log entry */
|
||||
size_t maxSize =
|
||||
NFC_SCENE_EMULATE_NFCV_LOG_SIZE_MAX - (strlen(nfcv_data->last_command) + 1);
|
||||
if(furi_string_size(nfc->text_box_store) >= maxSize) {
|
||||
furi_string_right(nfc->text_box_store, (strlen(nfcv_data->last_command) + 1));
|
||||
}
|
||||
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));
|
||||
|
||||
/* clear previously logged command */
|
||||
strcpy(nfcv_data->last_command, "");
|
||||
}
|
||||
consumed = true;
|
||||
} else if(event.event == NfcCustomEventSaveShadow) {
|
||||
if(furi_string_size(nfc->dev->load_path)) {
|
||||
nfc_device_save_shadow(nfc->dev, furi_string_get_cstr(nfc->dev->load_path));
|
||||
}
|
||||
consumed = true;
|
||||
} else if(event.event == GuiButtonTypeCenter && state == NfcSceneNfcVSniffStateWidget) {
|
||||
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox);
|
||||
scene_manager_set_scene_state(
|
||||
nfc->scene_manager, NfcSceneNfcVSniff, NfcSceneNfcVSniffStateTextBox);
|
||||
consumed = true;
|
||||
} else if(event.event == NfcCustomEventViewExit && state == NfcSceneNfcVSniffStateTextBox) {
|
||||
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
|
||||
scene_manager_set_scene_state(
|
||||
nfc->scene_manager, NfcSceneNfcVSniff, NfcSceneNfcVSniffStateWidget);
|
||||
consumed = true;
|
||||
}
|
||||
} else if(event.type == SceneManagerEventTypeBack) {
|
||||
if(state == NfcSceneNfcVSniffStateTextBox) {
|
||||
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
|
||||
scene_manager_set_scene_state(
|
||||
nfc->scene_manager, NfcSceneNfcVSniff, NfcSceneNfcVSniffStateWidget);
|
||||
consumed = true;
|
||||
}
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void nfc_scene_nfcv_sniff_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);
|
||||
}
|
154
applications/main/nfc/scenes/nfc_scene_nfcv_unlock.c
Normal file
154
applications/main/nfc/scenes/nfc_scene_nfcv_unlock.c
Normal file
@ -0,0 +1,154 @@
|
||||
#include "../nfc_i.h"
|
||||
#include <dolphin/dolphin.h>
|
||||
|
||||
typedef enum {
|
||||
NfcSceneNfcVUnlockStateIdle,
|
||||
NfcSceneNfcVUnlockStateDetecting,
|
||||
NfcSceneNfcVUnlockStateUnlocked,
|
||||
NfcSceneNfcVUnlockStateAlreadyUnlocked,
|
||||
NfcSceneNfcVUnlockStateNotSupportedCard,
|
||||
} NfcSceneNfcVUnlockState;
|
||||
|
||||
static bool nfc_scene_nfcv_unlock_worker_callback(NfcWorkerEvent event, void* context) {
|
||||
Nfc* nfc = context;
|
||||
NfcVSlixData* data = &nfc->dev->dev_data.nfcv_data.sub_data.slix;
|
||||
|
||||
if(event == NfcWorkerEventNfcVPassKey) {
|
||||
memcpy(data->key_privacy, nfc->byte_input_store, 4);
|
||||
} else {
|
||||
view_dispatcher_send_custom_event(nfc->view_dispatcher, event);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void nfc_scene_nfcv_unlock_popup_callback(void* context) {
|
||||
Nfc* nfc = context;
|
||||
view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit);
|
||||
}
|
||||
|
||||
void nfc_scene_nfcv_unlock_set_state(Nfc* nfc, NfcSceneNfcVUnlockState state) {
|
||||
FuriHalNfcDevData* nfc_data = &(nfc->dev->dev_data.nfc_data);
|
||||
NfcVData* nfcv_data = &(nfc->dev->dev_data.nfcv_data);
|
||||
|
||||
uint32_t curr_state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneNfcVUnlock);
|
||||
if(curr_state != state) {
|
||||
Popup* popup = nfc->popup;
|
||||
if(state == NfcSceneNfcVUnlockStateDetecting) {
|
||||
popup_reset(popup);
|
||||
popup_set_text(
|
||||
popup, "Put figurine on\nFlipper's back", 97, 24, AlignCenter, AlignTop);
|
||||
popup_set_icon(popup, 0, 8, &I_NFC_manual_60x50);
|
||||
} else if(state == NfcSceneNfcVUnlockStateUnlocked) {
|
||||
popup_reset(popup);
|
||||
|
||||
if(nfc_worker_get_state(nfc->worker) == NfcWorkerStateNfcVUnlockAndSave) {
|
||||
snprintf(
|
||||
nfc->dev->dev_name,
|
||||
sizeof(nfc->dev->dev_name),
|
||||
"SLIX_%02X%02X%02X%02X%02X%02X%02X%02X",
|
||||
nfc_data->uid[0],
|
||||
nfc_data->uid[1],
|
||||
nfc_data->uid[2],
|
||||
nfc_data->uid[3],
|
||||
nfc_data->uid[4],
|
||||
nfc_data->uid[5],
|
||||
nfc_data->uid[6],
|
||||
nfc_data->uid[7]);
|
||||
|
||||
nfc->dev->format = NfcDeviceSaveFormatNfcV;
|
||||
|
||||
if(nfc_save_file(nfc)) {
|
||||
popup_set_header(popup, "Successfully\nsaved", 94, 3, AlignCenter, AlignTop);
|
||||
} else {
|
||||
popup_set_header(
|
||||
popup, "Unlocked but\nsave failed!", 94, 3, AlignCenter, AlignTop);
|
||||
}
|
||||
} else {
|
||||
popup_set_header(popup, "Successfully\nunlocked", 94, 3, AlignCenter, AlignTop);
|
||||
}
|
||||
|
||||
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);
|
||||
popup_set_callback(popup, nfc_scene_nfcv_unlock_popup_callback);
|
||||
popup_set_timeout(popup, 1500);
|
||||
|
||||
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup);
|
||||
DOLPHIN_DEED(DolphinDeedNfcReadSuccess);
|
||||
|
||||
} else if(state == NfcSceneNfcVUnlockStateAlreadyUnlocked) {
|
||||
popup_reset(popup);
|
||||
|
||||
popup_set_header(popup, "Already\nUnlocked!", 94, 3, AlignCenter, AlignTop);
|
||||
popup_set_icon(popup, 0, 6, &I_RFIDDolphinSuccess_108x57);
|
||||
popup_set_context(popup, nfc);
|
||||
popup_set_callback(popup, nfc_scene_nfcv_unlock_popup_callback);
|
||||
popup_set_timeout(popup, 1500);
|
||||
|
||||
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup);
|
||||
} else if(state == NfcSceneNfcVUnlockStateNotSupportedCard) {
|
||||
popup_reset(popup);
|
||||
popup_set_header(popup, "Wrong Type Of Card!", 64, 3, AlignCenter, AlignTop);
|
||||
popup_set_text(popup, nfcv_data->error, 4, 22, AlignLeft, AlignTop);
|
||||
popup_set_icon(popup, 73, 20, &I_DolphinCommon_56x48);
|
||||
}
|
||||
scene_manager_set_scene_state(nfc->scene_manager, NfcSceneNfcVUnlock, state);
|
||||
}
|
||||
}
|
||||
|
||||
void nfc_scene_nfcv_unlock_on_enter(void* context) {
|
||||
Nfc* nfc = context;
|
||||
|
||||
nfc_device_clear(nfc->dev);
|
||||
// Setup view
|
||||
nfc_scene_nfcv_unlock_set_state(nfc, NfcSceneNfcVUnlockStateDetecting);
|
||||
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup);
|
||||
|
||||
// Start worker
|
||||
nfc_worker_start(
|
||||
nfc->worker,
|
||||
NfcWorkerStateNfcVUnlockAndSave,
|
||||
&nfc->dev->dev_data,
|
||||
nfc_scene_nfcv_unlock_worker_callback,
|
||||
nfc);
|
||||
|
||||
nfc_blink_read_start(nfc);
|
||||
}
|
||||
|
||||
bool nfc_scene_nfcv_unlock_on_event(void* context, SceneManagerEvent event) {
|
||||
Nfc* nfc = context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == NfcWorkerEventCardDetected) {
|
||||
nfc_scene_nfcv_unlock_set_state(nfc, NfcSceneNfcVUnlockStateUnlocked);
|
||||
consumed = true;
|
||||
} else if(event.event == NfcWorkerEventAborted) {
|
||||
nfc_scene_nfcv_unlock_set_state(nfc, NfcSceneNfcVUnlockStateAlreadyUnlocked);
|
||||
consumed = true;
|
||||
} else if(event.event == NfcWorkerEventNoCardDetected) {
|
||||
nfc_scene_nfcv_unlock_set_state(nfc, NfcSceneNfcVUnlockStateDetecting);
|
||||
consumed = true;
|
||||
} else if(event.event == NfcWorkerEventWrongCardDetected) {
|
||||
nfc_scene_nfcv_unlock_set_state(nfc, NfcSceneNfcVUnlockStateNotSupportedCard);
|
||||
}
|
||||
} else if(event.type == SceneManagerEventTypeBack) {
|
||||
consumed = scene_manager_search_and_switch_to_previous_scene(
|
||||
nfc->scene_manager, NfcSceneNfcVUnlockMenu);
|
||||
}
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void nfc_scene_nfcv_unlock_on_exit(void* context) {
|
||||
Nfc* nfc = context;
|
||||
|
||||
// Stop worker
|
||||
nfc_worker_stop(nfc->worker);
|
||||
// Clear view
|
||||
popup_reset(nfc->popup);
|
||||
nfc_blink_stop(nfc);
|
||||
scene_manager_set_scene_state(
|
||||
nfc->scene_manager, NfcSceneNfcVUnlock, NfcSceneNfcVUnlockStateIdle);
|
||||
}
|
60
applications/main/nfc/scenes/nfc_scene_nfcv_unlock_menu.c
Normal file
60
applications/main/nfc/scenes/nfc_scene_nfcv_unlock_menu.c
Normal file
@ -0,0 +1,60 @@
|
||||
#include "../nfc_i.h"
|
||||
#include <dolphin/dolphin.h>
|
||||
|
||||
enum SubmenuIndex {
|
||||
SubmenuIndexNfcVUnlockMenuManual,
|
||||
SubmenuIndexNfcVUnlockMenuTonieBox,
|
||||
};
|
||||
|
||||
void nfc_scene_nfcv_unlock_menu_submenu_callback(void* context, uint32_t index) {
|
||||
Nfc* nfc = context;
|
||||
|
||||
view_dispatcher_send_custom_event(nfc->view_dispatcher, index);
|
||||
}
|
||||
|
||||
void nfc_scene_nfcv_unlock_menu_on_enter(void* context) {
|
||||
Nfc* nfc = context;
|
||||
Submenu* submenu = nfc->submenu;
|
||||
|
||||
uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneNfcVUnlockMenu);
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
"Enter PWD Manually",
|
||||
SubmenuIndexNfcVUnlockMenuManual,
|
||||
nfc_scene_nfcv_unlock_menu_submenu_callback,
|
||||
nfc);
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
"Auth As TonieBox",
|
||||
SubmenuIndexNfcVUnlockMenuTonieBox,
|
||||
nfc_scene_nfcv_unlock_menu_submenu_callback,
|
||||
nfc);
|
||||
submenu_set_selected_item(submenu, state);
|
||||
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu);
|
||||
}
|
||||
|
||||
bool nfc_scene_nfcv_unlock_menu_on_event(void* context, SceneManagerEvent event) {
|
||||
Nfc* nfc = context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == SubmenuIndexNfcVUnlockMenuManual) {
|
||||
nfc->dev->dev_data.nfcv_data.auth_method = NfcVAuthMethodManual;
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVKeyInput);
|
||||
consumed = true;
|
||||
} else if(event.event == SubmenuIndexNfcVUnlockMenuTonieBox) {
|
||||
nfc->dev->dev_data.nfcv_data.auth_method = NfcVAuthMethodTonieBox;
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVUnlock);
|
||||
DOLPHIN_DEED(DolphinDeedNfcRead);
|
||||
consumed = true;
|
||||
}
|
||||
scene_manager_set_scene_state(nfc->scene_manager, NfcSceneNfcVUnlockMenu, event.event);
|
||||
}
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void nfc_scene_nfcv_unlock_menu_on_exit(void* context) {
|
||||
Nfc* nfc = context;
|
||||
|
||||
submenu_reset(nfc->submenu);
|
||||
}
|
@ -68,6 +68,11 @@ bool nfc_scene_read_on_event(void* context, SceneManagerEvent event) {
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcaReadSuccess);
|
||||
DOLPHIN_DEED(DolphinDeedNfcReadSuccess);
|
||||
consumed = true;
|
||||
} else if(event.event == NfcWorkerEventReadNfcV) {
|
||||
notification_message(nfc->notifications, &sequence_success);
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVReadSuccess);
|
||||
DOLPHIN_DEED(DolphinDeedNfcReadSuccess);
|
||||
consumed = true;
|
||||
} else if(event.event == NfcWorkerEventReadMfUltralight) {
|
||||
notification_message(nfc->notifications, &sequence_success);
|
||||
// Set unlock password input to 0xFFFFFFFF only on fresh read
|
||||
|
@ -55,6 +55,13 @@ bool nfc_scene_rpc_on_event(void* context, SceneManagerEvent event) {
|
||||
&nfc->dev->dev_data,
|
||||
nfc_scene_rpc_emulate_callback,
|
||||
nfc);
|
||||
} else if(nfc->dev->format == NfcDeviceSaveFormatNfcV) {
|
||||
nfc_worker_start(
|
||||
nfc->worker,
|
||||
NfcWorkerStateNfcVEmulate,
|
||||
&nfc->dev->dev_data,
|
||||
nfc_scene_rpc_emulate_callback,
|
||||
nfc);
|
||||
} else {
|
||||
nfc_worker_start(
|
||||
nfc->worker, NfcWorkerStateUidEmulate, &nfc->dev->dev_data, NULL, nfc);
|
||||
|
@ -44,6 +44,7 @@ void nfc_scene_saved_menu_on_enter(void* context) {
|
||||
} else if(
|
||||
(nfc->dev->format == NfcDeviceSaveFormatMifareUl &&
|
||||
mf_ul_emulation_supported(&nfc->dev->dev_data.mf_ul_data)) ||
|
||||
nfc->dev->format == NfcDeviceSaveFormatNfcV ||
|
||||
nfc->dev->format == NfcDeviceSaveFormatMifareClassic) {
|
||||
submenu_add_item(
|
||||
submenu, "Emulate", SubmenuIndexEmulate, nfc_scene_saved_menu_submenu_callback, nfc);
|
||||
@ -118,6 +119,8 @@ bool nfc_scene_saved_menu_on_event(void* context, SceneManagerEvent event) {
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightEmulate);
|
||||
} else if(nfc->dev->format == NfcDeviceSaveFormatMifareClassic) {
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicEmulate);
|
||||
} else if(nfc->dev->format == NfcDeviceSaveFormatNfcV) {
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVEmulate);
|
||||
} else {
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid);
|
||||
}
|
||||
|
@ -2090,6 +2090,14 @@ 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,NfcVData*
|
||||
Function,+,nfcv_emu_init,void,"FuriHalNfcDevData*, NfcVData*"
|
||||
Function,+,nfcv_emu_loop,_Bool,"FuriHalNfcTxRxContext*, FuriHalNfcDevData*, NfcVData*, uint32_t"
|
||||
Function,+,nfcv_emu_send,void,"FuriHalNfcTxRxContext*, NfcVData*, uint8_t*, uint8_t, NfcVSendFlags, 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*"
|
||||
Function,+,notification_message,void,"NotificationApp*, const NotificationSequence*"
|
||||
|
|
@ -9,7 +9,7 @@
|
||||
#include <stm32wbxx_ll_tim.h>
|
||||
|
||||
/* must be on bank B */
|
||||
#define DEBUG_OUTPUT gpio_ext_pb3
|
||||
// For debugging purposes use `--extra-define=DIGITAL_SIGNAL_DEBUG_OUTPUT_PIN=gpio_ext_pb3` fbt option
|
||||
|
||||
struct ReloadBuffer {
|
||||
uint32_t* buffer; /* DMA ringbuffer */
|
||||
@ -194,9 +194,9 @@ void digital_signal_prepare_arr(DigitalSignal* signal) {
|
||||
uint32_t bit_set = internals->gpio->pin;
|
||||
uint32_t bit_reset = internals->gpio->pin << 16;
|
||||
|
||||
#ifdef DEBUG_OUTPUT
|
||||
bit_set |= DEBUG_OUTPUT.pin;
|
||||
bit_reset |= DEBUG_OUTPUT.pin << 16;
|
||||
#ifdef DIGITAL_SIGNAL_DEBUG_OUTPUT_PIN
|
||||
bit_set |= DIGITAL_SIGNAL_DEBUG_OUTPUT_PIN.pin;
|
||||
bit_reset |= DIGITAL_SIGNAL_DEBUG_OUTPUT_PIN.pin << 16;
|
||||
#endif
|
||||
|
||||
if(signal->start_level) {
|
||||
@ -540,8 +540,9 @@ bool digital_sequence_send(DigitalSequence* sequence) {
|
||||
struct ReloadBuffer* dma_buffer = sequence->dma_buffer;
|
||||
|
||||
furi_hal_gpio_init(sequence->gpio, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
|
||||
#ifdef DEBUG_OUTPUT
|
||||
furi_hal_gpio_init(&DEBUG_OUTPUT, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
|
||||
#ifdef DIGITAL_SIGNAL_DEBUG_OUTPUT_PIN
|
||||
furi_hal_gpio_init(
|
||||
&DIGITAL_SIGNAL_DEBUG_OUTPUT_PIN, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
|
||||
#endif
|
||||
|
||||
if(sequence->bake) {
|
||||
|
@ -58,6 +58,8 @@ static void nfc_device_prepare_format_string(NfcDevice* dev, FuriString* format_
|
||||
furi_string_set(format_string, "Mifare Classic");
|
||||
} else if(dev->format == NfcDeviceSaveFormatMifareDesfire) {
|
||||
furi_string_set(format_string, "Mifare DESFire");
|
||||
} else if(dev->format == NfcDeviceSaveFormatNfcV) {
|
||||
furi_string_set(format_string, "ISO15693");
|
||||
} else {
|
||||
furi_string_set(format_string, "Unknown");
|
||||
}
|
||||
@ -93,6 +95,11 @@ static bool nfc_device_parse_format_string(NfcDevice* dev, FuriString* format_st
|
||||
dev->dev_data.protocol = NfcDeviceProtocolMifareDesfire;
|
||||
return true;
|
||||
}
|
||||
if(furi_string_start_with_str(format_string, "ISO15693")) {
|
||||
dev->format = NfcDeviceSaveFormatNfcV;
|
||||
dev->dev_data.protocol = NfcDeviceProtocolNfcV;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -650,7 +657,327 @@ bool nfc_device_load_mifare_df_data(FlipperFormat* file, NfcDevice* dev) {
|
||||
return parsed;
|
||||
}
|
||||
|
||||
// Leave for backward compatibility
|
||||
static bool nfc_device_save_slix_data(FlipperFormat* file, NfcDevice* dev) {
|
||||
bool saved = false;
|
||||
NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix;
|
||||
|
||||
do {
|
||||
if(!flipper_format_write_comment_cstr(file, "SLIX specific data")) break;
|
||||
if(!flipper_format_write_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas)))
|
||||
break;
|
||||
saved = true;
|
||||
} while(false);
|
||||
|
||||
return saved;
|
||||
}
|
||||
|
||||
bool nfc_device_load_slix_data(FlipperFormat* file, NfcDevice* dev) {
|
||||
bool parsed = false;
|
||||
NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix;
|
||||
memset(data, 0, sizeof(NfcVSlixData));
|
||||
|
||||
do {
|
||||
if(!flipper_format_read_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas)))
|
||||
break;
|
||||
|
||||
parsed = true;
|
||||
} while(false);
|
||||
|
||||
return parsed;
|
||||
}
|
||||
|
||||
static bool nfc_device_save_slix_s_data(FlipperFormat* file, NfcDevice* dev) {
|
||||
bool saved = false;
|
||||
NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix;
|
||||
|
||||
do {
|
||||
if(!flipper_format_write_comment_cstr(file, "SLIX-S specific data")) break;
|
||||
if(!flipper_format_write_hex(file, "Password Read", data->key_read, sizeof(data->key_read)))
|
||||
break;
|
||||
if(!flipper_format_write_hex(
|
||||
file, "Password Write", data->key_write, sizeof(data->key_write)))
|
||||
break;
|
||||
if(!flipper_format_write_hex(
|
||||
file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy)))
|
||||
break;
|
||||
if(!flipper_format_write_hex(
|
||||
file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy)))
|
||||
break;
|
||||
if(!flipper_format_write_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas)))
|
||||
break;
|
||||
if(!flipper_format_write_bool(file, "Privacy Mode", &data->privacy, 1)) break;
|
||||
saved = true;
|
||||
} while(false);
|
||||
|
||||
return saved;
|
||||
}
|
||||
|
||||
bool nfc_device_load_slix_s_data(FlipperFormat* file, NfcDevice* dev) {
|
||||
bool parsed = false;
|
||||
NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix;
|
||||
memset(data, 0, sizeof(NfcVSlixData));
|
||||
|
||||
do {
|
||||
if(!flipper_format_read_hex(file, "Password Read", data->key_read, sizeof(data->key_read)))
|
||||
break;
|
||||
if(!flipper_format_read_hex(
|
||||
file, "Password Write", data->key_write, sizeof(data->key_write)))
|
||||
break;
|
||||
if(!flipper_format_read_hex(
|
||||
file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy)))
|
||||
break;
|
||||
if(!flipper_format_read_hex(
|
||||
file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy)))
|
||||
break;
|
||||
if(!flipper_format_read_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas)))
|
||||
break;
|
||||
if(!flipper_format_read_bool(file, "Privacy Mode", &data->privacy, 1)) break;
|
||||
|
||||
parsed = true;
|
||||
} while(false);
|
||||
|
||||
return parsed;
|
||||
}
|
||||
|
||||
static bool nfc_device_save_slix_l_data(FlipperFormat* file, NfcDevice* dev) {
|
||||
bool saved = false;
|
||||
NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix;
|
||||
|
||||
do {
|
||||
if(!flipper_format_write_comment_cstr(file, "SLIX-L specific data")) break;
|
||||
if(!flipper_format_write_hex(
|
||||
file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy)))
|
||||
break;
|
||||
if(!flipper_format_write_hex(
|
||||
file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy)))
|
||||
break;
|
||||
if(!flipper_format_write_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas)))
|
||||
break;
|
||||
if(!flipper_format_write_bool(file, "Privacy Mode", &data->privacy, 1)) break;
|
||||
saved = true;
|
||||
} while(false);
|
||||
|
||||
return saved;
|
||||
}
|
||||
|
||||
bool nfc_device_load_slix_l_data(FlipperFormat* file, NfcDevice* dev) {
|
||||
bool parsed = false;
|
||||
NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix;
|
||||
memset(data, 0, sizeof(NfcVSlixData));
|
||||
|
||||
do {
|
||||
if(!flipper_format_read_hex(
|
||||
file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy)))
|
||||
break;
|
||||
if(!flipper_format_read_hex(
|
||||
file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy)))
|
||||
break;
|
||||
if(!flipper_format_read_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas)))
|
||||
break;
|
||||
if(!flipper_format_read_bool(file, "Privacy Mode", &data->privacy, 1)) break;
|
||||
|
||||
parsed = true;
|
||||
} while(false);
|
||||
|
||||
return parsed;
|
||||
}
|
||||
|
||||
static bool nfc_device_save_slix2_data(FlipperFormat* file, NfcDevice* dev) {
|
||||
bool saved = false;
|
||||
NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix;
|
||||
|
||||
do {
|
||||
if(!flipper_format_write_comment_cstr(file, "SLIX2 specific data")) break;
|
||||
if(!flipper_format_write_hex(file, "Password Read", data->key_read, sizeof(data->key_read)))
|
||||
break;
|
||||
if(!flipper_format_write_hex(
|
||||
file, "Password Write", data->key_write, sizeof(data->key_write)))
|
||||
break;
|
||||
if(!flipper_format_write_hex(
|
||||
file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy)))
|
||||
break;
|
||||
if(!flipper_format_write_hex(
|
||||
file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy)))
|
||||
break;
|
||||
if(!flipper_format_write_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas)))
|
||||
break;
|
||||
if(!flipper_format_write_bool(file, "Privacy Mode", &data->privacy, 1)) break;
|
||||
saved = true;
|
||||
} while(false);
|
||||
|
||||
return saved;
|
||||
}
|
||||
|
||||
bool nfc_device_load_slix2_data(FlipperFormat* file, NfcDevice* dev) {
|
||||
bool parsed = false;
|
||||
NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix;
|
||||
memset(data, 0, sizeof(NfcVSlixData));
|
||||
|
||||
do {
|
||||
if(!flipper_format_read_hex(file, "Password Read", data->key_read, sizeof(data->key_read)))
|
||||
break;
|
||||
if(!flipper_format_read_hex(
|
||||
file, "Password Write", data->key_write, sizeof(data->key_write)))
|
||||
break;
|
||||
if(!flipper_format_read_hex(
|
||||
file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy)))
|
||||
break;
|
||||
if(!flipper_format_read_hex(
|
||||
file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy)))
|
||||
break;
|
||||
if(!flipper_format_read_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas)))
|
||||
break;
|
||||
if(!flipper_format_read_bool(file, "Privacy Mode", &data->privacy, 1)) break;
|
||||
|
||||
parsed = true;
|
||||
} while(false);
|
||||
|
||||
return parsed;
|
||||
}
|
||||
|
||||
static bool nfc_device_save_nfcv_data(FlipperFormat* file, NfcDevice* dev) {
|
||||
bool saved = false;
|
||||
NfcVData* data = &dev->dev_data.nfcv_data;
|
||||
|
||||
do {
|
||||
uint32_t temp_uint32 = 0;
|
||||
uint8_t temp_uint8 = 0;
|
||||
|
||||
if(!flipper_format_write_comment_cstr(file, "Data Storage Format Identifier")) break;
|
||||
if(!flipper_format_write_hex(file, "DSFID", &(data->dsfid), 1)) break;
|
||||
if(!flipper_format_write_comment_cstr(file, "Application Family Identifier")) break;
|
||||
if(!flipper_format_write_hex(file, "AFI", &(data->afi), 1)) break;
|
||||
if(!flipper_format_write_hex(file, "IC Reference", &(data->ic_ref), 1)) break;
|
||||
temp_uint32 = data->block_num;
|
||||
if(!flipper_format_write_comment_cstr(file, "Number of memory blocks, usually 0 to 256"))
|
||||
break;
|
||||
if(!flipper_format_write_uint32(file, "Block Count", &temp_uint32, 1)) break;
|
||||
if(!flipper_format_write_comment_cstr(file, "Size of a single memory block, usually 4"))
|
||||
break;
|
||||
if(!flipper_format_write_hex(file, "Block Size", &(data->block_size), 1)) break;
|
||||
if(!flipper_format_write_hex(
|
||||
file, "Data Content", data->data, data->block_num * data->block_size))
|
||||
break;
|
||||
if(!flipper_format_write_comment_cstr(
|
||||
file, "First byte: DSFID (0x01) / AFI (0x02) lock info, others: block lock info"))
|
||||
break;
|
||||
if(!flipper_format_write_hex(
|
||||
file, "Security Status", data->security_status, 1 + data->block_num))
|
||||
break;
|
||||
if(!flipper_format_write_comment_cstr(
|
||||
file,
|
||||
"Subtype of this card (0 = ISO15693, 1 = SLIX, 2 = SLIX-S, 3 = SLIX-L, 4 = SLIX2)"))
|
||||
break;
|
||||
temp_uint8 = (uint8_t)data->sub_type;
|
||||
if(!flipper_format_write_hex(file, "Subtype", &temp_uint8, 1)) break;
|
||||
|
||||
switch(data->sub_type) {
|
||||
case NfcVTypePlain:
|
||||
if(!flipper_format_write_comment_cstr(file, "End of ISO15693 parameters")) break;
|
||||
saved = true;
|
||||
break;
|
||||
case NfcVTypeSlix:
|
||||
saved = nfc_device_save_slix_data(file, dev);
|
||||
break;
|
||||
case NfcVTypeSlixS:
|
||||
saved = nfc_device_save_slix_s_data(file, dev);
|
||||
break;
|
||||
case NfcVTypeSlixL:
|
||||
saved = nfc_device_save_slix_l_data(file, dev);
|
||||
break;
|
||||
case NfcVTypeSlix2:
|
||||
saved = nfc_device_save_slix2_data(file, dev);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} while(false);
|
||||
|
||||
return saved;
|
||||
}
|
||||
|
||||
bool nfc_device_load_nfcv_data(FlipperFormat* file, NfcDevice* dev) {
|
||||
bool parsed = false;
|
||||
NfcVData* data = &dev->dev_data.nfcv_data;
|
||||
|
||||
memset(data, 0x00, sizeof(NfcVData));
|
||||
|
||||
do {
|
||||
uint32_t temp_uint32 = 0;
|
||||
uint8_t temp_value = 0;
|
||||
|
||||
if(!flipper_format_read_hex(file, "DSFID", &(data->dsfid), 1)) break;
|
||||
if(!flipper_format_read_hex(file, "AFI", &(data->afi), 1)) break;
|
||||
if(!flipper_format_read_hex(file, "IC Reference", &(data->ic_ref), 1)) break;
|
||||
if(!flipper_format_read_uint32(file, "Block Count", &temp_uint32, 1)) break;
|
||||
data->block_num = temp_uint32;
|
||||
if(!flipper_format_read_hex(file, "Block Size", &(data->block_size), 1)) break;
|
||||
if(!flipper_format_read_hex(
|
||||
file, "Data Content", data->data, data->block_num * data->block_size))
|
||||
break;
|
||||
|
||||
/* optional, as added later */
|
||||
if(flipper_format_key_exist(file, "Security Status")) {
|
||||
if(!flipper_format_read_hex(
|
||||
file, "Security Status", data->security_status, 1 + data->block_num))
|
||||
break;
|
||||
}
|
||||
if(!flipper_format_read_hex(file, "Subtype", &temp_value, 1)) break;
|
||||
data->sub_type = temp_value;
|
||||
|
||||
switch(data->sub_type) {
|
||||
case NfcVTypePlain:
|
||||
parsed = true;
|
||||
break;
|
||||
case NfcVTypeSlix:
|
||||
parsed = nfc_device_load_slix_data(file, dev);
|
||||
break;
|
||||
case NfcVTypeSlixS:
|
||||
parsed = nfc_device_load_slix_s_data(file, dev);
|
||||
break;
|
||||
case NfcVTypeSlixL:
|
||||
parsed = nfc_device_load_slix_l_data(file, dev);
|
||||
break;
|
||||
case NfcVTypeSlix2:
|
||||
parsed = nfc_device_load_slix2_data(file, dev);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} while(false);
|
||||
|
||||
return parsed;
|
||||
}
|
||||
|
||||
static bool nfc_device_save_bank_card_data(FlipperFormat* file, NfcDevice* dev) {
|
||||
bool saved = false;
|
||||
EmvData* data = &dev->dev_data.emv_data;
|
||||
uint32_t data_temp = 0;
|
||||
|
||||
do {
|
||||
// Write Bank card specific data
|
||||
if(!flipper_format_write_comment_cstr(file, "Bank card specific data")) break;
|
||||
if(!flipper_format_write_hex(file, "AID", data->aid, data->aid_len)) break;
|
||||
if(!flipper_format_write_string_cstr(file, "Name", data->name)) break;
|
||||
if(!flipper_format_write_hex(file, "Number", data->number, data->number_len)) break;
|
||||
if(data->exp_mon) {
|
||||
uint8_t exp_data[2] = {data->exp_mon, data->exp_year};
|
||||
if(!flipper_format_write_hex(file, "Exp data", exp_data, sizeof(exp_data))) break;
|
||||
}
|
||||
if(data->country_code) {
|
||||
data_temp = data->country_code;
|
||||
if(!flipper_format_write_uint32(file, "Country code", &data_temp, 1)) break;
|
||||
}
|
||||
if(data->currency_code) {
|
||||
data_temp = data->currency_code;
|
||||
if(!flipper_format_write_uint32(file, "Currency code", &data_temp, 1)) break;
|
||||
}
|
||||
saved = true;
|
||||
} while(false);
|
||||
|
||||
return saved;
|
||||
}
|
||||
|
||||
bool nfc_device_load_bank_card_data(FlipperFormat* file, NfcDevice* dev) {
|
||||
bool parsed = false;
|
||||
EmvData* data = &dev->dev_data.emv_data;
|
||||
@ -1069,23 +1396,32 @@ bool nfc_device_save(NfcDevice* dev, const char* dev_name) {
|
||||
if(!flipper_format_write_header_cstr(file, nfc_file_header, nfc_file_version)) break;
|
||||
// Write nfc device type
|
||||
if(!flipper_format_write_comment_cstr(
|
||||
file, "Nfc device type can be UID, Mifare Ultralight, Mifare Classic"))
|
||||
file, "Nfc device type can be UID, Mifare Ultralight, Mifare Classic or ISO15693"))
|
||||
break;
|
||||
nfc_device_prepare_format_string(dev, temp_str);
|
||||
if(!flipper_format_write_string(file, "Device type", temp_str)) break;
|
||||
// Write UID, ATQA, SAK
|
||||
if(!flipper_format_write_comment_cstr(file, "UID, ATQA and SAK are common for all formats"))
|
||||
break;
|
||||
// Write UID
|
||||
if(!flipper_format_write_comment_cstr(file, "UID is common for all formats")) break;
|
||||
if(!flipper_format_write_hex(file, "UID", data->uid, data->uid_len)) break;
|
||||
// Save ATQA in MSB order for correct companion apps display
|
||||
uint8_t atqa[2] = {data->atqa[1], data->atqa[0]};
|
||||
if(!flipper_format_write_hex(file, "ATQA", atqa, 2)) break;
|
||||
if(!flipper_format_write_hex(file, "SAK", &data->sak, 1)) break;
|
||||
|
||||
if(dev->format != NfcDeviceSaveFormatNfcV) {
|
||||
// Write ATQA, SAK
|
||||
if(!flipper_format_write_comment_cstr(file, "ISO14443 specific fields")) break;
|
||||
// Save ATQA in MSB order for correct companion apps display
|
||||
uint8_t atqa[2] = {data->atqa[1], data->atqa[0]};
|
||||
if(!flipper_format_write_hex(file, "ATQA", atqa, 2)) break;
|
||||
if(!flipper_format_write_hex(file, "SAK", &data->sak, 1)) break;
|
||||
}
|
||||
|
||||
// Save more data if necessary
|
||||
if(dev->format == NfcDeviceSaveFormatMifareUl) {
|
||||
if(!nfc_device_save_mifare_ul_data(file, dev)) break;
|
||||
} else if(dev->format == NfcDeviceSaveFormatMifareDesfire) {
|
||||
if(!nfc_device_save_mifare_df_data(file, dev)) break;
|
||||
} else if(dev->format == NfcDeviceSaveFormatNfcV) {
|
||||
if(!nfc_device_save_nfcv_data(file, dev)) break;
|
||||
} else if(dev->format == NfcDeviceSaveFormatBankCard) {
|
||||
if(!nfc_device_save_bank_card_data(file, dev)) break;
|
||||
} else if(dev->format == NfcDeviceSaveFormatMifareClassic) {
|
||||
// Save data
|
||||
if(!nfc_device_save_mifare_classic_data(file, dev)) break;
|
||||
@ -1160,18 +1496,20 @@ static bool nfc_device_load_data(NfcDevice* dev, FuriString* path, bool show_dia
|
||||
if(!nfc_device_parse_format_string(dev, temp_str)) break;
|
||||
// Read and parse UID, ATQA and SAK
|
||||
if(!flipper_format_get_value_count(file, "UID", &data_cnt)) break;
|
||||
if(!(data_cnt == 4 || data_cnt == 7)) break;
|
||||
if(!(data_cnt == 4 || data_cnt == 7 || data_cnt == 8)) break;
|
||||
data->uid_len = data_cnt;
|
||||
if(!flipper_format_read_hex(file, "UID", data->uid, data->uid_len)) break;
|
||||
if(version == version_with_lsb_atqa) {
|
||||
if(!flipper_format_read_hex(file, "ATQA", data->atqa, 2)) break;
|
||||
} else {
|
||||
uint8_t atqa[2] = {};
|
||||
if(!flipper_format_read_hex(file, "ATQA", atqa, 2)) break;
|
||||
data->atqa[0] = atqa[1];
|
||||
data->atqa[1] = atqa[0];
|
||||
if(dev->format != NfcDeviceSaveFormatNfcV) {
|
||||
if(version == version_with_lsb_atqa) {
|
||||
if(!flipper_format_read_hex(file, "ATQA", data->atqa, 2)) break;
|
||||
} else {
|
||||
uint8_t atqa[2] = {};
|
||||
if(!flipper_format_read_hex(file, "ATQA", atqa, 2)) break;
|
||||
data->atqa[0] = atqa[1];
|
||||
data->atqa[1] = atqa[0];
|
||||
}
|
||||
if(!flipper_format_read_hex(file, "SAK", &data->sak, 1)) break;
|
||||
}
|
||||
if(!flipper_format_read_hex(file, "SAK", &data->sak, 1)) break;
|
||||
// Load CUID
|
||||
uint8_t* cuid_start = data->uid;
|
||||
if(data->uid_len == 7) {
|
||||
@ -1186,6 +1524,8 @@ static bool nfc_device_load_data(NfcDevice* dev, FuriString* path, bool show_dia
|
||||
if(!nfc_device_load_mifare_classic_data(file, dev)) break;
|
||||
} else if(dev->format == NfcDeviceSaveFormatMifareDesfire) {
|
||||
if(!nfc_device_load_mifare_df_data(file, dev)) break;
|
||||
} else if(dev->format == NfcDeviceSaveFormatNfcV) {
|
||||
if(!nfc_device_load_nfcv_data(file, dev)) break;
|
||||
} else if(dev->format == NfcDeviceSaveFormatBankCard) {
|
||||
if(!nfc_device_load_bank_card_data(file, dev)) break;
|
||||
}
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include <lib/nfc/protocols/mifare_ultralight.h>
|
||||
#include <lib/nfc/protocols/mifare_classic.h>
|
||||
#include <lib/nfc/protocols/mifare_desfire.h>
|
||||
#include <lib/nfc/protocols/nfcv.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@ -31,6 +32,7 @@ typedef enum {
|
||||
NfcDeviceProtocolMifareUl,
|
||||
NfcDeviceProtocolMifareClassic,
|
||||
NfcDeviceProtocolMifareDesfire,
|
||||
NfcDeviceProtocolNfcV
|
||||
} NfcProtocol;
|
||||
|
||||
typedef enum {
|
||||
@ -39,6 +41,7 @@ typedef enum {
|
||||
NfcDeviceSaveFormatMifareUl,
|
||||
NfcDeviceSaveFormatMifareClassic,
|
||||
NfcDeviceSaveFormatMifareDesfire,
|
||||
NfcDeviceSaveFormatNfcV,
|
||||
} NfcDeviceSaveFormat;
|
||||
|
||||
typedef struct {
|
||||
@ -73,6 +76,7 @@ typedef struct {
|
||||
MfUltralightData mf_ul_data;
|
||||
MfClassicData mf_classic_data;
|
||||
MifareDesfireData mf_df_data;
|
||||
NfcVData nfcv_data;
|
||||
};
|
||||
FuriString* parsed_data;
|
||||
} NfcDeviceData;
|
||||
|
@ -111,6 +111,14 @@ int32_t nfc_worker_task(void* context) {
|
||||
nfc_worker_mf_classic_dict_attack(nfc_worker);
|
||||
} else if(nfc_worker->state == NfcWorkerStateAnalyzeReader) {
|
||||
nfc_worker_analyze_reader(nfc_worker);
|
||||
} else if(nfc_worker->state == NfcWorkerStateNfcVEmulate) {
|
||||
nfc_worker_nfcv_emulate(nfc_worker);
|
||||
} else if(nfc_worker->state == NfcWorkerStateNfcVSniff) {
|
||||
nfc_worker_nfcv_sniff(nfc_worker);
|
||||
} else if(nfc_worker->state == NfcWorkerStateNfcVUnlock) {
|
||||
nfc_worker_nfcv_unlock(nfc_worker);
|
||||
} else if(nfc_worker->state == NfcWorkerStateNfcVUnlockAndSave) {
|
||||
nfc_worker_nfcv_unlock(nfc_worker);
|
||||
}
|
||||
furi_hal_nfc_sleep();
|
||||
nfc_worker_change_state(nfc_worker, NfcWorkerStateReady);
|
||||
@ -118,6 +126,236 @@ int32_t nfc_worker_task(void* context) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool nfc_worker_read_nfcv(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) {
|
||||
bool read_success = false;
|
||||
NfcVReader reader = {};
|
||||
|
||||
FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data;
|
||||
NfcVData* nfcv_data = &nfc_worker->dev_data->nfcv_data;
|
||||
|
||||
furi_hal_nfc_sleep();
|
||||
|
||||
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
|
||||
reader_analyzer_prepare_tx_rx(nfc_worker->reader_analyzer, tx_rx, false);
|
||||
reader_analyzer_start(nfc_worker->reader_analyzer, ReaderAnalyzerModeDebugLog);
|
||||
}
|
||||
|
||||
do {
|
||||
if(!furi_hal_nfc_detect(&nfc_worker->dev_data->nfc_data, 200)) break;
|
||||
if(!nfcv_read_card(&reader, nfc_data, nfcv_data)) break;
|
||||
|
||||
read_success = true;
|
||||
} while(false);
|
||||
|
||||
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
|
||||
reader_analyzer_stop(nfc_worker->reader_analyzer);
|
||||
}
|
||||
|
||||
return read_success;
|
||||
}
|
||||
|
||||
void nfc_worker_nfcv_emulate(NfcWorker* nfc_worker) {
|
||||
FuriHalNfcTxRxContext tx_rx = {};
|
||||
FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data;
|
||||
NfcVData* nfcv_data = &nfc_worker->dev_data->nfcv_data;
|
||||
|
||||
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
|
||||
reader_analyzer_prepare_tx_rx(nfc_worker->reader_analyzer, &tx_rx, true);
|
||||
reader_analyzer_start(nfc_worker->reader_analyzer, ReaderAnalyzerModeDebugLog);
|
||||
}
|
||||
|
||||
nfcv_emu_init(nfc_data, nfcv_data);
|
||||
while(nfc_worker->state == NfcWorkerStateNfcVEmulate) {
|
||||
if(nfcv_emu_loop(&tx_rx, nfc_data, nfcv_data, 100)) {
|
||||
if(nfc_worker->callback) {
|
||||
nfc_worker->callback(NfcWorkerEventNfcVCommandExecuted, nfc_worker->context);
|
||||
if(nfcv_data->modified) {
|
||||
nfc_worker->callback(NfcWorkerEventNfcVContentChanged, nfc_worker->context);
|
||||
nfcv_data->modified = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
nfcv_emu_deinit(nfcv_data);
|
||||
|
||||
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
|
||||
reader_analyzer_stop(nfc_worker->reader_analyzer);
|
||||
}
|
||||
}
|
||||
|
||||
void nfc_worker_nfcv_sniff(NfcWorker* nfc_worker) {
|
||||
FuriHalNfcTxRxContext tx_rx = {};
|
||||
FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data;
|
||||
NfcVData* nfcv_data = &nfc_worker->dev_data->nfcv_data;
|
||||
|
||||
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
|
||||
reader_analyzer_prepare_tx_rx(nfc_worker->reader_analyzer, &tx_rx, true);
|
||||
reader_analyzer_start(nfc_worker->reader_analyzer, ReaderAnalyzerModeDebugLog);
|
||||
}
|
||||
|
||||
nfcv_data->sub_type = NfcVTypeSniff;
|
||||
nfcv_emu_init(nfc_data, nfcv_data);
|
||||
|
||||
while(nfc_worker->state == NfcWorkerStateNfcVSniff) {
|
||||
if(nfcv_emu_loop(&tx_rx, nfc_data, nfcv_data, 100)) {
|
||||
if(nfc_worker->callback) {
|
||||
nfc_worker->callback(NfcWorkerEventNfcVCommandExecuted, nfc_worker->context);
|
||||
}
|
||||
}
|
||||
}
|
||||
nfcv_emu_deinit(nfcv_data);
|
||||
|
||||
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
|
||||
reader_analyzer_stop(nfc_worker->reader_analyzer);
|
||||
}
|
||||
}
|
||||
|
||||
void nfc_worker_nfcv_unlock(NfcWorker* nfc_worker) {
|
||||
furi_assert(nfc_worker);
|
||||
furi_assert(nfc_worker->callback);
|
||||
|
||||
NfcVData* nfcv_data = &nfc_worker->dev_data->nfcv_data;
|
||||
FuriHalNfcTxRxContext tx_rx = {};
|
||||
uint8_t* key_data = nfcv_data->sub_data.slix.key_privacy;
|
||||
uint32_t key = 0;
|
||||
|
||||
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
|
||||
reader_analyzer_prepare_tx_rx(nfc_worker->reader_analyzer, &tx_rx, true);
|
||||
reader_analyzer_start(nfc_worker->reader_analyzer, ReaderAnalyzerModeDebugLog);
|
||||
}
|
||||
|
||||
furi_hal_nfc_sleep();
|
||||
|
||||
while((nfc_worker->state == NfcWorkerStateNfcVUnlock) ||
|
||||
(nfc_worker->state == NfcWorkerStateNfcVUnlockAndSave)) {
|
||||
furi_hal_nfc_exit_sleep();
|
||||
furi_hal_nfc_ll_txrx_on();
|
||||
furi_hal_nfc_ll_poll();
|
||||
if(furi_hal_nfc_ll_set_mode(
|
||||
FuriHalNfcModePollNfcv, FuriHalNfcBitrate26p48, FuriHalNfcBitrate26p48) !=
|
||||
FuriHalNfcReturnOk) {
|
||||
break;
|
||||
}
|
||||
|
||||
furi_hal_nfc_ll_set_fdt_listen(FURI_HAL_NFC_LL_FDT_LISTEN_NFCV_POLLER);
|
||||
furi_hal_nfc_ll_set_fdt_poll(FURI_HAL_NFC_LL_FDT_POLL_NFCV_POLLER);
|
||||
furi_hal_nfc_ll_set_error_handling(FuriHalNfcErrorHandlingNfc);
|
||||
furi_hal_nfc_ll_set_guard_time(FURI_HAL_NFC_LL_GT_NFCV);
|
||||
|
||||
FURI_LOG_D(TAG, "Detect presence");
|
||||
ReturnCode ret = slix_get_random(nfcv_data);
|
||||
|
||||
if(ret == ERR_NONE) {
|
||||
/* there is some chip, responding with a RAND */
|
||||
nfc_worker->dev_data->protocol = NfcDeviceProtocolNfcV;
|
||||
FURI_LOG_D(TAG, " Chip detected. In privacy?");
|
||||
ret = nfcv_inventory(NULL);
|
||||
|
||||
if(ret == ERR_NONE) {
|
||||
/* chip is also visible, so no action required, just save */
|
||||
if(nfc_worker->state == NfcWorkerStateNfcVUnlockAndSave) {
|
||||
NfcVReader reader = {};
|
||||
|
||||
if(!nfcv_read_card(&reader, &nfc_worker->dev_data->nfc_data, nfcv_data)) {
|
||||
FURI_LOG_D(TAG, " => failed, wait for chip to disappear.");
|
||||
snprintf(nfcv_data->error, sizeof(nfcv_data->error), "Read card\nfailed");
|
||||
nfc_worker->callback(NfcWorkerEventWrongCardDetected, nfc_worker->context);
|
||||
} else {
|
||||
FURI_LOG_D(TAG, " => success, wait for chip to disappear.");
|
||||
nfc_worker->callback(NfcWorkerEventCardDetected, nfc_worker->context);
|
||||
}
|
||||
} else {
|
||||
FURI_LOG_D(TAG, " => success, wait for chip to disappear.");
|
||||
nfc_worker->callback(NfcWorkerEventCardDetected, nfc_worker->context);
|
||||
}
|
||||
|
||||
while(slix_get_random(NULL) == ERR_NONE) {
|
||||
furi_delay_ms(100);
|
||||
}
|
||||
|
||||
FURI_LOG_D(TAG, " => chip is already visible, wait for chip to disappear.\r\n");
|
||||
nfc_worker->callback(NfcWorkerEventAborted, nfc_worker->context);
|
||||
while(slix_get_random(NULL) == ERR_NONE) {
|
||||
furi_delay_ms(100);
|
||||
}
|
||||
|
||||
key_data[0] = 0;
|
||||
key_data[1] = 0;
|
||||
key_data[2] = 0;
|
||||
key_data[3] = 0;
|
||||
|
||||
} else {
|
||||
/* chip is invisible, try to unlock */
|
||||
FURI_LOG_D(TAG, " chip is invisible, unlocking");
|
||||
|
||||
if(nfcv_data->auth_method == NfcVAuthMethodManual) {
|
||||
key |= key_data[0] << 24;
|
||||
key |= key_data[1] << 16;
|
||||
key |= key_data[2] << 8;
|
||||
key |= key_data[3] << 0;
|
||||
|
||||
ret = slix_unlock(nfcv_data, 4);
|
||||
} else {
|
||||
key = 0x7FFD6E5B;
|
||||
key_data[0] = key >> 24;
|
||||
key_data[1] = key >> 16;
|
||||
key_data[2] = key >> 8;
|
||||
key_data[3] = key >> 0;
|
||||
ret = slix_unlock(nfcv_data, 4);
|
||||
|
||||
if(ret != ERR_NONE) {
|
||||
/* main key failed, trying second one */
|
||||
FURI_LOG_D(TAG, " trying second key after resetting");
|
||||
|
||||
/* reset chip */
|
||||
furi_hal_nfc_ll_txrx_off();
|
||||
furi_delay_ms(20);
|
||||
furi_hal_nfc_ll_txrx_on();
|
||||
|
||||
if(slix_get_random(nfcv_data) != ERR_NONE) {
|
||||
FURI_LOG_D(TAG, " reset failed");
|
||||
}
|
||||
|
||||
key = 0x0F0F0F0F;
|
||||
key_data[0] = key >> 24;
|
||||
key_data[1] = key >> 16;
|
||||
key_data[2] = key >> 8;
|
||||
key_data[3] = key >> 0;
|
||||
ret = slix_unlock(nfcv_data, 4);
|
||||
}
|
||||
}
|
||||
if(ret != ERR_NONE) {
|
||||
/* unlock failed */
|
||||
FURI_LOG_D(TAG, " => failed, wait for chip to disappear.");
|
||||
snprintf(
|
||||
nfcv_data->error, sizeof(nfcv_data->error), "Passwords not\naccepted");
|
||||
nfc_worker->callback(NfcWorkerEventWrongCardDetected, nfc_worker->context);
|
||||
|
||||
/* reset chip */
|
||||
furi_hal_nfc_ll_txrx_off();
|
||||
furi_delay_ms(20);
|
||||
furi_hal_nfc_ll_txrx_on();
|
||||
|
||||
/* wait for disappearing */
|
||||
while(slix_get_random(NULL) == ERR_NONE) {
|
||||
furi_delay_ms(100);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
nfc_worker->callback(NfcWorkerEventNoCardDetected, nfc_worker->context);
|
||||
}
|
||||
|
||||
furi_hal_nfc_ll_txrx_off();
|
||||
furi_hal_nfc_sleep();
|
||||
furi_delay_ms(100);
|
||||
}
|
||||
|
||||
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
|
||||
reader_analyzer_stop(nfc_worker->reader_analyzer);
|
||||
}
|
||||
}
|
||||
|
||||
static bool nfc_worker_read_mf_ultralight(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) {
|
||||
bool read_success = false;
|
||||
MfUltralightReader reader = {};
|
||||
@ -317,7 +555,12 @@ void nfc_worker_read(NfcWorker* nfc_worker) {
|
||||
event = NfcWorkerEventReadUidNfcF;
|
||||
break;
|
||||
} else if(nfc_data->type == FuriHalNfcTypeV) {
|
||||
event = NfcWorkerEventReadUidNfcV;
|
||||
FURI_LOG_I(TAG, "NfcV detected");
|
||||
nfc_worker->dev_data->protocol = NfcDeviceProtocolNfcV;
|
||||
if(nfc_worker_read_nfcv(nfc_worker, &tx_rx)) {
|
||||
FURI_LOG_I(TAG, "nfc_worker_read_nfcv success");
|
||||
}
|
||||
event = NfcWorkerEventReadNfcV;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
|
@ -18,6 +18,10 @@ typedef enum {
|
||||
NfcWorkerStateReadMfUltralightReadAuth,
|
||||
NfcWorkerStateMfClassicDictAttack,
|
||||
NfcWorkerStateAnalyzeReader,
|
||||
NfcWorkerStateNfcVEmulate,
|
||||
NfcWorkerStateNfcVUnlock,
|
||||
NfcWorkerStateNfcVUnlockAndSave,
|
||||
NfcWorkerStateNfcVSniff,
|
||||
// Debug
|
||||
NfcWorkerStateEmulateApdu,
|
||||
NfcWorkerStateField,
|
||||
@ -39,6 +43,7 @@ typedef enum {
|
||||
NfcWorkerEventReadMfClassicDone,
|
||||
NfcWorkerEventReadMfClassicLoadKeyCache,
|
||||
NfcWorkerEventReadMfClassicDictAttackRequired,
|
||||
NfcWorkerEventReadNfcV,
|
||||
|
||||
// Nfc worker common events
|
||||
NfcWorkerEventSuccess,
|
||||
@ -69,6 +74,9 @@ typedef enum {
|
||||
// Mifare Ultralight events
|
||||
NfcWorkerEventMfUltralightPassKey, // NFC worker requesting manual key
|
||||
NfcWorkerEventMfUltralightPwdAuth, // Reader sent auth command
|
||||
NfcWorkerEventNfcVPassKey, // NFC worker requesting manual key
|
||||
NfcWorkerEventNfcVCommandExecuted,
|
||||
NfcWorkerEventNfcVContentChanged,
|
||||
} NfcWorkerEvent;
|
||||
|
||||
typedef bool (*NfcWorkerCallback)(NfcWorkerEvent event, void* context);
|
||||
@ -87,3 +95,6 @@ void nfc_worker_start(
|
||||
void* context);
|
||||
|
||||
void nfc_worker_stop(NfcWorker* nfc_worker);
|
||||
void nfc_worker_nfcv_unlock(NfcWorker* nfc_worker);
|
||||
void nfc_worker_nfcv_emulate(NfcWorker* nfc_worker);
|
||||
void nfc_worker_nfcv_sniff(NfcWorker* nfc_worker);
|
@ -11,6 +11,8 @@
|
||||
#include <lib/nfc/protocols/mifare_classic.h>
|
||||
#include <lib/nfc/protocols/mifare_desfire.h>
|
||||
#include <lib/nfc/protocols/nfca.h>
|
||||
#include <lib/nfc/protocols/nfcv.h>
|
||||
#include <lib/nfc/protocols/slix.h>
|
||||
#include <lib/nfc/helpers/reader_analyzer.h>
|
||||
|
||||
struct NfcWorker {
|
||||
|
1398
lib/nfc/protocols/nfcv.c
Normal file
1398
lib/nfc/protocols/nfcv.c
Normal file
File diff suppressed because it is too large
Load Diff
291
lib/nfc/protocols/nfcv.h
Normal file
291
lib/nfc/protocols/nfcv.h
Normal file
@ -0,0 +1,291 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <lib/digital_signal/digital_signal.h>
|
||||
#include <lib/pulse_reader/pulse_reader.h>
|
||||
#include "nfc_util.h"
|
||||
#include <furi_hal_nfc.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* true: modulating releases load, false: modulating adds load resistor to field coil */
|
||||
#define NFCV_LOAD_MODULATION_POLARITY (false)
|
||||
|
||||
#define NFCV_FC (13560000.0f) /* MHz */
|
||||
#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 NFCV_PULSE_DURATION_NS (128.0f * 1000000000.0f / NFCV_FC)
|
||||
|
||||
/* ISO/IEC 15693-3:2019(E) 10.4.12: maximum number of blocks is defined as 256 */
|
||||
#define NFCV_BLOCKS_MAX 256
|
||||
/* ISO/IEC 15693-3:2019(E) 10.4.12: maximum size of blocks is defined as 32 */
|
||||
#define NFCV_BLOCKSIZE_MAX 32
|
||||
/* the resulting memory size a card can have */
|
||||
#define NFCV_MEMSIZE_MAX (NFCV_BLOCKS_MAX * NFCV_BLOCKSIZE_MAX)
|
||||
/* ISO/IEC 15693-3:2019(E) 7.1b: standard allows up to 8192, the maxium frame length that we are expected to receive/send is less */
|
||||
#define NFCV_FRAMESIZE_MAX (1 + NFCV_MEMSIZE_MAX + NFCV_BLOCKS_MAX)
|
||||
|
||||
/* maximum string length for log messages */
|
||||
#define NFCV_LOG_STR_LEN 128
|
||||
/* maximum of pulses to be buffered by pulse reader */
|
||||
#define NFCV_PULSE_BUFFER 512
|
||||
|
||||
//#define NFCV_DIAGNOSTIC_DUMPS
|
||||
//#define NFCV_DIAGNOSTIC_DUMP_SIZE 256
|
||||
//#define NFCV_VERBOSE
|
||||
|
||||
/* helpers to calculate the send time based on DWT->CYCCNT */
|
||||
#define NFCV_FDT_USEC(usec) ((usec)*64)
|
||||
#define NFCV_FDT_FC(ticks) ((ticks)*6400 / 1356)
|
||||
|
||||
/* state machine when receiving frame bits */
|
||||
#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
|
||||
|
||||
/* sequences for every section of a frame */
|
||||
#define NFCV_SIG_SOF 0
|
||||
#define NFCV_SIG_BIT0 1
|
||||
#define NFCV_SIG_BIT1 2
|
||||
#define NFCV_SIG_EOF 3
|
||||
#define NFCV_SIG_LOW_SOF 4
|
||||
#define NFCV_SIG_LOW_BIT0 5
|
||||
#define NFCV_SIG_LOW_BIT1 6
|
||||
#define NFCV_SIG_LOW_EOF 7
|
||||
|
||||
/* various constants */
|
||||
#define NFCV_COMMAND_RETRIES 5
|
||||
#define NFCV_UID_LENGTH 8
|
||||
|
||||
/* ISO15693 protocol flags */
|
||||
typedef enum {
|
||||
/* ISO15693 protocol flags when INVENTORY is NOT set */
|
||||
NFCV_REQ_FLAG_SUB_CARRIER = (1 << 0),
|
||||
NFCV_REQ_FLAG_DATA_RATE = (1 << 1),
|
||||
NFCV_REQ_FLAG_INVENTORY = (1 << 2),
|
||||
NFCV_REQ_FLAG_PROTOCOL_EXT = (1 << 3),
|
||||
NFCV_REQ_FLAG_SELECT = (1 << 4),
|
||||
NFCV_REQ_FLAG_ADDRESS = (1 << 5),
|
||||
NFCV_REQ_FLAG_OPTION = (1 << 6),
|
||||
/* ISO15693 protocol flags when INVENTORY flag is set */
|
||||
NFCV_REQ_FLAG_AFI = (1 << 4),
|
||||
NFCV_REQ_FLAG_NB_SLOTS = (1 << 5)
|
||||
} NfcVRequestFlags;
|
||||
|
||||
/* ISO15693 protocol flags */
|
||||
typedef enum {
|
||||
NFCV_RES_FLAG_ERROR = (1 << 0),
|
||||
NFCV_RES_FLAG_VALIDITY = (1 << 1),
|
||||
NFCV_RES_FLAG_FINAL = (1 << 2),
|
||||
NFCV_RES_FLAG_PROTOCOL_EXT = (1 << 3),
|
||||
NFCV_RES_FLAG_SEC_LEN1 = (1 << 4),
|
||||
NFCV_RES_FLAG_SEC_LEN2 = (1 << 5),
|
||||
NFCV_RES_FLAG_WAIT_EXT = (1 << 6),
|
||||
} NfcVRsponseFlags;
|
||||
|
||||
/* flags for SYSINFO response */
|
||||
typedef enum {
|
||||
NFCV_SYSINFO_FLAG_DSFID = (1 << 0),
|
||||
NFCV_SYSINFO_FLAG_AFI = (1 << 1),
|
||||
NFCV_SYSINFO_FLAG_MEMSIZE = (1 << 2),
|
||||
NFCV_SYSINFO_FLAG_ICREF = (1 << 3)
|
||||
} NfcVSysinfoFlags;
|
||||
|
||||
/* ISO15693 command codes */
|
||||
typedef enum {
|
||||
/* mandatory command codes */
|
||||
NFCV_CMD_INVENTORY = 0x01,
|
||||
NFCV_CMD_STAY_QUIET = 0x02,
|
||||
/* optional command codes */
|
||||
NFCV_CMD_READ_BLOCK = 0x20,
|
||||
NFCV_CMD_WRITE_BLOCK = 0x21,
|
||||
NFCV_CMD_LOCK_BLOCK = 0x22,
|
||||
NFCV_CMD_READ_MULTI_BLOCK = 0x23,
|
||||
NFCV_CMD_WRITE_MULTI_BLOCK = 0x24,
|
||||
NFCV_CMD_SELECT = 0x25,
|
||||
NFCV_CMD_RESET_TO_READY = 0x26,
|
||||
NFCV_CMD_WRITE_AFI = 0x27,
|
||||
NFCV_CMD_LOCK_AFI = 0x28,
|
||||
NFCV_CMD_WRITE_DSFID = 0x29,
|
||||
NFCV_CMD_LOCK_DSFID = 0x2A,
|
||||
NFCV_CMD_GET_SYSTEM_INFO = 0x2B,
|
||||
NFCV_CMD_READ_MULTI_SECSTATUS = 0x2C,
|
||||
/* advanced command codes */
|
||||
NFCV_CMD_ADVANCED = 0xA0,
|
||||
/* flipper zero custom command codes */
|
||||
NFCV_CMD_CUST_ECHO_MODE = 0xDE,
|
||||
NFCV_CMD_CUST_ECHO_DATA = 0xDF
|
||||
} NfcVCommands;
|
||||
|
||||
/* ISO15693 Response error codes */
|
||||
typedef enum {
|
||||
NFCV_NOERROR = 0x00,
|
||||
NFCV_ERROR_CMD_NOT_SUP = 0x01, // Command not supported
|
||||
NFCV_ERROR_CMD_NOT_REC = 0x02, // Command not recognized (eg. parameter error)
|
||||
NFCV_ERROR_CMD_OPTION = 0x03, // Command option not supported
|
||||
NFCV_ERROR_GENERIC = 0x0F, // No additional Info about this error
|
||||
NFCV_ERROR_BLOCK_UNAVAILABLE = 0x10,
|
||||
NFCV_ERROR_BLOCK_LOCKED_ALREADY = 0x11, // cannot lock again
|
||||
NFCV_ERROR_BLOCK_LOCKED = 0x12, // cannot be changed
|
||||
NFCV_ERROR_BLOCK_WRITE = 0x13, // Writing was unsuccessful
|
||||
NFCV_ERROR_BLOCL_WRITELOCK = 0x14 // Locking was unsuccessful
|
||||
} NfcVErrorcodes;
|
||||
|
||||
typedef enum {
|
||||
NfcVLockBitDsfid = 1,
|
||||
NfcVLockBitAfi = 2,
|
||||
} NfcVLockBits;
|
||||
|
||||
typedef enum {
|
||||
NfcVAuthMethodManual,
|
||||
NfcVAuthMethodTonieBox,
|
||||
} NfcVAuthMethod;
|
||||
|
||||
typedef enum {
|
||||
NfcVTypePlain = 0,
|
||||
NfcVTypeSlix = 1,
|
||||
NfcVTypeSlixS = 2,
|
||||
NfcVTypeSlixL = 3,
|
||||
NfcVTypeSlix2 = 4,
|
||||
NfcVTypeSniff = 255,
|
||||
} NfcVSubtype;
|
||||
|
||||
typedef enum {
|
||||
NfcVSendFlagsNormal = 0,
|
||||
NfcVSendFlagsSof = 1 << 0,
|
||||
NfcVSendFlagsCrc = 1 << 1,
|
||||
NfcVSendFlagsEof = 1 << 2,
|
||||
NfcVSendFlagsOneSubcarrier = 0,
|
||||
NfcVSendFlagsTwoSubcarrier = 1 << 3,
|
||||
NfcVSendFlagsLowRate = 0,
|
||||
NfcVSendFlagsHighRate = 1 << 4
|
||||
} NfcVSendFlags;
|
||||
|
||||
typedef struct {
|
||||
uint8_t key_read[4];
|
||||
uint8_t key_write[4];
|
||||
uint8_t key_privacy[4];
|
||||
uint8_t key_destroy[4];
|
||||
uint8_t key_eas[4];
|
||||
uint8_t rand[2];
|
||||
bool privacy;
|
||||
} NfcVSlixData;
|
||||
|
||||
typedef union {
|
||||
NfcVSlixData slix;
|
||||
} NfcVSubtypeData;
|
||||
|
||||
typedef struct {
|
||||
DigitalSignal* nfcv_resp_sof;
|
||||
DigitalSignal* nfcv_resp_one;
|
||||
DigitalSignal* nfcv_resp_zero;
|
||||
DigitalSignal* nfcv_resp_eof;
|
||||
} NfcVEmuAirSignals;
|
||||
|
||||
typedef struct {
|
||||
PulseReader* reader_signal;
|
||||
DigitalSignal* nfcv_resp_pulse; /* pulse length, fc/32 */
|
||||
DigitalSignal* nfcv_resp_half_pulse; /* half pulse length, fc/32 */
|
||||
DigitalSignal* nfcv_resp_unmod; /* unmodulated length 256/fc */
|
||||
NfcVEmuAirSignals signals_high;
|
||||
NfcVEmuAirSignals signals_low;
|
||||
DigitalSequence* nfcv_signal;
|
||||
} NfcVEmuAir;
|
||||
|
||||
typedef void (*NfcVEmuProtocolHandler)(
|
||||
FuriHalNfcTxRxContext* tx_rx,
|
||||
FuriHalNfcDevData* nfc_data,
|
||||
void* nfcv_data);
|
||||
typedef bool (*NfcVEmuProtocolFilter)(
|
||||
FuriHalNfcTxRxContext* tx_rx,
|
||||
FuriHalNfcDevData* nfc_data,
|
||||
void* nfcv_data);
|
||||
|
||||
/* the default ISO15693 handler context */
|
||||
typedef struct {
|
||||
uint8_t flags; /* ISO15693-3 flags of the header as specified */
|
||||
uint8_t command; /* ISO15693-3 command at offset 1 as specified */
|
||||
bool selected; /* ISO15693-3 flags: selected frame */
|
||||
bool addressed; /* ISO15693-3 flags: addressed frame */
|
||||
bool advanced; /* ISO15693-3 command: advanced command */
|
||||
uint8_t address_offset; /* ISO15693-3 offset of the address in frame, if addressed is set */
|
||||
uint8_t payload_offset; /* ISO15693-3 offset of the payload in frame */
|
||||
|
||||
uint8_t response_buffer[NFCV_FRAMESIZE_MAX]; /* pre-allocated response buffer */
|
||||
NfcVSendFlags response_flags; /* flags to use when sending response */
|
||||
uint32_t send_time; /* timestamp when to send the response */
|
||||
|
||||
NfcVEmuProtocolFilter emu_protocol_filter;
|
||||
} NfcVEmuProtocolCtx;
|
||||
|
||||
typedef struct {
|
||||
/* common ISO15693 fields, being specified in ISO15693-3 */
|
||||
uint8_t dsfid;
|
||||
uint8_t afi;
|
||||
uint8_t ic_ref;
|
||||
uint16_t block_num;
|
||||
uint8_t block_size;
|
||||
uint8_t data[NFCV_MEMSIZE_MAX];
|
||||
uint8_t security_status[1 + NFCV_BLOCKS_MAX];
|
||||
bool selected;
|
||||
bool quiet;
|
||||
|
||||
bool modified;
|
||||
bool ready;
|
||||
bool echo_mode;
|
||||
|
||||
/* specfic variant infos */
|
||||
NfcVSubtype sub_type;
|
||||
NfcVSubtypeData sub_data;
|
||||
NfcVAuthMethod auth_method;
|
||||
|
||||
/* precalced air level data */
|
||||
NfcVEmuAir emu_air;
|
||||
|
||||
uint8_t* frame; /* [NFCV_FRAMESIZE_MAX] ISO15693-2 incoming raw data from air layer */
|
||||
uint8_t frame_length; /* ISO15693-2 length of incoming data */
|
||||
uint32_t eof_timestamp; /* ISO15693-2 EOF timestamp, read from DWT->CYCCNT */
|
||||
|
||||
/* handler for the protocol layer as specified in ISO15693-3 */
|
||||
NfcVEmuProtocolHandler emu_protocol_handler;
|
||||
void* emu_protocol_ctx;
|
||||
/* runtime data */
|
||||
char last_command[NFCV_LOG_STR_LEN];
|
||||
char error[NFCV_LOG_STR_LEN];
|
||||
} NfcVData;
|
||||
|
||||
typedef struct {
|
||||
uint16_t blocks_to_read;
|
||||
int16_t blocks_read;
|
||||
} NfcVReader;
|
||||
|
||||
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(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data);
|
||||
void nfcv_emu_deinit(NfcVData* nfcv_data);
|
||||
bool nfcv_emu_loop(
|
||||
FuriHalNfcTxRxContext* tx_rx,
|
||||
FuriHalNfcDevData* nfc_data,
|
||||
NfcVData* nfcv_data,
|
||||
uint32_t timeout_ms);
|
||||
void nfcv_emu_send(
|
||||
FuriHalNfcTxRxContext* tx_rx,
|
||||
NfcVData* nfcv,
|
||||
uint8_t* data,
|
||||
uint8_t length,
|
||||
NfcVSendFlags flags,
|
||||
uint32_t send_time);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
412
lib/nfc/protocols/slix.c
Normal file
412
lib/nfc/protocols/slix.c
Normal file
@ -0,0 +1,412 @@
|
||||
|
||||
#include <limits.h>
|
||||
#include "nfcv.h"
|
||||
#include "slix.h"
|
||||
#include "nfc_util.h"
|
||||
#include <furi.h>
|
||||
#include "furi_hal_nfc.h"
|
||||
#include <furi_hal_random.h>
|
||||
|
||||
#define TAG "SLIX"
|
||||
|
||||
static uint32_t slix_read_be(uint8_t* data, uint32_t length) {
|
||||
uint32_t value = 0;
|
||||
|
||||
for(uint32_t pos = 0; pos < length; pos++) {
|
||||
value <<= 8;
|
||||
value |= data[pos];
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
uint8_t slix_get_ti(FuriHalNfcDevData* nfc_data) {
|
||||
return (nfc_data->uid[3] >> 3) & 3;
|
||||
}
|
||||
|
||||
bool slix_check_card_type(FuriHalNfcDevData* nfc_data) {
|
||||
if((nfc_data->uid[0] == 0xE0) && (nfc_data->uid[1] == 0x04) && (nfc_data->uid[2] == 0x01) &&
|
||||
slix_get_ti(nfc_data) == 2) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool slix2_check_card_type(FuriHalNfcDevData* nfc_data) {
|
||||
if((nfc_data->uid[0] == 0xE0) && (nfc_data->uid[1] == 0x04) && (nfc_data->uid[2] == 0x01) &&
|
||||
slix_get_ti(nfc_data) == 1) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool slix_s_check_card_type(FuriHalNfcDevData* nfc_data) {
|
||||
if((nfc_data->uid[0] == 0xE0) && (nfc_data->uid[1] == 0x04) && (nfc_data->uid[2] == 0x02)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool slix_l_check_card_type(FuriHalNfcDevData* nfc_data) {
|
||||
if((nfc_data->uid[0] == 0xE0) && (nfc_data->uid[1] == 0x04) && (nfc_data->uid[2] == 0x03)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
ReturnCode slix_get_random(NfcVData* data) {
|
||||
uint16_t received = 0;
|
||||
uint8_t rxBuf[32];
|
||||
|
||||
ReturnCode ret = rfalNfcvPollerTransceiveReq(
|
||||
NFCV_CMD_NXP_GET_RANDOM_NUMBER,
|
||||
RFAL_NFCV_REQ_FLAG_DEFAULT,
|
||||
NFCV_MANUFACTURER_NXP,
|
||||
NULL,
|
||||
NULL,
|
||||
0,
|
||||
rxBuf,
|
||||
sizeof(rxBuf),
|
||||
&received);
|
||||
|
||||
if(ret == ERR_NONE) {
|
||||
if(received != 3) {
|
||||
return ERR_PROTO;
|
||||
}
|
||||
if(data != NULL) {
|
||||
data->sub_data.slix.rand[0] = rxBuf[2];
|
||||
data->sub_data.slix.rand[1] = rxBuf[1];
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
ReturnCode slix_unlock(NfcVData* data, uint32_t password_id) {
|
||||
furi_assert(rand);
|
||||
|
||||
uint16_t received = 0;
|
||||
uint8_t rxBuf[32];
|
||||
uint8_t cmd_set_pass[] = {
|
||||
password_id,
|
||||
data->sub_data.slix.rand[1],
|
||||
data->sub_data.slix.rand[0],
|
||||
data->sub_data.slix.rand[1],
|
||||
data->sub_data.slix.rand[0]};
|
||||
uint8_t* password = NULL;
|
||||
|
||||
switch(password_id) {
|
||||
case SLIX_PASS_READ:
|
||||
password = data->sub_data.slix.key_read;
|
||||
break;
|
||||
case SLIX_PASS_WRITE:
|
||||
password = data->sub_data.slix.key_write;
|
||||
break;
|
||||
case SLIX_PASS_PRIVACY:
|
||||
password = data->sub_data.slix.key_privacy;
|
||||
break;
|
||||
case SLIX_PASS_DESTROY:
|
||||
password = data->sub_data.slix.key_destroy;
|
||||
break;
|
||||
case SLIX_PASS_EASAFI:
|
||||
password = data->sub_data.slix.key_eas;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if(!password) {
|
||||
return ERR_NOTSUPP;
|
||||
}
|
||||
|
||||
for(int pos = 0; pos < 4; pos++) {
|
||||
cmd_set_pass[1 + pos] ^= password[3 - pos];
|
||||
}
|
||||
|
||||
ReturnCode ret = rfalNfcvPollerTransceiveReq(
|
||||
NFCV_CMD_NXP_SET_PASSWORD,
|
||||
RFAL_NFCV_REQ_FLAG_DATA_RATE,
|
||||
NFCV_MANUFACTURER_NXP,
|
||||
NULL,
|
||||
cmd_set_pass,
|
||||
sizeof(cmd_set_pass),
|
||||
rxBuf,
|
||||
sizeof(rxBuf),
|
||||
&received);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool slix_generic_protocol_filter(
|
||||
FuriHalNfcTxRxContext* tx_rx,
|
||||
FuriHalNfcDevData* nfc_data,
|
||||
void* nfcv_data_in,
|
||||
uint32_t password_supported) {
|
||||
furi_assert(tx_rx);
|
||||
furi_assert(nfc_data);
|
||||
furi_assert(nfcv_data_in);
|
||||
|
||||
NfcVData* nfcv_data = (NfcVData*)nfcv_data_in;
|
||||
NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx;
|
||||
NfcVSlixData* slix = &nfcv_data->sub_data.slix;
|
||||
|
||||
if(slix->privacy && ctx->command != NFCV_CMD_NXP_GET_RANDOM_NUMBER &&
|
||||
ctx->command != NFCV_CMD_NXP_SET_PASSWORD) {
|
||||
snprintf(
|
||||
nfcv_data->last_command,
|
||||
sizeof(nfcv_data->last_command),
|
||||
"command 0x%02X ignored, privacy mode",
|
||||
ctx->command);
|
||||
FURI_LOG_D(TAG, "%s", nfcv_data->last_command);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool handled = false;
|
||||
|
||||
switch(ctx->command) {
|
||||
case NFCV_CMD_NXP_GET_RANDOM_NUMBER: {
|
||||
slix->rand[0] = furi_hal_random_get();
|
||||
slix->rand[1] = furi_hal_random_get();
|
||||
|
||||
ctx->response_buffer[0] = NFCV_NOERROR;
|
||||
ctx->response_buffer[1] = slix->rand[1];
|
||||
ctx->response_buffer[2] = slix->rand[0];
|
||||
|
||||
nfcv_emu_send(
|
||||
tx_rx, nfcv_data, ctx->response_buffer, 3, ctx->response_flags, ctx->send_time);
|
||||
snprintf(
|
||||
nfcv_data->last_command,
|
||||
sizeof(nfcv_data->last_command),
|
||||
"GET_RANDOM_NUMBER -> 0x%02X%02X",
|
||||
slix->rand[0],
|
||||
slix->rand[1]);
|
||||
|
||||
handled = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case NFCV_CMD_NXP_SET_PASSWORD: {
|
||||
uint8_t password_id = nfcv_data->frame[ctx->payload_offset];
|
||||
|
||||
if(!(password_id & password_supported)) {
|
||||
break;
|
||||
}
|
||||
|
||||
uint8_t* password_xored = &nfcv_data->frame[ctx->payload_offset + 1];
|
||||
uint8_t* rand = slix->rand;
|
||||
uint8_t* password = NULL;
|
||||
uint8_t password_rcv[4];
|
||||
|
||||
switch(password_id) {
|
||||
case SLIX_PASS_READ:
|
||||
password = slix->key_read;
|
||||
break;
|
||||
case SLIX_PASS_WRITE:
|
||||
password = slix->key_write;
|
||||
break;
|
||||
case SLIX_PASS_PRIVACY:
|
||||
password = slix->key_privacy;
|
||||
break;
|
||||
case SLIX_PASS_DESTROY:
|
||||
password = slix->key_destroy;
|
||||
break;
|
||||
case SLIX_PASS_EASAFI:
|
||||
password = slix->key_eas;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if(!password) {
|
||||
break;
|
||||
}
|
||||
|
||||
for(int pos = 0; pos < 4; pos++) {
|
||||
password_rcv[pos] = password_xored[3 - pos] ^ rand[pos % 2];
|
||||
}
|
||||
uint32_t pass_expect = slix_read_be(password, 4);
|
||||
uint32_t pass_received = slix_read_be(password_rcv, 4);
|
||||
|
||||
/* if the password is all-zeroes, just accept any password*/
|
||||
if(!pass_expect || pass_expect == pass_received) {
|
||||
switch(password_id) {
|
||||
case SLIX_PASS_READ:
|
||||
break;
|
||||
case SLIX_PASS_WRITE:
|
||||
break;
|
||||
case SLIX_PASS_PRIVACY:
|
||||
slix->privacy = false;
|
||||
nfcv_data->modified = true;
|
||||
break;
|
||||
case SLIX_PASS_DESTROY:
|
||||
FURI_LOG_D(TAG, "Pooof! Got destroyed");
|
||||
break;
|
||||
case SLIX_PASS_EASAFI:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
ctx->response_buffer[0] = NFCV_NOERROR;
|
||||
nfcv_emu_send(
|
||||
tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time);
|
||||
snprintf(
|
||||
nfcv_data->last_command,
|
||||
sizeof(nfcv_data->last_command),
|
||||
"SET_PASSWORD #%02X 0x%08lX OK",
|
||||
password_id,
|
||||
pass_received);
|
||||
} else {
|
||||
snprintf(
|
||||
nfcv_data->last_command,
|
||||
sizeof(nfcv_data->last_command),
|
||||
"SET_PASSWORD #%02X 0x%08lX/%08lX FAIL",
|
||||
password_id,
|
||||
pass_received,
|
||||
pass_expect);
|
||||
}
|
||||
handled = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case NFCV_CMD_NXP_ENABLE_PRIVACY: {
|
||||
ctx->response_buffer[0] = NFCV_NOERROR;
|
||||
|
||||
nfcv_emu_send(
|
||||
tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time);
|
||||
snprintf(
|
||||
nfcv_data->last_command,
|
||||
sizeof(nfcv_data->last_command),
|
||||
"NFCV_CMD_NXP_ENABLE_PRIVACY");
|
||||
|
||||
slix->privacy = true;
|
||||
handled = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return handled;
|
||||
}
|
||||
|
||||
bool slix_l_protocol_filter(
|
||||
FuriHalNfcTxRxContext* tx_rx,
|
||||
FuriHalNfcDevData* nfc_data,
|
||||
void* nfcv_data_in) {
|
||||
furi_assert(tx_rx);
|
||||
furi_assert(nfc_data);
|
||||
furi_assert(nfcv_data_in);
|
||||
|
||||
bool handled = false;
|
||||
|
||||
/* many SLIX share some of the functions, place that in a generic handler */
|
||||
if(slix_generic_protocol_filter(
|
||||
tx_rx,
|
||||
nfc_data,
|
||||
nfcv_data_in,
|
||||
SLIX_PASS_PRIVACY | SLIX_PASS_DESTROY | SLIX_PASS_EASAFI)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return handled;
|
||||
}
|
||||
|
||||
void slix_l_prepare(NfcVData* nfcv_data) {
|
||||
FURI_LOG_D(
|
||||
TAG, " Privacy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_privacy, 4));
|
||||
FURI_LOG_D(
|
||||
TAG, " Destroy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_destroy, 4));
|
||||
FURI_LOG_D(TAG, " EAS pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_eas, 4));
|
||||
FURI_LOG_D(TAG, " Privacy mode: %s", nfcv_data->sub_data.slix.privacy ? "ON" : "OFF");
|
||||
|
||||
NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx;
|
||||
ctx->emu_protocol_filter = &slix_l_protocol_filter;
|
||||
}
|
||||
|
||||
bool slix_s_protocol_filter(
|
||||
FuriHalNfcTxRxContext* tx_rx,
|
||||
FuriHalNfcDevData* nfc_data,
|
||||
void* nfcv_data_in) {
|
||||
furi_assert(tx_rx);
|
||||
furi_assert(nfc_data);
|
||||
furi_assert(nfcv_data_in);
|
||||
|
||||
bool handled = false;
|
||||
|
||||
/* many SLIX share some of the functions, place that in a generic handler */
|
||||
if(slix_generic_protocol_filter(tx_rx, nfc_data, nfcv_data_in, SLIX_PASS_ALL)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return handled;
|
||||
}
|
||||
|
||||
void slix_s_prepare(NfcVData* nfcv_data) {
|
||||
FURI_LOG_D(
|
||||
TAG, " Privacy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_privacy, 4));
|
||||
FURI_LOG_D(
|
||||
TAG, " Destroy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_destroy, 4));
|
||||
FURI_LOG_D(TAG, " EAS pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_eas, 4));
|
||||
FURI_LOG_D(TAG, " Privacy mode: %s", nfcv_data->sub_data.slix.privacy ? "ON" : "OFF");
|
||||
|
||||
NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx;
|
||||
ctx->emu_protocol_filter = &slix_s_protocol_filter;
|
||||
}
|
||||
|
||||
bool slix_protocol_filter(
|
||||
FuriHalNfcTxRxContext* tx_rx,
|
||||
FuriHalNfcDevData* nfc_data,
|
||||
void* nfcv_data_in) {
|
||||
furi_assert(tx_rx);
|
||||
furi_assert(nfc_data);
|
||||
furi_assert(nfcv_data_in);
|
||||
|
||||
bool handled = false;
|
||||
|
||||
/* many SLIX share some of the functions, place that in a generic handler */
|
||||
if(slix_generic_protocol_filter(tx_rx, nfc_data, nfcv_data_in, SLIX_PASS_EASAFI)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return handled;
|
||||
}
|
||||
|
||||
void slix_prepare(NfcVData* nfcv_data) {
|
||||
FURI_LOG_D(
|
||||
TAG, " Privacy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_privacy, 4));
|
||||
FURI_LOG_D(
|
||||
TAG, " Destroy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_destroy, 4));
|
||||
FURI_LOG_D(TAG, " EAS pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_eas, 4));
|
||||
FURI_LOG_D(TAG, " Privacy mode: %s", nfcv_data->sub_data.slix.privacy ? "ON" : "OFF");
|
||||
|
||||
NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx;
|
||||
ctx->emu_protocol_filter = &slix_protocol_filter;
|
||||
}
|
||||
|
||||
bool slix2_protocol_filter(
|
||||
FuriHalNfcTxRxContext* tx_rx,
|
||||
FuriHalNfcDevData* nfc_data,
|
||||
void* nfcv_data_in) {
|
||||
furi_assert(tx_rx);
|
||||
furi_assert(nfc_data);
|
||||
furi_assert(nfcv_data_in);
|
||||
|
||||
bool handled = false;
|
||||
|
||||
/* many SLIX share some of the functions, place that in a generic handler */
|
||||
if(slix_generic_protocol_filter(tx_rx, nfc_data, nfcv_data_in, SLIX_PASS_ALL)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return handled;
|
||||
}
|
||||
|
||||
void slix2_prepare(NfcVData* nfcv_data) {
|
||||
FURI_LOG_D(
|
||||
TAG, " Privacy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_privacy, 4));
|
||||
FURI_LOG_D(
|
||||
TAG, " Destroy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_destroy, 4));
|
||||
FURI_LOG_D(TAG, " EAS pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_eas, 4));
|
||||
FURI_LOG_D(TAG, " Privacy mode: %s", nfcv_data->sub_data.slix.privacy ? "ON" : "OFF");
|
||||
|
||||
NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx;
|
||||
ctx->emu_protocol_filter = &slix2_protocol_filter;
|
||||
}
|
46
lib/nfc/protocols/slix.h
Normal file
46
lib/nfc/protocols/slix.h
Normal file
@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "nfc_util.h"
|
||||
#include <furi_hal_nfc.h>
|
||||
|
||||
#define NFCV_MANUFACTURER_NXP 0x04
|
||||
|
||||
/* ISO15693-3 CUSTOM NXP COMMANDS */
|
||||
#define NFCV_CMD_NXP_SET_EAS 0xA2
|
||||
#define NFCV_CMD_NXP_RESET_EAS 0xA3
|
||||
#define NFCV_CMD_NXP_LOCK_EAS 0xA4
|
||||
#define NFCV_CMD_NXP_EAS_ALARM 0xA5
|
||||
#define NFCV_CMD_NXP_PASSWORD_PROTECT_EAS_AFI 0xA6
|
||||
#define NFCV_CMD_NXP_WRITE_EAS_ID 0xA7
|
||||
#define NFCV_CMD_NXP_INVENTORY_PAGE_READ 0xB0
|
||||
#define NFCV_CMD_NXP_INVENTORY_PAGE_READ_FAST 0xB1
|
||||
#define NFCV_CMD_NXP_GET_RANDOM_NUMBER 0xB2
|
||||
#define NFCV_CMD_NXP_SET_PASSWORD 0xB3
|
||||
#define NFCV_CMD_NXP_WRITE_PASSWORD 0xB4
|
||||
#define NFCV_CMD_NXP_DESTROY 0xB9
|
||||
#define NFCV_CMD_NXP_ENABLE_PRIVACY 0xBA
|
||||
|
||||
/* available passwords */
|
||||
#define SLIX_PASS_READ 0x01
|
||||
#define SLIX_PASS_WRITE 0x02
|
||||
#define SLIX_PASS_PRIVACY 0x04
|
||||
#define SLIX_PASS_DESTROY 0x08
|
||||
#define SLIX_PASS_EASAFI 0x10
|
||||
|
||||
#define SLIX_PASS_ALL \
|
||||
(SLIX_PASS_READ | SLIX_PASS_WRITE | SLIX_PASS_PRIVACY | SLIX_PASS_DESTROY | SLIX_PASS_EASAFI)
|
||||
|
||||
bool slix_check_card_type(FuriHalNfcDevData* nfc_data);
|
||||
bool slix2_check_card_type(FuriHalNfcDevData* nfc_data);
|
||||
bool slix_s_check_card_type(FuriHalNfcDevData* nfc_data);
|
||||
bool slix_l_check_card_type(FuriHalNfcDevData* nfc_data);
|
||||
|
||||
ReturnCode slix_get_random(NfcVData* data);
|
||||
ReturnCode slix_unlock(NfcVData* data, uint32_t password_id);
|
||||
|
||||
void slix_prepare(NfcVData* nfcv_data);
|
||||
void slix_s_prepare(NfcVData* nfcv_data);
|
||||
void slix_l_prepare(NfcVData* nfcv_data);
|
||||
void slix2_prepare(NfcVData* nfcv_data);
|
Loading…
Reference in New Issue
Block a user