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, |     NfcCustomEventDictAttackSkip, | ||||||
|     NfcCustomEventRpcLoad, |     NfcCustomEventRpcLoad, | ||||||
|     NfcCustomEventRpcSessionClose, |     NfcCustomEventRpcSessionClose, | ||||||
|  |     NfcCustomEventUpdateLog, | ||||||
|  |     NfcCustomEventSaveShadow, | ||||||
| }; | }; | ||||||
|  | |||||||
| @ -290,6 +290,9 @@ int32_t nfc_app(void* p) { | |||||||
|                 } else if(nfc->dev->format == NfcDeviceSaveFormatMifareClassic) { |                 } else if(nfc->dev->format == NfcDeviceSaveFormatMifareClassic) { | ||||||
|                     scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicEmulate); |                     scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicEmulate); | ||||||
|                     DOLPHIN_DEED(DolphinDeedNfcEmulate); |                     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) { |                 } else if(nfc->dev->format == NfcDeviceSaveFormatBankCard) { | ||||||
|                     scene_manager_next_scene(nfc->scene_manager, NfcSceneDeviceInfo); |                     scene_manager_next_scene(nfc->scene_manager, NfcSceneDeviceInfo); | ||||||
|                 } else { |                 } else { | ||||||
|  | |||||||
| @ -14,6 +14,13 @@ ADD_SCENE(nfc, file_select, FileSelect) | |||||||
| ADD_SCENE(nfc, emulate_uid, EmulateUid) | ADD_SCENE(nfc, emulate_uid, EmulateUid) | ||||||
| ADD_SCENE(nfc, nfca_read_success, NfcaReadSuccess) | ADD_SCENE(nfc, nfca_read_success, NfcaReadSuccess) | ||||||
| ADD_SCENE(nfc, nfca_menu, NfcaMenu) | ADD_SCENE(nfc, nfca_menu, NfcaMenu) | ||||||
|  | ADD_SCENE(nfc, nfcv_menu, NfcVMenu) | ||||||
|  | ADD_SCENE(nfc, nfcv_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_read_success, MfUltralightReadSuccess) | ||||||
| ADD_SCENE(nfc, mf_ultralight_data, MfUltralightData) | ADD_SCENE(nfc, mf_ultralight_data, MfUltralightData) | ||||||
| ADD_SCENE(nfc, mf_ultralight_menu, MfUltralightMenu) | 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)); |         nfc->widget, 64, 24, AlignCenter, AlignTop, FontSecondary, furi_string_get_cstr(temp_str)); | ||||||
| 
 | 
 | ||||||
|     NfcProtocol protocol = nfc->dev->dev_data.protocol; |     NfcProtocol protocol = nfc->dev->dev_data.protocol; | ||||||
|  |     const char* nfc_type = "NFC-A"; | ||||||
|  | 
 | ||||||
|     if(protocol == NfcDeviceProtocolEMV) { |     if(protocol == NfcDeviceProtocolEMV) { | ||||||
|         furi_string_set(temp_str, "EMV bank card"); |         furi_string_set(temp_str, "EMV bank card"); | ||||||
|     } else if(protocol == NfcDeviceProtocolMifareUl) { |     } 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)); |         furi_string_set(temp_str, nfc_mf_classic_type(nfc->dev->dev_data.mf_classic_data.type)); | ||||||
|     } else if(protocol == NfcDeviceProtocolMifareDesfire) { |     } else if(protocol == NfcDeviceProtocolMifareDesfire) { | ||||||
|         furi_string_set(temp_str, "MIFARE DESFire"); |         furi_string_set(temp_str, "MIFARE DESFire"); | ||||||
|  |     } else if(protocol == NfcDeviceProtocolNfcV) { | ||||||
|  |         furi_string_set(temp_str, "ISO15693 tag"); | ||||||
|  |         nfc_type = "NFC-V"; | ||||||
|     } else { |     } else { | ||||||
|         furi_string_set(temp_str, "Unknown ISO tag"); |         furi_string_set(temp_str, "Unknown ISO tag"); | ||||||
|     } |     } | ||||||
|     widget_add_string_element( |     widget_add_string_element( | ||||||
|         nfc->widget, 64, 34, AlignCenter, AlignTop, FontSecondary, furi_string_get_cstr(temp_str)); |         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); |     furi_string_free(temp_str); | ||||||
| 
 | 
 | ||||||
