added ISO15693 reading, saving and revealing from privacy mode (unlock)

This commit is contained in:
g3gg0
2022-11-09 14:08:37 +01:00
parent 9f0aef330e
commit c840e9ad49
20 changed files with 888 additions and 20 deletions

View File

@@ -14,6 +14,10 @@ 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_read_auth, NfcVReadAuth)
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)

View File

@@ -3,6 +3,7 @@
enum SubmenuIndex { enum SubmenuIndex {
SubmenuIndexMfClassicKeys, SubmenuIndexMfClassicKeys,
SubmenuIndexMfUltralightUnlock, SubmenuIndexMfUltralightUnlock,
SubmenuIndexNfcVUnlock,
}; };
void nfc_scene_extra_actions_submenu_callback(void* context, uint32_t index) { void nfc_scene_extra_actions_submenu_callback(void* context, uint32_t index) {
@@ -27,6 +28,12 @@ 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);
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu);
} }
@@ -44,6 +51,8 @@ bool nfc_scene_extra_actions_on_event(void* context, SceneManagerEvent event) {
consumed = true; consumed = true;
} else if(event.event == SubmenuIndexMfUltralightUnlock) { } else if(event.event == SubmenuIndexMfUltralightUnlock) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockMenu); scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockMenu);
} else if(event.event == SubmenuIndexNfcVUnlock) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVUnlockMenu);
} }
scene_manager_set_scene_state(nfc->scene_manager, NfcSceneExtraActions, event.event); scene_manager_set_scene_state(nfc->scene_manager, NfcSceneExtraActions, event.event);
} }

View File

@@ -14,7 +14,7 @@ void nfc_scene_nfc_data_info_on_enter(void* context) {
NfcDeviceData* dev_data = &nfc->dev->dev_data; NfcDeviceData* dev_data = &nfc->dev->dev_data;
NfcProtocol protocol = dev_data->protocol; NfcProtocol protocol = dev_data->protocol;
uint8_t text_scroll_height = 0; uint8_t text_scroll_height = 0;
if((protocol == NfcDeviceProtocolMifareDesfire) || (protocol == NfcDeviceProtocolMifareUl)) { if((protocol == NfcDeviceProtocolMifareDesfire) || (protocol == NfcDeviceProtocolMifareUl)|| (protocol == NfcDeviceProtocolSlixL)) {
widget_add_button_element( widget_add_button_element(
widget, GuiButtonTypeRight, "More", nfc_scene_nfc_data_info_widget_callback, nfc); widget, GuiButtonTypeRight, "More", nfc_scene_nfc_data_info_widget_callback, nfc);
text_scroll_height = 52; text_scroll_height = 52;
@@ -40,19 +40,47 @@ 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 == NfcDeviceProtocolSlixL) {
furi_string_cat_printf(temp_str, "\e#ISO15693 SLIX-L\n");
} 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
char iso_type = FURI_BIT(nfc_data->sak, 5) ? '4' : '3'; if(protocol == NfcDeviceProtocolSlixL) {
furi_string_cat_printf(temp_str, "ISO 14443-%c (NFC-A)\n", iso_type); NfcVData* nfcv_data = &nfc->dev->dev_data.nfcv_data;
furi_string_cat_printf(temp_str, "UID:");
for(size_t i = 0; i < nfc_data->uid_len; i++) { furi_string_cat_printf(temp_str, "UID:\n");
furi_string_cat_printf(temp_str, " %02X", nfc_data->uid[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, "\n");
furi_string_cat_printf(temp_str, "DSFID: %02X\n", nfcv_data->dsfid);
furi_string_cat_printf(temp_str, "AFI: %02X\n", nfcv_data->afi);
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);
furi_string_cat_printf(temp_str, "Data (%d byte)\n", nfcv_data->block_num * nfcv_data->block_size);
for(int block = 0; block < nfcv_data->block_num; block++) {
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, "\n");
}
furi_string_cat_printf(temp_str, "\n");
} else {
char iso_type = FURI_BIT(nfc_data->sak, 5) ? '4' : '3';
furi_string_cat_printf(temp_str, "ISO 14443-%c (NFC-A)\n", iso_type);
furi_string_cat_printf(temp_str, "UID:");
for(size_t i = 0; i < nfc_data->uid_len; i++) {
furi_string_cat_printf(temp_str, " %02X", nfc_data->uid[i]);
}
furi_string_cat_printf(temp_str, "\nATQA: %02X %02X ", nfc_data->atqa[1], nfc_data->atqa[0]);
furi_string_cat_printf(temp_str, " SAK: %02X", nfc_data->sak);
} }
furi_string_cat_printf(temp_str, "\nATQA: %02X %02X ", nfc_data->atqa[1], nfc_data->atqa[0]);
furi_string_cat_printf(temp_str, " SAK: %02X", nfc_data->sak);
// Set application specific data // Set application specific data
if(protocol == NfcDeviceProtocolMifareDesfire) { if(protocol == NfcDeviceProtocolMifareDesfire) {
@@ -120,6 +148,9 @@ bool nfc_scene_nfc_data_info_on_event(void* context, SceneManagerEvent event) {
} else if(protocol == NfcDeviceProtocolMifareUl) { } else if(protocol == NfcDeviceProtocolMifareUl) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightData); scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightData);
consumed = true; consumed = true;
} else if(protocol == NfcDeviceProtocolSlixL) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVMenu);
consumed = true;
} }
} }
} }

