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
 | ||||
|     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, "\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; | ||||
| 
 | ||||
|         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,9 +1496,10 @@ 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(dev->format != NfcDeviceSaveFormatNfcV) { | ||||
|             if(version == version_with_lsb_atqa) { | ||||
|                 if(!flipper_format_read_hex(file, "ATQA", data->atqa, 2)) break; | ||||
|             } else { | ||||
| @ -1172,6 +1509,7 @@ static bool nfc_device_load_data(NfcDevice* dev, FuriString* path, bool show_dia | ||||
|                 data->atqa[1] = atqa[0]; | ||||
|             } | ||||
|             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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 g3gg0.de
						g3gg0.de