|     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); |     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); | ||||||
|  | |||||||
| @ -4,6 +4,8 @@ enum SubmenuIndex { | |||||||
|     SubmenuIndexReadCardType, |     SubmenuIndexReadCardType, | ||||||
|     SubmenuIndexMfClassicKeys, |     SubmenuIndexMfClassicKeys, | ||||||
|     SubmenuIndexMfUltralightUnlock, |     SubmenuIndexMfUltralightUnlock, | ||||||
|  |     SubmenuIndexNfcVUnlock, | ||||||
|  |     SubmenuIndexNfcVSniff, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| void nfc_scene_extra_actions_submenu_callback(void* context, uint32_t index) { | 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, |         SubmenuIndexMfUltralightUnlock, | ||||||
|         nfc_scene_extra_actions_submenu_callback, |         nfc_scene_extra_actions_submenu_callback, | ||||||
|         nfc); |         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_set_selected_item( | ||||||
|         submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneExtraActions)); |         submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneExtraActions)); | ||||||
|     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); |     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_set_scene_state(nfc->scene_manager, NfcSceneReadCardType, 0); | ||||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneReadCardType); |             scene_manager_next_scene(nfc->scene_manager, NfcSceneReadCardType); | ||||||
|             consumed = true; |             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); |         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)); |             temp_str, "\e#%s\n", nfc_mf_classic_type(dev_data->mf_classic_data.type)); | ||||||
|     } else if(protocol == NfcDeviceProtocolMifareDesfire) { |     } else if(protocol == NfcDeviceProtocolMifareDesfire) { | ||||||
|         furi_string_cat_printf(temp_str, "\e#MIFARE DESFire\n"); |         furi_string_cat_printf(temp_str, "\e#MIFARE DESFire\n"); | ||||||
|  |     } else if(protocol == NfcDeviceProtocolNfcV) { | ||||||
|  |         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 { |     } else { | ||||||
|         furi_string_cat_printf(temp_str, "\e#Unknown ISO tag\n"); |         furi_string_cat_printf(temp_str, "\e#Unknown ISO tag\n"); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // Set tag iso data
 |     // 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'; |         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, "ISO 14443-%c (NFC-A)\n", iso_type); | ||||||
|         furi_string_cat_printf(temp_str, "UID:"); |         furi_string_cat_printf(temp_str, "UID:"); | ||||||
|         for(size_t i = 0; i < nfc_data->uid_len; i++) { |         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, " %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); |         furi_string_cat_printf(temp_str, " SAK: %02X", nfc_data->sak); | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     // Set application specific data
 |     // Set application specific data
 | ||||||
|     if(protocol == NfcDeviceProtocolMifareDesfire) { |     if(protocol == NfcDeviceProtocolMifareDesfire) { | ||||||
| @ -139,6 +285,8 @@ bool nfc_scene_nfc_data_info_on_event(void* context, SceneManagerEvent event) { | |||||||
|                 consumed = true; |                 consumed = true; | ||||||
|             } else if(protocol == NfcDeviceProtocolMifareClassic) { |             } else if(protocol == NfcDeviceProtocolMifareClassic) { | ||||||
|                 scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicData); |                 scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicData); | ||||||
|  |             } else if(protocol == NfcDeviceProtocolNfcV) { | ||||||
|  |                 scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVMenu); | ||||||
|                 consumed = true; |                 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); |             scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcaReadSuccess); | ||||||
|             DOLPHIN_DEED(DolphinDeedNfcReadSuccess); |             DOLPHIN_DEED(DolphinDeedNfcReadSuccess); | ||||||
|             consumed = true; |             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) { |         } else if(event.event == NfcWorkerEventReadMfUltralight) { | ||||||
|             notification_message(nfc->notifications, &sequence_success); |             notification_message(nfc->notifications, &sequence_success); | ||||||
|             // Set unlock password input to 0xFFFFFFFF only on fresh read
 |             // 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->dev->dev_data, | ||||||
|                             nfc_scene_rpc_emulate_callback, |                             nfc_scene_rpc_emulate_callback, | ||||||
|                             nfc); |                             nfc); | ||||||
|  |                     } else if(nfc->dev->format == NfcDeviceSaveFormatNfcV) { | ||||||
|  |                         nfc_worker_start( | ||||||
|  |                             nfc->worker, | ||||||
|  |                             NfcWorkerStateNfcVEmulate, | ||||||
|  |                             &nfc->dev->dev_data, | ||||||
|  |                             nfc_scene_rpc_emulate_callback, | ||||||
|  |                             nfc); | ||||||
|                     } else { |                     } else { | ||||||
|                         nfc_worker_start( |                         nfc_worker_start( | ||||||
|                             nfc->worker, NfcWorkerStateUidEmulate, &nfc->dev->dev_data, NULL, nfc); |                             nfc->worker, NfcWorkerStateUidEmulate, &nfc->dev->dev_data, NULL, nfc); | ||||||
|  | |||||||
| @ -44,6 +44,7 @@ void nfc_scene_saved_menu_on_enter(void* context) { | |||||||
|     } else if( |     } else if( | ||||||
|         (nfc->dev->format == NfcDeviceSaveFormatMifareUl && |         (nfc->dev->format == NfcDeviceSaveFormatMifareUl && | ||||||
|          mf_ul_emulation_supported(&nfc->dev->dev_data.mf_ul_data)) || |          mf_ul_emulation_supported(&nfc->dev->dev_data.mf_ul_data)) || | ||||||
|  |         nfc->dev->format == NfcDeviceSaveFormatNfcV || | ||||||
|         nfc->dev->format == NfcDeviceSaveFormatMifareClassic) { |         nfc->dev->format == NfcDeviceSaveFormatMifareClassic) { | ||||||
|         submenu_add_item( |         submenu_add_item( | ||||||
|             submenu, "Emulate", SubmenuIndexEmulate, nfc_scene_saved_menu_submenu_callback, nfc); |             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); |                 scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightEmulate); | ||||||
|             } else if(nfc->dev->format == NfcDeviceSaveFormatMifareClassic) { |             } else if(nfc->dev->format == NfcDeviceSaveFormatMifareClassic) { | ||||||
|                 scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicEmulate); |                 scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicEmulate); | ||||||
|  |             } else if(nfc->dev->format == NfcDeviceSaveFormatNfcV) { | ||||||
|  |                 scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVEmulate); | ||||||
|             } else { |             } else { | ||||||
|                 scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid); |                 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_alloc,NfcaSignal*, | ||||||
| Function,-,nfca_signal_encode,void,"NfcaSignal*, uint8_t*, uint16_t, uint8_t*" | Function,-,nfca_signal_encode,void,"NfcaSignal*, uint8_t*, uint16_t, uint8_t*" | ||||||
| Function,-,nfca_signal_free,void,NfcaSignal* | Function,-,nfca_signal_free,void,NfcaSignal* | ||||||
|  | Function,+,nfcv_emu_deinit,void,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,void,"NotificationApp*, const NotificationSequence*" | ||||||
| Function,+,notification_internal_message_block,void,"NotificationApp*, const NotificationSequence*" | Function,+,notification_internal_message_block,void,"NotificationApp*, const NotificationSequence*" | ||||||
| Function,+,notification_message,void,"NotificationApp*, const NotificationSequence*" | Function,+,notification_message,void,"NotificationApp*, const NotificationSequence*" | ||||||
|  | |||||||
| 
 | 