View File

@@ -0,0 +1,47 @@
#include "../nfc_i.h"
#include <dolphin/dolphin.h>
void nfc_scene_nfcv_key_input_byte_input_callback(void* context) {
Nfc* nfc = context;
memcpy(nfc->dev->dev_data.nfcv_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, NfcSceneNfcVReadAuth);
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, "");
}

View File

@@ -0,0 +1,53 @@
#include "../nfc_i.h"
#include <dolphin/dolphin.h>
enum SubmenuIndex {
SubmenuIndexSave,
};
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, "Save", SubmenuIndexSave, 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 = NfcDeviceSaveFormatSlixL;
// Clear device name
nfc_device_set_name(nfc->dev, "");
scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName);
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);
}

View File

@@ -0,0 +1,133 @@
#include "../nfc_i.h"
#include <dolphin/dolphin.h>
typedef enum {
NfcSceneNfcVReadStateIdle,
NfcSceneNfcVReadStateDetecting,
NfcSceneNfcVReadStateUnlocked,
NfcSceneNfcVReadStateAlreadyUnlocked,
NfcSceneNfcVReadStateNotSupportedCard,
} NfcSceneNfcVReadState;
bool nfc_scene_nfcv_read_auth_worker_callback(NfcWorkerEvent event, void* context) {
Nfc* nfc = context;
if(event == NfcWorkerEventNfcVPassKey) {
memcpy(nfc->dev->dev_data.nfcv_data.key_privacy, nfc->byte_input_store, 4);
} else {
view_dispatcher_send_custom_event(nfc->view_dispatcher, event);
}
return true;
}
void nfc_scene_nfcv_read_auth_popup_callback(void* context) {
Nfc* nfc = context;
view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit);
}
void nfc_scene_nfcv_read_auth_set_state(Nfc* nfc, NfcSceneNfcVReadState state) {
NfcVData* nfcv_data = &(nfc->dev->dev_data.nfcv_data);
uint32_t curr_state =
scene_manager_get_scene_state(nfc->scene_manager, NfcSceneNfcVReadAuth);
if(curr_state != state) {
Popup* popup = nfc->popup;
if(state == NfcSceneNfcVReadStateDetecting) {
popup_reset(popup);
popup_set_text(
popup, "Put Tonie On\nFlipper's Back", 97, 24, AlignCenter, AlignTop);
popup_set_icon(popup, 0, 8, &I_NFC_manual_60x50);
} else if(state == NfcSceneNfcVReadStateUnlocked) {
popup_reset(popup);
notification_message(nfc->notifications, &sequence_success);
popup_set_header(popup, "Successfully\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_read_auth_popup_callback);
popup_set_timeout(popup, 1500);
//popup_enable_timeout(popup);
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup);
DOLPHIN_DEED(DolphinDeedNfcReadSuccess);
} else if(state == NfcSceneNfcVReadStateAlreadyUnlocked) {
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_read_auth_popup_callback);
popup_set_timeout(popup, 1500);
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup);
} else if(state == NfcSceneNfcVReadStateNotSupportedCard) {
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, NfcSceneNfcVReadAuth, state);
}
}
void nfc_scene_nfcv_read_auth_on_enter(void* context) {
Nfc* nfc = context;
nfc_device_clear(nfc->dev);
// Setup view
nfc_scene_nfcv_read_auth_set_state(nfc, NfcSceneNfcVReadStateDetecting);
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup);
// Start worker
nfc_worker_start(
nfc->worker,
NfcWorkerStateNfcVReadAuth,
&nfc->dev->dev_data,
nfc_scene_nfcv_read_auth_worker_callback,
nfc);
nfc_blink_read_start(nfc);
}
bool nfc_scene_nfcv_read_auth_on_event(void* context, SceneManagerEvent event) {
Nfc* nfc = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == NfcWorkerEventCardDetected) {
nfc_scene_nfcv_read_auth_set_state(nfc, NfcSceneNfcVReadStateUnlocked);
consumed = true;
} else if(event.event == NfcWorkerEventAborted) {
nfc_scene_nfcv_read_auth_set_state(nfc, NfcSceneNfcVReadStateAlreadyUnlocked);
consumed = true;
} else if(event.event == NfcWorkerEventNoCardDetected) {
nfc_scene_nfcv_read_auth_set_state(nfc, NfcSceneNfcVReadStateDetecting);
consumed = true;
} else if(event.event == NfcWorkerEventWrongCardDetected) {
nfc_scene_nfcv_read_auth_set_state(nfc, NfcSceneNfcVReadStateNotSupportedCard);
}
} else if(event.type == SceneManagerEventTypeBack) {
consumed = scene_manager_search_and_switch_to_previous_scene(
nfc->scene_manager, NfcSceneNfcVUnlockMenu);
}
return consumed;
}
void nfc_scene_nfcv_read_auth_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, NfcSceneNfcVReadAuth, NfcSceneNfcVReadStateIdle);
}