| @ -9,7 +9,7 @@ | |||||||
| #include <stm32wbxx_ll_tim.h> | #include <stm32wbxx_ll_tim.h> | ||||||
| 
 | 
 | ||||||
| /* must be on bank B */ | /* 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 { | struct ReloadBuffer { | ||||||
|     uint32_t* buffer; /* DMA ringbuffer */ |     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_set = internals->gpio->pin; | ||||||
|         uint32_t bit_reset = internals->gpio->pin << 16; |         uint32_t bit_reset = internals->gpio->pin << 16; | ||||||
| 
 | 
 | ||||||
| #ifdef DEBUG_OUTPUT | #ifdef DIGITAL_SIGNAL_DEBUG_OUTPUT_PIN | ||||||
|         bit_set |= DEBUG_OUTPUT.pin; |         bit_set |= DIGITAL_SIGNAL_DEBUG_OUTPUT_PIN.pin; | ||||||
|         bit_reset |= DEBUG_OUTPUT.pin << 16; |         bit_reset |= DIGITAL_SIGNAL_DEBUG_OUTPUT_PIN.pin << 16; | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|         if(signal->start_level) { |         if(signal->start_level) { | ||||||
| @ -540,8 +540,9 @@ bool digital_sequence_send(DigitalSequence* sequence) { | |||||||
|     struct ReloadBuffer* dma_buffer = sequence->dma_buffer; |     struct ReloadBuffer* dma_buffer = sequence->dma_buffer; | ||||||
| 
 | 
 | ||||||
|     furi_hal_gpio_init(sequence->gpio, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); |     furi_hal_gpio_init(sequence->gpio, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); | ||||||
| #ifdef DEBUG_OUTPUT | #ifdef DIGITAL_SIGNAL_DEBUG_OUTPUT_PIN | ||||||
|     furi_hal_gpio_init(&DEBUG_OUTPUT, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); |     furi_hal_gpio_init( | ||||||
|  |         &DIGITAL_SIGNAL_DEBUG_OUTPUT_PIN, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|     if(sequence->bake) { |     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"); |         furi_string_set(format_string, "Mifare Classic"); | ||||||
|     } else if(dev->format == NfcDeviceSaveFormatMifareDesfire) { |     } else if(dev->format == NfcDeviceSaveFormatMifareDesfire) { | ||||||
|         furi_string_set(format_string, "Mifare DESFire"); |         furi_string_set(format_string, "Mifare DESFire"); | ||||||
|  |     } else if(dev->format == NfcDeviceSaveFormatNfcV) { | ||||||
|  |         furi_string_set(format_string, "ISO15693"); | ||||||
|     } else { |     } else { | ||||||
|         furi_string_set(format_string, "Unknown"); |         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; |         dev->dev_data.protocol = NfcDeviceProtocolMifareDesfire; | ||||||
|         return true; |         return true; | ||||||
|     } |     } | ||||||
|  |     if(furi_string_start_with_str(format_string, "ISO15693")) { | ||||||
|  |         dev->format = NfcDeviceSaveFormatNfcV; | ||||||
|  |         dev->dev_data.protocol = NfcDeviceProtocolNfcV; | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|     return false; |     return false; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -650,7 +657,327 @@ bool nfc_device_load_mifare_df_data(FlipperFormat* file, NfcDevice* dev) { | |||||||
|     return parsed; |     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 nfc_device_load_bank_card_data(FlipperFormat* file, NfcDevice* dev) { | ||||||
|     bool parsed = false; |     bool parsed = false; | ||||||
|     EmvData* data = &dev->dev_data.emv_data; |     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; |         if(!flipper_format_write_header_cstr(file, nfc_file_header, nfc_file_version)) break; | ||||||
|         // Write nfc device type
 |         // Write nfc device type
 | ||||||
|         if(!flipper_format_write_comment_cstr( |         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; |             break; | ||||||
|         nfc_device_prepare_format_string(dev, temp_str); |         nfc_device_prepare_format_string(dev, temp_str); | ||||||
|         if(!flipper_format_write_string(file, "Device type", temp_str)) break; |         if(!flipper_format_write_string(file, "Device type", temp_str)) break; | ||||||
|         // Write UID, ATQA, SAK
 |         // Write UID
 | ||||||
|         if(!flipper_format_write_comment_cstr(file, "UID, ATQA and SAK are common for all formats")) |         if(!flipper_format_write_comment_cstr(file, "UID is common for all formats")) break; | ||||||
|             break; |  | ||||||
|         if(!flipper_format_write_hex(file, "UID", data->uid, data->uid_len)) 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
 |             // Save ATQA in MSB order for correct companion apps display
 | ||||||
|             uint8_t atqa[2] = {data->atqa[1], data->atqa[0]}; |             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, "ATQA", atqa, 2)) break; | ||||||
|             if(!flipper_format_write_hex(file, "SAK", &data->sak, 1)) break; |             if(!flipper_format_write_hex(file, "SAK", &data->sak, 1)) break; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         // Save more data if necessary
 |         // Save more data if necessary
 | ||||||
|         if(dev->format == NfcDeviceSaveFormatMifareUl) { |         if(dev->format == NfcDeviceSaveFormatMifareUl) { | ||||||
|             if(!nfc_device_save_mifare_ul_data(file, dev)) break; |             if(!nfc_device_save_mifare_ul_data(file, dev)) break; | ||||||
|         } else if(dev->format == NfcDeviceSaveFormatMifareDesfire) { |         } else if(dev->format == NfcDeviceSaveFormatMifareDesfire) { | ||||||
|             if(!nfc_device_save_mifare_df_data(file, dev)) break; |             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) { |         } else if(dev->format == NfcDeviceSaveFormatMifareClassic) { | ||||||
|             // Save data
 |             // Save data
 | ||||||
|             if(!nfc_device_save_mifare_classic_data(file, dev)) break; |             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; |         if(!nfc_device_parse_format_string(dev, temp_str)) break; | ||||||
|         // Read and parse UID, ATQA and SAK
 |         // Read and parse UID, ATQA and SAK
 | ||||||
|         if(!flipper_format_get_value_count(file, "UID", &data_cnt)) break; |         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; |         data->uid_len = data_cnt; | ||||||
|         if(!flipper_format_read_hex(file, "UID", data->uid, data->uid_len)) break; |         if(!flipper_format_read_hex(file, "UID", data->uid, data->uid_len)) break; | ||||||
|  |         if(dev->format != NfcDeviceSaveFormatNfcV) { | ||||||
|             if(version == version_with_lsb_atqa) { |             if(version == version_with_lsb_atqa) { | ||||||
|                 if(!flipper_format_read_hex(file, "ATQA", data->atqa, 2)) break; |                 if(!flipper_format_read_hex(file, "ATQA", data->atqa, 2)) break; | ||||||
|             } else { |             } else { | ||||||
| @ -1172,6 +1509,7 @@ static bool nfc_device_load_data(NfcDevice* dev, FuriString* path, bool show_dia | |||||||
|                 data->atqa[1] = atqa[0]; |                 data->atqa[1] = atqa[0]; | ||||||
|             } |             } | ||||||
|             if(!flipper_format_read_hex(file, "SAK", &data->sak, 1)) break; |             if(!flipper_format_read_hex(file, "SAK", &data->sak, 1)) break; | ||||||
|  |         } | ||||||
|         // Load CUID
 |         // Load CUID
 | ||||||
|         uint8_t* cuid_start = data->uid; |         uint8_t* cuid_start = data->uid; | ||||||
|         if(data->uid_len == 7) { |         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; |             if(!nfc_device_load_mifare_classic_data(file, dev)) break; | ||||||
|         } else if(dev->format == NfcDeviceSaveFormatMifareDesfire) { |         } else if(dev->format == NfcDeviceSaveFormatMifareDesfire) { | ||||||
|             if(!nfc_device_load_mifare_df_data(file, dev)) break; |             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) { |         } else if(dev->format == NfcDeviceSaveFormatBankCard) { | ||||||
|             if(!nfc_device_load_bank_card_data(file, dev)) break; |             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_ultralight.h> | ||||||
| #include <lib/nfc/protocols/mifare_classic.h> | #include <lib/nfc/protocols/mifare_classic.h> | ||||||
| #include <lib/nfc/protocols/mifare_desfire.h> | #include <lib/nfc/protocols/mifare_desfire.h> | ||||||
|  | #include <lib/nfc/protocols/nfcv.h> | ||||||
| 
 | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
| extern "C" { | extern "C" { | ||||||
| @ -31,6 +32,7 @@ typedef enum { | |||||||
|     NfcDeviceProtocolMifareUl, |     NfcDeviceProtocolMifareUl, | ||||||
|     NfcDeviceProtocolMifareClassic, |     NfcDeviceProtocolMifareClassic, | ||||||
|     NfcDeviceProtocolMifareDesfire, |     NfcDeviceProtocolMifareDesfire, | ||||||
|  |     NfcDeviceProtocolNfcV | ||||||
| } NfcProtocol; | } NfcProtocol; | ||||||
| 
 | 
 | ||||||
| typedef enum { | typedef enum { | ||||||
| @ -39,6 +41,7 @@ typedef enum { | |||||||
|     NfcDeviceSaveFormatMifareUl, |     NfcDeviceSaveFormatMifareUl, | ||||||
|     NfcDeviceSaveFormatMifareClassic, |     NfcDeviceSaveFormatMifareClassic, | ||||||
|     NfcDeviceSaveFormatMifareDesfire, |     NfcDeviceSaveFormatMifareDesfire, | ||||||
|  |     NfcDeviceSaveFormatNfcV, | ||||||
| } NfcDeviceSaveFormat; | } NfcDeviceSaveFormat; | ||||||
| 
 | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
| @ -73,6 +76,7 @@ typedef struct { | |||||||
|         MfUltralightData mf_ul_data; |         MfUltralightData mf_ul_data; | ||||||
|         MfClassicData mf_classic_data; |         MfClassicData mf_classic_data; | ||||||
|         MifareDesfireData mf_df_data; |         MifareDesfireData mf_df_data; | ||||||
|  |         NfcVData nfcv_data; | ||||||
|     }; |     }; | ||||||
|     FuriString* parsed_data; |     FuriString* parsed_data; | ||||||
| } NfcDeviceData; | } NfcDeviceData; | ||||||
|  | |||||||
| @ -111,6 +111,14 @@ int32_t nfc_worker_task(void* context) { | |||||||
|         nfc_worker_mf_classic_dict_attack(nfc_worker); |         nfc_worker_mf_classic_dict_attack(nfc_worker); | ||||||
|     } else if(nfc_worker->state == NfcWorkerStateAnalyzeReader) { |     } else if(nfc_worker->state == NfcWorkerStateAnalyzeReader) { | ||||||
|         nfc_worker_analyze_reader(nfc_worker); |         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(); |     furi_hal_nfc_sleep(); | ||||||
|     nfc_worker_change_state(nfc_worker, NfcWorkerStateReady); |     nfc_worker_change_state(nfc_worker, NfcWorkerStateReady); | ||||||
| @ -118,6 +126,236 @@ int32_t nfc_worker_task(void* context) { | |||||||
|     return 0; |     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) { | static bool nfc_worker_read_mf_ultralight(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { | ||||||
|     bool read_success = false; |     bool read_success = false; | ||||||
|     MfUltralightReader reader = {}; |     MfUltralightReader reader = {}; | ||||||
| @ -317,7 +555,12 @@ void nfc_worker_read(NfcWorker* nfc_worker) { | |||||||
|                 event = NfcWorkerEventReadUidNfcF; |                 event = NfcWorkerEventReadUidNfcF; | ||||||
|                 break; |                 break; | ||||||
|             } else if(nfc_data->type == FuriHalNfcTypeV) { |             } 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; |                 break; | ||||||
|             } |             } | ||||||
|         } else { |         } else { | ||||||
|  | |||||||
| @ -18,6 +18,10 @@ typedef enum { | |||||||
|     NfcWorkerStateReadMfUltralightReadAuth, |     NfcWorkerStateReadMfUltralightReadAuth, | ||||||
|     NfcWorkerStateMfClassicDictAttack, |     NfcWorkerStateMfClassicDictAttack, | ||||||
|     NfcWorkerStateAnalyzeReader, |     NfcWorkerStateAnalyzeReader, | ||||||
|  |     NfcWorkerStateNfcVEmulate, | ||||||
|  |     NfcWorkerStateNfcVUnlock, | ||||||
|  |     NfcWorkerStateNfcVUnlockAndSave, | ||||||
|  |     NfcWorkerStateNfcVSniff, | ||||||
|     // Debug
 |     // Debug
 | ||||||
|     NfcWorkerStateEmulateApdu, |     NfcWorkerStateEmulateApdu, | ||||||
|     NfcWorkerStateField, |     NfcWorkerStateField, | ||||||
| @ -39,6 +43,7 @@ typedef enum { | |||||||
|     NfcWorkerEventReadMfClassicDone, |     NfcWorkerEventReadMfClassicDone, | ||||||
|     NfcWorkerEventReadMfClassicLoadKeyCache, |     NfcWorkerEventReadMfClassicLoadKeyCache, | ||||||
|     NfcWorkerEventReadMfClassicDictAttackRequired, |     NfcWorkerEventReadMfClassicDictAttackRequired, | ||||||
|  |     NfcWorkerEventReadNfcV, | ||||||
| 
 | 
 | ||||||
|     // Nfc worker common events
 |     // Nfc worker common events
 | ||||||
|     NfcWorkerEventSuccess, |     NfcWorkerEventSuccess, | ||||||
| @ -69,6 +74,9 @@ typedef enum { | |||||||
|     // Mifare Ultralight events
 |     // Mifare Ultralight events
 | ||||||
|     NfcWorkerEventMfUltralightPassKey, // NFC worker requesting manual key
 |     NfcWorkerEventMfUltralightPassKey, // NFC worker requesting manual key
 | ||||||
|     NfcWorkerEventMfUltralightPwdAuth, // Reader sent auth command
 |     NfcWorkerEventMfUltralightPwdAuth, // Reader sent auth command
 | ||||||
|  |     NfcWorkerEventNfcVPassKey, // NFC worker requesting manual key
 | ||||||
|  |     NfcWorkerEventNfcVCommandExecuted, | ||||||
|  |     NfcWorkerEventNfcVContentChanged, | ||||||
| } NfcWorkerEvent; | } NfcWorkerEvent; | ||||||
| 
 | 
 | ||||||
| typedef bool (*NfcWorkerCallback)(NfcWorkerEvent event, void* context); | typedef bool (*NfcWorkerCallback)(NfcWorkerEvent event, void* context); | ||||||
| @ -87,3 +95,6 @@ void nfc_worker_start( | |||||||
|     void* context); |     void* context); | ||||||
| 
 | 
 | ||||||
| void nfc_worker_stop(NfcWorker* nfc_worker); | void nfc_worker_stop(NfcWorker* nfc_worker); | ||||||
|  | void nfc_worker_nfcv_unlock(NfcWorker* nfc_worker); | ||||||
|  | void nfc_worker_nfcv_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_classic.h> | ||||||
| #include <lib/nfc/protocols/mifare_desfire.h> | #include <lib/nfc/protocols/mifare_desfire.h> | ||||||
| #include <lib/nfc/protocols/nfca.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> | #include <lib/nfc/helpers/reader_analyzer.h> | ||||||
| 
 | 
 | ||||||
| struct NfcWorker { | 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