View File

@@ -0,0 +1,62 @@
#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, NfcSceneNfcVReadAuth);
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);
}

View File

@@ -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, NfcSceneNfcDataInfo);
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);
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightReadSuccess); scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightReadSuccess);

View File

@@ -31,6 +31,9 @@ bool nfc_scene_save_success_on_event(void* context, SceneManagerEvent event) {
} else if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSavedMenu)) { } else if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSavedMenu)) {
consumed = scene_manager_search_and_switch_to_previous_scene( consumed = scene_manager_search_and_switch_to_previous_scene(
nfc->scene_manager, NfcSceneSavedMenu); nfc->scene_manager, NfcSceneSavedMenu);
} else if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneNfcDataInfo)) {
consumed = scene_manager_search_and_switch_to_previous_scene(
nfc->scene_manager, NfcSceneNfcDataInfo);
} else { } else {
consumed = scene_manager_search_and_switch_to_another_scene( consumed = scene_manager_search_and_switch_to_another_scene(
nfc->scene_manager, NfcSceneFileSelect); nfc->scene_manager, NfcSceneFileSelect);

View File

@@ -8,13 +8,13 @@ TARGET_HW = 7
# Optimization flags # Optimization flags
## Optimize for size ## Optimize for size
COMPACT = 0 COMPACT = 1
## Optimize for debugging experience ## Optimize for debugging experience
DEBUG = 1 DEBUG = 0
# Suffix to add to files when building distribution # Suffix to add to files when building distribution
# If OS environment has DIST_SUFFIX set, it will be used instead # If OS environment has DIST_SUFFIX set, it will be used instead
DIST_SUFFIX = "local" DIST_SUFFIX = "RevvoX"
# Coprocessor firmware # Coprocessor firmware
COPRO_OB_DATA = "scripts/ob.data" COPRO_OB_DATA = "scripts/ob.data"

View File

@@ -1,5 +1,5 @@
entry,status,name,type,params entry,status,name,type,params
Version,+,7.3,, Version,+,7.4,,
Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/bt/bt_service/bt.h,,
Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli.h,,
Header,+,applications/services/cli/cli_vcp.h,, Header,+,applications/services/cli/cli_vcp.h,,
@@ -1948,12 +1948,19 @@ Function,+,nfc_device_save_shadow,_Bool,"NfcDevice*, const char*"
Function,+,nfc_device_set_loading_callback,void,"NfcDevice*, NfcLoadingCallback, void*" Function,+,nfc_device_set_loading_callback,void,"NfcDevice*, NfcLoadingCallback, void*"
Function,+,nfc_device_set_name,void,"NfcDevice*, const char*" Function,+,nfc_device_set_name,void,"NfcDevice*, const char*"
Function,+,nfc_file_select,_Bool,NfcDevice* Function,+,nfc_file_select,_Bool,NfcDevice*
Function,-,nfc_util_bytes2num,uint64_t,"uint8_t*, uint8_t"
Function,-,nfc_util_even_parity32,uint8_t,uint32_t
Function,-,nfc_util_num2bytes,void,"uint64_t, uint8_t, uint8_t*"
Function,-,nfc_util_odd_parity8,uint8_t,uint8_t
Function,-,nfca_append_crc16,void,"uint8_t*, uint16_t" Function,-,nfca_append_crc16,void,"uint8_t*, uint16_t"
Function,-,nfca_emulation_handler,_Bool,"uint8_t*, uint16_t, uint8_t*, uint16_t*" Function,-,nfca_emulation_handler,_Bool,"uint8_t*, uint16_t, uint8_t*, uint16_t*"
Function,-,nfca_get_crc16,uint16_t,"uint8_t*, uint16_t" 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_inventory,ReturnCode,uint8_t*
Function,-,nfcv_read_blocks,ReturnCode,"NfcVReader*, NfcVData*"
Function,-,nfcv_read_sysinfo,ReturnCode,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*"
1 entry status name type params
2 Version + 7.3 7.4
3 Header + applications/services/bt/bt_service/bt.h
4 Header + applications/services/cli/cli.h
5 Header + applications/services/cli/cli_vcp.h
1948 Function + nfc_device_set_loading_callback void NfcDevice*, NfcLoadingCallback, void*
1949 Function + nfc_device_set_name void NfcDevice*, const char*
1950 Function + nfc_file_select _Bool NfcDevice*
1951 Function - nfc_util_bytes2num uint64_t uint8_t*, uint8_t
1952 Function - nfc_util_even_parity32 uint8_t uint32_t
1953 Function - nfc_util_num2bytes void uint64_t, uint8_t, uint8_t*
1954 Function - nfc_util_odd_parity8 uint8_t uint8_t
1955 Function - nfca_append_crc16 void uint8_t*, uint16_t
1956 Function - nfca_emulation_handler _Bool uint8_t*, uint16_t, uint8_t*, uint16_t*
1957 Function - nfca_get_crc16 uint16_t uint8_t*, uint16_t
1958 Function - nfca_signal_alloc NfcaSignal*
1959 Function - nfca_signal_encode void NfcaSignal*, uint8_t*, uint16_t, uint8_t*
1960 Function - nfca_signal_free void NfcaSignal*
1961 Function - nfcv_inventory ReturnCode uint8_t*
1962 Function - nfcv_read_blocks ReturnCode NfcVReader*, NfcVData*
1963 Function - nfcv_read_sysinfo ReturnCode NfcVData*
1964 Function + notification_internal_message void NotificationApp*, const NotificationSequence*
1965 Function + notification_internal_message_block void NotificationApp*, const NotificationSequence*
1966 Function + notification_message void NotificationApp*, const NotificationSequence*

View File

@@ -50,6 +50,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 == NfcDeviceSaveFormatSlixL) {
furi_string_set(format_string, "NXP SLIX-L");
} else { } else {
furi_string_set(format_string, "Unknown"); furi_string_set(format_string, "Unknown");
} }
@@ -85,6 +87,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, "NXP SLIX-L")) {
dev->format = NfcDeviceSaveFormatSlixL;
dev->dev_data.protocol = NfcDeviceProtocolSlixL;
return true;
}
return false; return false;
} }
@@ -636,7 +643,87 @@ 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_l_data(FlipperFormat* file, NfcDevice* dev) {
bool saved = false;
NfcVData* data = &dev->dev_data.nfcv_data;
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_hex(file, "DSFID", &(data->dsfid), 1)) 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;
if(!flipper_format_write_hex(file, "Block Count", &(data->block_num), 1)) 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;
saved = true;
} while(false);
return saved;
}
bool nfc_device_load_slix_l_data(FlipperFormat* file, NfcDevice* dev) {
bool parsed = false;
NfcVData* data = &dev->dev_data.nfcv_data;
memset(data, 0, sizeof(NfcVData));
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_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_hex(file, "Block Count", &(data->block_num), 1)) break;
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;
parsed = true;
} 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;
@@ -1044,17 +1131,25 @@ static bool nfc_device_save_file(
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, ATQA and SAK are common for all formats except ISO15693"))
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(!flipper_format_write_hex(file, "ATQA", data->atqa, 2)) break;
if(!flipper_format_write_hex(file, "SAK", &data->sak, 1)) break; if(dev->format != NfcDeviceSaveFormatSlixL) {
// Write ATQA, SAK
if(!flipper_format_write_hex(file, "ATQA", data->atqa, 2)) 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 == NfcDeviceSaveFormatSlixL) {
if(!nfc_device_save_slix_l_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;
@@ -1117,11 +1212,13 @@ 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(!flipper_format_read_hex(file, "ATQA", data->atqa, 2)) break; if(dev->format != NfcDeviceSaveFormatSlixL) {
if(!flipper_format_read_hex(file, "SAK", &data->sak, 1)) break; if(!flipper_format_read_hex(file, "ATQA", data->atqa, 2)) 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) {
@@ -1136,6 +1233,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 == NfcDeviceSaveFormatSlixL) {
if(!nfc_device_load_slix_l_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;
} }

View File

@@ -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" {
@@ -32,6 +33,7 @@ typedef enum {
NfcDeviceProtocolMifareUl, NfcDeviceProtocolMifareUl,
NfcDeviceProtocolMifareClassic, NfcDeviceProtocolMifareClassic,
NfcDeviceProtocolMifareDesfire, NfcDeviceProtocolMifareDesfire,
NfcDeviceProtocolSlixL,
} NfcProtocol; } NfcProtocol;
typedef enum { typedef enum {
@@ -40,6 +42,7 @@ typedef enum {
NfcDeviceSaveFormatMifareUl, NfcDeviceSaveFormatMifareUl,
NfcDeviceSaveFormatMifareClassic, NfcDeviceSaveFormatMifareClassic,
NfcDeviceSaveFormatMifareDesfire, NfcDeviceSaveFormatMifareDesfire,
NfcDeviceSaveFormatSlixL,
} NfcDeviceSaveFormat; } NfcDeviceSaveFormat;
typedef struct { typedef struct {
@@ -63,6 +66,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;

View File

@@ -109,6 +109,8 @@ 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 == NfcWorkerStateNfcVReadAuth) {
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);
@@ -116,6 +118,145 @@ int32_t nfc_worker_task(void* context) {
return 0; return 0;
} }
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 = {};
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 == NfcWorkerStateNfcVReadAuth) {
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);
uint8_t rand[2];
furi_hal_console_printf("Detect presence\r\n");
ReturnCode ret = slix_l_get_random(rand);
if(ret == ERR_NONE) {
/* there is some chip, responding with a RAND */
furi_hal_console_printf(" Chip detected. In privacy?\r\n");
ret = nfcv_inventory(NULL);
if(ret == ERR_NONE) {
/* chip is also visible, so no action required */
furi_hal_console_printf(" => chip is already visible, wait for chip to disappear.\r\n");
nfc_worker->callback(NfcWorkerEventAborted, nfc_worker->context);
while(slix_l_get_random(NULL) == ERR_NONE) {
furi_delay_ms(100);
}
} else {
/* chip is invisible, try to unlock */
furi_hal_console_printf(" chip is invisible, unlocking\r\n");
if(nfcv_data->auth_method == NfcVAuthMethodManual) {
uint32_t key = 0;
key |= nfc_worker->dev_data->nfcv_data.key_privacy[0] << 24;
key |= nfc_worker->dev_data->nfcv_data.key_privacy[1] << 16;
key |= nfc_worker->dev_data->nfcv_data.key_privacy[2] << 8;
key |= nfc_worker->dev_data->nfcv_data.key_privacy[3] << 0;
ret = slix_l_unlock(4, rand, key);
} else {
ret = slix_l_unlock(4, rand, 0x7FFD6E5B);
if(ret != ERR_NONE) {
/* main key failed, trying second one */
furi_hal_console_printf(" trying second key after resetting\r\n");
/* reset chip */
furi_hal_nfc_ll_txrx_off();
furi_delay_ms(20);
furi_hal_nfc_ll_txrx_on();
if(slix_l_get_random(rand) != ERR_NONE) {
furi_hal_console_printf(" reset failed\r\n");
}
ret = slix_l_unlock(4, rand, 0x0F0F0F0F);
}
}
if(ret == ERR_NONE) {
/* unlock succesful */
furi_hal_console_printf(" => success, wait for chip to disappear.\r\n");
nfc_worker->callback(NfcWorkerEventCardDetected, nfc_worker->context);
while(slix_l_get_random(NULL) == ERR_NONE) {
furi_delay_ms(100);
}
} else {
/* unlock failed */
furi_hal_console_printf(" => failed, wait for chip to disappear.\r\n");
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_l_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_slix_l(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) {
bool read_success = false;
NfcVReader reader = {};
NfcVData data = {};
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(!slix_l_read_card(&reader, &data)) break;
// Copy data
nfc_worker->dev_data->nfcv_data = data;
read_success = true;
} while(false);
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
reader_analyzer_stop(nfc_worker->reader_analyzer);
}
return read_success;
}
static bool nfc_worker_read_mf_ultralight(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { static bool nfc_worker_read_mf_ultralight(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) {
bool read_success = false; bool read_success = false;
MfUltralightReader reader = {}; MfUltralightReader reader = {};
@@ -329,6 +470,27 @@ static bool nfc_worker_read_nfca(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* t
return card_read; return card_read;
} }
static bool nfc_worker_read_nfcv(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) {
furi_assert(nfc_worker);
furi_assert(tx_rx);
FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data;
bool card_read = false;
furi_hal_nfc_sleep();
if(slix_l_check_card_type(nfc_data->uid[7], nfc_data->uid[6], nfc_data->uid[5])) {
FURI_LOG_I(TAG, "NXP SLIX-L detected");
nfc_worker->dev_data->protocol = NfcDeviceProtocolSlixL;
card_read = nfc_worker_read_slix_l(nfc_worker, tx_rx);
} else {
FURI_LOG_I(TAG, "unknown detected");
nfc_worker->dev_data->protocol = NfcDeviceProtocolUnknown;
card_read = true;
}
return card_read;
}
void nfc_worker_read(NfcWorker* nfc_worker) { void nfc_worker_read(NfcWorker* nfc_worker) {
furi_assert(nfc_worker); furi_assert(nfc_worker);
furi_assert(nfc_worker->callback); furi_assert(nfc_worker->callback);
@@ -376,6 +538,14 @@ 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) {
FURI_LOG_I(TAG, "NfcV detected");
if(nfc_worker_read_nfcv(nfc_worker, &tx_rx)) {
FURI_LOG_I(TAG, "nfc_worker_read_nfcv success");
if(dev_data->protocol == NfcDeviceProtocolSlixL) {
event = NfcWorkerEventReadNfcV;
break;
}
}
event = NfcWorkerEventReadUidNfcV; event = NfcWorkerEventReadUidNfcV;
break; break;
} }

View File

@@ -19,6 +19,7 @@ typedef enum {
NfcWorkerStateReadMfUltralightReadAuth, NfcWorkerStateReadMfUltralightReadAuth,
NfcWorkerStateMfClassicDictAttack, NfcWorkerStateMfClassicDictAttack,
NfcWorkerStateAnalyzeReader, NfcWorkerStateAnalyzeReader,
NfcWorkerStateNfcVReadAuth,
// Debug // Debug
NfcWorkerStateEmulateApdu, NfcWorkerStateEmulateApdu,
NfcWorkerStateField, NfcWorkerStateField,
@@ -40,6 +41,7 @@ typedef enum {
NfcWorkerEventReadMfClassicDone, NfcWorkerEventReadMfClassicDone,
NfcWorkerEventReadMfClassicLoadKeyCache, NfcWorkerEventReadMfClassicLoadKeyCache,
NfcWorkerEventReadMfClassicDictAttackRequired, NfcWorkerEventReadMfClassicDictAttackRequired,
NfcWorkerEventReadNfcV,
NfcWorkerEventReadBankCard, NfcWorkerEventReadBankCard,
// Nfc worker common events // Nfc worker common events
@@ -67,6 +69,7 @@ typedef enum {
// Mifare Ultralight events // Mifare Ultralight events
NfcWorkerEventMfUltralightPassKey, NfcWorkerEventMfUltralightPassKey,
NfcWorkerEventNfcVPassKey, // NFC worker requesting manual key
} NfcWorkerEvent; } NfcWorkerEvent;
@@ -86,3 +89,5 @@ 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);

View File

@@ -12,6 +12,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_l.h>
#include <lib/nfc/helpers/reader_analyzer.h> #include <lib/nfc/helpers/reader_analyzer.h>
struct NfcWorker { struct NfcWorker {

79
lib/nfc/protocols/nfcv.c Normal file
View File

@@ -0,0 +1,79 @@
#include <limits.h>
#include "nfcv.h"
#include "nfc_util.h"
#include <furi.h>
#include "furi_hal_nfc.h"
#define TAG "NfcV"
ReturnCode nfcv_inventory(uint8_t* uid) {
uint16_t received = 0;
rfalNfcvInventoryRes res;
/* TODO: needs proper abstraction via fury_hal(_ll)_* */
ReturnCode ret = rfalNfcvPollerInventory(RFAL_NFCV_NUM_SLOTS_1, 0, NULL, &res, &received);
if(ret == ERR_NONE) {
if(uid != NULL) {
memcpy(uid, res.UID, 8);
}
}
return ret;
}
ReturnCode nfcv_read_blocks(
NfcVReader* reader,
NfcVData* data) {
reader->blocks_read = 0;
uint16_t received = 0;
for(size_t block = 0; block < data->block_num; block++) {
uint8_t rxBuf[32];
FURI_LOG_D(TAG, "Reading block %d", block);
ReturnCode ret = rfalNfcvPollerReadSingleBlock(
RFAL_NFCV_REQ_FLAG_DEFAULT, NULL, block,
rxBuf, sizeof(rxBuf), &received);
if(ret != ERR_NONE) {
FURI_LOG_D(TAG, "failed to read: %d", ret);
return ret;
}
memcpy(&(data->data[block * data->block_size]), &rxBuf[1], data->block_size);
FURI_LOG_D(TAG, " %02X %02X %02X %02X",
data->data[block * data->block_size + 0], data->data[block * data->block_size + 1],
data->data[block * data->block_size + 2], data->data[block * data->block_size + 3]);
reader->blocks_read++;
}
FURI_LOG_D(TAG, "Read %d blocks", reader->blocks_read);
return ERR_NONE;
}
ReturnCode nfcv_read_sysinfo(NfcVData* data) {
uint8_t rxBuf[32];
uint16_t received = 0;
FURI_LOG_D(TAG, "Read SystemInformation...");
ReturnCode ret = rfalNfcvPollerGetSystemInformation(
RFAL_NFCV_REQ_FLAG_DEFAULT, NULL,
rxBuf, sizeof(rxBuf), &received);
if(ret == ERR_NONE) {
data->dsfid = rxBuf[10];
data->afi = rxBuf[11];
data->block_num = rxBuf[12] + 1;
data->block_size = rxBuf[13] + 1;
data->ic_ref = rxBuf[14];
FURI_LOG_D(TAG, " DSFID %d, AFI %d, Blocks %d, Size %d, IC Ref %d", data->dsfid, data->afi, data->block_num, data->block_size, data->ic_ref);
return ret;
}
FURI_LOG_D(TAG, "Failed: %d", ret);
return ret;
}

54
lib/nfc/protocols/nfcv.h Normal file
View File

@@ -0,0 +1,54 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include <lib/digital_signal/digital_signal.h>
#include "nfc_util.h"
#include <furi_hal_nfc.h>
#define NFCV_TOTAL_BLOCKS_MAX 32
#define NFCV_BLOCK_SIZE 4
#define NFCV_MAX_DUMP_SIZE (NFCV_BLOCK_SIZE*NFCV_TOTAL_BLOCKS_MAX)
typedef enum {
NfcVAuthMethodManual,
NfcVAuthMethodTonieBox,
} NfcVAuthMethod;
typedef enum {
NfcVTypeSlix,
NfcVTypeSlixS,
NfcVTypeSlixL,
NfcVTypeSlix2,
} NfcVType;
typedef struct {
NfcVType type;
NfcVAuthMethod auth_method;
bool auth_success;
uint8_t key_privacy[4];
uint8_t key_destroy[4];
uint8_t key_eas[4];
uint8_t dsfid;
uint8_t afi;
uint8_t ic_ref;
uint8_t block_num;
uint8_t block_size;
uint8_t data[NFCV_MAX_DUMP_SIZE];
char error[32];
} 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(NfcVData* data);
ReturnCode nfcv_inventory(uint8_t* uid);

View File

@@ -0,0 +1,83 @@
#include <limits.h>
#include "nfcv.h"
#include "slix_l.h"
#include "nfc_util.h"
#include <furi.h>
#include "furi_hal_nfc.h"
bool slix_l_check_card_type(uint8_t UID0, uint8_t UID1, uint8_t UID2) {
if((UID0 == 0xE0) && (UID1 == 0x04) && (UID2 == 0x03)) {
return true;
}
return false;
}
bool slix_l_read_card(
NfcVReader* reader,
NfcVData* data) {
furi_assert(reader);
furi_assert(data);
if(nfcv_read_sysinfo(data) != ERR_NONE) {
return false;
}
reader->blocks_to_read = data->block_num;
return (nfcv_read_blocks(reader, data) == ERR_NONE);
}
ReturnCode slix_l_get_random(uint8_t* rand) {
uint16_t received = 0;
uint8_t rxBuf[32];
ReturnCode ret = rfalNfcvPollerTransceiveReq(
ISO15693_CMD_NXP_GET_RANDOM_NUMBER,
RFAL_NFCV_REQ_FLAG_DEFAULT,
ISO15693_MANUFACTURER_NXP,
NULL,
NULL,
0,
rxBuf,
sizeof(rxBuf),
&received);
if(ret == ERR_NONE) {
if(received != 3) {
return ERR_PROTO;
}
if(rand != NULL) {
memcpy(rand, &rxBuf[1], 2);
}
}
return ret;
}
ReturnCode slix_l_unlock(uint32_t id, uint8_t* rand, uint32_t password) {
furi_assert(rand);
uint16_t received = 0;
uint8_t rxBuf[32];
uint8_t cmd_set_pass[] = {
id,
rand[0] ^ ((password>>0) & 0xFF),
rand[1] ^ ((password>>8) & 0xFF),
rand[0] ^ ((password>>16) & 0xFF),
rand[1] ^ ((password>>24) & 0xFF)
};
ReturnCode ret = rfalNfcvPollerTransceiveReq(
ISO15693_CMD_NXP_SET_PASSWORD,
RFAL_NFCV_REQ_FLAG_DATA_RATE,
ISO15693_MANUFACTURER_NXP,
NULL,
cmd_set_pass,
sizeof(cmd_set_pass),
rxBuf,
sizeof(rxBuf),
&received);
return ret;
}

View File

@@ -0,0 +1,18 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include "nfc_util.h"
#include <furi_hal_nfc.h>
#define ISO15693_CMD_NXP_GET_RANDOM_NUMBER 0xB2
#define ISO15693_CMD_NXP_SET_PASSWORD 0xB3
#define ISO15693_MANUFACTURER_NXP 0x04
bool slix_l_check_card_type(uint8_t UID0, uint8_t UID1, uint8_t UID2);
bool slix_l_read_card(NfcVReader* reader, NfcVData* data);
ReturnCode slix_l_get_random(uint8_t* rand);
ReturnCode slix_l_unlock(uint32_t id, uint8_t* rand, uint32_t password);