Reset NFC stuff

This commit is contained in:
Willy-JL
2023-03-28 21:32:00 +01:00
parent 265473dff6
commit 6ebbbcc306
51 changed files with 67 additions and 6167 deletions
-16
View File
@@ -87,13 +87,6 @@ Nfc* nfc_alloc() {
nfc->view_dispatcher, NfcViewTextBox, text_box_get_view(nfc->text_box));
nfc->text_box_store = furi_string_alloc();
// Variable Item List
nfc->variable_item_list = variable_item_list_alloc();
view_dispatcher_add_view(
nfc->view_dispatcher,
NfcViewVarItemList,
variable_item_list_get_view(nfc->variable_item_list));
// Custom Widget
nfc->widget = widget_alloc();
view_dispatcher_add_view(nfc->view_dispatcher, NfcViewWidget, widget_get_view(nfc->widget));
@@ -166,10 +159,6 @@ void nfc_free(Nfc* nfc) {
text_box_free(nfc->text_box);
furi_string_free(nfc->text_box_store);
// Variable Item List
view_dispatcher_remove_view(nfc->view_dispatcher, NfcViewVarItemList);
variable_item_list_free(nfc->variable_item_list);
// Custom Widget
view_dispatcher_remove_view(nfc->view_dispatcher, NfcViewWidget);
widget_free(nfc->widget);
@@ -301,11 +290,6 @@ int32_t nfc_app(void* p) {
} else if(nfc->dev->format == NfcDeviceSaveFormatMifareClassic) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicEmulate);
DOLPHIN_DEED(DolphinDeedNfcEmulate);
} else if(nfc->dev->format == NfcDeviceSaveFormatNfcV) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateNfcV);
DOLPHIN_DEED(DolphinDeedNfcEmulate);
} else if(nfc->dev->format == NfcDeviceSaveFormatBankCard) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid);
} else {
scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid);
DOLPHIN_DEED(DolphinDeedNfcEmulate);
-3
View File
@@ -21,7 +21,6 @@
#include <gui/modules/byte_input.h>
#include <gui/modules/text_box.h>
#include <gui/modules/widget.h>
#include <gui/modules/variable_item_list.h>
#include <lib/nfc/nfc_types.h>
#include <lib/nfc/nfc_worker.h>
@@ -78,7 +77,6 @@ struct Nfc {
TextInput* text_input;
ByteInput* byte_input;
TextBox* text_box;
VariableItemList* variable_item_list;
Widget* widget;
DictAttack* dict_attack;
DetectReader* detect_reader;
@@ -94,7 +92,6 @@ typedef enum {
NfcViewTextInput,
NfcViewByteInput,
NfcViewTextBox,
NfcViewVarItemList,
NfcViewWidget,
NfcViewDictAttack,
NfcViewDetectReader,
@@ -4,7 +4,7 @@ ADD_SCENE(nfc, saved_menu, SavedMenu)
ADD_SCENE(nfc, extra_actions, ExtraActions)
ADD_SCENE(nfc, set_type, SetType)
ADD_SCENE(nfc, set_sak, SetSak)
ADD_SCENE(nfc, set_atqa, SetAtqua)
ADD_SCENE(nfc, set_atqa, SetAtqa)
ADD_SCENE(nfc, set_uid, SetUid)
ADD_SCENE(nfc, generate_info, GenerateInfo)
ADD_SCENE(nfc, read_card_success, ReadCardSuccess)
@@ -14,11 +14,6 @@ ADD_SCENE(nfc, file_select, FileSelect)
ADD_SCENE(nfc, emulate_uid, EmulateUid)
ADD_SCENE(nfc, nfca_read_success, NfcaReadSuccess)
ADD_SCENE(nfc, nfca_menu, NfcaMenu)
ADD_SCENE(nfc, nfcv_menu, NfcVMenu)
ADD_SCENE(nfc, nfcv_unlock_menu, NfcVUnlockMenu)
ADD_SCENE(nfc, nfcv_key_input, NfcVKeyInput)
ADD_SCENE(nfc, nfcv_unlock, NfcVUnlock)
ADD_SCENE(nfc, emulate_nfcv, EmulateNfcV)
ADD_SCENE(nfc, mf_ultralight_read_success, MfUltralightReadSuccess)
ADD_SCENE(nfc, mf_ultralight_data, MfUltralightData)
ADD_SCENE(nfc, mf_ultralight_menu, MfUltralightMenu)
@@ -51,14 +46,6 @@ ADD_SCENE(nfc, mf_classic_update_success, MfClassicUpdateSuccess)
ADD_SCENE(nfc, mf_classic_wrong_card, MfClassicWrongCard)
ADD_SCENE(nfc, emv_read_success, EmvReadSuccess)
ADD_SCENE(nfc, emv_menu, EmvMenu)
ADD_SCENE(nfc, passport_read, PassportReadSuccess)
ADD_SCENE(nfc, passport_read_auth, PassportReadAuthSuccess)
ADD_SCENE(nfc, passport_menu, PassportMenu)
ADD_SCENE(nfc, passport_auth, PassportAuth)
ADD_SCENE(nfc, passport_auth_save_name, PassportAuthSaveName)
ADD_SCENE(nfc, passport_date, PassportDate)
ADD_SCENE(nfc, passport_docnr, PassportDocNr)
ADD_SCENE(nfc, passport_pace_todo, PassportPaceTodo)
ADD_SCENE(nfc, emulate_apdu_sequence, EmulateApduSequence)
ADD_SCENE(nfc, device_info, DeviceInfo)
ADD_SCENE(nfc, delete, Delete)
@@ -31,7 +31,7 @@ bool nfc_scene_delete_success_on_event(void* context, SceneManagerEvent event) {
nfc->scene_manager, NfcSceneMfClassicKeys);
} else {
consumed = scene_manager_search_and_switch_to_previous_scene(
nfc->scene_manager, NfcSceneStart);
nfc->scene_manager, NfcSceneFileSelect);
}
}
}
@@ -1,151 +0,0 @@
#include "../nfc_i.h"
#include "xtreme/assets.h"
#define NFC_SCENE_EMULATE_NFCV_LOG_SIZE_MAX (100)
enum {
NfcSceneEmulateNfcVStateWidget,
NfcSceneEmulateNfcVStateTextBox,
};
bool nfc_emulate_nfcv_worker_callback(NfcWorkerEvent event, void* context) {
UNUSED(event);
furi_assert(context);
Nfc* nfc = context;
view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventWorkerExit);
return true;
}
void nfc_scene_emulate_nfcv_widget_callback(GuiButtonType result, InputType type, void* context) {
furi_assert(context);
Nfc* nfc = context;
if(type == InputTypeShort) {
view_dispatcher_send_custom_event(nfc->view_dispatcher, result);
}
}
void nfc_emulate_nfcv_textbox_callback(void* context) {
furi_assert(context);
Nfc* nfc = context;
view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit);
}
// Add widget with device name or inform that data received
static void nfc_scene_emulate_nfcv_widget_config(Nfc* nfc, bool data_received) {
FuriHalNfcDevData* data = &nfc->dev->dev_data.nfc_data;
Widget* widget = nfc->widget;
widget_reset(widget);
FuriString* info_str;
info_str = furi_string_alloc();
widget_add_icon_element(widget, 0, 3, XTREME_ASSETS()->I_RFIDDolphinSend_97x61);
widget_add_string_element(
widget, 89, 32, AlignCenter, AlignTop, FontPrimary, "Emulating NfcV");
if(strcmp(nfc->dev->dev_name, "")) {
furi_string_printf(info_str, "%s", nfc->dev->dev_name);
} else {
for(uint8_t i = 0; i < data->uid_len; i++) {
furi_string_cat_printf(info_str, "%02X ", data->uid[i]);
}
}
furi_string_trim(info_str);
widget_add_text_box_element(
widget, 56, 43, 70, 21, AlignCenter, AlignTop, furi_string_get_cstr(info_str), true);
furi_string_free(info_str);
if(data_received) {
widget_add_button_element(
widget, GuiButtonTypeCenter, "Log", nfc_scene_emulate_nfcv_widget_callback, nfc);
}
}
void nfc_scene_emulate_nfcv_on_enter(void* context) {
Nfc* nfc = context;
// Setup Widget
nfc_scene_emulate_nfcv_widget_config(nfc, false);
// Setup TextBox
TextBox* text_box = nfc->text_box;
text_box_set_font(text_box, TextBoxFontHex);
text_box_set_focus(text_box, TextBoxFocusEnd);
furi_string_reset(nfc->text_box_store);
// Set Widget state and view
scene_manager_set_scene_state(
nfc->scene_manager, NfcSceneEmulateNfcV, NfcSceneEmulateNfcVStateWidget);
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
// Start worker
memset(&nfc->dev->dev_data.reader_data, 0, sizeof(NfcReaderRequestData));
nfc_worker_start(
nfc->worker,
NfcWorkerStateNfcVEmulate,
&nfc->dev->dev_data,
nfc_emulate_nfcv_worker_callback,
nfc);
nfc_blink_emulate_start(nfc);
}
bool nfc_scene_emulate_nfcv_on_event(void* context, SceneManagerEvent event) {
Nfc* nfc = context;
NfcVData* nfcv_data = &nfc->dev->dev_data.nfcv_data;
uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneEmulateNfcV);
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == NfcCustomEventWorkerExit) {
// Add data button to widget if data is received for the first time
if(!furi_string_size(nfc->text_box_store)) {
nfc_scene_emulate_nfcv_widget_config(nfc, true);
}
if(strlen(nfcv_data->last_command) > 0) {
/* 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 == GuiButtonTypeCenter && state == NfcSceneEmulateNfcVStateWidget) {
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox);
scene_manager_set_scene_state(
nfc->scene_manager, NfcSceneEmulateNfcV, NfcSceneEmulateNfcVStateTextBox);
consumed = true;
} else if(event.event == NfcCustomEventViewExit && state == NfcSceneEmulateNfcVStateTextBox) {
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
scene_manager_set_scene_state(
nfc->scene_manager, NfcSceneEmulateNfcV, NfcSceneEmulateNfcVStateWidget);
consumed = true;
}
} else if(event.type == SceneManagerEventTypeBack) {
if(state == NfcSceneEmulateNfcVStateTextBox) {
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
scene_manager_set_scene_state(
nfc->scene_manager, NfcSceneEmulateNfcV, NfcSceneEmulateNfcVStateWidget);
consumed = true;
}
}
return consumed;
}
void nfc_scene_emulate_nfcv_on_exit(void* context) {
Nfc* nfc = context;
// Stop worker
nfc_worker_stop(nfc->worker);
// Clear view
widget_reset(nfc->widget);
text_box_reset(nfc->text_box);
furi_string_reset(nfc->text_box_store);
nfc_blink_stop(nfc);
}
@@ -4,7 +4,6 @@ enum SubmenuIndex {
SubmenuIndexReadCardType,
SubmenuIndexMfClassicKeys,
SubmenuIndexMfUltralightUnlock,
SubmenuIndexNfcVUnlock,
};
void nfc_scene_extra_actions_submenu_callback(void* context, uint32_t index) {
@@ -35,12 +34,8 @@ void nfc_scene_extra_actions_on_enter(void* context) {
SubmenuIndexMfUltralightUnlock,
nfc_scene_extra_actions_submenu_callback,
nfc);
submenu_add_item(
submenu,
"Unlock SLIX-L",
SubmenuIndexNfcVUnlock,
nfc_scene_extra_actions_submenu_callback,
nfc);
submenu_set_selected_item(
submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneExtraActions));
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu);
}
@@ -63,9 +58,6 @@ bool nfc_scene_extra_actions_on_event(void* context, SceneManagerEvent event) {
scene_manager_set_scene_state(nfc->scene_manager, NfcSceneReadCardType, 0);
scene_manager_next_scene(nfc->scene_manager, NfcSceneReadCardType);
consumed = true;
} else if(event.event == SubmenuIndexNfcVUnlock) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVUnlockMenu);
consumed = true;
}
scene_manager_set_scene_state(nfc->scene_manager, NfcSceneExtraActions, event.event);
}
@@ -1,105 +1,21 @@
#include "../nfc_i.h"
#include <lib/nfc/protocols/mifare_ultralight.h>
#include "xtreme/assets.h"
#define NFC_SCENE_MF_ULTRALIGHT_EMULATE_LOG_SIZE_MAX (200)
#define NFC_MF_UL_DATA_NOT_CHANGED (0UL)
#define NFC_MF_UL_DATA_CHANGED (1UL)
enum {
// View states
NfcSceneMfUltralightEmulateStateWidget,
NfcSceneMfUltralightEmulateStateTextBox,
NfcSceneMfUltralightEmulateStateMax = 0xFF,
// State flags
NfcSceneMfUltralightEmulateStateDataChanged = 1 << 8,
NfcSceneMfUltralightEmulateStateAuthAttempted = 1 << 9,
NfcSceneMfUltralightEmulateStateLogButtonShown = 1 << 10,
};
bool nfc_mf_ultralight_emulate_worker_callback(NfcWorkerEvent event, void* context) {
UNUSED(event);
Nfc* nfc = context;
uint32_t state =
scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfUltralightEmulate);
if(event == NfcWorkerEventSuccess)
state |= NfcSceneMfUltralightEmulateStateDataChanged;
else if(event == NfcWorkerEventMfUltralightPwdAuth) {
// Don't update if we're exiting
if(nfc_worker_get_state(nfc->worker) != NfcWorkerStateStop) {
// Event data is only available for the duration of this callback, so we're updating the
// text box right here
MfUltralightAuth* auth = nfc_worker_get_event_data(nfc->worker);
if(auth != NULL && furi_string_size(nfc->text_box_store) <
NFC_SCENE_MF_ULTRALIGHT_EMULATE_LOG_SIZE_MAX) {
furi_string_cat(nfc->text_box_store, "PWD:");
for(size_t i = 0; i < sizeof(auth->pwd.raw); ++i) {
furi_string_cat_printf(nfc->text_box_store, " %02X", auth->pwd.raw[i]);
}
furi_string_push_back(nfc->text_box_store, '\n');
text_box_set_text(nfc->text_box, furi_string_get_cstr(nfc->text_box_store));
}
state |= NfcSceneMfUltralightEmulateStateAuthAttempted;
}
}
scene_manager_set_scene_state(nfc->scene_manager, NfcSceneMfUltralightEmulate, state);
view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventWorkerExit);
scene_manager_set_scene_state(
nfc->scene_manager, NfcSceneMfUltralightEmulate, NFC_MF_UL_DATA_CHANGED);
return true;
}
void nfc_scene_mf_ultralight_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_mf_ultralight_emulate_widget_config(Nfc* nfc, bool auth_attempted) {
Widget* widget = nfc->widget;
widget_reset(widget);
FuriString* info_str;
info_str = furi_string_alloc();
widget_add_icon_element(widget, 0, 3, XTREME_ASSETS()->I_NFC_dolphin_emulation_47x61);
if(strcmp(nfc->dev->dev_name, "")) {
furi_string_printf(info_str, "Emulating\n%s", nfc->dev->dev_name);
} else {
furi_string_printf(info_str, "Emulating\nMf Ultralight");
}
widget_add_string_multiline_element(
widget, 56, 31, AlignLeft, AlignTop, FontPrimary, furi_string_get_cstr(info_str));
furi_string_free(info_str);
if(auth_attempted) {
widget_add_button_element(
widget,
GuiButtonTypeCenter,
"Log",
nfc_scene_mf_ultralight_emulate_widget_callback,
nfc);
}
}
void nfc_scene_mf_ultralight_emulate_on_enter(void* context) {
Nfc* nfc = context;
uint32_t state =
scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfUltralightEmulate);
// Setup Widget
nfc_scene_mf_ultralight_emulate_widget_config(nfc, false);
state &= ~NfcSceneMfUltralightEmulateStateLogButtonShown;
// Setup TextBox
TextBox* text_box = nfc->text_box;
text_box_set_font(text_box, TextBoxFontHex);
text_box_set_focus(text_box, TextBoxFocusEnd);
furi_string_reset(nfc->text_box_store);
// Setup view
MfUltralightType type = nfc->dev->dev_data.mf_ul_data.type;
bool is_ultralight = (type == MfUltralightTypeUL11) || (type == MfUltralightTypeUL21) ||
@@ -116,12 +32,8 @@ void nfc_scene_mf_ultralight_emulate_on_enter(void* context) {
popup_set_icon(popup, 0, 3, XTREME_ASSETS()->I_NFC_dolphin_emulation_47x61);
popup_set_text(popup, nfc->text_store, 90, 28, AlignCenter, AlignTop);
// Set Widget state and view
state = (state & ~NfcSceneMfUltralightEmulateStateMax) |
NfcSceneMfUltralightEmulateStateWidget;
scene_manager_set_scene_state(nfc->scene_manager, NfcSceneMfUltralightEmulate, state);
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
// Start worker
// Setup and start worker
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup);
nfc_worker_start(
nfc->worker,
NfcWorkerStateMfUltralightEmulate,
@@ -148,28 +60,16 @@ bool nfc_scene_mf_ultralight_emulate_on_event(void* context, SceneManagerEvent e
nfc_device_save_shadow(nfc->dev, furi_string_get_cstr(nfc->dev->load_path));
}
}
consumed = false;
}
return consumed;
}
void nfc_scene_mf_ultralight_emulate_on_exit(void* context) {
Nfc* nfc = context;
uint32_t state =
scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfUltralightEmulate);
// Stop worker
nfc_worker_stop(nfc->worker);
// Check if data changed and save in shadow file
if(state & NfcSceneMfUltralightEmulateStateDataChanged) {
state &= ~NfcSceneMfUltralightEmulateStateDataChanged;
scene_manager_set_scene_state(nfc->scene_manager, NfcSceneMfUltralightEmulate, state);
nfc_device_save_shadow(nfc->dev, nfc->dev->dev_name);
}
// Clear view
widget_reset(nfc->widget);
text_box_reset(nfc->text_box);
furi_string_reset(nfc->text_box_store);
popup_reset(nfc->popup);
nfc_blink_stop(nfc);
}
@@ -2,8 +2,7 @@
#include <dolphin/dolphin.h>
enum SubmenuIndex {
SubmenuIndexUnlockByReader,
SubmenuIndexUnlockByPassword,
SubmenuIndexUnlock,
SubmenuIndexSave,
SubmenuIndexEmulate,
SubmenuIndexInfo,
@@ -23,14 +22,8 @@ void nfc_scene_mf_ultralight_menu_on_enter(void* context) {
if(!mf_ul_is_full_capture(data)) {
submenu_add_item(
submenu,
"Unlock With Reader",
SubmenuIndexUnlockByReader,
nfc_scene_mf_ultralight_menu_submenu_callback,
nfc);
submenu_add_item(
submenu,
"Unlock With Password",
SubmenuIndexUnlockByPassword,
"Unlock",
SubmenuIndexUnlock,
nfc_scene_mf_ultralight_menu_submenu_callback,
nfc);
}
@@ -70,10 +63,7 @@ bool nfc_scene_mf_ultralight_menu_on_event(void* context, SceneManagerEvent even
DOLPHIN_DEED(DolphinDeedNfcEmulate);
}
consumed = true;
} else if(event.event == SubmenuIndexUnlockByReader) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockAuto);
consumed = true;
} else if(event.event == SubmenuIndexUnlockByPassword) {
} else if(event.event == SubmenuIndexUnlock) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockMenu);
consumed = true;
} else if(event.event == SubmenuIndexInfo) {
@@ -31,7 +31,7 @@ void nfc_scene_mf_ultralight_unlock_auto_on_enter(void* context) {
nfc_scene_mf_ultralight_unlock_auto_worker_callback,
nfc);
nfc_blink_emulate_start(nfc);
nfc_blink_read_start(nfc);
}
bool nfc_scene_mf_ultralight_unlock_auto_on_event(void* context, SceneManagerEvent event) {
@@ -58,7 +58,6 @@ void nfc_scene_mf_ultralight_unlock_auto_on_exit(void* context) {
// Stop worker
nfc_worker_stop(nfc->worker);
// Clear view
popup_reset(nfc->popup);
widget_reset(nfc->widget);
nfc_blink_stop(nfc);
@@ -7,17 +7,6 @@ void nfc_scene_nfc_data_info_widget_callback(GuiButtonType result, InputType typ
}
}
uint32_t nfc_scene_nfc_data_info_get_key(uint8_t* data) {
uint32_t value = 0;
for(uint32_t pos = 0; pos < 4; pos++) {
value <<= 8;
value |= data[pos];
}
return value;
}
void nfc_scene_nfc_data_info_on_enter(void* context) {
Nfc* nfc = context;
Widget* widget = nfc->widget;
@@ -26,7 +15,7 @@ void nfc_scene_nfc_data_info_on_enter(void* context) {
NfcProtocol protocol = dev_data->protocol;
uint8_t text_scroll_height = 0;
if((protocol == NfcDeviceProtocolMifareDesfire) || (protocol == NfcDeviceProtocolMifareUl) ||
(protocol == NfcDeviceProtocolNfcV) || (protocol == NfcDeviceProtocolMifareClassic)) {
(protocol == NfcDeviceProtocolMifareClassic)) {
widget_add_button_element(
widget, GuiButtonTypeRight, "More", nfc_scene_nfc_data_info_widget_callback, nfc);
text_scroll_height = 52;
@@ -44,8 +33,6 @@ void nfc_scene_nfc_data_info_on_enter(void* context) {
// Set tag type
if(protocol == NfcDeviceProtocolEMV) {
furi_string_cat_printf(temp_str, "\e#EMV Bank Card\n");
} else if(protocol == NfcDeviceProtocolMRTD) {
furi_string_cat_printf(temp_str, "\e#Passport/ID\n");
} else if(protocol == NfcDeviceProtocolMifareUl) {
furi_string_cat_printf(
temp_str, "\e#%s\n", nfc_mf_ul_type(dev_data->mf_ul_data.type, true));
@@ -54,156 +41,19 @@ void nfc_scene_nfc_data_info_on_enter(void* context) {
temp_str, "\e#%s\n", nfc_mf_classic_type(dev_data->mf_classic_data.type));
} else if(protocol == NfcDeviceProtocolMifareDesfire) {
furi_string_cat_printf(temp_str, "\e#MIFARE DESfire\n");
} else if(protocol == NfcDeviceProtocolNfcV) {
switch(dev_data->nfcv_data.sub_type) {
case NfcVTypePlain:
furi_string_cat_printf(temp_str, "\e#ISO15693\n");
break;
case NfcVTypeSlix:
furi_string_cat_printf(temp_str, "\e#ISO15693 SLIX\n");
break;
case NfcVTypeSlixS:
furi_string_cat_printf(temp_str, "\e#ISO15693 SLIX-S\n");
break;
case NfcVTypeSlixL:
furi_string_cat_printf(temp_str, "\e#ISO15693 SLIX-L\n");
break;
case NfcVTypeSlix2:
furi_string_cat_printf(temp_str, "\e#ISO15693 SLIX2\n");
break;
default:
furi_string_cat_printf(temp_str, "\e#ISO15693 (unknown)\n");
break;
}
} else {
furi_string_cat_printf(temp_str, "\e#Unknown ISO tag\n");
}
// Set tag iso data
if(protocol == NfcDeviceProtocolNfcV) {
NfcVData* nfcv_data = &nfc->dev->dev_data.nfcv_data;
furi_string_cat_printf(temp_str, "UID:\n");
for(size_t i = 0; i < nfc_data->uid_len; i++) {
furi_string_cat_printf(temp_str, " %02X", nfc_data->uid[i]);
}
furi_string_cat_printf(temp_str, "\n");
furi_string_cat_printf(temp_str, "DSFID: %02X\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);
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++) {
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");
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 %08lX\n",
nfc_scene_nfc_data_info_get_key(nfcv_data->sub_data.slix.key_eas));
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 %08lX\n",
nfc_scene_nfc_data_info_get_key(nfcv_data->sub_data.slix.key_read));
furi_string_cat_printf(
temp_str,
" Write %08lX\n",
nfc_scene_nfc_data_info_get_key(nfcv_data->sub_data.slix.key_write));
furi_string_cat_printf(
temp_str,
" Privacy %08lX\n",
nfc_scene_nfc_data_info_get_key(nfcv_data->sub_data.slix.key_privacy));
furi_string_cat_printf(
temp_str,
" Destroy %08lX\n",
nfc_scene_nfc_data_info_get_key(nfcv_data->sub_data.slix.key_destroy));
furi_string_cat_printf(
temp_str,
" EAS %08lX\n",
nfc_scene_nfc_data_info_get_key(nfcv_data->sub_data.slix.key_eas));
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 %08lX\n",
nfc_scene_nfc_data_info_get_key(nfcv_data->sub_data.slix.key_privacy));
furi_string_cat_printf(
temp_str,
" Destroy %08lX\n",
nfc_scene_nfc_data_info_get_key(nfcv_data->sub_data.slix.key_destroy));
furi_string_cat_printf(
temp_str,
" EAS %08lX\n",
nfc_scene_nfc_data_info_get_key(nfcv_data->sub_data.slix.key_eas));
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 %08lX\n",
nfc_scene_nfc_data_info_get_key(nfcv_data->sub_data.slix.key_read));
furi_string_cat_printf(
temp_str,
" Write %08lX\n",
nfc_scene_nfc_data_info_get_key(nfcv_data->sub_data.slix.key_write));
furi_string_cat_printf(
temp_str,
" Privacy %08lX\n",
nfc_scene_nfc_data_info_get_key(nfcv_data->sub_data.slix.key_privacy));
furi_string_cat_printf(
temp_str,
" Destroy %08lX\n",
nfc_scene_nfc_data_info_get_key(nfcv_data->sub_data.slix.key_destroy));
furi_string_cat_printf(
temp_str,
" EAS %08lX\n",
nfc_scene_nfc_data_info_get_key(nfcv_data->sub_data.slix.key_eas));
break;
default:
furi_string_cat_printf(temp_str, "\e#ISO15693 (unknown)\n");
break;
}
} 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);
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);
// Set application specific data
if(protocol == NfcDeviceProtocolMifareDesfire) {
@@ -287,9 +137,6 @@ bool nfc_scene_nfc_data_info_on_event(void* context, SceneManagerEvent event) {
} else if(protocol == NfcDeviceProtocolMifareUl) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightData);
consumed = true;
} else if(protocol == NfcDeviceProtocolNfcV) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVMenu);
consumed = true;
} else if(protocol == NfcDeviceProtocolMifareClassic) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicData);
consumed = true;
@@ -1,48 +0,0 @@
#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, "");
}
@@ -1,63 +0,0 @@
#include "../nfc_i.h"
#include <dolphin/dolphin.h>
enum SubmenuIndex {
SubmenuIndexSave,
SubmenuIndexEmulate,
};
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_add_item(
submenu, "Emulate", SubmenuIndexEmulate, 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, NfcSceneEmulateNfcV);
if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) {
DOLPHIN_DEED(DolphinDeedNfcAddEmulate);
} else {
DOLPHIN_DEED(DolphinDeedNfcEmulate);
}
consumed = true;
}
scene_manager_set_scene_state(nfc->scene_manager, NfcSceneNfcVMenu, event.event);
} 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);
}
@@ -1,155 +0,0 @@
#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) {
nfc_text_store_set(
nfc,
"%s/SLIX_%02X%02X%02X%02X%02X%02X%02X%02X%s",
NFC_APP_FOLDER,
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_APP_EXTENSION);
nfc->dev->format = NfcDeviceSaveFormatNfcV;
if(nfc_device_save(nfc->dev, nfc->text_store)) {
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);
}
@@ -1,60 +0,0 @@
#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);
}
@@ -1,194 +0,0 @@
#include "../nfc_i.h"
#define TAG "PassportAuth"
#define MRTD_AUTH_METHOD_COUNT 4
// Must match MrtdAuthMethod size (lib/nfc/protocols/mrtd_helpers.h)
typedef enum {
NfcScenePassportAuthSelectDob,
NfcScenePassportAuthSelectDoe,
NfcScenePassportAuthSelectDocNr,
NfcScenePassportAuthSelectMethod,
NfcScenePassportAuthSelectAuth,
NfcScenePassportAuthSelectSave,
NfcScenePassportAuthSelectLoad,
} NfcScenePassportAuthSelect;
void nfc_scene_passport_auth_var_list_enter_callback(void* context, uint32_t index) {
Nfc* nfc = context;
view_dispatcher_send_custom_event(nfc->view_dispatcher, index);
}
void nfc_scene_passport_auth_method_changed(VariableItem* item) {
Nfc* nfc = variable_item_get_context(item);
uint8_t index = variable_item_get_current_value_index(item);
nfc->dev->dev_data.mrtd_data.auth.method = index;
variable_item_set_current_value_text(item, mrtd_auth_method_string(index));
}
bool nfc_scene_passport_auth_load(Nfc* nfc) {
const DialogsFileBrowserOptions browser_options = {
.extension = MRTD_APP_EXTENSION,
.skip_assets = true,
.icon = &I_u2f_10px,
.hide_ext = true,
.item_loader_callback = NULL,
.item_loader_context = NULL,
};
FuriString* mrtd_app_folder;
mrtd_app_folder = furi_string_alloc_set(MRTD_APP_FOLDER);
FuriString* file_path;
file_path = furi_string_alloc();
bool res =
dialog_file_browser_show(nfc->dev->dialogs, file_path, mrtd_app_folder, &browser_options);
furi_string_free(mrtd_app_folder);
if(res) {
mrtd_auth_params_load(
nfc->dev->storage,
nfc->dev->dialogs,
&nfc->dev->dev_data.mrtd_data.auth,
furi_string_get_cstr(file_path),
true);
nfc_scene_passport_auth_on_enter(nfc);
variable_item_list_set_selected_item(
nfc->variable_item_list, NfcScenePassportAuthSelectAuth);
}
return res;
}
void nfc_scene_passport_auth_on_enter(void* context) {
Nfc* nfc = context;
MrtdData* mrtd_data = &nfc->dev->dev_data.mrtd_data;
// By entering the Auth menu, we default to Auth: Any
MrtdAuthMethod* auth_method = &mrtd_data->auth.method;
if(*auth_method == MrtdAuthMethodNone) {
*auth_method = MrtdAuthMethodAny;
}
VariableItemList* variable_item_list = nfc->variable_item_list;
variable_item_list_reset(variable_item_list);
VariableItem* item;
uint8_t value_index;
const size_t temp_str_size = 15;
char temp_str[temp_str_size];
snprintf(
temp_str,
temp_str_size,
"%02u%02u%02u",
mrtd_data->auth.birth_date.year,
mrtd_data->auth.birth_date.month,
mrtd_data->auth.birth_date.day);
item = variable_item_list_add(variable_item_list, "Birth Date", 1, NULL, NULL);
variable_item_set_current_value_text(item, temp_str);
snprintf(
temp_str,
temp_str_size,
"%02u%02u%02u",
mrtd_data->auth.expiry_date.year,
mrtd_data->auth.expiry_date.month,
mrtd_data->auth.expiry_date.day);
item = variable_item_list_add(variable_item_list, "Expiry Date", 1, NULL, NULL);
variable_item_set_current_value_text(item, temp_str);
item = variable_item_list_add(variable_item_list, "Document Nr.", 1, NULL, NULL);
strncpy(temp_str, mrtd_data->auth.doc_number, temp_str_size);
temp_str[temp_str_size - 1] = '\x00';
if(strlen(temp_str) > 8) {
temp_str[8] = '.';
temp_str[9] = '.';
temp_str[10] = '.';
temp_str[11] = '\x00';
}
variable_item_set_current_value_text(item, temp_str);
item = variable_item_list_add(
variable_item_list,
"Method",
MRTD_AUTH_METHOD_COUNT,
nfc_scene_passport_auth_method_changed,
nfc);
value_index = *auth_method;
variable_item_set_current_value_index(item, value_index);
variable_item_set_current_value_text(item, mrtd_auth_method_string(value_index));
variable_item_list_add(variable_item_list, "Authenticate and read", 1, NULL, NULL);
variable_item_list_add(variable_item_list, "Save parameters", 1, NULL, NULL);
variable_item_list_add(variable_item_list, "Load parameters", 1, NULL, NULL);
variable_item_list_set_enter_callback(
variable_item_list, nfc_scene_passport_auth_var_list_enter_callback, nfc);
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewVarItemList);
}
bool nfc_scene_passport_auth_on_event(void* context, SceneManagerEvent event) {
Nfc* nfc = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
FURI_LOG_D(TAG, "event.event: %ld", event.event);
switch(event.event) {
case NfcScenePassportAuthSelectLoad:
nfc_scene_passport_auth_load(nfc);
consumed = true;
break;
case NfcScenePassportAuthSelectDob:
scene_manager_set_scene_state(nfc->scene_manager, NfcScenePassportDate, 0);
scene_manager_next_scene(nfc->scene_manager, NfcScenePassportDate);
consumed = true;
break;
case NfcScenePassportAuthSelectDoe:
scene_manager_set_scene_state(nfc->scene_manager, NfcScenePassportDate, 1);
scene_manager_next_scene(nfc->scene_manager, NfcScenePassportDate);
consumed = true;
break;
case NfcScenePassportAuthSelectDocNr:
scene_manager_next_scene(nfc->scene_manager, NfcScenePassportDocNr);
consumed = true;
break;
case NfcScenePassportAuthSelectMethod:
consumed = true;
break;
case NfcScenePassportAuthSelectAuth:
if(nfc->dev->dev_data.mrtd_data.auth.method == MrtdAuthMethodPace) {
scene_manager_next_scene(nfc->scene_manager, NfcScenePassportPaceTodo);
} else {
nfc_device_clear(nfc->dev);
scene_manager_next_scene(nfc->scene_manager, NfcSceneRead);
}
consumed = true;
break;
case NfcScenePassportAuthSelectSave:
scene_manager_next_scene(nfc->scene_manager, NfcScenePassportAuthSaveName);
consumed = true;
break;
}
} else if(event.type == SceneManagerEventTypeBack) {
consumed = scene_manager_previous_scene(nfc->scene_manager);
}
return consumed;
}
void nfc_scene_passport_auth_on_exit(void* context) {
Nfc* nfc = context;
// Clear view
variable_item_list_reset(nfc->variable_item_list);
}
@@ -1,82 +0,0 @@
#include "../nfc_i.h"
#include <lib/toolbox/random_name.h>
#include <gui/modules/validators.h>
#include <toolbox/path.h>
void nfc_scene_passport_auth_save_name_text_input_callback(void* context) {
Nfc* nfc = context;
view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventTextInputDone);
}
void nfc_scene_passport_auth_save_name_on_enter(void* context) {
Nfc* nfc = context;
MrtdData* mrtd_data = &nfc->dev->dev_data.mrtd_data;
// Setup view
TextInput* text_input = nfc->text_input;
bool docnr_empty = false;
if(!strcmp(mrtd_data->auth.doc_number, "")) {
set_random_name(nfc->text_store, sizeof(nfc->text_store));
docnr_empty = true;
} else {
nfc_text_store_set(nfc, mrtd_data->auth.doc_number);
}
text_input_set_header_text(text_input, "Name the parameters");
text_input_set_result_callback(
text_input,
nfc_scene_passport_auth_save_name_text_input_callback,
nfc,
nfc->text_store,
NFC_DEV_NAME_MAX_LEN,
docnr_empty);
FuriString* folder_path;
folder_path = furi_string_alloc();
if(furi_string_end_with(nfc->dev->load_path, NFC_APP_EXTENSION)) {
path_extract_dirname(furi_string_get_cstr(nfc->dev->load_path), folder_path);
} else {
furi_string_set(folder_path, NFC_APP_FOLDER);
}
ValidatorIsFile* validator_is_file =
validator_is_file_alloc_init(furi_string_get_cstr(folder_path), NFC_APP_EXTENSION, NULL);
text_input_set_validator(text_input, validator_is_file_callback, validator_is_file);
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextInput);
furi_string_free(folder_path);
}
bool nfc_scene_passport_auth_save_name_on_event(void* context, SceneManagerEvent event) {
Nfc* nfc = context;
MrtdData* mrtd_data = &nfc->dev->dev_data.mrtd_data;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == NfcCustomEventTextInputDone) {
if(mrtd_auth_params_save(
nfc->dev->storage, nfc->dev->dialogs, &mrtd_data->auth, nfc->text_store)) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveSuccess);
consumed = true;
} else {
consumed = scene_manager_search_and_switch_to_previous_scene(
nfc->scene_manager, NfcSceneStart);
}
}
}
return consumed;
}
void nfc_scene_passport_auth_save_name_on_exit(void* context) {
Nfc* nfc = context;
// Clear view
void* validator_context = text_input_get_validator_callback_context(nfc->text_input);
text_input_set_validator(nfc->text_input, NULL, NULL);
validator_is_file_free(validator_context);
text_input_reset(nfc->text_input);
}
@@ -1,125 +0,0 @@
#include "../nfc_i.h"
#include "m-string.h"
#include <gui/modules/validators.h>
#define TAG "PassportDate"
#define DATE_LENGTH 6
//TODO: use types in .h file? also in nfc_scene_passport_bac.c
#define NFC_PASSPORT_DATE_BIRTH 0
#define NFC_PASSPORT_DATE_EXPIRY 1
void nfc_scene_passport_date_text_input_callback(void* context) {
Nfc* nfc = context;
view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventTextInputDone);
}
void nfc_scene_passport_date_on_enter(void* context) {
Nfc* nfc = context;
MrtdDate date_value;
uint32_t date_type = scene_manager_get_scene_state(nfc->scene_manager, NfcScenePassportDate);
//TODO: numbers only
TextInput* text_input = nfc->text_input;
// TODO: Clean this up because I'm bad
date_value.year = 0;
date_value.month = 0;
date_value.day = 0;
switch(date_type) {
case NFC_PASSPORT_DATE_BIRTH:
text_input_set_header_text(text_input, "Birth Date");
date_value = nfc->dev->dev_data.mrtd_data.auth.birth_date;
break;
case NFC_PASSPORT_DATE_EXPIRY:
text_input_set_header_text(text_input, "Expiry Date");
date_value = nfc->dev->dev_data.mrtd_data.auth.expiry_date;
break;
}
bool date_empty = false;
if(date_value.year == 0 || date_value.month == 0 || date_value.day == 0 ||
date_value.year > 100 || date_value.month > 13 || date_value.day > 31) {
nfc_text_store_set(nfc, "YYMMDD");
date_empty = true;
} else {
char temp_str[10];
snprintf(temp_str, 10, "%02u%02u%02u", date_value.year, date_value.month, date_value.day);
memcpy(nfc->text_store, temp_str, DATE_LENGTH);
nfc->text_store[DATE_LENGTH] = '\x00';
}
text_input_set_result_callback(
text_input,
nfc_scene_passport_date_text_input_callback,
nfc,
nfc->text_store,
DATE_LENGTH + 1, // incl. '\x00'
date_empty); // Use as template
//TODO: add validator for valid date (YYMMDD)
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextInput);
}
bool nfc_scene_passport_date_save(Nfc* nfc) {
int year;
int month;
int day;
int ret = sscanf(nfc->text_store, "%02d%02d%02d", &year, &month, &day);
if(ret != 3) {
FURI_LOG_E(TAG, "Invalid date entered (YYMMDD): %s", nfc->text_store);
return false;
}
MrtdDate date_value;
date_value.year = year;
date_value.month = month;
date_value.day = day;
uint32_t date_type = scene_manager_get_scene_state(nfc->scene_manager, NfcScenePassportDate);
//TODO: use types in .h file? also in nfc_scene_passport_bac.c
switch(date_type) {
case NFC_PASSPORT_DATE_BIRTH:
nfc->dev->dev_data.mrtd_data.auth.birth_date = date_value;
break;
case NFC_PASSPORT_DATE_EXPIRY:
nfc->dev->dev_data.mrtd_data.auth.expiry_date = date_value;
break;
}
return true;
}
bool nfc_scene_passport_date_on_event(void* context, SceneManagerEvent event) {
Nfc* nfc = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == NfcCustomEventTextInputDone) {
nfc_scene_passport_date_save(nfc);
//TODO: handle invalid date (returned false)
consumed = scene_manager_search_and_switch_to_previous_scene(
nfc->scene_manager, NfcScenePassportAuth);
}
}
return consumed;
}
void nfc_scene_passport_date_on_exit(void* context) {
Nfc* nfc = context;
// Clear view
// TODO: clear validator
text_input_reset(nfc->text_input);
}
@@ -1,67 +0,0 @@
#include "../nfc_i.h"
#include "m-string.h"
#include <gui/modules/validators.h>
#define TAG "PassportDocnr"
void nfc_scene_passport_docnr_text_input_callback(void* context) {
Nfc* nfc = context;
view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventTextInputDone);
}
void nfc_scene_passport_docnr_on_enter(void* context) {
Nfc* nfc = context;
TextInput* text_input = nfc->text_input;
text_input_set_header_text(text_input, "Document Nr.");
char* docnr = nfc->dev->dev_data.mrtd_data.auth.doc_number;
bool docnr_empty = false;
if(*docnr) {
nfc_text_store_set(nfc, docnr);
docnr_empty = false;
} else {
nfc_text_store_set(nfc, "PA7HJ34M8");
docnr_empty = true;
}
text_input_set_result_callback(
text_input,
nfc_scene_passport_docnr_text_input_callback,
nfc,
nfc->text_store,
MRTD_DOCNR_MAX_LENGTH, // incl. '\x00'
docnr_empty); // Use as template
//TODO: add validator?
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextInput);
}
bool nfc_scene_passport_docnr_save(Nfc* nfc) {
strncpy(nfc->dev->dev_data.mrtd_data.auth.doc_number, nfc->text_store, MRTD_DOCNR_MAX_LENGTH);
return true;
}
bool nfc_scene_passport_docnr_on_event(void* context, SceneManagerEvent event) {
Nfc* nfc = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == NfcCustomEventTextInputDone) {
nfc_scene_passport_docnr_save(nfc);
consumed = scene_manager_search_and_switch_to_previous_scene(
nfc->scene_manager, NfcScenePassportAuth);
}
}
return consumed;
}
void nfc_scene_passport_docnr_on_exit(void* context) {
Nfc* nfc = context;
text_input_reset(nfc->text_input);
}
@@ -1,57 +0,0 @@
#include "../nfc_i.h"
enum SubmenuIndex {
SubmenuIndexSave,
SubmenuIndexInfo,
};
void nfc_scene_passport_menu_submenu_callback(void* context, uint32_t index) {
Nfc* nfc = context;
view_dispatcher_send_custom_event(nfc->view_dispatcher, index);
}
void nfc_scene_passport_menu_on_enter(void* context) {
Nfc* nfc = context;
Submenu* submenu = nfc->submenu;
submenu_add_item(
submenu, "Save", SubmenuIndexSave, nfc_scene_passport_menu_submenu_callback, nfc);
submenu_add_item(
submenu, "Info", SubmenuIndexInfo, nfc_scene_passport_menu_submenu_callback, nfc);
submenu_set_selected_item(
nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcScenePassportMenu));
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu);
}
bool nfc_scene_passport_menu_on_event(void* context, SceneManagerEvent event) {
Nfc* nfc = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SubmenuIndexSave) {
//TODO: save more than just UID
nfc->dev->format = NfcDeviceSaveFormatUid;
// Clear device name
nfc_device_set_name(nfc->dev, "");
scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName);
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, NfcScenePassportMenu, event.event);
} else if(event.type == SceneManagerEventTypeBack) {
consumed = scene_manager_previous_scene(nfc->scene_manager);
}
return consumed;
}
void nfc_scene_passport_menu_on_exit(void* context) {
Nfc* nfc = context;
// Clear view
submenu_reset(nfc->submenu);
}
@@ -1,40 +0,0 @@
#include "../nfc_i.h"
void nfc_scene_passport_pace_todo_popup_callback(void* context) {
Nfc* nfc = context;
view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit);
}
void nfc_scene_passport_pace_todo_on_enter(void* context) {
Nfc* nfc = context;
// Setup view
Popup* popup = nfc->popup;
popup_set_icon(popup, 64, 16, &I_DolphinCommon_56x48);
popup_set_header(popup, "PACE not yet implemented", 4, 4, AlignLeft, AlignTop);
popup_set_timeout(popup, 2000);
popup_set_context(popup, nfc);
popup_set_callback(popup, nfc_scene_passport_pace_todo_popup_callback);
popup_enable_timeout(popup);
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup);
}
bool nfc_scene_passport_pace_todo_on_event(void* context, SceneManagerEvent event) {
Nfc* nfc = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == NfcCustomEventViewExit) {
consumed = scene_manager_search_and_switch_to_previous_scene(
nfc->scene_manager, NfcScenePassportAuth);
}
}
return consumed;
}
void nfc_scene_passport_pace_todo_on_exit(void* context) {
Nfc* nfc = context;
// Clear view
popup_reset(nfc->popup);
}
@@ -1,90 +0,0 @@
#include "../nfc_i.h"
#include <dolphin/dolphin.h>
void nfc_scene_passport_read_widget_callback(GuiButtonType result, InputType type, void* context) {
Nfc* nfc = context;
if(type == InputTypeShort) {
view_dispatcher_send_custom_event(nfc->view_dispatcher, result);
}
}
void nfc_scene_passport_read_on_enter(void* context) {
Nfc* nfc = context;
FuriHalNfcDevData* data = &nfc->dev->dev_data.nfc_data;
MrtdData* mrtd_data = &nfc->dev->dev_data.mrtd_data;
DOLPHIN_DEED(DolphinDeedNfcReadSuccess);
Widget* widget = nfc->widget;
// Setup Custom Widget view
FuriString* temp_str;
temp_str = furi_string_alloc();
furi_string_set(temp_str, "\e#Passport\n");
char iso_type = FURI_BIT(data->sak, 5) ? '4' : '3';
char nfc_type;
switch(data->type) {
case FuriHalNfcTypeA:
nfc_type = 'A';
break;
case FuriHalNfcTypeB:
nfc_type = 'B';
break;
default:
nfc_type = '?';
break;
}
furi_string_cat_printf(temp_str, "ISO 14443-%c (NFC-%c)\n", iso_type, nfc_type);
furi_string_cat_printf(temp_str, "UID:");
for(size_t i = 0; i < data->uid_len; i++) {
furi_string_cat_printf(temp_str, " %02X", data->uid[i]);
}
furi_string_cat_printf(temp_str, "\nATQA: %02X %02X ", data->atqa[1], data->atqa[0]);
furi_string_cat_printf(temp_str, " SAK: %02X\n", data->sak);
if(mrtd_data->auth.method != MrtdAuthMethodNone && !mrtd_data->auth_success) {
furi_string_cat_printf(temp_str, "Auth failed. Wrong params?");
}
widget_add_text_scroll_element(widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str));
furi_string_free(temp_str);
widget_add_button_element(
nfc->widget, GuiButtonTypeLeft, "Retry", nfc_scene_passport_read_widget_callback, nfc);
widget_add_button_element(
nfc->widget, GuiButtonTypeCenter, "Auth", nfc_scene_passport_read_widget_callback, nfc);
widget_add_button_element(
nfc->widget, GuiButtonTypeRight, "More", nfc_scene_passport_read_widget_callback, nfc);
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
}
bool nfc_scene_passport_read_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 == GuiButtonTypeCenter) {
scene_manager_next_scene(nfc->scene_manager, NfcScenePassportAuth);
consumed = true;
} else if(event.event == GuiButtonTypeRight) {
scene_manager_next_scene(nfc->scene_manager, NfcScenePassportMenu);
consumed = true;
}
} else if(event.type == SceneManagerEventTypeBack) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneExitConfirm);
consumed = true;
}
return consumed;
}
void nfc_scene_passport_read_on_exit(void* context) {
Nfc* nfc = context;
// Clear view
widget_reset(nfc->widget);
}
@@ -1,142 +0,0 @@
#include "../nfc_i.h"
#include <dolphin/dolphin.h>
const char months[13][4] = {
"---",
"JAN",
"FEB",
"MAR",
"APR",
"MAY",
"JUN",
"JUL",
"AUG",
"SEP",
"OCT",
"NOV",
"DEC",
};
void nfc_scene_passport_read_auth_widget_callback(
GuiButtonType result,
InputType type,
void* context) {
Nfc* nfc = context;
if(type == InputTypeShort) {
view_dispatcher_send_custom_event(nfc->view_dispatcher, result);
}
}
void nfc_scene_passport_read_auth_on_enter(void* context) {
Nfc* nfc = context;
MrtdData* mrtd_data = &nfc->dev->dev_data.mrtd_data;
Widget* widget = nfc->widget;
// Setup Custom Widget view
FuriString* temp_str;
temp_str = furi_string_alloc();
furi_string_set(temp_str, "\e#Passport\n");
furi_string_cat_printf(
temp_str, "Auth.method: %s\n", mrtd_auth_method_string(mrtd_data->auth_method_used));
// TODO: indicate BAC / PACE used
uint16_t lds_version = mrtd_data->files.EF_COM.lds_version;
furi_string_cat_printf(temp_str, "LDS version: %d.%d\n", lds_version / 100, lds_version % 100);
uint32_t unicode_version = mrtd_data->files.EF_COM.unicode_version;
furi_string_cat_printf(
temp_str,
"Unicode version: %d.%d.%d\n",
(uint8_t)(unicode_version / 10000),
(uint8_t)(unicode_version / 100 % 100),
(uint8_t)(unicode_version % 100));
furi_string_cat_printf(temp_str, "Avail.files: ");
for(size_t i = 0; i < MAX_EFCOM_TAGS; ++i) {
uint8_t tag = mrtd_data->files.EF_COM.tag_list[i];
const EFFile* file = mrtd_tag_to_file(tag);
if(file->tag) {
if(i > 0) furi_string_cat_printf(temp_str, ", ");
furi_string_cat_printf(temp_str, "%s", file->name);
}
}
furi_string_cat_printf(temp_str, "\n");
EF_DIR_contents* EF_DIR = &mrtd_data->files.EF_DIR;
if(EF_DIR->applications_count > 0) {
furi_string_cat_printf(temp_str, "Apps:\n");
for(uint8_t i = 0; i < EF_DIR->applications_count; ++i) {
for(uint8_t n = 0; n < sizeof(AIDValue); ++n) {
furi_string_cat_printf(temp_str, "%02X ", EF_DIR->applications[i][n]);
}
furi_string_cat_printf(temp_str, "\n");
}
}
EF_DG1_contents* DG1 = &mrtd_data->files.DG1;
furi_string_cat_printf(temp_str, "\e#DG1\n");
furi_string_cat_printf(temp_str, "Doc Type: %s\n", DG1->doctype);
furi_string_cat_printf(temp_str, "Issuing State: %s\n", DG1->issuing_state);
furi_string_cat_printf(temp_str, "Name: %s\n", DG1->name);
furi_string_cat_printf(temp_str, "DocNr: %s\n", DG1->docnr);
furi_string_cat_printf(temp_str, "Nationality: %s\n", DG1->nationality);
furi_string_cat_printf(
temp_str,
"Birth Date: %02d %s %02d\n",
DG1->birth_date.day,
months[DG1->birth_date.month],
DG1->birth_date.year);
furi_string_cat_printf(temp_str, "Sex: %s\n", DG1->sex);
furi_string_cat_printf(
temp_str,
"Expiry Date: %02d %s %02d\n",
DG1->expiry_date.day,
months[DG1->expiry_date.month],
DG1->expiry_date.year);
widget_add_text_scroll_element(widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str));
furi_string_free(temp_str);
widget_add_button_element(
nfc->widget, GuiButtonTypeLeft, "Retry", nfc_scene_passport_read_auth_widget_callback, nfc);
/*
widget_add_button_element(
nfc->widget, GuiButtonTypeCenter, "Auth", nfc_scene_passport_read_auth_widget_callback, nfc);
widget_add_button_element(
nfc->widget, GuiButtonTypeRight, "More", nfc_scene_passport_read_auth_widget_callback, nfc);
*/
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
}
bool nfc_scene_passport_read_auth_on_event(void* context, SceneManagerEvent event) {
Nfc* nfc = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == GuiButtonTypeLeft) {
nfc->dev->dev_data.mrtd_data.auth_success = false;
nfc->dev->dev_data.mrtd_data.auth.method = MrtdAuthMethodNone;
scene_manager_next_scene(nfc->scene_manager, NfcSceneRetryConfirm);
consumed = true;
} else if(event.event == GuiButtonTypeCenter) {
//scene_manager_next_scene(nfc->scene_manager, NfcScenePassportAuth);
//consumed = true;
} else if(event.event == GuiButtonTypeRight) {
//scene_manager_next_scene(nfc->scene_manager, NfcScenePassportMenu);
//consumed = true;
}
} else if(event.type == SceneManagerEventTypeBack) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneExitConfirm);
consumed = true;
}
return consumed;
}
void nfc_scene_passport_read_auth_on_exit(void* context) {
Nfc* nfc = context;
// Clear view
widget_reset(nfc->widget);
}
+2 -20
View File
@@ -25,12 +25,12 @@ void nfc_scene_read_set_state(Nfc* nfc, NfcSceneReadState state) {
if(state == NfcSceneReadStateDetecting) {
popup_reset(nfc->popup);
popup_set_text(
nfc->popup, "Apply Card To\nFlipper's Back", 97, 24, AlignCenter, AlignTop);
nfc->popup, "Apply card to\nFlipper's back", 97, 24, AlignCenter, AlignTop);
popup_set_icon(nfc->popup, 0, 8, &I_NFC_manual_60x50);
} else if(state == NfcSceneReadStateReading) {
popup_reset(nfc->popup);
popup_set_header(
nfc->popup, "Reading Card\nDon't Move...", 85, 24, AlignCenter, AlignTop);
nfc->popup, "Reading card\nDon't move...", 85, 24, AlignCenter, AlignTop);
popup_set_icon(nfc->popup, 12, 23, &A_Loading_24);
}
scene_manager_set_scene_state(nfc->scene_manager, NfcSceneRead, state);
@@ -68,11 +68,6 @@ bool nfc_scene_read_on_event(void* context, SceneManagerEvent event) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcaReadSuccess);
DOLPHIN_DEED(DolphinDeedNfcReadSuccess);
consumed = true;
} else if(event.event == NfcWorkerEventReadNfcV) {
notification_message(nfc->notifications, &sequence_success);
scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcDataInfo);
DOLPHIN_DEED(DolphinDeedNfcReadSuccess);
consumed = true;
} else if(event.event == NfcWorkerEventReadMfUltralight) {
notification_message(nfc->notifications, &sequence_success);
// Set unlock password input to 0xFFFFFFFF only on fresh read
@@ -95,19 +90,6 @@ bool nfc_scene_read_on_event(void* context, SceneManagerEvent event) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneEmvReadSuccess);
DOLPHIN_DEED(DolphinDeedNfcReadSuccess);
consumed = true;
} else if(event.event == NfcWorkerEventReadPassport) {
notification_message(nfc->notifications, &sequence_success);
FURI_LOG_D(
"NFC",
"Read passport, auth: %d, success: %d",
nfc->dev->dev_data.mrtd_data.auth.method,
nfc->dev->dev_data.mrtd_data.auth_success);
if(nfc->dev->dev_data.mrtd_data.auth_success) {
scene_manager_next_scene(nfc->scene_manager, NfcScenePassportReadAuthSuccess);
} else {
scene_manager_next_scene(nfc->scene_manager, NfcScenePassportReadSuccess);
}
consumed = true;
} else if(event.event == NfcWorkerEventReadMfClassicDictAttackRequired) {
if(mf_classic_dict_check_presence(MfClassicDictTypeSystem)) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicDictAttack);
@@ -56,13 +56,6 @@ bool nfc_scene_rpc_on_event(void* context, SceneManagerEvent event) {
&nfc->dev->dev_data,
nfc_scene_rpc_emulate_callback,
nfc);
} else if(nfc->dev->format == NfcDeviceSaveFormatNfcV) {
nfc_worker_start(
nfc->worker,
NfcWorkerStateNfcVEmulate,
&nfc->dev->dev_data,
nfc_scene_rpc_emulate_callback,
nfc);
} else {
nfc_worker_start(
nfc->worker, NfcWorkerStateUidEmulate, &nfc->dev->dev_data, NULL, nfc);
@@ -32,12 +32,6 @@ bool nfc_scene_save_success_on_event(void* context, SceneManagerEvent event) {
} else if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSavedMenu)) {
consumed = scene_manager_search_and_switch_to_previous_scene(
nfc->scene_manager, NfcSceneSavedMenu);
} else if(scene_manager_has_previous_scene(nfc->scene_manager, NfcScenePassportAuth)) {
consumed = scene_manager_search_and_switch_to_previous_scene(
nfc->scene_manager, NfcScenePassportAuth);
} 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 {
consumed = scene_manager_search_and_switch_to_another_scene(
nfc->scene_manager, NfcSceneFileSelect);
@@ -117,8 +117,6 @@ bool nfc_scene_saved_menu_on_event(void* context, SceneManagerEvent event) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightEmulate);
} else if(nfc->dev->format == NfcDeviceSaveFormatMifareClassic) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicEmulate);
} else if(nfc->dev->format == NfcDeviceSaveFormatNfcV) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateNfcV);
} else {
scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid);
}
@@ -11,7 +11,7 @@ void nfc_scene_set_atqa_on_enter(void* context) {
// Setup view
ByteInput* byte_input = nfc->byte_input;
byte_input_set_header_text(byte_input, "Enter atqa in hex");
byte_input_set_header_text(byte_input, "Enter ATQA in hex");
byte_input_set_result_callback(
byte_input,
nfc_scene_set_atqa_byte_input_callback,
@@ -28,7 +28,7 @@ bool nfc_scene_set_sak_on_event(void* context, SceneManagerEvent event) {
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == NfcCustomEventByteInputDone) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneSetAtqua);
scene_manager_next_scene(nfc->scene_manager, NfcSceneSetAtqa);
consumed = true;
}
}
@@ -11,7 +11,7 @@ void nfc_scene_set_uid_on_enter(void* context) {
// Setup view
ByteInput* byte_input = nfc->byte_input;
byte_input_set_header_text(byte_input, "Enter uid in hex");
byte_input_set_header_text(byte_input, "Enter UID in hex");
nfc->dev_edit_data = nfc->dev->dev_data.nfc_data;
byte_input_set_result_callback(
byte_input,
+16 -9
View File
@@ -30,14 +30,10 @@ void nfc_scene_start_on_enter(void* context) {
submenu_add_item(
submenu, "Add Manually", SubmenuIndexAddManually, nfc_scene_start_submenu_callback, nfc);
submenu_add_lockable_item(
submenu,
"Debug",
SubmenuIndexDebug,
nfc_scene_start_submenu_callback,
nfc,
!furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug),
"Enable\nDebug!");
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
submenu_add_item(
submenu, "Debug", SubmenuIndexDebug, nfc_scene_start_submenu_callback, nfc);
}
submenu_set_selected_item(
submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneStart));
@@ -52,11 +48,14 @@ bool nfc_scene_start_on_event(void* context, SceneManagerEvent event) {
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SubmenuIndexRead) {
scene_manager_set_scene_state(nfc->scene_manager, NfcSceneStart, SubmenuIndexRead);
nfc->dev->dev_data.read_mode = NfcReadModeAuto;
scene_manager_next_scene(nfc->scene_manager, NfcSceneRead);
DOLPHIN_DEED(DolphinDeedNfcRead);
consumed = true;
} else if(event.event == SubmenuIndexDetectReader) {
scene_manager_set_scene_state(
nfc->scene_manager, NfcSceneStart, SubmenuIndexDetectReader);
bool sd_exist = storage_sd_status(nfc->dev->storage) == FSE_OK;
if(sd_exist) {
nfc_device_data_clear(&nfc->dev->dev_data);
@@ -67,19 +66,27 @@ bool nfc_scene_start_on_event(void* context, SceneManagerEvent event) {
}
consumed = true;
} else if(event.event == SubmenuIndexSaved) {
// Save the scene state explicitly in each branch, so that
// if the user cancels loading a file, the Saved menu item
// is properly reselected.
scene_manager_set_scene_state(nfc->scene_manager, NfcSceneStart, SubmenuIndexSaved);
scene_manager_next_scene(nfc->scene_manager, NfcSceneFileSelect);
consumed = true;
} else if(event.event == SubmenuIndexExtraAction) {
scene_manager_set_scene_state(
nfc->scene_manager, NfcSceneStart, SubmenuIndexExtraAction);
scene_manager_next_scene(nfc->scene_manager, NfcSceneExtraActions);
consumed = true;
} else if(event.event == SubmenuIndexAddManually) {
scene_manager_set_scene_state(
nfc->scene_manager, NfcSceneStart, SubmenuIndexAddManually);
scene_manager_next_scene(nfc->scene_manager, NfcSceneSetType);
consumed = true;
} else if(event.event == SubmenuIndexDebug) {
scene_manager_set_scene_state(nfc->scene_manager, NfcSceneStart, SubmenuIndexDebug);
scene_manager_next_scene(nfc->scene_manager, NfcSceneDebug);
consumed = true;
}
scene_manager_set_scene_state(nfc->scene_manager, NfcSceneStart, event.event);
}
return consumed;
}
-85
View File
@@ -1,85 +0,0 @@
#include "iso7816.h"
// ISO7816-5
// Simple-TLV (§5.2.1)
// BER-TLV (§5.2.2)
TlvInfo iso7816_tlv_parse(const uint8_t* data) {
TlvInfo tlv;
// Simple-TLV: tag can be any value from 1 to 254 (not '00' or 'FF')
// BER-TLV: TODO describe
// 00000 - 11110 => 0 - 30 (single byte)
// 11111 00011111 - 11111 01111111 => 31 - 127 (2 byte)
// 11111 10000001 00000001 - 11111 11111111 01111111 => 128 - 16383 (3 byte)
tlv.tag = *(data++);
tlv.ber.constructed = ((tlv.tag & 0x20) != 0);
tlv.ber.classVar = (tlv.tag >> 6) & 0x03;
if((tlv.tag & 0x1f) == 0x1f) {
// BER-TLV, multi byte tag
tlv.tag <<= 8;
tlv.tag |= *(data++);
tlv.ber.tag = tlv.tag & 0x7f;
if(tlv.tag & 0x80) {
// BER-TLV, 3 byte tag
tlv.tag &= ~0x80;
tlv.tag <<= 7;
tlv.tag |= *(data++) & 0x7f;
tlv.ber.tag = tlv.tag & 0x3fff;
}
} else {
tlv.ber.tag = tlv.tag & 0x1f;
}
//TODO: check for invalid 'indefinite length'
tlv.length = *(data++);
if(tlv.length == 0xff) {
// Simple-TLV 2 byte length
tlv.length = *(data++) << 8;
tlv.length += *(data++);
} else if(tlv.length > 0x7f) {
uint8_t length_bytes = tlv.length & 0x7f;
//printf("BER length of %d bytes\n", length_bytes);
if(length_bytes < 1 || length_bytes > 4) {
//TODO: error: ISO7816 doesn't support more than 4 length bytes
return (TlvInfo){.tag = 0};
}
tlv.length = 0;
for(uint8_t i = 0; i < length_bytes; ++i) {
//printf("byte %d: %02x\n", i, *data);
tlv.length <<= 8;
tlv.length |= *(data++);
}
}
tlv.value = data;
tlv.next = data + tlv.length;
return tlv;
}
TlvInfo
iso7816_tlv_select(const uint8_t* data, size_t length, const uint16_t tags[], size_t num_tags) {
TlvInfo tlv;
size_t offset = 0;
if(num_tags == 0) {
return (TlvInfo){.tag = 0x0000};
}
while(offset < length) {
tlv = iso7816_tlv_parse(data + offset);
if(tlv.tag == tags[0]) {
if(num_tags == 1) {
return tlv;
} else {
return iso7816_tlv_select(tlv.value, tlv.length, tags + 1, num_tags - 1);
}
}
offset =
tlv.next - data; // TODO: use some length value of TlvInfo instead of this monstrosity
}
return (TlvInfo){.tag = 0x0000};
}
-31
View File
@@ -1,31 +0,0 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include <string.h>
#define BER_CLASS_UNIVERSAL 0x0
#define BER_CLASS_APPLICATION 0x1
#define BER_CLASS_CONTEXT 0x2
#define BER_CLASS_PRIVATE 0x3
typedef struct {
uint16_t tag; // TODO: use define/typedef for this data format?
struct {
uint16_t tag;
uint8_t constructed : 1;
uint8_t classVar : 2;
} ber;
size_t length;
const uint8_t* value;
const uint8_t* next;
} TlvInfo;
// ISO7816-5 §5.2
// Simple-TLV and BER-TLV parsing
TlvInfo iso7816_tlv_parse(const uint8_t* data);
TlvInfo
iso7816_tlv_select(const uint8_t* data, size_t length, const uint16_t tags[], size_t num_tags);
-658
View File
@@ -1,658 +0,0 @@
#include "mrtd_helpers.h"
#include "../helpers/iso7816.h"
#include <stdio.h> //TODO: remove
#include <stdlib.h>
#include <mbedtls/sha1.h>
#include <mbedtls/des.h>
static inline unsigned char* ucstr(const char* str) {
return (unsigned char*)str;
}
const char* mrtd_auth_method_string(MrtdAuthMethod method) {
switch(method) {
case MrtdAuthMethodBac:
return "BAC";
case MrtdAuthMethodPace:
return "PACE";
case MrtdAuthMethodNone:
return "None";
case MrtdAuthMethodAny:
return "Any";
default:
return "Unknown";
}
}
bool mrtd_auth_method_parse_string(MrtdAuthMethod* method, const char* str) {
if(!strcmp(str, "BAC")) {
*method = MrtdAuthMethodBac;
return true;
}
if(!strcmp(str, "PACE")) {
*method = MrtdAuthMethodPace;
return true;
}
if(!strcmp(str, "None")) {
*method = MrtdAuthMethodNone;
return true;
}
if(!strcmp(str, "Any")) {
*method = MrtdAuthMethodAny;
return true;
}
return false;
}
uint8_t mrtd_bac_check_digit(const char* input, const uint8_t length) {
const uint8_t num_weights = 3;
uint8_t weights[] = {7, 3, 1};
uint8_t check_digit = 0;
uint8_t idx;
for(uint8_t i = 0; i < length; ++i) {
char c = input[i];
if(c >= 'A' && c <= 'Z') {
idx = c - 'A' + 10;
} else if(c >= 'a' && c <= 'z') {
idx = c - 'a' + 10;
} else if(c >= '0' && c <= '9') {
idx = c - '0';
} else {
idx = 0;
}
check_digit = (check_digit + idx * weights[i % num_weights]) % 10;
}
return check_digit;
}
void mrtd_print_date(char* output, MrtdDate* date) {
output[0] = (date->year / 10) + '0';
output[1] = (date->year % 10) + '0';
output[2] = (date->month / 10) + '0';
output[3] = (date->month % 10) + '0';
output[4] = (date->day / 10) + '0';
output[5] = (date->day % 10) + '0';
}
uint8_t charval(char c) {
if(c >= '0' && c <= '9') {
return c - '0';
}
return 0;
}
void mrtd_parse_date(MrtdDate* date, const unsigned char* input) {
date->year = charval(input[0]) * 10 + charval(input[1]);
date->month = charval(input[2]) * 10 + charval(input[3]);
date->day = charval(input[4]) * 10 + charval(input[5]);
}
bool mrtd_bac_get_kmrz(MrtdAuthData* auth, char* output, uint8_t output_size) {
uint8_t idx = 0;
uint8_t docnr_length = strlen(auth->doc_number);
uint8_t cd_idx = 0;
if(output_size < docnr_length + 16) {
return false;
}
cd_idx = idx;
for(uint8_t i = 0; i < docnr_length; ++i) {
char c = auth->doc_number[i];
if(c >= 'a' && c <= 'z') {
c = c - 'a' + 'A';
}
output[idx++] = c;
}
if(docnr_length < 9) {
memset(output + idx, '<', 9 - docnr_length);
idx += 9 - docnr_length;
}
output[idx++] = mrtd_bac_check_digit(output + cd_idx, docnr_length) + '0';
cd_idx = idx;
mrtd_print_date(output + idx, &auth->birth_date);
idx += 6;
output[idx++] = mrtd_bac_check_digit(output + cd_idx, 6) + '0';
cd_idx = idx;
mrtd_print_date(output + idx, &auth->expiry_date);
idx += 6;
output[idx++] = mrtd_bac_check_digit(output + cd_idx, 6) + '0';
output[idx++] = '\x00';
return true;
}
bool mrtd_bac_keys_from_seed(const uint8_t kseed[16], uint8_t ksenc[16], uint8_t ksmac[16]) {
uint8_t hash[20];
mbedtls_sha1_context ctx;
mbedtls_sha1_init(&ctx);
do {
for(uint8_t i = 1; i <= 2; ++i) {
if(mbedtls_sha1_starts(&ctx)) break;
if(mbedtls_sha1_update(&ctx, kseed, 16)) break;
if(mbedtls_sha1_update(&ctx, ucstr("\x00\x00\x00"), 3)) break;
if(mbedtls_sha1_update(&ctx, &i, 1)) break;
if(mbedtls_sha1_finish(&ctx, hash)) break;
switch(i) {
case 1:
memcpy(ksenc, hash, 16);
mbedtls_des_key_set_parity(ksenc);
mbedtls_des_key_set_parity(ksenc + 8);
break;
case 2:
memcpy(ksmac, hash, 16);
mbedtls_des_key_set_parity(ksmac);
mbedtls_des_key_set_parity(ksmac + 8);
break;
}
}
} while(false);
mbedtls_sha1_free(&ctx);
return true;
}
bool mrtd_bac_keys(MrtdAuthData* auth, uint8_t ksenc[16], uint8_t ksmac[16]) {
uint8_t kmrz_max_length = MRTD_DOCNR_MAX_LENGTH + 16;
char kmrz[kmrz_max_length];
if(!mrtd_bac_get_kmrz(auth, kmrz, kmrz_max_length)) {
return false;
}
printf("kmrz: %s\r\n", kmrz); //TODO: remove
uint8_t hash[20];
mbedtls_sha1((uint8_t*)kmrz, strlen(kmrz), hash);
if(!mrtd_bac_keys_from_seed(hash, ksenc, ksmac)) {
return false;
}
return true;
}
//NOTE: output size will be ((data_length+8)/8)*8
bool mrtd_bac_encrypt(const uint8_t* data, size_t data_length, const uint8_t* key, uint8_t* output) {
uint8_t IV[8] = "\x00\x00\x00\x00\x00\x00\x00\x00";
mbedtls_des3_context ctx;
mbedtls_des3_init(&ctx);
mbedtls_des3_set2key_enc(&ctx, key);
if(mbedtls_des3_crypt_cbc(&ctx, MBEDTLS_DES_ENCRYPT, data_length, IV, data, output)) {
return false;
}
mbedtls_des3_free(&ctx);
return true;
}
bool mrtd_bac_decrypt(const uint8_t* data, size_t data_length, uint8_t* key, uint8_t* output) {
uint8_t IV[8] = "\x00\x00\x00\x00\x00\x00\x00\x00";
mbedtls_des3_context ctx;
mbedtls_des3_init(&ctx);
mbedtls_des3_set2key_dec(&ctx, key);
if(mbedtls_des3_crypt_cbc(&ctx, MBEDTLS_DES_DECRYPT, data_length, IV, data, output)) {
return false;
}
mbedtls_des3_free(&ctx);
return true;
}
bool mrtd_bac_decrypt_verify(
const uint8_t* data,
size_t data_length,
uint8_t* key_enc,
uint8_t* key_mac,
uint8_t* output) {
mrtd_bac_decrypt(data, data_length - 8, key_enc, output);
uint8_t mac_calc[8];
mrtd_bac_padded_mac(data, data_length - 8, key_mac, mac_calc);
if(memcmp(mac_calc, data + data_length - 8, 8)) {
printf("MAC failed\r\n");
for(uint8_t i = 0; i < 8; ++i) {
printf("%02X <=> %02X\r\n", mac_calc[i], data[data_length - 8 + i]);
}
return false;
}
return true;
}
// If output or output_written are NULL-pointers, no output is written
// Otherwise, and if DO'87 is present, data is written to *output
// output should have enough room for additional padding (rounded up by 8 bytes)
// output_written will be the length without padding
uint16_t mrtd_bac_decrypt_verify_sm(
const uint8_t* data,
size_t data_length,
uint8_t* key_enc,
uint8_t* key_mac,
uint64_t ssc,
uint8_t* output,
size_t* output_written) {
// Message: [DO'85 or DO'87] || [DO'99] || DO'8E
// Lengths: Var 1+1+2=4 1+1+8=10
//TODO: check for DO'99 presence, instead of assuming
uint16_t ret_code = data[data_length - 10 - 2] << 8 | data[data_length - 10 - 1];
//ntohs(data + data_length - 10 - 2);
TlvInfo do87 = iso7816_tlv_select(data, data_length, (uint16_t[]){0x87}, 1);
//printf("DO87.Tag: %X\n", do87.tag);
//printf("DO87.Length: %ld\n", do87.length);
//printf("DO87.Value: ");
//for(uint8_t i=1; i<do87.length; ++i) { printf("%02X ", do87.value[i]); }
//printf("\r\n");
if(do87.tag) {
if(output_written != NULL && output != NULL) {
// Skip the first byte '01'
const uint8_t* encdata = do87.value + 1;
size_t enclength = do87.length - 1;
mrtd_bac_decrypt(encdata, enclength, key_enc, output);
printf("Decrypted: ");
for(uint8_t i = 0; i < enclength; ++i) printf("%02X ", output[i]);
printf("\r\n");
//TODO: function mrtd_bac_unpad?
int padidx;
for(padidx = enclength - 1; padidx >= 0; --padidx) {
if(output[padidx] == 0x00) {
continue;
} else if(output[padidx] == 0x80) {
break;
} else {
printf("Invalid padding\r\n");
return 0xff01;
}
}
printf(" ");
for(int i = 0; i < padidx; ++i) {
printf(" ");
}
printf("^^\r\n");
printf("Pad starts at: %d\r\n", padidx);
*output_written = padidx - 1;
}
} else {
if(output_written != NULL) {
*output_written = 0;
}
}
mrtd_bac_mac_ctx ctx;
mrtd_bac_mac_init(&ctx, key_mac);
uint64_t ssc_n = htonll(ssc);
mrtd_bac_mac_update(&ctx, (uint8_t*)&ssc_n, 8);
mrtd_bac_mac_update(
&ctx, data, data_length - 10); // 10 = len(DO'8E) = len(header + length + MAC) = 1 + 1 + 8
uint8_t mac_calc[8];
mrtd_bac_mac_finalize(&ctx, mac_calc);
if(memcmp(mac_calc, data + data_length - 8, 8)) {
printf("SM MAC failed\r\n");
for(uint8_t i = 0; i < 8; ++i) {
printf("%02X <=> %02X\r\n", mac_calc[i], data[data_length - 8 + i]);
}
return 0xff02;
}
return ret_code;
}
bool mrtd_bac_mac_init(mrtd_bac_mac_ctx* ctx, const uint8_t key[16]) {
mbedtls_des_init(&ctx->des);
mbedtls_des_setkey_enc(&ctx->des, key);
memset(ctx->mac, 0, 8);
ctx->idx_in = 0;
memcpy(ctx->key, key, 16);
return true;
}
bool mrtd_bac_mac_update(mrtd_bac_mac_ctx* ctx, const uint8_t* data, size_t data_length) {
//printf("MAC add %d: ", data_length); print_hex(data, data_length); printf("\n");
size_t data_idx = 0;
//uint8_t* xormac = ctx->xormac;
if(ctx->idx_in != 0) {
uint8_t buff_add = 8 - ctx->idx_in;
if(data_length < buff_add) {
buff_add = data_length;
}
memcpy(ctx->buffer_in + ctx->idx_in, data, buff_add);
ctx->idx_in = (ctx->idx_in + buff_add) % 8;
data_idx += buff_add;
if(ctx->idx_in == 0) { // buffer_in filled
for(uint8_t j = 0; j < 8; ++j) {
ctx->xormac[j] = ctx->mac[j] ^ ctx->buffer_in[j];
}
mbedtls_des_crypt_ecb(&ctx->des, ctx->xormac, ctx->mac);
printf(
"DES buf: %02X %02X %02X %02X %02X %02X %02X %02X\r\n",
ctx->buffer_in[0],
ctx->buffer_in[1],
ctx->buffer_in[2],
ctx->buffer_in[3],
ctx->buffer_in[4],
ctx->buffer_in[5],
ctx->buffer_in[6],
ctx->buffer_in[7]);
//printf("DES1: %02X %02X %02X %02X %02X %02X %02X %02X\n",
//xormac[0], xormac[1], xormac[2], xormac[3],
//xormac[4], xormac[5], xormac[6], xormac[7]);
}
}
while(true) {
if(data_idx + 8 > data_length) {
// Not a full block
break;
}
for(uint8_t j = 0; j < 8; ++j) {
ctx->xormac[j] = ctx->mac[j] ^ data[data_idx++];
}
mbedtls_des_crypt_ecb(&ctx->des, ctx->xormac, ctx->mac);
printf(
"DES add: %02X %02X %02X %02X %02X %02X %02X %02X\r\n",
data[data_idx - 8 + 0],
data[data_idx - 8 + 1],
data[data_idx - 8 + 2],
data[data_idx - 8 + 3],
data[data_idx - 8 + 4],
data[data_idx - 8 + 5],
data[data_idx - 8 + 6],
data[data_idx - 8 + 7]);
//printf("DES1: %02X %02X %02X %02X %02X %02X %02X %02X\n",
//xormac[0], xormac[1], xormac[2], xormac[3],
//xormac[4], xormac[5], xormac[6], xormac[7]);
}
if(data_idx < data_length) {
ctx->idx_in = data_length - data_idx;
memcpy(ctx->buffer_in, data + data_idx, ctx->idx_in);
}
return true;
}
bool mrtd_bac_mac_pad(mrtd_bac_mac_ctx* ctx) {
memset(ctx->buffer_in + ctx->idx_in, 0x00, 8 - ctx->idx_in);
ctx->buffer_in[ctx->idx_in] = 0x80;
ctx->idx_in = 8;
mrtd_bac_mac_update(ctx, NULL, 0); // Force processing the buffer_in
return true;
}
bool mrtd_bac_mac_finalize(mrtd_bac_mac_ctx* ctx, uint8_t output[8]) {
mrtd_bac_mac_pad(ctx);
uint8_t tmp[8];
mbedtls_des_init(&ctx->des);
mbedtls_des_setkey_dec(&ctx->des, ctx->key + 8);
mbedtls_des_crypt_ecb(&ctx->des, ctx->mac, tmp);
mbedtls_des_init(&ctx->des);
mbedtls_des_setkey_enc(&ctx->des, ctx->key);
mbedtls_des_crypt_ecb(&ctx->des, tmp, output);
mbedtls_des_free(&ctx->des);
return true;
}
bool mrtd_bac_mac(const uint8_t* data, size_t data_length, const uint8_t* key, uint8_t* output) {
// MAC
uint8_t mac[8];
uint8_t xormac[8];
uint8_t tmp[8];
mbedtls_des_context ctx;
mbedtls_des_init(&ctx);
mbedtls_des_setkey_enc(&ctx, key);
memset(mac, 0, 8);
for(size_t i = 0; i < data_length / 8; ++i) {
for(uint8_t j = 0; j < 8; ++j) {
xormac[j] = mac[j] ^ data[i * 8 + j];
}
mbedtls_des_crypt_ecb(&ctx, xormac, mac);
printf(
"DES1: %02X %02X %02X %02X %02X %02X %02X %02X\r\n",
xormac[0],
xormac[1],
xormac[2],
xormac[3],
xormac[4],
xormac[5],
xormac[6],
xormac[7]);
}
mbedtls_des_init(&ctx);
mbedtls_des_setkey_dec(&ctx, key + 8);
mbedtls_des_crypt_ecb(&ctx, mac, tmp);
mbedtls_des_init(&ctx);
mbedtls_des_setkey_enc(&ctx, key);
mbedtls_des_crypt_ecb(&ctx, tmp, output);
mbedtls_des_free(&ctx);
return true;
}
bool mrtd_bac_padded_mac(const uint8_t* data, size_t data_length, uint8_t* key, uint8_t* output) {
//TODO: bufferless padding should be possible with 3DES
size_t newlength = ((data_length + 8) / 8) * 8; // TODO: return this value too?
uint8_t padded[newlength]; //TODO: input parameter
memset(padded, 0, newlength);
memcpy(padded, data, data_length);
padded[data_length] = 0x80;
if(!mrtd_bac_mac(padded, newlength, key, output)) {
return false;
}
return true;
}
size_t mrtd_protect_apdu(
uint8_t cla,
uint8_t ins,
uint8_t p1,
uint8_t p2,
uint8_t lc,
const void* data,
int16_t le,
const uint8_t* key_enc,
const uint8_t* key_mac,
uint64_t ssc,
uint8_t* output) {
//TODO: max size on output?
size_t idx = 0;
// CC = MAC( SSC || CmdHeader || DO'87 )
mrtd_bac_mac_ctx mac_ctx;
mrtd_bac_mac_init(&mac_ctx, key_mac);
uint64_t ssc_n = htonll(ssc);
//printf("ssc: %016llx\r\n", ssc);
//printf("ssc_n: "); print_hex(ssc_n, 8); printf("\n");
mrtd_bac_mac_update(&mac_ctx, (uint8_t*)&ssc_n, 8);
// Mask cla
output[idx++] = cla | 0x0c;
output[idx++] = ins;
output[idx++] = p1;
output[idx++] = p2;
// Pad Header
mrtd_bac_mac_update(&mac_ctx, output, idx);
mrtd_bac_mac_pad(&mac_ctx);
size_t idx_lc = idx;
output[idx++] = 0xff; // place holder for Lc
// Build DO'87
// TODO: condition on data presence
// TODO: if ins is odd, use 0x85
if(lc > 0) {
size_t newlength = ((lc + 8) / 8) * 8;
uint8_t padded[newlength];
output[idx++] = 0x87; // Header
output[idx++] = newlength + 1; // Length
output[idx++] = 0x01; //TODO: check this value
memset(padded, 0, newlength);
memcpy(padded, data, lc);
padded[lc] = 0x80;
mrtd_bac_encrypt(padded, newlength, key_enc, output + idx);
idx += newlength;
}
// Build DO'97
if(le >= 0) {
output[idx++] = 0x97; // Header
output[idx++] = 0x01; // Length
output[idx++] = le;
}
mrtd_bac_mac_update(&mac_ctx, output + idx_lc + 1, idx - idx_lc - 1);
// Build DO'8E
// TODO: conditions?
{
output[idx++] = 0x8E; // Header
output[idx++] = 0x08; // Length
mrtd_bac_mac_finalize(&mac_ctx, output + idx);
idx += 8;
printf("MAC: ");
for(uint8_t i = 0; i < 8; ++i) {
printf("%02X ", output[idx - 8 + i]);
}
printf("\r\n");
}
output[idx_lc] = idx - idx_lc - 1; // Set Lc
output[idx++] = 0x00;
return idx;
}
EFFile EFNone = {.name = NULL, .file_id = 0x0000, .short_id = 0x00, .tag = 0x00};
const struct EFFormat EF = {
.ATR = {.name = "ATR", .file_id = 0x2F01, .short_id = 0x01},
.DIR = {.name = "DIR", .file_id = 0x2F00, .short_id = 0x1E},
.CardAccess = {.name = "CardAccess", .file_id = 0x011C, .short_id = 0x1C},
.CardSecurity = {.name = "CardSecurity", .file_id = 0x011D, .short_id = 0x1D},
.COM = {.name = "COM", .file_id = 0x011E, .short_id = 0x1E, .tag = 0x60},
.SOD = {.name = "SOD", .file_id = 0X011D, .short_id = 0X1D, .tag = 0x77},
.DG1 = {.name = "DG1", .file_id = 0X0101, .short_id = 0X01, .tag = 0x61},
.DG2 = {.name = "DG2", .file_id = 0X0102, .short_id = 0X02, .tag = 0x75},
.DG3 = {.name = "DG3", .file_id = 0X0103, .short_id = 0X03, .tag = 0x63},
.DG4 = {.name = "DG4", .file_id = 0X0104, .short_id = 0X04, .tag = 0x76},
.DG5 = {.name = "DG5", .file_id = 0X0105, .short_id = 0X05, .tag = 0x65},
.DG6 = {.name = "DG6", .file_id = 0X0106, .short_id = 0X06, .tag = 0x66},
.DG7 = {.name = "DG7", .file_id = 0X0107, .short_id = 0X07, .tag = 0x67},
.DG8 = {.name = "DG8", .file_id = 0X0108, .short_id = 0X08, .tag = 0x68},
.DG9 = {.name = "DG9", .file_id = 0X0109, .short_id = 0X09, .tag = 0x69},
.DG10 = {.name = "DG10", .file_id = 0X010A, .short_id = 0X0A, .tag = 0x6a},
.DG11 = {.name = "DG11", .file_id = 0X010B, .short_id = 0X0B, .tag = 0x6b},
.DG12 = {.name = "DG12", .file_id = 0X010C, .short_id = 0X0C, .tag = 0x6c},
.DG13 = {.name = "DG13", .file_id = 0X010D, .short_id = 0X0D, .tag = 0x6d},
.DG14 = {.name = "DG14", .file_id = 0X010E, .short_id = 0X0E, .tag = 0x6e},
.DG15 = {.name = "DG15", .file_id = 0X010F, .short_id = 0X0F, .tag = 0x6f},
.DG16 = {.name = "DG16", .file_id = 0X0110, .short_id = 0X10, .tag = 0x70},
};
struct AIDSet AID = {
.eMRTDApplication = {0xA0, 0x00, 0x00, 0x02, 0x47, 0x10, 0x01},
.TravelRecords = {0xA0, 0x00, 0x00, 0x02, 0x47, 0x20, 0x01},
.VisaRecords = {0xA0, 0x00, 0x00, 0x02, 0x47, 0x20, 0x02},
.AdditionalBiometrics = {0xA0, 0x00, 0x00, 0x02, 0x47, 0x20, 0x03},
};
const EFFile* mrtd_tag_to_file(uint8_t tag) {
//TODO: generate this code with macros?
switch(tag) {
case 0x60:
return &EF.COM;
case 0x77:
return &EF.SOD;
case 0x61:
return &EF.DG1;
case 0x75:
return &EF.DG2;
case 0x63:
return &EF.DG3;
case 0x76:
return &EF.DG4;
case 0x65:
return &EF.DG5;
case 0x66:
return &EF.DG6;
case 0x67:
return &EF.DG7;
case 0x68:
return &EF.DG8;
case 0x69:
return &EF.DG9;
case 0x6a:
return &EF.DG10;
case 0x6b:
return &EF.DG11;
case 0x6c:
return &EF.DG12;
case 0x6d:
return &EF.DG13;
case 0x6e:
return &EF.DG14;
case 0x6f:
return &EF.DG15;
case 0x70:
return &EF.DG16;
default:
return &EFNone;
}
};
int tlv_number(TlvInfo tlv) {
//TODO: negative numbers?
const uint8_t* str = tlv.value;
size_t length = tlv.length;
int value = 0;
while(length--) {
char c = *(str++);
if(c >= '0' && c <= '9') {
value = value * 10 + (c - '0');
} else {
//TODO: warning? return? crash?
}
}
return value;
}
-228
View File
@@ -1,228 +0,0 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include <string.h>
#include <mbedtls/des.h>
#include "../helpers/iso7816.h"
typedef struct {
uint8_t year;
uint8_t month;
uint8_t day;
} MrtdDate;
// NULL terminated document ID
#define MRTD_DOCNR_MAX_LENGTH 21
typedef enum {
MrtdAuthMethodNone,
MrtdAuthMethodAny,
MrtdAuthMethodBac,
MrtdAuthMethodPace,
} MrtdAuthMethod;
typedef enum {
MrtdTypeUnknown,
MrtdTypeTD1,
MrtdTypeTD2,
MrtdTypeTD3,
} MrtdType;
typedef struct {
MrtdAuthMethod method;
// BAC input fields
MrtdDate birth_date;
MrtdDate expiry_date;
char doc_number[MRTD_DOCNR_MAX_LENGTH];
//TODO: PACE
} MrtdAuthData;
typedef struct {
mbedtls_des_context des;
uint8_t key[16];
uint8_t mac[8];
uint8_t xormac[8];
uint8_t buffer_in[8];
uint8_t idx_in;
} mrtd_bac_mac_ctx;
typedef struct {
const char* name;
const uint8_t short_id;
const uint16_t file_id;
const uint8_t tag;
} EFFile;
struct EFFormat {
// Under Master File (MF)
const EFFile ATR;
const EFFile DIR;
const EFFile CardAccess;
const EFFile CardSecurity;
// Under LDS1 eMRTD Application
const EFFile COM;
const EFFile SOD;
const EFFile DG1;
const EFFile DG2;
const EFFile DG3;
const EFFile DG4;
const EFFile DG5;
const EFFile DG6;
const EFFile DG7;
const EFFile DG8;
const EFFile DG9;
const EFFile DG10;
const EFFile DG11;
const EFFile DG12;
const EFFile DG13;
const EFFile DG14;
const EFFile DG15;
const EFFile DG16;
};
extern const struct EFFormat EF;
typedef uint8_t AIDValue[7];
struct AIDSet {
AIDValue eMRTDApplication;
AIDValue TravelRecords;
AIDValue VisaRecords;
AIDValue AdditionalBiometrics;
};
extern struct AIDSet AID;
#define MAX_EFDIR_APPS 4
typedef struct {
AIDValue applications[MAX_EFDIR_APPS];
uint8_t applications_count;
} EF_DIR_contents;
#define MAX_EFCOM_TAGS 18
typedef struct {
uint16_t lds_version; // xxyy => xx.yy (major.minor)
uint32_t unicode_version; // aabbcc => aa.bb.cc (major.minor.release)
uint8_t tag_list[MAX_EFCOM_TAGS];
} EF_COM_contents;
typedef struct {
MrtdType type;
// ICAO9303 max sizes + 1 for 0-byte
uint8_t doctype[3];
uint8_t issuing_state[4];
uint8_t name[40];
MrtdDate birth_date;
uint8_t docnr[10];
uint8_t nationality[4];
uint8_t sex[2];
MrtdDate expiry_date;
} EF_DG1_contents;
typedef struct {
MrtdAuthData auth;
bool auth_success;
MrtdAuthMethod auth_method_used;
struct {
EF_DIR_contents EF_DIR;
EF_COM_contents EF_COM;
EF_DG1_contents DG1;
} files;
} MrtdData;
const char* mrtd_auth_method_string(MrtdAuthMethod method);
bool mrtd_auth_method_parse_string(MrtdAuthMethod* method, const char* str);
uint8_t mrtd_bac_check_digit(const char* input, const uint8_t length);
//TODO: swap order, all other functions have output last
void mrtd_print_date(char* output, MrtdDate* date);
void mrtd_parse_date(MrtdDate* date, const unsigned char* input);
bool mrtd_bac_get_kmrz(MrtdAuthData* auth, char* output, uint8_t output_size);
bool mrtd_bac_keys_from_seed(const uint8_t* kseed, uint8_t* ksenc, uint8_t* ksmac);
bool mrtd_bac_keys(MrtdAuthData* auth, uint8_t ksenc[16], uint8_t ksmac[16]);
bool mrtd_bac_encrypt(const uint8_t* data, size_t data_length, const uint8_t* key, uint8_t* output);
bool mrtd_bac_mac(const uint8_t* data, size_t data_length, const uint8_t* key, uint8_t* output);
bool mrtd_bac_mac_init(mrtd_bac_mac_ctx* ctx, const uint8_t key[16]);
bool mrtd_bac_mac_update(mrtd_bac_mac_ctx* ctx, const uint8_t* data, size_t data_length);
bool mrtd_bac_mac_finalize(mrtd_bac_mac_ctx* ctx, uint8_t output[8]);
bool mrtd_bac_mac_pad(mrtd_bac_mac_ctx* ctx); // TODO: internal only, remove from .h?
bool mrtd_bac_padded_mac(const uint8_t* data, size_t data_length, uint8_t* key, uint8_t* output);
bool mrtd_bac_decrypt(const uint8_t* data, size_t data_length, uint8_t* key, uint8_t* output);
bool mrtd_bac_decrypt_verify(
const uint8_t* data,
size_t data_length,
uint8_t* key_enc,
uint8_t* key_mac,
uint8_t* output);
//TODO: add some consts
uint16_t mrtd_bac_decrypt_verify_sm(
const uint8_t* data,
size_t data_length,
uint8_t* key_enc,
uint8_t* key_mac,
uint64_t ssc,
uint8_t* output,
size_t* output_written);
#include <machine/_endian.h>
#define htonll(x) ((((uint64_t)__htonl(x)) << 32) + __htonl((x) >> 32))
static __inline uint64_t mrtd_ssc_from_data(const uint8_t* rnd_ic, const uint8_t* rnd_ifd) {
#if _BYTE_ORDER == _LITTLE_ENDIAN
return (((uint64_t)rnd_ic[4] << 56) & 0xff00000000000000) |
(((uint64_t)rnd_ic[5] << 48) & 0x00ff000000000000) |
(((uint64_t)rnd_ic[6] << 40) & 0x0000ff0000000000) |
(((uint64_t)rnd_ic[7] << 32) & 0x000000ff00000000) |
(((uint64_t)rnd_ifd[4] << 24) & 0x00000000ff000000) |
(((uint64_t)rnd_ifd[5] << 16) & 0x0000000000ff0000) |
(((uint64_t)rnd_ifd[6] << 8) & 0x000000000000ff00) |
(((uint64_t)rnd_ifd[7]) & 0x00000000000000ff);
#else
#error Using untested code, please verify first!
return (*((uint64_t*)(rnd_ic + 4)) & 0xffffffff) + (*((uint64_t*)(rnd_ifd + 4)) * 0x100000000);
#endif
}
size_t mrtd_protect_apdu(
uint8_t cla,
uint8_t ins,
uint8_t p1,
uint8_t p2,
uint8_t lc,
const void* data,
int16_t le,
const uint8_t* key_enc,
const uint8_t* key_mac,
uint64_t ssc,
uint8_t* output);
int tlv_number(TlvInfo tlv);
const EFFile* mrtd_tag_to_file(uint8_t tag);
+17 -308
View File
@@ -58,8 +58,6 @@ static void nfc_device_prepare_format_string(NfcDevice* dev, FuriString* format_
furi_string_set(format_string, "Mifare Classic");
} else if(dev->format == NfcDeviceSaveFormatMifareDesfire) {
furi_string_set(format_string, "Mifare DESFire");
} else if(dev->format == NfcDeviceSaveFormatNfcV) {
furi_string_set(format_string, "ISO15693");
} else {
furi_string_set(format_string, "Unknown");
}
@@ -95,11 +93,6 @@ static bool nfc_device_parse_format_string(NfcDevice* dev, FuriString* format_st
dev->dev_data.protocol = NfcDeviceProtocolMifareDesfire;
return true;
}
if(furi_string_start_with_str(format_string, "ISO15693")) {
dev->format = NfcDeviceSaveFormatNfcV;
dev->dev_data.protocol = NfcDeviceProtocolNfcV;
return true;
}
return false;
}
@@ -657,281 +650,6 @@ bool nfc_device_load_mifare_df_data(FlipperFormat* file, NfcDevice* dev) {
return parsed;
}
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(NfcVData));
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(NfcVData));
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(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_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(NfcVData));
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,
"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;
}
} 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, 0, 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;
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;
}
} 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;
@@ -1379,28 +1097,23 @@ bool nfc_device_save(NfcDevice* dev, const char* dev_name) {
if(!flipper_format_write_header_cstr(file, nfc_file_header, nfc_file_version)) break;
// Write nfc device type
if(!flipper_format_write_comment_cstr(
file, "Nfc device type can be UID, Mifare Ultralight, Mifare Classic or ISO15693"))
file, "Nfc device type can be UID, Mifare Ultralight, Mifare Classic, Bank card"))
break;
nfc_device_prepare_format_string(dev, temp_str);
if(!flipper_format_write_string(file, "Device type", temp_str)) break;
// Write UID
if(!flipper_format_write_comment_cstr(file, "UID is common for all formats")) break;
// Write UID, ATQA, SAK
if(!flipper_format_write_comment_cstr(file, "UID, ATQA and SAK are common for all formats"))
break;
if(!flipper_format_write_hex(file, "UID", data->uid, data->uid_len)) break;
if(dev->format != NfcDeviceSaveFormatNfcV) {
// Save ATQA in MSB order for correct companion apps display
uint8_t atqa[2] = {data->atqa[1], data->atqa[0]};
if(!flipper_format_write_hex(file, "ATQA", atqa, 2)) break;
if(!flipper_format_write_hex(file, "SAK", &data->sak, 1)) break;
}
// Save ATQA in MSB order for correct companion apps display
uint8_t atqa[2] = {data->atqa[1], data->atqa[0]};
if(!flipper_format_write_hex(file, "ATQA", atqa, 2)) break;
if(!flipper_format_write_hex(file, "SAK", &data->sak, 1)) break;
// Save more data if necessary
if(dev->format == NfcDeviceSaveFormatMifareUl) {
if(!nfc_device_save_mifare_ul_data(file, dev)) break;
} else if(dev->format == NfcDeviceSaveFormatMifareDesfire) {
if(!nfc_device_save_mifare_df_data(file, dev)) break;
} else if(dev->format == NfcDeviceSaveFormatNfcV) {
if(!nfc_device_save_nfcv_data(file, dev)) break;
} else if(dev->format == NfcDeviceSaveFormatBankCard) {
if(!nfc_device_save_bank_card_data(file, dev)) break;
} else if(dev->format == NfcDeviceSaveFormatMifareClassic) {
@@ -1477,20 +1190,18 @@ static bool nfc_device_load_data(NfcDevice* dev, FuriString* path, bool show_dia
if(!nfc_device_parse_format_string(dev, temp_str)) break;
// Read and parse UID, ATQA and SAK
if(!flipper_format_get_value_count(file, "UID", &data_cnt)) break;
if(!(data_cnt == 4 || data_cnt == 7 || data_cnt == 8)) break;
if(!(data_cnt == 4 || data_cnt == 7)) break;
data->uid_len = data_cnt;
if(!flipper_format_read_hex(file, "UID", data->uid, data->uid_len)) break;
if(dev->format != NfcDeviceSaveFormatNfcV) {
if(version == version_with_lsb_atqa) {
if(!flipper_format_read_hex(file, "ATQA", data->atqa, 2)) break;
} else {
uint8_t atqa[2] = {};
if(!flipper_format_read_hex(file, "ATQA", atqa, 2)) break;
data->atqa[0] = atqa[1];
data->atqa[1] = atqa[0];
}
if(!flipper_format_read_hex(file, "SAK", &data->sak, 1)) break;
if(version == version_with_lsb_atqa) {
if(!flipper_format_read_hex(file, "ATQA", data->atqa, 2)) break;
} else {
uint8_t atqa[2] = {};
if(!flipper_format_read_hex(file, "ATQA", atqa, 2)) break;
data->atqa[0] = atqa[1];
data->atqa[1] = atqa[0];
}
if(!flipper_format_read_hex(file, "SAK", &data->sak, 1)) break;
// Load CUID
uint8_t* cuid_start = data->uid;
if(data->uid_len == 7) {
@@ -1505,8 +1216,6 @@ static bool nfc_device_load_data(NfcDevice* dev, FuriString* path, bool show_dia
if(!nfc_device_load_mifare_classic_data(file, dev)) break;
} else if(dev->format == NfcDeviceSaveFormatMifareDesfire) {
if(!nfc_device_load_mifare_df_data(file, dev)) break;
} else if(dev->format == NfcDeviceSaveFormatNfcV) {
if(!nfc_device_load_nfcv_data(file, dev)) break;
} else if(dev->format == NfcDeviceSaveFormatBankCard) {
if(!nfc_device_load_bank_card_data(file, dev)) break;
}
-7
View File
@@ -8,11 +8,9 @@
#include <furi_hal_nfc.h>
#include <lib/nfc/helpers/mf_classic_dict.h>
#include <lib/nfc/protocols/emv.h>
#include <lib/nfc/protocols/mrtd.h>
#include <lib/nfc/protocols/mifare_ultralight.h>
#include <lib/nfc/protocols/mifare_classic.h>
#include <lib/nfc/protocols/mifare_desfire.h>
#include <lib/nfc/protocols/nfcv.h>
#ifdef __cplusplus
extern "C" {
@@ -30,11 +28,9 @@ typedef void (*NfcLoadingCallback)(void* context, bool state);
typedef enum {
NfcDeviceProtocolUnknown,
NfcDeviceProtocolEMV,
NfcDeviceProtocolMRTD,
NfcDeviceProtocolMifareUl,
NfcDeviceProtocolMifareClassic,
NfcDeviceProtocolMifareDesfire,
NfcDeviceProtocolNfcV
} NfcProtocol;
typedef enum {
@@ -43,7 +39,6 @@ typedef enum {
NfcDeviceSaveFormatMifareUl,
NfcDeviceSaveFormatMifareClassic,
NfcDeviceSaveFormatMifareDesfire,
NfcDeviceSaveFormatNfcV,
} NfcDeviceSaveFormat;
typedef struct {
@@ -76,11 +71,9 @@ typedef struct {
};
union {
EmvData emv_data;
MrtdData mrtd_data;
MfUltralightData mf_ul_data;
MfClassicData mf_classic_data;
MifareDesfireData mf_df_data;
NfcVData nfcv_data;
};
FuriString* parsed_data;
} NfcDeviceData;
+4 -339
View File
@@ -16,7 +16,6 @@ NfcWorker* nfc_worker_alloc() {
nfc_worker->callback = NULL;
nfc_worker->context = NULL;
nfc_worker->event_data = NULL;
nfc_worker->storage = furi_record_open(RECORD_STORAGE);
// Initialize rfal
@@ -46,10 +45,6 @@ NfcWorkerState nfc_worker_get_state(NfcWorker* nfc_worker) {
return nfc_worker->state;
}
void* nfc_worker_get_event_data(NfcWorker* nfc_worker) {
return nfc_worker->event_data;
}
void nfc_worker_start(
NfcWorker* nfc_worker,
NfcWorkerState state,
@@ -116,12 +111,6 @@ int32_t nfc_worker_task(void* context) {
nfc_worker_mf_classic_dict_attack(nfc_worker);
} else if(nfc_worker->state == NfcWorkerStateAnalyzeReader) {
nfc_worker_analyze_reader(nfc_worker);
} else if(nfc_worker->state == NfcWorkerStateNfcVEmulate) {
nfc_worker_emulate_nfcv(nfc_worker);
} else if(nfc_worker->state == NfcWorkerStateNfcVUnlock) {
nfc_worker_nfcv_unlock(nfc_worker);
} else if(nfc_worker->state == NfcWorkerStateNfcVUnlockAndSave) {
nfc_worker_nfcv_unlock(nfc_worker);
}
furi_hal_nfc_sleep();
nfc_worker_change_state(nfc_worker, NfcWorkerStateReady);
@@ -129,179 +118,6 @@ int32_t nfc_worker_task(void* context) {
return 0;
}
static bool nfc_worker_read_nfcv_content(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;
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_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_hal_console_printf("Detect presence\r\n");
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_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, just save */
if(nfc_worker->state == NfcWorkerStateNfcVUnlockAndSave) {
NfcVReader reader = {};
if(!nfcv_read_card(&reader, &nfc_worker->dev_data->nfc_data, nfcv_data)) {
furi_hal_console_printf(" => failed, wait for chip to disappear.\r\n");
snprintf(nfcv_data->error, sizeof(nfcv_data->error), "Read card\nfailed");
nfc_worker->callback(NfcWorkerEventWrongCardDetected, nfc_worker->context);
} else {
furi_hal_console_printf(" => success, wait for chip to disappear.\r\n");
nfc_worker->callback(NfcWorkerEventCardDetected, nfc_worker->context);
}
} else {
furi_hal_console_printf(" => success, wait for chip to disappear.\r\n");
nfc_worker->callback(NfcWorkerEventCardDetected, nfc_worker->context);
}
while(slix_get_random(NULL) == ERR_NONE) {
furi_delay_ms(100);
}
furi_hal_console_printf(
" => 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_hal_console_printf(" chip is invisible, unlocking\r\n");
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_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_get_random(nfcv_data) != ERR_NONE) {
furi_hal_console_printf(" reset failed\r\n");
}
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_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_get_random(NULL) == ERR_NONE) {
furi_delay_ms(100);
}
}
}
} else {
nfc_worker->callback(NfcWorkerEventNoCardDetected, nfc_worker->context);
}
furi_hal_nfc_ll_txrx_off();
furi_hal_nfc_sleep();
furi_delay_ms(100);
}
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
reader_analyzer_stop(nfc_worker->reader_analyzer);
}
}
static bool nfc_worker_read_mf_ultralight(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) {
bool read_success = false;
MfUltralightReader reader = {};
@@ -476,49 +292,6 @@ static bool nfc_worker_read_bank_card(NfcWorker* nfc_worker, FuriHalNfcTxRxConte
return read_success;
}
static bool nfc_worker_read_mrtd(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) {
bool read_success = false;
MrtdData* mrtd_data = &nfc_worker->dev_data->mrtd_data;
MrtdApplication* mrtd_app = mrtd_alloc_init(tx_rx, mrtd_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 {
// Read passport
if(!furi_hal_nfc_detect(&nfc_worker->dev_data->nfc_data, 300)) break;
//TODO: try select eMRTDApp first, but when PACE, read CardAccess first!
if(!mrtd_select_app(mrtd_app, AID.eMRTDApplication)) break; // Passport app not selected
if(mrtd_data->auth.method == MrtdAuthMethodNone) {
// Selected the passport app, but auth. not selected
// Successfully read what we could
read_success = true;
break;
}
if(!mrtd_authenticate(mrtd_app)) {
// At least we're reading an MRTD and should the app switch to the NFC scenes
read_success = true;
break; // Authentication failed
}
mrtd_read_parse_file(mrtd_app, EF.COM);
mrtd_read_parse_file(mrtd_app, EF.DG1);
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_nfca(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) {
FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data;
@@ -544,24 +317,11 @@ static bool nfc_worker_read_nfca(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* t
card_read = true;
} else if(nfc_data->interface == FuriHalNfcInterfaceIsoDep) {
FURI_LOG_I(TAG, "ISO14443-4 card detected");
//TODO: thoughts on improving logic/readability here?
do {
FURI_LOG_D(TAG, "Try reading EMV");
if(nfc_worker_read_bank_card(nfc_worker, tx_rx)) {
nfc_worker->dev_data->protocol = NfcDeviceProtocolEMV;
break;
}
furi_hal_nfc_sleep(); // Needed between checks
FURI_LOG_D(TAG, "Try reading MRTD");
if(nfc_worker_read_mrtd(nfc_worker, tx_rx)) {
nfc_worker->dev_data->protocol = NfcDeviceProtocolMRTD;
break;
}
nfc_worker->dev_data->protocol = NfcDeviceProtocolEMV;
if(!nfc_worker_read_bank_card(nfc_worker, tx_rx)) {
FURI_LOG_I(TAG, "Unknown card. Save UID");
nfc_worker->dev_data->protocol = NfcDeviceProtocolUnknown;
} while(false);
}
card_read = true;
} else {
nfc_worker->dev_data->protocol = NfcDeviceProtocolUnknown;
@@ -571,47 +331,6 @@ static bool nfc_worker_read_nfca(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* t
return card_read;
}
static bool nfc_worker_read_nfcb(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) {
FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data;
bool card_read = false;
furi_hal_nfc_sleep();
if(nfc_data->interface == FuriHalNfcInterfaceIsoDep) {
FURI_LOG_I(TAG, "ISO14443-4B card detected");
//TODO: thoughts on improving logic/readability here?
do {
FURI_LOG_D(TAG, "Try reading MRTD");
if(nfc_worker_read_mrtd(nfc_worker, tx_rx)) {
nfc_worker->dev_data->protocol = NfcDeviceProtocolMRTD;
break;
}
FURI_LOG_I(TAG, "Unknown card. Save UID");
nfc_worker->dev_data->protocol = NfcDeviceProtocolUnknown;
} while(false);
card_read = true;
} else {
nfc_worker->dev_data->protocol = NfcDeviceProtocolUnknown;
card_read = true;
}
return card_read;
}
static bool nfc_worker_read_nfcv(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) {
furi_assert(nfc_worker);
furi_assert(tx_rx);
bool card_read = false;
furi_hal_nfc_sleep();
/* until here the UID field is reversed from the reader IC.
we will read it here again and it will get placed in the right order. */
card_read = nfc_worker_read_nfcv_content(nfc_worker, tx_rx);
return card_read;
}
void nfc_worker_read(NfcWorker* nfc_worker) {
furi_assert(nfc_worker);
furi_assert(nfc_worker->callback);
@@ -642,9 +361,6 @@ void nfc_worker_read(NfcWorker* nfc_worker) {
} else if(dev_data->protocol == NfcDeviceProtocolEMV) {
event = NfcWorkerEventReadBankCard;
break;
} else if(dev_data->protocol == NfcDeviceProtocolMRTD) {
event = NfcWorkerEventReadPassport;
break;
} else if(dev_data->protocol == NfcDeviceProtocolUnknown) {
event = NfcWorkerEventReadUidNfcA;
break;
@@ -656,27 +372,13 @@ void nfc_worker_read(NfcWorker* nfc_worker) {
}
}
} else if(nfc_data->type == FuriHalNfcTypeB) {
if(nfc_worker_read_nfcb(nfc_worker, &tx_rx)) {
if(dev_data->protocol == NfcDeviceProtocolMRTD) {
event = NfcWorkerEventReadPassport;
break;
}
}
event = NfcWorkerEventReadUidNfcB;
break;
} else if(nfc_data->type == FuriHalNfcTypeF) {
event = NfcWorkerEventReadUidNfcF;
break;
} else if(nfc_data->type == FuriHalNfcTypeV) {
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;
}
event = NfcWorkerEventReadNfcV;
event = NfcWorkerEventReadUidNfcV;
break;
}
} else {
@@ -794,32 +496,6 @@ void nfc_worker_emulate_uid(NfcWorker* nfc_worker) {
}
}
void nfc_worker_emulate_nfcv(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, 50)) {
if(nfc_worker->callback) {
nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context);
}
}
furi_delay_ms(0);
}
nfcv_emu_deinit(nfcv_data);
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
reader_analyzer_stop(nfc_worker->reader_analyzer);
}
}
void nfc_worker_emulate_apdu(NfcWorker* nfc_worker) {
FuriHalNfcTxRxContext tx_rx = {};
FuriHalNfcDevData params = {
@@ -882,15 +558,6 @@ void nfc_worker_emulate_mf_ultralight(NfcWorker* nfc_worker) {
mf_ul_prepare_emulation_response,
&emulator,
5000);
// Check if there was an auth attempt
if(emulator.auth_attempted) {
nfc_worker->event_data = &emulator.auth_attempt;
if(nfc_worker->callback) {
nfc_worker->callback(NfcWorkerEventMfUltralightPwdAuth, nfc_worker->context);
}
emulator.auth_attempted = false;
nfc_worker->event_data = NULL;
}
// Check if data was modified
if(emulator.data_changed) {
nfc_worker->dev_data->mf_ul_data = emulator.data;
@@ -1091,7 +758,6 @@ void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker) {
break;
}
nfc_worker_mf_classic_key_attack(nfc_worker, key, &tx_rx, i + 1);
deactivated = true;
}
furi_hal_nfc_sleep();
deactivated = true;
@@ -1112,7 +778,6 @@ void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker) {
mf_classic_set_key_found(data, i, MfClassicKeyB, key);
nfc_worker->callback(NfcWorkerEventFoundKeyB, nfc_worker->context);
nfc_worker_mf_classic_key_attack(nfc_worker, key, &tx_rx, i + 1);
deactivated = true;
}
deactivated = true;
} else {
-11
View File
@@ -18,9 +18,6 @@ typedef enum {
NfcWorkerStateReadMfUltralightReadAuth,
NfcWorkerStateMfClassicDictAttack,
NfcWorkerStateAnalyzeReader,
NfcWorkerStateNfcVEmulate,
NfcWorkerStateNfcVUnlock,
NfcWorkerStateNfcVUnlockAndSave,
// Debug
NfcWorkerStateEmulateApdu,
NfcWorkerStateField,
@@ -42,9 +39,7 @@ typedef enum {
NfcWorkerEventReadMfClassicDone,
NfcWorkerEventReadMfClassicLoadKeyCache,
NfcWorkerEventReadMfClassicDictAttackRequired,
NfcWorkerEventReadNfcV,
NfcWorkerEventReadBankCard,
NfcWorkerEventReadPassport,
// Nfc worker common events
NfcWorkerEventSuccess,
@@ -75,8 +70,6 @@ typedef enum {
// Mifare Ultralight events
NfcWorkerEventMfUltralightPassKey, // NFC worker requesting manual key
NfcWorkerEventMfUltralightPwdAuth, // Reader sent auth command
NfcWorkerEventNfcVPassKey, // NFC worker requesting manual key
} NfcWorkerEvent;
typedef bool (*NfcWorkerCallback)(NfcWorkerEvent event, void* context);
@@ -85,8 +78,6 @@ NfcWorker* nfc_worker_alloc();
NfcWorkerState nfc_worker_get_state(NfcWorker* nfc_worker);
void* nfc_worker_get_event_data(NfcWorker* nfc_worker);
void nfc_worker_free(NfcWorker* nfc_worker);
void nfc_worker_start(
@@ -97,5 +88,3 @@ void nfc_worker_start(
void* context);
void nfc_worker_stop(NfcWorker* nfc_worker);
void nfc_worker_nfcv_unlock(NfcWorker* nfc_worker);
void nfc_worker_emulate_nfcv(NfcWorker* nfc_worker);
-4
View File
@@ -7,14 +7,11 @@
#include <lib/nfc/protocols/nfc_util.h>
#include <lib/nfc/protocols/emv.h>
#include <lib/nfc/protocols/mrtd.h>
#include <lib/nfc/protocols/mifare_common.h>
#include <lib/nfc/protocols/mifare_ultralight.h>
#include <lib/nfc/protocols/mifare_classic.h>
#include <lib/nfc/protocols/mifare_desfire.h>
#include <lib/nfc/protocols/nfca.h>
#include <lib/nfc/protocols/nfcv.h>
#include <lib/nfc/protocols/slix.h>
#include <lib/nfc/helpers/reader_analyzer.h>
struct NfcWorker {
@@ -26,7 +23,6 @@ struct NfcWorker {
NfcWorkerCallback callback;
void* context;
void* event_data;
NfcWorkerState state;
-4
View File
@@ -659,7 +659,6 @@ void mf_classic_read_sector(FuriHalNfcTxRxContext* tx_rx, MfClassicData* data, u
for(size_t i = start_block; i < start_block + total_blocks; i++) {
if(!mf_classic_is_block_read(data, i)) {
if(!mf_classic_auth(tx_rx, i, key, MfClassicKeyA, &crypto, false, 0)) continue;
if(mf_classic_read_block(tx_rx, &crypto, i, &block_tmp)) {
mf_classic_set_block_read(data, i, &block_tmp);
blocks_read++;
@@ -676,7 +675,6 @@ void mf_classic_read_sector(FuriHalNfcTxRxContext* tx_rx, MfClassicData* data, u
blocks_read++;
}
}
furi_hal_nfc_sleep();
} else {
blocks_read++;
}
@@ -699,7 +697,6 @@ void mf_classic_read_sector(FuriHalNfcTxRxContext* tx_rx, MfClassicData* data, u
for(size_t i = start_block; i < start_block + total_blocks; i++) {
if(!mf_classic_is_block_read(data, i)) {
if(!mf_classic_auth(tx_rx, i, key, MfClassicKeyB, &crypto, false, 0)) continue;
if(mf_classic_read_block(tx_rx, &crypto, i, &block_tmp)) {
mf_classic_set_block_read(data, i, &block_tmp);
blocks_read++;
@@ -716,7 +713,6 @@ void mf_classic_read_sector(FuriHalNfcTxRxContext* tx_rx, MfClassicData* data, u
blocks_read++;
}
}
furi_hal_nfc_sleep();
} else {
blocks_read++;
}
+1 -2
View File
@@ -9,7 +9,6 @@
#define MF_MINI_TOTAL_SECTORS_NUM (5)
#define MF_CLASSIC_1K_TOTAL_SECTORS_NUM (16)
#define MF_CLASSIC_4K_TOTAL_SECTORS_NUM (40)
#define MF_MINI_TOTAL_SECTORS_NUM (5)
#define MF_CLASSIC_SECTORS_MAX (40)
#define MF_CLASSIC_BLOCKS_IN_SECTOR_MAX (16)
@@ -20,9 +19,9 @@
#define MF_CLASSIC_ACCESS_BYTES_SIZE (4)
typedef enum {
MfClassicTypeMini,
MfClassicType1k,
MfClassicType4k,
MfClassicTypeMini,
} MfClassicType;
typedef enum {
-758
View File
@@ -1,758 +0,0 @@
#include <furi_hal_random.h>
#include <storage/storage.h>
#include <dialogs/dialogs.h>
#include <nfc/nfc_device.h>
#include "../helpers/iso7816.h"
#include "mrtd.h"
#define TAG "Mrtd"
//TODO: Check EF.DIR first? Before LDS1
//TODO: ICAO 9303 p11 §4.2 steps
//- Read EF.CardAccess (REQUIRED)
// If not available or does not contain PACE params, try BAC
//- Read EF.DIR (OPTIONAL)
// Check list of applications present
//- PACE (CONDITIONAL)
//- BAC (CONDITIONAL)
//TODO: idea - generalize ISO7816 reading. List available apps
#define num_elements(A) (sizeof(A) / sizeof(A[0]))
static const char* mrtd_auth_file_header = "Flipper MRTD params";
static const uint32_t mrtd_auth_file_version = 1;
static void hexdump(FuriLogLevel level, char* prefix, void* data, size_t length) {
if(furi_log_get_level() >= level) {
printf("%s ", prefix);
for(size_t i = 0; i < length; i++) {
printf("%02X ", ((uint8_t*)data)[i]);
}
printf("\r\n");
}
}
static void mrtd_trace(MrtdApplication* app) {
FuriHalNfcTxRxContext* tx_rx = app->tx_rx;
if(furi_log_get_level() == FuriLogLevelTrace) {
printf("TX: ");
for(size_t i = 0; i < tx_rx->tx_bits / 8; i++) {
printf("%02X ", tx_rx->tx_data[i]);
}
printf("\r\nRX: ");
for(size_t i = 0; i < tx_rx->rx_bits / 8; i++) {
printf("%02X ", tx_rx->rx_data[i]);
}
printf("\r\n");
}
}
uint16_t mrtd_decode_response(uint8_t* buffer, size_t len) {
// Last two bytes are return code
return (buffer[len - 2] << 8) | buffer[len - 1];
}
//TODO: rename to transceive?
//TODO: PRIO output and output written writing seems to crash flipper, sometimes
bool mrtd_send_apdu(
MrtdApplication* app,
uint8_t cla,
uint8_t ins,
uint8_t p1,
uint8_t p2,
uint8_t lc,
const void* data,
int16_t le,
uint8_t* output,
size_t* output_written) {
FuriHalNfcTxRxContext* tx_rx = app->tx_rx;
size_t idx = 0;
FURI_LOG_T(TAG, "Send APDU, lc: %d, le: %d", lc, le);
if(app->secure_messaging) {
app->ssc_long++;
idx = mrtd_protect_apdu(
cla, ins, p1, p2, lc, data, le, app->ksenc, app->ksmac, app->ssc_long, tx_rx->tx_data);
} else {
tx_rx->tx_data[idx++] = cla;
tx_rx->tx_data[idx++] = ins;
tx_rx->tx_data[idx++] = p1;
tx_rx->tx_data[idx++] = p2;
if(lc > 0) {
tx_rx->tx_data[idx++] = lc;
memcpy(tx_rx->tx_data + idx, data, lc);
idx += lc;
}
if(le >= 0) {
tx_rx->tx_data[idx++] = le & 0xff;
}
}
tx_rx->tx_bits = idx * 8;
tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault;
//TODO: timeout as param?
if(furi_hal_nfc_tx_rx(tx_rx, 300)) {
mrtd_trace(app);
uint16_t ret_code = mrtd_decode_response(tx_rx->rx_data, tx_rx->rx_bits / 8);
if(app->secure_messaging && ret_code == 0x9000) {
app->ssc_long++;
ret_code = mrtd_bac_decrypt_verify_sm(
tx_rx->rx_data,
tx_rx->rx_bits / 8 - 2,
app->ksenc,
app->ksmac,
app->ssc_long,
output,
output_written);
//ret_code = 0x1337; //TODO: remove PRIO
}
//TODO: handle other return codes?
if(ret_code == 0x9000) {
if(!app->secure_messaging && le > 0) {
// Secure Messaging sets output while decrypting
output_written = memcpy(output, tx_rx->rx_data, le);
}
return true;
} else {
FURI_LOG_I(TAG, "APDU answer is not 0x9000, but 0x%04X", ret_code);
switch(ret_code) {
case 0x6987:
FURI_LOG_I(TAG, "'expected secure messaging data objects are missing'");
app->secure_messaging = false;
break;
case 0x6988:
FURI_LOG_I(TAG, "'secure messaging data objects are incorrect'");
app->secure_messaging = false;
break;
case 0xff01:
//CUSTOM ERROR CODE from mrtd_helpers.c
FURI_LOG_I(TAG, "'invalid padding'");
break;
case 0xff02:
//CUSTOM ERROR CODE from mrtd_helpers.c
FURI_LOG_I(TAG, "'verify failed'");
break;
}
return false;
}
} else {
FURI_LOG_D(TAG, "Sending - failed");
}
return false;
}
//TODO: rename commands to "mrtd_cmd_..."
bool mrtd_select_app(MrtdApplication* app, AIDValue aid) {
FURI_LOG_D(
TAG,
"Send select App: %02X %02X %02X %02X %02X %02X %02X",
aid[0],
aid[1],
aid[2],
aid[3],
aid[4],
aid[5],
aid[6]);
if(!mrtd_send_apdu(app, 0x00, 0xA4, 0x04, 0x0C, 0x07, aid, -1, NULL, NULL)) {
FURI_LOG_W(TAG, "Failed select App");
return false;
}
return true;
}
bool mrtd_get_challenge(MrtdApplication* app, uint8_t challenge[8]) {
FURI_LOG_D(TAG, "Send Get Challenge");
size_t chal_size;
if(!mrtd_send_apdu(app, 0x00, 0x84, 0x00, 0x00, 0x00, NULL, 0x08, challenge, &chal_size)) {
FURI_LOG_W(TAG, "Failed get challenge");
return false;
}
return true;
}
bool mrtd_external_authenticate(
MrtdApplication* app,
uint8_t* cmd_data,
size_t cmd_size,
uint8_t* out_data,
size_t out_size) {
furi_assert(cmd_size == 0x28);
furi_assert(out_size >= 0x28);
FURI_LOG_D(TAG, "Send External Authenticate");
if(!mrtd_send_apdu(
app, 0x00, 0x82, 0x00, 0x00, cmd_size, cmd_data, 0x28, out_data, &out_size)) {
FURI_LOG_W(TAG, "Failed External Authenticate");
return false;
}
return true;
}
bool mrtd_select_file(MrtdApplication* app, EFFile file) {
uint8_t data[] = {file.file_id >> 8, file.file_id & 0xff};
FURI_LOG_D(TAG, "Send select EF: %s (0x%04X)", file.name, file.file_id);
if(!mrtd_send_apdu(app, 0x00, 0xA4, 0x02, 0x0C, 0x02, data, -1, NULL, NULL)) {
FURI_LOG_E(TAG, "Failed select EF 0x%04X", file.file_id);
return false;
}
return true;
}
size_t mrtd_read_binary(MrtdApplication* app, uint8_t* buffer, size_t bufsize, size_t offset) {
UNUSED(buffer);
UNUSED(bufsize);
// 00 B0 offst -
FURI_LOG_D(TAG, "Read binary, offset: %d", offset);
//TODO: read first 4 bytes, determine length, iterate through file
//TODO: limit reading/buffer fill to max bufsize
//TODO: test with max_read = bufsize (value !0, > file size)
int16_t max_read = 0; // 0 = 'everything', -1 = 'nothing', >0 = amount of bytes
size_t buf_written = 0;
if(!mrtd_send_apdu(
app, 0x00, 0xB0, offset >> 8, offset & 0xff, 0x00, NULL, max_read, buffer, &buf_written)) {
FURI_LOG_E(TAG, "Failed to read");
return 0;
}
FURI_LOG_D(TAG, "buf_written: %d\n", buf_written);
return buf_written;
}
//TODO: use short id to read, because it's mandatory for eMRTD
//TODO: check for support of extended length in EF.ATR/INFO, see ISO7816-4
void mrtd_read_dump(MrtdApplication* app, EFFile file) {
FURI_LOG_D(TAG, "Read and dump %s:", file.name);
if(!mrtd_select_file(app, file)) {
return;
}
uint8_t data[2048];
size_t read = 0;
size_t offset = 0;
do {
read = mrtd_read_binary(app, data, sizeof(data), offset);
offset += read;
hexdump(FuriLogLevelDebug, "Data:", data, read);
} while(read > 0);
}
bool parse_ef_dir(EF_DIR_contents* EF_DIR, const uint8_t* data, size_t length) {
size_t offset = 0;
uint8_t app_idx = 0;
memset(EF_DIR->applications, 0x00, sizeof(EF_DIR->applications));
EF_DIR->applications_count = 0;
while(offset < length) {
TlvInfo tlv = iso7816_tlv_parse(data + offset);
if(tlv.tag != 0x61 || tlv.length != 0x09) {
FURI_LOG_E(
TAG,
"Invalid EF.DIR, tag at offset %d must be '61' and length 9. Got '%02X' and %d",
offset,
tlv.tag,
tlv.length);
return false;
}
tlv = iso7816_tlv_parse(tlv.value);
if(tlv.tag != 0x4F || tlv.length != 0x07) {
FURI_LOG_E(
TAG, "Invalid EF.DIR, subtag at offset %d must be '4F' and length 7", offset);
return false;
}
memcpy(EF_DIR->applications[app_idx], tlv.value, tlv.length);
EF_DIR->applications_count = ++app_idx;
offset = tlv.next - data;
}
//TODO: remove testing block:
FURI_LOG_D(TAG, "EF.DIR applications: %d", EF_DIR->applications_count);
if(furi_log_get_level() >= FuriLogLevelDebug) {
for(uint8_t i = 0; i < EF_DIR->applications_count; ++i) {
printf("- ");
for(uint8_t n = 0; n < sizeof(AIDValue); ++n) {
printf("%02X ", EF_DIR->applications[i][n]);
}
printf("\r\n");
}
}
return true;
}
bool parse_ef_com(EF_COM_contents* EF_COM, const uint8_t* data, size_t length) {
uint16_t lds_tag_path[] = {0x60, 0x5f01};
uint16_t unicode_tag_path[] = {0x60, 0x5f36};
uint16_t tags_tag_path[] = {0x60, 0x5c};
TlvInfo tlv_lds_version =
iso7816_tlv_select(data, length, lds_tag_path, num_elements(lds_tag_path));
if(!tlv_lds_version.tag) {
FURI_LOG_W(TAG, "EF.COM LDS version not found");
return false;
}
EF_COM->lds_version = tlv_number(tlv_lds_version);
TlvInfo tlv_unicode_version =
iso7816_tlv_select(data, length, unicode_tag_path, num_elements(unicode_tag_path));
if(!tlv_unicode_version.tag) {
FURI_LOG_W(TAG, "EF.COM Unicode info not found!");
return false;
}
EF_COM->unicode_version = tlv_number(tlv_unicode_version);
TlvInfo tlv_tag_list =
iso7816_tlv_select(data, length, tags_tag_path, num_elements(tags_tag_path));
if(!tlv_tag_list.tag) {
FURI_LOG_W(TAG, "EF.CO Tag List not found!");
return false;
}
for(size_t i = 0; i < MAX_EFCOM_TAGS; ++i) {
EF_COM->tag_list[i] = (i < tlv_tag_list.length) ? tlv_tag_list.value[i] : 0x00;
}
return true;
}
void mrzcpy(uint8_t* dest, const uint8_t* src, size_t* idx, size_t n) {
//FURI_LOG_D(TAG, "mrzcpy %d: %.*s", n, n, src + *idx);
//memcpy(dest, src + *idx, n);
for(size_t i = 0; i < n; ++i) {
uint8_t c = src[i + *idx];
if(c == '<') {
c = ' ';
}
dest[i] = c;
}
dest[n] = 0x00;
*idx += n;
}
bool parse_ef_dg1(EF_DG1_contents* DG1, const uint8_t* data, size_t length) {
TlvInfo tlv_mrz = iso7816_tlv_select(data, length, (uint16_t[]){0x61, 0x5f1f}, 2);
if(!tlv_mrz.tag) {
FURI_LOG_W(TAG, "DG1, unexpected content. Could not find tag 0x61, 0x5f1f");
return false;
}
const uint8_t* mrz = tlv_mrz.value;
size_t idx = 0;
switch(tlv_mrz.length) {
case 90:
DG1->type = MrtdTypeTD1;
mrzcpy(DG1->doctype, mrz, &idx, 2);
mrzcpy(DG1->issuing_state, mrz, &idx, 3);
mrzcpy(DG1->docnr, mrz, &idx, 9);
idx += 1; // docnr check digit
idx += 15; // optional data
mrtd_parse_date(&DG1->birth_date, mrz + idx);
idx += 6; // birth_date
idx += 1; // birth date check digit
mrzcpy(DG1->sex, mrz, &idx, 1);
mrtd_parse_date(&DG1->expiry_date, mrz + idx);
idx += 6; // expiry_date
idx += 1; // expiry date check digit
mrzcpy(DG1->nationality, mrz, &idx, 3);
idx += 11; // optional data
idx += 1; // check digit
mrzcpy(DG1->name, mrz, &idx, 30);
// 30 + 30 + 30
break;
case 72:
DG1->type = MrtdTypeTD2;
mrzcpy(DG1->doctype, mrz, &idx, 2);
mrzcpy(DG1->issuing_state, mrz, &idx, 3);
mrzcpy(DG1->name, mrz, &idx, 31);
mrzcpy(DG1->docnr, mrz, &idx, 9);
idx += 1; // docnr check digit
mrzcpy(DG1->nationality, mrz, &idx, 3);
mrtd_parse_date(&DG1->birth_date, mrz + idx);
idx += 6; // birth_date
idx += 1; // birth date check digit
mrzcpy(DG1->sex, mrz, &idx, 1);
mrtd_parse_date(&DG1->expiry_date, mrz + idx);
idx += 6; // expiry_date
idx += 1; // expiry date check digit
idx += 7; // optional data
idx += 1; // check digit
// 36 + 36
break;
case 88:
DG1->type = MrtdTypeTD3;
mrzcpy(DG1->doctype, mrz, &idx, 2);
mrzcpy(DG1->issuing_state, mrz, &idx, 3);
mrzcpy(DG1->name, mrz, &idx, 39);
mrzcpy(DG1->docnr, mrz, &idx, 9);
idx += 1; // docnr check digit
mrzcpy(DG1->nationality, mrz, &idx, 3);
mrtd_parse_date(&DG1->birth_date, mrz + idx);
idx += 1; // birth date check digit
idx += 6; // birth_date
mrzcpy(DG1->sex, mrz, &idx, 1);
mrtd_parse_date(&DG1->expiry_date, mrz + idx);
idx += 6; // expiry_date
idx += 1; // expiry date check digit
idx += 14; // optional data
idx += 1; // check digit
idx += 1; // check digit
// 44 + 44
break;
default:
FURI_LOG_W(
TAG, "Unexpected MRZ length in DG1: %d. TD1=90, TD2=72, TD3=88.", tlv_mrz.length);
return false;
}
return true;
}
bool mrtd_read_parse_file(MrtdApplication* app, EFFile file) {
uint8_t buffer[100];
size_t buf_len;
FURI_LOG_D(TAG, "Read and parse %s (%04X)", file.name, file.file_id);
if(!mrtd_select_file(app, file)) {
FURI_LOG_E(TAG, "Could not select %s", file.name);
return false;
}
FURI_LOG_D(TAG, "Selected %s", file.name);
buf_len = mrtd_read_binary(app, buffer, num_elements(buffer), 0);
if(!buf_len) {
FURI_LOG_E(TAG, "Could not read %s", file.name);
return false;
}
FURI_LOG_D(TAG, "Read %s", file.name);
bool result = false;
if(file.file_id == EF.COM.file_id) {
result = parse_ef_com(&app->mrtd_data->files.EF_COM, buffer, buf_len);
FURI_LOG_D(TAG, "Parsed EF.COM");
} else if(file.file_id == EF.DIR.file_id) {
result = parse_ef_dir(&app->mrtd_data->files.EF_DIR, buffer, buf_len);
FURI_LOG_D(TAG, "Parsed EF.DIR");
} else if(file.file_id == EF.DG1.file_id) {
result = parse_ef_dg1(&app->mrtd_data->files.DG1, buffer, buf_len);
} else {
FURI_LOG_W(TAG, "Don't know how to parse file with id 0x%04X", file.file_id);
}
return result;
}
MrtdApplication* mrtd_alloc_init(FuriHalNfcTxRxContext* tx_rx, MrtdData* mrtd_data) {
MrtdApplication* app = malloc(sizeof(MrtdApplication));
app->tx_rx = tx_rx;
app->mrtd_data = mrtd_data;
return app;
}
void mrtd_free(MrtdApplication* app) {
furi_assert(app);
free(app);
}
bool mrtd_bac(MrtdApplication* app, MrtdAuthData* auth) {
UNUSED(app);
static bool rand_generator_inited = false;
uint8_t rnd_ic[8];
uint8_t rnd_ifd[8];
uint8_t k_ifd[16];
if(!rand_generator_inited) {
// TODO: should random initialization maybe be system wide?
srand(DWT->CYCCNT);
rand_generator_inited = true;
}
mrtd_get_challenge(app, rnd_ic);
//TODO: remove memcpy rnd_ic
//memcpy(rnd_ic, "\x46\x08\xF9\x19\x88\x70\x22\x12", 8);
furi_hal_random_fill_buf(rnd_ifd, 8);
furi_hal_random_fill_buf(k_ifd, 16);
//TODO: remove testing code:
//memcpy(rnd_ifd, "\x78\x17\x23\x86\x0C\x06\xC2\x26", 8);
//memcpy(k_ifd, "\x0B\x79\x52\x40\xCB\x70\x49\xB0\x1C\x19\xB3\x3E\x32\x80\x4F\x0B", 16);
hexdump(FuriLogLevelDebug, "rnd_ifd:", rnd_ifd, 8);
hexdump(FuriLogLevelDebug, "k_ifd:", k_ifd, 16);
uint8_t kenc[16];
uint8_t kmac[16];
if(!mrtd_bac_keys(auth, kenc, kmac)) {
FURI_LOG_E(TAG, "Failed to calculate BAC keys");
return false;
}
uint8_t S[32];
memcpy(S, rnd_ifd, 8);
memcpy(S + 8, rnd_ic, 8);
memcpy(S + 16, k_ifd, 16);
hexdump(FuriLogLevelDebug, "S:", S, 32);
uint8_t cmd_data[40];
uint8_t* eifd = cmd_data;
uint8_t* mifd = cmd_data + 32;
mrtd_bac_encrypt(S, 32, kenc, eifd);
mrtd_bac_padded_mac(eifd, 32, kmac, mifd);
uint8_t response[40];
if(!mrtd_external_authenticate(app, cmd_data, 40, response, 40)) {
FURI_LOG_E(TAG, "BAC External Authenticate failed");
return false;
}
uint8_t buffer[32]; // Received R = RND.IC (8) || RND.IFD (8) || KIC (16)
if(!mrtd_bac_decrypt_verify(response, 40, kenc, kmac, buffer)) {
FURI_LOG_W(TAG, "BAC DecryptVerify failed");
}
uint8_t* rnd_ifd_recv = buffer + 8;
uint8_t* kic = buffer + 16;
hexdump(FuriLogLevelDebug, "kic:", kic, 16);
if(memcmp(rnd_ifd, rnd_ifd_recv, 8)) {
FURI_LOG_W(TAG, "BAC RND.IFD sent and received mismatch.");
}
uint8_t kseed[16];
for(uint8_t i = 0; i < 16; ++i) {
kseed[i] = k_ifd[i] ^ kic[i];
//printf("seed %2d = %02X ^ %02X = %02X\r\n", i, k_ifd[i], kic[i], kseed[i]);
}
hexdump(FuriLogLevelDebug, "kseed:", kseed, 16);
if(!mrtd_bac_keys_from_seed(kseed, app->ksenc, app->ksmac)) {
FURI_LOG_E(TAG, "BAC error, could not derive KSenc and KSmac");
return false;
}
hexdump(FuriLogLevelDebug, "ksenc:", app->ksenc, 16);
hexdump(FuriLogLevelDebug, "ksmac:", app->ksmac, 16);
hexdump(FuriLogLevelTrace, "RND.IC:", rnd_ic, 8);
hexdump(FuriLogLevelTrace, "RND.IFS:", rnd_ifd, 8);
app->ssc_long = mrtd_ssc_from_data(rnd_ic, rnd_ifd);
FURI_LOG_D(TAG, "SSC: %01llX", app->ssc_long);
app->secure_messaging = true;
return true;
}
bool mrtd_authenticate(MrtdApplication* app) {
MrtdAuthMethod method = app->mrtd_data->auth.method;
app->mrtd_data->auth_success = false;
app->mrtd_data->auth_method_used = MrtdAuthMethodNone;
FURI_LOG_D(TAG, "Auth method: %d", method);
switch(method) {
case MrtdAuthMethodAny:
//TODO: try PACE, then BAC. For now, fall through to just BAC
case MrtdAuthMethodBac:
app->mrtd_data->auth_success = mrtd_bac(app, &app->mrtd_data->auth);
app->mrtd_data->auth_method_used = MrtdAuthMethodBac;
break;
case MrtdAuthMethodPace:
FURI_LOG_E(TAG, "Auth method PACE not implemented");
break;
case MrtdAuthMethodNone:
default:
break;
}
if(!app->mrtd_data->auth_success) {
return false;
}
return true;
}
bool mrtd_auth_params_save(
Storage* storage,
DialogsApp* dialogs,
MrtdAuthData* auth_data,
const char* file_name) {
return mrtd_auth_params_save_file(
storage, dialogs, auth_data, file_name, MRTD_APP_FOLDER, MRTD_APP_EXTENSION);
}
void mrtd_date_prepare_format_string(MrtdDate date, FuriString* format_string) {
furi_string_printf(format_string, "%02u%02u%02u", date.year, date.month, date.day);
}
bool mrtd_date_parse_format_string(MrtdDate* date, FuriString* format_string) {
int year;
int month;
int day;
int ret = sscanf(furi_string_get_cstr(format_string), "%02d%02d%02d", &year, &month, &day);
if(ret != 3) {
return false;
}
date->year = year;
date->month = month;
date->day = day;
return true;
}
bool mrtd_auth_params_save_file(
Storage* storage,
DialogsApp* dialogs,
MrtdAuthData* auth_data,
const char* file_name,
const char* folder,
const char* extension) {
furi_assert(auth_data);
bool saved = false;
FlipperFormat* file = flipper_format_file_alloc(storage);
FuriString* temp_str;
temp_str = furi_string_alloc();
do {
// Create mrtd directory if necessary
if(!storage_simply_mkdir(storage, MRTD_APP_FOLDER)) break;
furi_string_printf(temp_str, "%s/%s%s", folder, file_name, extension);
// Open file
if(!flipper_format_file_open_always(file, furi_string_get_cstr(temp_str))) break;
// Write header
if(!flipper_format_write_header_cstr(file, mrtd_auth_file_header, mrtd_auth_file_version))
break;
// Write auth method
furi_string_set(temp_str, mrtd_auth_method_string(auth_data->method));
if(!flipper_format_write_string(file, "Method", temp_str)) break;
// Write birth date
mrtd_date_prepare_format_string(auth_data->birth_date, temp_str);
if(!flipper_format_write_string(file, "BirthDate", temp_str)) break;
// Write expiry date
mrtd_date_prepare_format_string(auth_data->expiry_date, temp_str);
if(!flipper_format_write_string(file, "ExpiryDate", temp_str)) break;
// Write docnr
furi_string_set(temp_str, auth_data->doc_number);
if(!flipper_format_write_string(file, "DocNr", temp_str)) break;
saved = true;
} while(false);
if(!saved) {
dialog_message_show_storage_error(dialogs, "Can not save\nparams file");
}
furi_string_free(temp_str);
flipper_format_free(file);
return saved;
}
bool mrtd_auth_params_load(
Storage* storage,
DialogsApp* dialogs,
MrtdAuthData* auth_data,
const char* file_path,
bool show_dialog) {
furi_assert(storage);
furi_assert(dialogs);
furi_assert(auth_data);
furi_assert(file_path);
bool parsed = false;
FlipperFormat* file = flipper_format_file_alloc(storage);
bool deprecated_version = false;
FuriString* temp_str;
temp_str = furi_string_alloc();
MrtdAuthData copy;
FURI_LOG_D(TAG, "Load auth params");
do {
if(!flipper_format_file_open_existing(file, file_path)) break;
uint32_t version = 0;
if(!flipper_format_read_header(file, temp_str, &version)) break;
FURI_LOG_D(TAG, "Version: %s", furi_string_get_cstr(temp_str));
if(furi_string_cmp_str(temp_str, mrtd_auth_file_header) ||
(version != mrtd_auth_file_version)) {
deprecated_version = true;
break;
}
if(!flipper_format_read_string(file, "Method", temp_str)) break;
FURI_LOG_D(TAG, "Method: %s", furi_string_get_cstr(temp_str));
if(!mrtd_auth_method_parse_string(&copy.method, furi_string_get_cstr(temp_str))) break;
if(!flipper_format_read_string(file, "BirthDate", temp_str)) break;
FURI_LOG_D(TAG, "BirthDate: %s", furi_string_get_cstr(temp_str));
if(!mrtd_date_parse_format_string(&copy.birth_date, temp_str)) break;
if(!flipper_format_read_string(file, "ExpiryDate", temp_str)) break;
FURI_LOG_D(TAG, "ExpiryDate: %s", furi_string_get_cstr(temp_str));
if(!mrtd_date_parse_format_string(&copy.expiry_date, temp_str)) break;
if(!flipper_format_read_string(file, "DocNr", temp_str)) break;
FURI_LOG_D(TAG, "DocNr: %s", furi_string_get_cstr(temp_str));
strlcpy(copy.doc_number, furi_string_get_cstr(temp_str), MRTD_DOCNR_MAX_LENGTH);
// Everything went fine. Save copy to pointed auth data
*auth_data = copy;
parsed = true;
} while(false);
FURI_LOG_D(TAG, "Load done, success: %d", parsed);
if(!parsed && show_dialog) {
if(deprecated_version) {
dialog_message_show_storage_error(dialogs, "File format deprecated");
} else {
dialog_message_show_storage_error(dialogs, "Can not parse\nfile");
}
}
furi_string_free(temp_str);
flipper_format_free(file);
return parsed;
}
-44
View File
@@ -1,44 +0,0 @@
#pragma once
#include <furi_hal_nfc.h>
#include <helpers/mrtd_helpers.h>
#define MRTD_APP_FOLDER "/any/nfc/mrtd"
#define MRTD_APP_EXTENSION ".mrtd"
typedef struct {
FuriHalNfcTxRxContext* tx_rx;
MrtdData* mrtd_data;
uint16_t file_offset;
uint8_t ksenc[16];
uint8_t ksmac[16];
uint64_t ssc_long; // TODO: rename without _long
bool secure_messaging;
} MrtdApplication;
//TODO: description
MrtdApplication* mrtd_alloc_init(FuriHalNfcTxRxContext* tx_rx, MrtdData* mrtd_data);
bool mrtd_select_app(MrtdApplication* app, AIDValue aid);
bool mrtd_authenticate(MrtdApplication* app);
bool mrtd_read_parse_file(MrtdApplication* app, EFFile file);
bool mrtd_auth_params_save(
Storage* storage,
DialogsApp* dialogs,
MrtdAuthData* auth_data,
const char* file_name);
bool mrtd_auth_params_save_file(
Storage* storage,
DialogsApp* dialogs,
MrtdAuthData* auth_data,
const char* file_name,
const char* folder,
const char* extension);
bool mrtd_auth_params_load(
Storage* storage,
DialogsApp* dialogs,
MrtdAuthData* auth_data,
const char* file_path,
bool show_dialog);
-169
View File
@@ -1,169 +0,0 @@
#include <limits.h>
#include <furi.h>
#include <furi_hal.h>
#include <furi_hal_nfc.h>
#include <furi_hal_spi.h>
#include <furi_hal_gpio.h>
#include <furi_hal_cortex.h>
#include <furi_hal_resources.h>
#include <st25r3916.h>
#include <st25r3916_irq.h>
#include "nfca_trans_rx.h"
#define TAG "NfcA-trans-rx"
void nfca_trans_rx_init(NfcaTransRxState* state) {
FURI_LOG_D(TAG, "Starting NfcA transparent rx");
st25r3916ExecuteCommand(ST25R3916_CMD_STOP);
st25r3916WriteRegister(ST25R3916_REG_OP_CONTROL, 0xC3);
st25r3916WriteRegister(ST25R3916_REG_MODE, 0x88);
st25r3916ExecuteCommand(ST25R3916_CMD_TRANSPARENT_MODE);
furi_hal_spi_bus_handle_deinit(&furi_hal_spi_bus_handle_nfc);
/* allocate a 512 edge buffer, more than enough */
state->reader_signal = pulse_reader_alloc(&gpio_spi_r_miso, 512);
/* timebase shall be 1 ns */
pulse_reader_set_timebase(state->reader_signal, PulseReaderUnitNanosecond);
pulse_reader_start(state->reader_signal);
/* set start values */
state->bits_received = 0;
state->have_sof = false;
state->valid_frame = false;
}
void nfca_trans_rx_deinit(NfcaTransRxState* state) {
furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_nfc);
pulse_reader_free(state->reader_signal);
}
void nfca_trans_rx_pause(NfcaTransRxState* state) {
pulse_reader_stop(state->reader_signal);
}
void nfca_trans_rx_continue(NfcaTransRxState* state) {
pulse_reader_start(state->reader_signal);
}
static void nfca_bit_received(NfcaTransRxState* state, uint8_t bit) {
/* According to ISO14443-3 short frames have 7 bits and standard 9 bits per byte,
where the 9th bit is odd parity. Data is transmitted LSB first. */
uint32_t byte_num = (state->bits_received / 9);
uint32_t bit_num = (state->bits_received % 9);
if(byte_num >= NFCA_FRAME_LENGTH) {
return;
}
if(bit_num == 8) {
uint32_t parity_value = 1 << (state->bits_received / 9);
state->parity_bits &= ~parity_value;
state->parity_bits |= bit ? parity_value : 0;
} else {
uint32_t bit_value = 1 << bit_num;
state->frame_data[byte_num] &= ~bit_value;
state->frame_data[byte_num] |= bit ? bit_value : 0;
}
state->bits_received++;
}
bool nfca_trans_rx_loop(NfcaTransRxState* state, uint32_t timeout_ms) {
furi_assert(state);
state->valid_frame = false;
state->have_sof = false;
state->bits_received = 0;
bool done = false;
uint32_t timeout_us = timeout_ms * 1000;
while(!done) {
uint32_t nsec = pulse_reader_receive(state->reader_signal, timeout_us);
bool eof = state->have_sof && (nsec >= (2 * NFCA_TB));
bool lost_pulse = false;
if(state->have_sof && nsec == PULSE_READER_LOST_EDGE) {
nsec = NFCA_T1;
lost_pulse = true;
} else if(nsec == PULSE_READER_NO_EDGE) {
done = true;
}
if(IS_T1(nsec) || eof) {
timeout_us = (3 * NFCA_TB) / 1000;
if(!state->have_sof) {
state->frame_time = -(NFCA_TB - nsec);
state->have_sof = true;
state->valid_frame = false;
state->bits_received = 0;
state->debug_pos = 0;
if(lost_pulse) {
state->frame_time -= nsec;
}
continue;
}
if(state->frame_time > NFCA_TB_MIN) {
state->frame_time -= NFCA_TB;
nfca_bit_received(state, 0);
}
if(IS_ZERO(state->frame_time)) {
state->frame_time = -(NFCA_TB - nsec);
nfca_bit_received(state, 0);
} else if(IS_TX(state->frame_time)) {
state->frame_time = -(NFCA_TX - nsec);
nfca_bit_received(state, 1);
} else {
if(eof) {
state->have_sof = false;
state->valid_frame = true;
done = true;
} else {
}
}
} else {
if(!state->have_sof) {
if(IS_TB(nsec)) {
state->frame_time = 0;
state->have_sof = true;
state->valid_frame = false;
state->bits_received = 0;
state->debug_pos = 0;
if(lost_pulse) {
state->frame_time -= nsec;
}
continue;
} else {
state->frame_time = 0;
}
} else {
state->frame_time += nsec;
}
}
if(lost_pulse) {
state->frame_time -= nsec;
}
}
if(state->valid_frame) {
if(state->bits_received > 7) {
/* a last 0-bit will look like a missing bit */
if((state->bits_received % 9) == 8) {
nfca_bit_received(state, 0);
state->bits_received++;
}
}
}
return state->valid_frame;
}
-53
View File
@@ -1,53 +0,0 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include <lib/digital_signal/digital_signal.h>
#include <lib/pulse_reader/pulse_reader.h>
#include <furi_hal_nfc.h>
#include "nfc_util.h"
/* assume fc/128 */
#define NFCA_FC (13560000.0f) /* MHz */
#define NFCA_FC_K ((uint32_t)(NFCA_FC / 1000)) /* kHz */
#define NFCA_T1 (28.0f / NFCA_FC * 1000000000)
#define NFCA_T1_MIN (24.0f / NFCA_FC * 1000000000)
#define NFCA_T1_MAX (41.0f / NFCA_FC * 1000000000)
#define NFCA_TX (64.0f / NFCA_FC * 1000000000) /* 4.7198 µs */
#define NFCA_TX_MIN (0.90f * NFCA_TX)
#define NFCA_TX_MAX (1.10f * NFCA_TX)
#define NFCA_TB (128.0f / NFCA_FC * 1000000000) /* 9.4395 µs */
#define NFCA_TB_MIN (0.80f * NFCA_TB)
#define NFCA_TB_MAX (1.20f * NFCA_TB)
#define IS_T1(x) ((x) >= NFCA_T1_MIN && (x) <= NFCA_T1_MAX)
#define IS_TX(x) ((x) >= NFCA_TX_MIN && (x) <= NFCA_TX_MAX)
#define IS_TB(x) ((x) >= NFCA_TB_MIN && (x) <= NFCA_TB_MAX)
#define IS_ZERO(x) ((x) >= -NFCA_T1_MIN / 2 && (x) <= NFCA_T1_MIN / 2)
#define DIGITAL_SIGNAL_UNIT_S (100000000000.0f)
#define DIGITAL_SIGNAL_UNIT_US (100000.0f)
#define NFCA_FRAME_LENGTH 32
#define NFCA_DEBUG_LENGTH 128
typedef struct {
bool have_sof;
bool valid_frame;
int32_t frame_time;
size_t bits_received;
uint8_t frame_data[NFCA_FRAME_LENGTH];
uint32_t debug_buffer[NFCA_DEBUG_LENGTH];
size_t debug_pos;
uint32_t parity_bits;
PulseReader* reader_signal;
} NfcaTransRxState;
bool nfca_trans_rx_loop(NfcaTransRxState* state, uint32_t timeout_ms);
void nfca_trans_rx_deinit(NfcaTransRxState* state);
void nfca_trans_rx_init(NfcaTransRxState* state);
void nfca_trans_rx_pause(NfcaTransRxState* state);
void nfca_trans_rx_continue(NfcaTransRxState* state);
-827
View File
@@ -1,827 +0,0 @@
#include <limits.h>
#include <furi.h>
#include <furi_hal.h>
#include <furi_hal_nfc.h>
#include <furi_hal_spi.h>
#include <furi_hal_gpio.h>
#include <furi_hal_cortex.h>
#include <furi_hal_resources.h>
#include <st25r3916.h>
#include <st25r3916_irq.h>
#include "nfcv.h"
#include "nfc_util.h"
#include "slix.h"
#define TAG "NfcV"
ReturnCode nfcv_inventory(uint8_t* uid) {
uint16_t received = 0;
rfalNfcvInventoryRes res;
ReturnCode ret = ERR_NONE;
for(int tries = 0; tries < 5; tries++) {
/* TODO: needs proper abstraction via fury_hal(_ll)_* */
ret = rfalNfcvPollerInventory(RFAL_NFCV_NUM_SLOTS_1, 0, NULL, &res, &received);
if(ret == ERR_NONE) {
break;
}
}
if(ret == ERR_NONE) {
if(uid != NULL) {
memcpy(uid, res.UID, 8);
}
}
return ret;
}
ReturnCode nfcv_read_blocks(NfcVReader* reader, NfcVData* nfcv_data) {
UNUSED(reader);
uint16_t received = 0;
for(size_t block = 0; block < nfcv_data->block_num; block++) {
uint8_t rxBuf[32];
FURI_LOG_D(TAG, "Reading block %d/%d", block, (nfcv_data->block_num - 1));
ReturnCode ret = ERR_NONE;
for(int tries = 0; tries < 5; tries++) {
ret = rfalNfcvPollerReadSingleBlock(
RFAL_NFCV_REQ_FLAG_DEFAULT, NULL, block, rxBuf, sizeof(rxBuf), &received);
if(ret == ERR_NONE) {
break;
}
}
if(ret != ERR_NONE) {
FURI_LOG_D(TAG, "failed to read: %d", ret);
return ret;
}
memcpy(
&(nfcv_data->data[block * nfcv_data->block_size]), &rxBuf[1], nfcv_data->block_size);
FURI_LOG_D(
TAG,
" %02X %02X %02X %02X",
nfcv_data->data[block * nfcv_data->block_size + 0],
nfcv_data->data[block * nfcv_data->block_size + 1],
nfcv_data->data[block * nfcv_data->block_size + 2],
nfcv_data->data[block * nfcv_data->block_size + 3]);
}
return ERR_NONE;
}
ReturnCode nfcv_read_sysinfo(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data) {
uint8_t rxBuf[32];
uint16_t received = 0;
ReturnCode ret = ERR_NONE;
FURI_LOG_D(TAG, "Read SYSTEM INFORMATION...");
for(int tries = 0; tries < 5; tries++) {
/* TODO: needs proper abstraction via fury_hal(_ll)_* */
ret = rfalNfcvPollerGetSystemInformation(
RFAL_NFCV_REQ_FLAG_DEFAULT, NULL, rxBuf, sizeof(rxBuf), &received);
if(ret == ERR_NONE) {
break;
}
}
if(ret == ERR_NONE) {
nfc_data->type = FuriHalNfcTypeV;
nfc_data->uid_len = 8;
/* UID is stored reversed in this response */
for(int pos = 0; pos < nfc_data->uid_len; pos++) {
nfc_data->uid[pos] = rxBuf[2 + (7 - pos)];
}
nfcv_data->dsfid = rxBuf[10];
nfcv_data->afi = rxBuf[11];
nfcv_data->block_num = rxBuf[12] + 1;
nfcv_data->block_size = rxBuf[13] + 1;
nfcv_data->ic_ref = rxBuf[14];
FURI_LOG_D(
TAG,
" UID: %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]);
FURI_LOG_D(
TAG,
" DSFID %d, AFI %d, Blocks %d, Size %d, IC Ref %d",
nfcv_data->dsfid,
nfcv_data->afi,
nfcv_data->block_num,
nfcv_data->block_size,
nfcv_data->ic_ref);
return ret;
}
FURI_LOG_D(TAG, "Failed: %d", ret);
return ret;
}
bool nfcv_read_card(NfcVReader* reader, FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data) {
furi_assert(reader);
furi_assert(nfc_data);
furi_assert(nfcv_data);
if(nfcv_read_sysinfo(nfc_data, nfcv_data) != ERR_NONE) {
return false;
}
if(nfcv_read_blocks(reader, nfcv_data) != ERR_NONE) {
return false;
}
if(slix_check_card_type(nfc_data)) {
FURI_LOG_I(TAG, "NXP SLIX detected");
nfcv_data->sub_type = NfcVTypeSlix;
} else if(slix2_check_card_type(nfc_data)) {
FURI_LOG_I(TAG, "NXP SLIX2 detected");
nfcv_data->sub_type = NfcVTypeSlix2;
} else if(slix_s_check_card_type(nfc_data)) {
FURI_LOG_I(TAG, "NXP SLIX-S detected");
nfcv_data->sub_type = NfcVTypeSlixS;
} else if(slix_l_check_card_type(nfc_data)) {
FURI_LOG_I(TAG, "NXP SLIX-L detected");
nfcv_data->sub_type = NfcVTypeSlixL;
} else {
nfcv_data->sub_type = NfcVTypePlain;
}
return true;
}
void nfcv_crc(uint8_t* data, uint32_t length) {
uint32_t reg = 0xFFFF;
for(size_t i = 0; i < length; i++) {
reg = reg ^ ((uint32_t)data[i]);
for(size_t j = 0; j < 8; j++) {
if(reg & 0x0001) {
reg = (reg >> 1) ^ 0x8408;
} else {
reg = (reg >> 1);
}
}
}
uint16_t crc = ~(uint16_t)(reg & 0xffff);
data[length + 0] = crc & 0xFF;
data[length + 1] = crc >> 8;
}
void nfcv_emu_free(NfcVData* nfcv_data) {
if(nfcv_data->emu_air.nfcv_signal) {
digital_sequence_free(nfcv_data->emu_air.nfcv_signal);
}
if(nfcv_data->emu_air.nfcv_resp_unmod_256) {
digital_signal_free(nfcv_data->emu_air.nfcv_resp_unmod_256);
}
if(nfcv_data->emu_air.nfcv_resp_pulse_32) {
digital_signal_free(nfcv_data->emu_air.nfcv_resp_pulse_32);
}
if(nfcv_data->emu_air.nfcv_resp_one) {
digital_signal_free(nfcv_data->emu_air.nfcv_resp_one);
}
if(nfcv_data->emu_air.nfcv_resp_zero) {
digital_signal_free(nfcv_data->emu_air.nfcv_resp_zero);
}
if(nfcv_data->emu_air.nfcv_resp_sof) {
digital_signal_free(nfcv_data->emu_air.nfcv_resp_sof);
}
if(nfcv_data->emu_air.nfcv_resp_eof) {
digital_signal_free(nfcv_data->emu_air.nfcv_resp_eof);
}
if(nfcv_data->emu_air.reader_signal) {
pulse_reader_free(nfcv_data->emu_air.reader_signal);
}
nfcv_data->emu_air.nfcv_signal = NULL;
nfcv_data->emu_air.nfcv_resp_unmod_256 = NULL;
nfcv_data->emu_air.nfcv_resp_pulse_32 = NULL;
nfcv_data->emu_air.nfcv_resp_one = NULL;
nfcv_data->emu_air.nfcv_resp_zero = NULL;
nfcv_data->emu_air.nfcv_resp_sof = NULL;
nfcv_data->emu_air.nfcv_resp_eof = NULL;
nfcv_data->emu_air.reader_signal = NULL;
}
void nfcv_emu_alloc(NfcVData* nfcv_data) {
if(!nfcv_data->emu_air.nfcv_signal) {
/* assuming max frame length is 255 bytes */
nfcv_data->emu_air.nfcv_signal = digital_sequence_alloc(8 * 255 + 2, &gpio_spi_r_mosi);
}
if(!nfcv_data->emu_air.nfcv_resp_unmod_256) {
/* unmodulated 256/fc signal as building block */
nfcv_data->emu_air.nfcv_resp_unmod_256 = digital_signal_alloc(4);
nfcv_data->emu_air.nfcv_resp_unmod_256->start_level = false;
nfcv_data->emu_air.nfcv_resp_unmod_256->edge_timings[0] =
(uint32_t)(NFCV_RESP_SUBC1_UNMOD_256 * DIGITAL_SIGNAL_UNIT_S);
nfcv_data->emu_air.nfcv_resp_unmod_256->edge_cnt = 1;
}
if(!nfcv_data->emu_air.nfcv_resp_pulse_32) {
/* modulated fc/32 pulse as building block */
nfcv_data->emu_air.nfcv_resp_pulse_32 = digital_signal_alloc(4);
nfcv_data->emu_air.nfcv_resp_pulse_32->start_level = true;
nfcv_data->emu_air.nfcv_resp_pulse_32->edge_timings[0] =
(uint32_t)(NFCV_RESP_SUBC1_PULSE_32 * DIGITAL_SIGNAL_UNIT_S);
nfcv_data->emu_air.nfcv_resp_pulse_32->edge_timings[1] =
(uint32_t)(NFCV_RESP_SUBC1_PULSE_32 * DIGITAL_SIGNAL_UNIT_S);
nfcv_data->emu_air.nfcv_resp_pulse_32->edge_cnt = 2;
}
if(!nfcv_data->emu_air.nfcv_resp_one) {
/* logical one: 256/fc unmodulated then 8 pulses fc/32 */
nfcv_data->emu_air.nfcv_resp_one = digital_signal_alloc(24);
digital_signal_append(
nfcv_data->emu_air.nfcv_resp_one, nfcv_data->emu_air.nfcv_resp_unmod_256);
for(size_t i = 0; i < 8; i++) {
digital_signal_append(
nfcv_data->emu_air.nfcv_resp_one, nfcv_data->emu_air.nfcv_resp_pulse_32);
}
}
if(!nfcv_data->emu_air.nfcv_resp_zero) {
/* logical zero: 8 pulses fc/32 then 256/fc unmodulated */
nfcv_data->emu_air.nfcv_resp_zero = digital_signal_alloc(24);
for(size_t i = 0; i < 8; i++) {
digital_signal_append(
nfcv_data->emu_air.nfcv_resp_zero, nfcv_data->emu_air.nfcv_resp_pulse_32);
}
digital_signal_append(
nfcv_data->emu_air.nfcv_resp_zero, nfcv_data->emu_air.nfcv_resp_unmod_256);
}
if(!nfcv_data->emu_air.nfcv_resp_sof) {
/* SOF: unmodulated 768/fc, 24 pulses fc/32, logic 1 */
nfcv_data->emu_air.nfcv_resp_sof = digital_signal_alloc(128);
digital_signal_append(
nfcv_data->emu_air.nfcv_resp_sof, nfcv_data->emu_air.nfcv_resp_unmod_256);
digital_signal_append(
nfcv_data->emu_air.nfcv_resp_sof, nfcv_data->emu_air.nfcv_resp_unmod_256);
digital_signal_append(
nfcv_data->emu_air.nfcv_resp_sof, nfcv_data->emu_air.nfcv_resp_unmod_256);
for(size_t i = 0; i < 24; i++) {
digital_signal_append(
nfcv_data->emu_air.nfcv_resp_sof, nfcv_data->emu_air.nfcv_resp_pulse_32);
}
digital_signal_append(nfcv_data->emu_air.nfcv_resp_sof, nfcv_data->emu_air.nfcv_resp_one);
}
if(!nfcv_data->emu_air.nfcv_resp_eof) {
/* EOF: logic 0, 24 pulses fc/32, unmodulated 768/fc */
nfcv_data->emu_air.nfcv_resp_eof = digital_signal_alloc(128);
digital_signal_append(nfcv_data->emu_air.nfcv_resp_eof, nfcv_data->emu_air.nfcv_resp_zero);
for(size_t i = 0; i < 24; i++) {
digital_signal_append(
nfcv_data->emu_air.nfcv_resp_eof, nfcv_data->emu_air.nfcv_resp_pulse_32);
}
digital_signal_append(
nfcv_data->emu_air.nfcv_resp_eof, nfcv_data->emu_air.nfcv_resp_unmod_256);
digital_signal_append(
nfcv_data->emu_air.nfcv_resp_eof, nfcv_data->emu_air.nfcv_resp_unmod_256);
digital_signal_append(
nfcv_data->emu_air.nfcv_resp_eof, nfcv_data->emu_air.nfcv_resp_unmod_256);
/* add extra silence */
digital_signal_append(
nfcv_data->emu_air.nfcv_resp_eof, nfcv_data->emu_air.nfcv_resp_unmod_256);
}
digital_sequence_set_signal(
nfcv_data->emu_air.nfcv_signal, NFCV_SIG_SOF, nfcv_data->emu_air.nfcv_resp_sof);
digital_sequence_set_signal(
nfcv_data->emu_air.nfcv_signal, NFCV_SIG_BIT0, nfcv_data->emu_air.nfcv_resp_zero);
digital_sequence_set_signal(
nfcv_data->emu_air.nfcv_signal, NFCV_SIG_BIT1, nfcv_data->emu_air.nfcv_resp_one);
digital_sequence_set_signal(
nfcv_data->emu_air.nfcv_signal, NFCV_SIG_EOF, nfcv_data->emu_air.nfcv_resp_eof);
}
void nfcv_emu_send(
FuriHalNfcTxRxContext* tx_rx,
NfcVData* nfcv,
uint8_t* data,
uint8_t length,
NfcVSendFlags flags,
uint32_t send_time) {
/* picked default value (0) to match the most common format */
if(!flags) {
flags = NfcVSendFlagsSof | NfcVSendFlagsCrc | NfcVSendFlagsEof |
NfcVSendFlagsOneSubcarrier | NfcVSendFlagsHighRate;
}
if(flags & NfcVSendFlagsCrc) {
nfcv_crc(data, length);
length += 2;
}
digital_sequence_clear(nfcv->emu_air.nfcv_signal);
if(flags & NfcVSendFlagsSof) {
digital_sequence_add(nfcv->emu_air.nfcv_signal, NFCV_SIG_SOF);
}
for(int bit_total = 0; bit_total < length * 8; bit_total++) {
uint32_t byte_pos = bit_total / 8;
uint32_t bit_pos = bit_total % 8;
uint8_t bit_val = 0x01 << bit_pos;
digital_sequence_add(
nfcv->emu_air.nfcv_signal, (data[byte_pos] & bit_val) ? NFCV_SIG_BIT1 : NFCV_SIG_BIT0);
}
if(flags & NfcVSendFlagsEof) {
digital_sequence_add(nfcv->emu_air.nfcv_signal, NFCV_SIG_EOF);
}
FURI_CRITICAL_ENTER();
digital_sequence_set_sendtime(nfcv->emu_air.nfcv_signal, send_time);
digital_sequence_send(nfcv->emu_air.nfcv_signal);
FURI_CRITICAL_EXIT();
furi_hal_gpio_write(&gpio_spi_r_mosi, false);
if(tx_rx->sniff_tx) {
tx_rx->sniff_tx(data, length * 8, false, tx_rx->sniff_context);
}
}
static void nfcv_revuidcpy(uint8_t* dst, uint8_t* src) {
for(int pos = 0; pos < 8; pos++) {
dst[pos] = src[7 - pos];
}
}
static int nfcv_revuidcmp(uint8_t* dst, uint8_t* src) {
for(int pos = 0; pos < 8; pos++) {
if(dst[pos] != src[7 - pos]) {
return 1;
}
}
return 0;
}
void nfcv_emu_handle_packet(
FuriHalNfcTxRxContext* tx_rx,
FuriHalNfcDevData* nfc_data,
void* nfcv_data_in) {
NfcVData* nfcv_data = (NfcVData*)nfcv_data_in;
NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx;
if(nfcv_data->frame_length < 2) {
return;
}
/* parse the frame data for the upcoming part 3 handling */
ctx->flags = nfcv_data->frame[0];
ctx->command = nfcv_data->frame[1];
ctx->addressed = !(ctx->flags & RFAL_NFCV_REQ_FLAG_INVENTORY) &&
(ctx->flags & RFAL_NFCV_REQ_FLAG_ADDRESS);
ctx->advanced = (ctx->command >= 0xA0);
ctx->address_offset = 2 + (ctx->advanced ? 1 : 0);
ctx->payload_offset = ctx->address_offset + (ctx->addressed ? 8 : 0);
ctx->response_flags = NfcVSendFlagsNormal;
ctx->send_time = nfcv_data->eof_timestamp + NFCV_FDT_FC(4130);
/* standard behavior is implemented */
if(ctx->addressed) {
uint8_t* address = &nfcv_data->frame[ctx->address_offset];
if(nfcv_revuidcmp(address, nfc_data->uid)) {
FURI_LOG_D(TAG, "addressed command 0x%02X, but not for us:", ctx->command);
FURI_LOG_D(
TAG,
" dest: %02X%02X%02X%02X%02X%02X%02X%02X",
address[7],
address[6],
address[5],
address[4],
address[3],
address[2],
address[1],
address[0]);
FURI_LOG_D(
TAG,
" our UID: %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]);
return;
}
}
/* then give control to the card subtype specific protocol filter */
if(ctx->emu_protocol_filter != NULL) {
if(ctx->emu_protocol_filter(tx_rx, nfc_data, nfcv_data)) {
if(strlen(nfcv_data->last_command) > 0) {
FURI_LOG_D(
TAG, "Received command %s (handled by filter)", nfcv_data->last_command);
}
return;
}
}
switch(ctx->command) {
case ISO15693_INVENTORY: {
ctx->response_buffer[0] = ISO15693_NOERROR;
ctx->response_buffer[1] = nfcv_data->dsfid;
nfcv_revuidcpy(&ctx->response_buffer[2], nfc_data->uid);
nfcv_emu_send(
tx_rx, nfcv_data, ctx->response_buffer, 10, ctx->response_flags, ctx->send_time);
snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "INVENTORY");
break;
}
case ISO15693_STAYQUIET: {
snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "STAYQUIET");
break;
}
case ISO15693_LOCKBLOCK: {
snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "LOCKBLOCK");
break;
}
case ISO15693_SELECT: {
ctx->response_buffer[0] = ISO15693_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), "SELECT");
break;
}
case ISO15693_READ_MULTI_BLOCK:
case ISO15693_READBLOCK: {
uint8_t block = nfcv_data->frame[ctx->payload_offset];
uint8_t blocks = 1;
if(ctx->command == ISO15693_READ_MULTI_BLOCK) {
blocks = nfcv_data->frame[ctx->payload_offset + 1] + 1;
}
if(block + blocks > nfcv_data->block_num) {
ctx->response_buffer[0] = ISO15693_ERROR_CMD_NOT_REC;
nfcv_emu_send(
tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time);
} else {
ctx->response_buffer[0] = ISO15693_NOERROR;
memcpy(
&ctx->response_buffer[1],
&nfcv_data->data[nfcv_data->block_size * block],
nfcv_data->block_size * blocks);
nfcv_emu_send(
tx_rx,
nfcv_data,
ctx->response_buffer,
1 + nfcv_data->block_size * blocks,
ctx->response_flags,
ctx->send_time);
}
snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "READ BLOCK %d", block);
break;
}
case ISO15693_WRITE_MULTI_BLOCK:
case ISO15693_WRITEBLOCK: {
uint8_t block = nfcv_data->frame[ctx->payload_offset];
uint8_t blocks = 1;
uint8_t data_pos = 1;
if(ctx->command == ISO15693_WRITE_MULTI_BLOCK) {
blocks = nfcv_data->frame[ctx->payload_offset + 1] + 1;
data_pos++;
}
uint8_t* data = &nfcv_data->frame[ctx->payload_offset + data_pos];
uint32_t data_len = nfcv_data->block_size * blocks;
if(block + blocks > nfcv_data->block_num ||
ctx->payload_offset + data_len + 2 > nfcv_data->frame_length) {
ctx->response_buffer[0] = ISO15693_ERROR_CMD_NOT_REC;
} else {
ctx->response_buffer[0] = ISO15693_NOERROR;
memcpy(
&nfcv_data->data[nfcv_data->block_size * block],
&nfcv_data->frame[ctx->payload_offset + data_pos],
data_len);
}
nfcv_emu_send(
tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time);
if(ctx->command == ISO15693_WRITE_MULTI_BLOCK) {
snprintf(
nfcv_data->last_command,
sizeof(nfcv_data->last_command),
"WRITE MULTI BLOCK %d, %d blocks",
block,
blocks);
} else {
snprintf(
nfcv_data->last_command,
sizeof(nfcv_data->last_command),
"WRITE BLOCK %d <- %02X %02X %02X %02X",
block,
data[0],
data[1],
data[2],
data[3]);
}
break;
}
case ISO15693_GET_SYSTEM_INFO: {
ctx->response_buffer[0] = ISO15693_NOERROR;
ctx->response_buffer[1] = 0x0F;
nfcv_revuidcpy(&ctx->response_buffer[2], nfc_data->uid);
ctx->response_buffer[10] = nfcv_data->dsfid; /* DSFID */
ctx->response_buffer[11] = nfcv_data->afi; /* AFI */
ctx->response_buffer[12] = nfcv_data->block_num - 1; /* number of blocks */
ctx->response_buffer[13] = nfcv_data->block_size - 1; /* block size */
ctx->response_buffer[14] = nfcv_data->ic_ref; /* IC reference */
nfcv_emu_send(
tx_rx, nfcv_data, ctx->response_buffer, 15, ctx->response_flags, ctx->send_time);
snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "SYSTEMINFO");
break;
}
default:
snprintf(
nfcv_data->last_command,
sizeof(nfcv_data->last_command),
"unsupported: %02X",
ctx->command);
break;
}
if(strlen(nfcv_data->last_command) > 0) {
FURI_LOG_D(TAG, "Received command %s", nfcv_data->last_command);
}
}
void nfcv_emu_init(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data) {
nfcv_emu_alloc(nfcv_data);
rfal_platform_spi_acquire();
/* configure for transparent and passive mode */
st25r3916ExecuteCommand(ST25R3916_CMD_STOP);
/* set enable, rx_enable and field detector enable */
st25r3916WriteRegister(ST25R3916_REG_OP_CONTROL, 0xC3);
/* target mode: ISO14443 passive mode */
st25r3916WriteRegister(ST25R3916_REG_MODE, 0x88);
/* let us modulate the field using MOSI, read modulation using MISO */
st25r3916ExecuteCommand(ST25R3916_CMD_TRANSPARENT_MODE);
furi_hal_spi_bus_handle_deinit(&furi_hal_spi_bus_handle_nfc);
/* if not set already, initialize the default protocol handler */
if(!nfcv_data->emu_protocol_ctx) {
nfcv_data->emu_protocol_ctx = malloc(sizeof(NfcVEmuProtocolCtx));
nfcv_data->emu_protocol_handler = &nfcv_emu_handle_packet;
}
FURI_LOG_D(TAG, "Starting NfcV emulation");
FURI_LOG_D(
TAG,
" UID: %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]);
switch(nfcv_data->sub_type) {
case NfcVTypeSlixL:
FURI_LOG_D(TAG, " Card type: SLIX-L");
slix_l_prepare(nfcv_data);
break;
case NfcVTypeSlixS:
FURI_LOG_D(TAG, " Card type: SLIX-S");
slix_s_prepare(nfcv_data);
break;
case NfcVTypeSlix2:
FURI_LOG_D(TAG, " Card type: SLIX2");
slix2_prepare(nfcv_data);
break;
case NfcVTypeSlix:
FURI_LOG_D(TAG, " Card type: SLIX");
slix_prepare(nfcv_data);
break;
case NfcVTypePlain:
FURI_LOG_D(TAG, " Card type: Plain");
break;
}
/* allocate a 512 edge buffer, more than enough */
nfcv_data->emu_air.reader_signal = pulse_reader_alloc(&gpio_spi_r_miso, 512);
/* timebase shall be 1 ns */
pulse_reader_set_timebase(nfcv_data->emu_air.reader_signal, PulseReaderUnitNanosecond);
/* and configure to already calculate the number of bits */
pulse_reader_set_bittime(nfcv_data->emu_air.reader_signal, PULSE_DURATION_NS);
pulse_reader_start(nfcv_data->emu_air.reader_signal);
}
void nfcv_emu_deinit(NfcVData* nfcv_data) {
furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_nfc);
rfal_platform_spi_release();
nfcv_emu_free(nfcv_data);
if(nfcv_data->emu_protocol_ctx) {
free(nfcv_data->emu_protocol_ctx);
nfcv_data->emu_protocol_ctx = NULL;
}
}
bool nfcv_emu_loop(
FuriHalNfcTxRxContext* tx_rx,
FuriHalNfcDevData* nfc_data,
NfcVData* nfcv_data,
uint32_t timeout_ms) {
bool ret = false;
uint32_t frame_state = NFCV_FRAME_STATE_SOF1;
uint32_t periods_previous = 0;
uint8_t frame_payload[128];
uint32_t frame_pos = 0;
uint32_t byte_value = 0;
uint32_t bits_received = 0;
char reset_reason[128];
bool wait_for_pulse = false;
while(true) {
uint32_t periods =
pulse_reader_receive(nfcv_data->emu_air.reader_signal, timeout_ms * 1000);
uint32_t timestamp = DWT->CYCCNT;
if(periods == PULSE_READER_NO_EDGE) {
break;
}
if(wait_for_pulse) {
wait_for_pulse = false;
if(periods != 1) {
snprintf(
reset_reason,
sizeof(reset_reason),
"SOF: Expected a single low pulse in state %lu, but got %lu",
frame_state,
periods);
frame_state = NFCV_FRAME_STATE_RESET;
}
continue;
}
switch(frame_state) {
case NFCV_FRAME_STATE_SOF1:
if(periods == 1) {
frame_state = NFCV_FRAME_STATE_SOF2;
} else {
frame_state = NFCV_FRAME_STATE_SOF1;
break;
}
break;
case NFCV_FRAME_STATE_SOF2:
/* waiting for the second low period, telling us about coding */
if(periods == 6) {
frame_state = NFCV_FRAME_STATE_CODING_256;
periods_previous = 0;
wait_for_pulse = true;
} else if(periods == 4) {
frame_state = NFCV_FRAME_STATE_CODING_4;
periods_previous = 2;
wait_for_pulse = true;
} else {
snprintf(
reset_reason,
sizeof(reset_reason),
"SOF: Expected 4/6 periods, got %lu",
periods);
frame_state = NFCV_FRAME_STATE_SOF1;
}
break;
case NFCV_FRAME_STATE_CODING_256:
if(periods_previous > periods) {
snprintf(
reset_reason,
sizeof(reset_reason),
"1oo256: Missing %lu periods from previous symbol, got %lu",
periods_previous,
periods);
frame_state = NFCV_FRAME_STATE_RESET;
break;
}
/* previous symbol left us with some pulse periods */
periods -= periods_previous;
if(periods > 512) {
snprintf(
reset_reason, sizeof(reset_reason), "1oo256: %lu periods is too much", periods);
frame_state = NFCV_FRAME_STATE_RESET;
break;
}
if(periods == 2) {
frame_state = NFCV_FRAME_STATE_EOF;
break;
}
periods_previous = 512 - (periods + 1);
byte_value = (periods - 1) / 2;
frame_payload[frame_pos++] = (uint8_t)byte_value;
wait_for_pulse = true;
break;
case NFCV_FRAME_STATE_CODING_4:
if(periods_previous > periods) {
snprintf(
reset_reason,
sizeof(reset_reason),
"1oo4: Missing %lu periods from previous symbol, got %lu",
periods_previous,
periods);
frame_state = NFCV_FRAME_STATE_RESET;
break;
}
/* previous symbol left us with some pulse periods */
periods -= periods_previous;
periods_previous = 0;
byte_value >>= 2;
bits_received += 2;
if(periods == 1) {
byte_value |= 0x00 << 6;
periods_previous = 6;
} else if(periods == 3) {
byte_value |= 0x01 << 6;
periods_previous = 4;
} else if(periods == 5) {
byte_value |= 0x02 << 6;
periods_previous = 2;
} else if(periods == 7) {
byte_value |= 0x03 << 6;
periods_previous = 0;
} else if(periods == 2) {
frame_state = NFCV_FRAME_STATE_EOF;
break;
} else {
snprintf(
reset_reason,
sizeof(reset_reason),
"1oo4: Expected 1/3/5/7 low pulses, but got %lu",
periods);
frame_state = NFCV_FRAME_STATE_RESET;
break;
}
if(bits_received >= 8) {
frame_payload[frame_pos++] = (uint8_t)byte_value;
bits_received = 0;
}
wait_for_pulse = true;
break;
}
/* post-state-machine cleanup and reset */
if(frame_state == NFCV_FRAME_STATE_RESET) {
frame_state = NFCV_FRAME_STATE_SOF1;
FURI_LOG_D(TAG, "Resetting state machine, reason: '%s'", reset_reason);
} else if(frame_state == NFCV_FRAME_STATE_EOF) {
nfcv_data->frame = frame_payload;
nfcv_data->frame_length = frame_pos;
nfcv_data->eof_timestamp = timestamp;
break;
}
}
if(frame_state == NFCV_FRAME_STATE_EOF) {
/* we know that this code uses TIM2, so stop pulse reader */
pulse_reader_stop(nfcv_data->emu_air.reader_signal);
if(tx_rx->sniff_rx) {
tx_rx->sniff_rx(frame_payload, frame_pos * 8, false, tx_rx->sniff_context);
}
nfcv_data->emu_protocol_handler(tx_rx, nfc_data, nfcv_data);
pulse_reader_start(nfcv_data->emu_air.reader_signal);
ret = true;
}
return ret;
}
-205
View File
@@ -1,205 +0,0 @@
#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
#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 PULSE_DURATION_NS (128.0f * 1000000000.0f / NFCV_FC) /* ns */
#define DIGITAL_SIGNAL_UNIT_S (100000000000.0f)
#define DIGITAL_SIGNAL_UNIT_US (100000.0f)
#define NFCV_TOTAL_BLOCKS_MAX 256
#define NFCV_BLOCK_SIZE 4
#define NFCV_MAX_DUMP_SIZE (NFCV_BLOCK_SIZE * NFCV_TOTAL_BLOCKS_MAX)
/* 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)
#define NFCV_FRAME_STATE_SOF1 0
#define NFCV_FRAME_STATE_SOF2 1
#define NFCV_FRAME_STATE_CODING_4 2
#define NFCV_FRAME_STATE_CODING_256 3
#define NFCV_FRAME_STATE_EOF 4
#define NFCV_FRAME_STATE_RESET 5
#define NFCV_SIG_SOF 0
#define NFCV_SIG_BIT0 1
#define NFCV_SIG_BIT1 2
#define NFCV_SIG_EOF 3
/* ISO15693 command codes */
#define ISO15693_INVENTORY 0x01
#define ISO15693_STAYQUIET 0x02
#define ISO15693_READBLOCK 0x20
#define ISO15693_WRITEBLOCK 0x21
#define ISO15693_LOCKBLOCK 0x22
#define ISO15693_READ_MULTI_BLOCK 0x23
#define ISO15693_WRITE_MULTI_BLOCK 0x24
#define ISO15693_SELECT 0x25
#define ISO15693_RESET_TO_READY 0x26
#define ISO15693_WRITE_AFI 0x27
#define ISO15693_LOCK_AFI 0x28
#define ISO15693_WRITE_DSFID 0x29
#define ISO15693_LOCK_DSFID 0x2A
#define ISO15693_GET_SYSTEM_INFO 0x2B
#define ISO15693_READ_MULTI_SECSTATUS 0x2C
/* ISO15693 RESPONSE ERROR CODES */
#define ISO15693_NOERROR 0x00
#define ISO15693_ERROR_CMD_NOT_SUP 0x01 // Command not supported
#define ISO15693_ERROR_CMD_NOT_REC 0x02 // Command not recognized (eg. parameter error)
#define ISO15693_ERROR_CMD_OPTION 0x03 // Command option not supported
#define ISO15693_ERROR_GENERIC 0x0F // No additional Info about this error
#define ISO15693_ERROR_BLOCK_UNAVAILABLE 0x10
#define ISO15693_ERROR_BLOCK_LOCKED_ALREADY 0x11 // cannot lock again
#define ISO15693_ERROR_BLOCK_LOCKED 0x12 // cannot be changed
#define ISO15693_ERROR_BLOCK_WRITE 0x13 // Writing was unsuccessful
#define ISO15693_ERROR_BLOCL_WRITELOCK 0x14 // Locking was unsuccessful
typedef enum {
NfcVAuthMethodManual,
NfcVAuthMethodTonieBox,
} NfcVAuthMethod;
typedef enum {
NfcVTypePlain = 0,
NfcVTypeSlix = 1,
NfcVTypeSlixS = 2,
NfcVTypeSlixL = 3,
NfcVTypeSlix2 = 4,
} 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 {
PulseReader* reader_signal;
DigitalSignal* nfcv_resp_pulse_32;
DigitalSignal* nfcv_resp_unmod;
DigitalSignal* nfcv_resp_one;
DigitalSignal* nfcv_resp_zero;
DigitalSignal* nfcv_resp_sof;
DigitalSignal* nfcv_resp_eof;
DigitalSignal* nfcv_resp_unmod_256;
DigitalSignal* nfcv_resp_unmod_768;
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);
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 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[128]; /* 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_MAX_DUMP_SIZE];
/* specfic variant infos */
NfcVSubtype sub_type;
NfcVSubtypeData sub_data;
NfcVAuthMethod auth_method;
/* precalced air level data */
NfcVEmuAir emu_air;
uint8_t* frame; /* 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[128];
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(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
-407
View File
@@ -1,407 +0,0 @@
#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(
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(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(
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;
}
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 != ISO15693_CMD_NXP_GET_RANDOM_NUMBER &&
ctx->command != ISO15693_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 ISO15693_CMD_NXP_GET_RANDOM_NUMBER: {
slix->rand[0] = furi_hal_random_get();
slix->rand[1] = furi_hal_random_get();
ctx->response_buffer[0] = ISO15693_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 ISO15693_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;
}
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;
break;
case SLIX_PASS_DESTROY:
FURI_LOG_D(TAG, "Pooof! Got destroyed");
break;
case SLIX_PASS_EASAFI:
break;
default:
break;
}
ctx->response_buffer[0] = ISO15693_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 ISO15693_CMD_NXP_ENABLE_PRIVACY: {
ctx->response_buffer[0] = ISO15693_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),
"ISO15693_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
View File
@@ -1,46 +0,0 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include "nfc_util.h"
#include <furi_hal_nfc.h>
#define ISO15693_MANUFACTURER_NXP 0x04
/* ISO15693-3 CUSTOM NXP COMMANDS */
#define ISO15693_CMD_NXP_SET_EAS 0xA2
#define ISO15693_CMD_NXP_RESET_EAS 0xA3
#define ISO15693_CMD_NXP_LOCK_EAS 0xA4
#define ISO15693_CMD_NXP_EAS_ALARM 0xA5
#define ISO15693_CMD_NXP_PASSWORD_PROTECT_EAS_AFI 0xA6
#define ISO15693_CMD_NXP_WRITE_EAS_ID 0xA7
#define ISO15693_CMD_NXP_INVENTORY_PAGE_READ 0xB0
#define ISO15693_CMD_NXP_INVENTORY_PAGE_READ_FAST 0xB1
#define ISO15693_CMD_NXP_GET_RANDOM_NUMBER 0xB2
#define ISO15693_CMD_NXP_SET_PASSWORD 0xB3
#define ISO15693_CMD_NXP_WRITE_PASSWORD 0xB4
#define ISO15693_CMD_NXP_DESTROY 0xB9
#define ISO15693_CMD_NXP_ENABLE_PRIVACY 0xBA
/* 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);
-202
View File
@@ -1,202 +0,0 @@
#include <limits.h>
#include <furi.h>
#include <furi_hal.h>
#include <furi_hal_gpio.h>
#include "pulse_reader.h"
#define GPIO_PIN_MAP(pin, prefix) \
(((pin) == (LL_GPIO_PIN_0)) ? prefix##0 : \
((pin) == (LL_GPIO_PIN_1)) ? prefix##1 : \
((pin) == (LL_GPIO_PIN_2)) ? prefix##2 : \
((pin) == (LL_GPIO_PIN_3)) ? prefix##3 : \
((pin) == (LL_GPIO_PIN_4)) ? prefix##4 : \
((pin) == (LL_GPIO_PIN_5)) ? prefix##5 : \
((pin) == (LL_GPIO_PIN_6)) ? prefix##6 : \
((pin) == (LL_GPIO_PIN_7)) ? prefix##7 : \
((pin) == (LL_GPIO_PIN_8)) ? prefix##8 : \
((pin) == (LL_GPIO_PIN_9)) ? prefix##9 : \
((pin) == (LL_GPIO_PIN_10)) ? prefix##10 : \
((pin) == (LL_GPIO_PIN_11)) ? prefix##11 : \
((pin) == (LL_GPIO_PIN_12)) ? prefix##12 : \
((pin) == (LL_GPIO_PIN_13)) ? prefix##13 : \
((pin) == (LL_GPIO_PIN_14)) ? prefix##14 : \
prefix##15)
#define GET_DMAMUX_EXTI_LINE(pin) GPIO_PIN_MAP(pin, LL_DMAMUX_REQ_GEN_EXTI_LINE)
PulseReader* pulse_reader_alloc(const GpioPin* gpio, uint32_t size) {
PulseReader* signal = malloc(sizeof(PulseReader));
signal->timer_buffer = malloc(size * sizeof(uint32_t));
signal->gpio_buffer = malloc(size * sizeof(uint32_t));
signal->dma_channel = LL_DMA_CHANNEL_4;
signal->gpio = gpio;
signal->size = size;
signal->timer_value = 0;
signal->pos = 0;
pulse_reader_set_timebase(signal, PulseReaderUnit64MHz);
pulse_reader_set_bittime(signal, 1);
signal->dma_config_timer.Direction = LL_DMA_DIRECTION_PERIPH_TO_MEMORY;
signal->dma_config_timer.PeriphOrM2MSrcAddress = (uint32_t) & (TIM2->CNT);
signal->dma_config_timer.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT;
signal->dma_config_timer.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD;
signal->dma_config_timer.MemoryOrM2MDstAddress = (uint32_t)signal->timer_buffer;
signal->dma_config_timer.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT;
signal->dma_config_timer.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD;
signal->dma_config_timer.Mode = LL_DMA_MODE_CIRCULAR;
signal->dma_config_timer.PeriphRequest =
LL_DMAMUX_REQ_GENERATOR0; /* executes LL_DMA_SetPeriphRequest */
signal->dma_config_timer.Priority = LL_DMA_PRIORITY_VERYHIGH;
signal->dma_config_gpio.Direction = LL_DMA_DIRECTION_PERIPH_TO_MEMORY;
signal->dma_config_gpio.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT;
signal->dma_config_gpio.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD;
signal->dma_config_gpio.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT;
signal->dma_config_gpio.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD;
signal->dma_config_gpio.Mode = LL_DMA_MODE_CIRCULAR;
signal->dma_config_gpio.PeriphRequest =
LL_DMAMUX_REQ_GENERATOR0; /* executes LL_DMA_SetPeriphRequest */
signal->dma_config_gpio.Priority = LL_DMA_PRIORITY_VERYHIGH;
return signal;
}
void pulse_reader_set_timebase(PulseReader* signal, PulseReaderUnit unit) {
switch(unit) {
case PulseReaderUnit64MHz:
signal->unit_multiplier = 1;
signal->unit_divider = 1;
break;
case PulseReaderUnitPicosecond:
signal->unit_multiplier = 15625;
signal->unit_divider = 1;
break;
case PulseReaderUnitNanosecond:
signal->unit_multiplier = 15625;
signal->unit_divider = 1000;
break;
case PulseReaderUnitMicrosecond:
signal->unit_multiplier = 15625;
signal->unit_divider = 1000000;
break;
}
}
void pulse_reader_set_bittime(PulseReader* signal, uint32_t bit_time) {
signal->bit_time = bit_time;
}
void pulse_reader_free(PulseReader* signal) {
free(signal->timer_buffer);
free(signal->gpio_buffer);
free(signal);
}
uint32_t pulse_reader_samples(PulseReader* signal) {
uint32_t dma_pos = signal->size - (uint32_t)LL_DMA_GetDataLength(DMA1, signal->dma_channel);
return ((signal->pos + signal->size) - dma_pos) % signal->size;
}
void pulse_reader_stop(PulseReader* signal) {
LL_DMA_DisableChannel(DMA1, signal->dma_channel);
LL_DMA_DisableChannel(DMA1, signal->dma_channel + 1);
LL_DMAMUX_DisableRequestGen(NULL, LL_DMAMUX_REQ_GEN_0);
LL_TIM_DisableCounter(TIM2);
}
void pulse_reader_start(PulseReader* signal) {
/* configure DMA to read from a timer peripheral */
signal->dma_config_timer.NbData = signal->size;
signal->dma_config_gpio.PeriphOrM2MSrcAddress = (uint32_t) & (signal->gpio->port->IDR);
signal->dma_config_gpio.MemoryOrM2MDstAddress = (uint32_t)signal->gpio_buffer;
signal->dma_config_gpio.NbData = signal->size;
/* start counter */
LL_TIM_SetCounterMode(TIM2, LL_TIM_COUNTERMODE_UP);
LL_TIM_SetClockDivision(TIM2, LL_TIM_CLOCKDIVISION_DIV1);
LL_TIM_SetPrescaler(TIM2, 0);
LL_TIM_SetAutoReload(TIM2, 0xFFFFFFFF);
LL_TIM_SetCounter(TIM2, 0);
LL_TIM_EnableCounter(TIM2);
/* generator 0 gets fed by EXTI_LINEn */
LL_DMAMUX_SetRequestSignalID(
NULL, LL_DMAMUX_REQ_GEN_0, GET_DMAMUX_EXTI_LINE(signal->gpio->pin));
/* trigger on rising edge of the interrupt */
LL_DMAMUX_SetRequestGenPolarity(NULL, LL_DMAMUX_REQ_GEN_0, LL_DMAMUX_REQ_GEN_POL_RISING);
/* now enable request generation again */
LL_DMAMUX_EnableRequestGen(NULL, LL_DMAMUX_REQ_GEN_0);
/* we need the EXTI to be configured as interrupt generating line, but no ISR registered */
furi_hal_gpio_init_ex(
signal->gpio, GpioModeInterruptRiseFall, GpioPullNo, GpioSpeedVeryHigh, GpioAltFnUnused);
/* capture current timer */
signal->pos = 0;
signal->start_level = furi_hal_gpio_read(signal->gpio);
signal->timer_value = TIM2->CNT;
signal->gpio_mask = signal->gpio->pin;
/* now set up DMA with these settings */
LL_DMA_Init(DMA1, signal->dma_channel, &signal->dma_config_timer);
LL_DMA_Init(DMA1, signal->dma_channel + 1, &signal->dma_config_gpio);
LL_DMA_EnableChannel(DMA1, signal->dma_channel);
LL_DMA_EnableChannel(DMA1, signal->dma_channel + 1);
}
uint32_t pulse_reader_receive(PulseReader* signal, int timeout_us) {
uint32_t start_time = DWT->CYCCNT;
uint32_t timeout_ticks = timeout_us * (F_TIM2 / 1000000);
do {
/* get the DMA's next write position by reading "remaining length" register */
uint32_t dma_pos =
signal->size - (uint32_t)LL_DMA_GetDataLength(DMA1, signal->dma_channel);
/* the DMA has advanced in the ringbuffer */
if(dma_pos != signal->pos) {
uint32_t delta = signal->timer_buffer[signal->pos] - signal->timer_value;
uint32_t last_gpio_value = signal->gpio_value;
signal->gpio_value = signal->gpio_buffer[signal->pos];
/* check if the GPIO really toggled. if not, we lost an edge :( */
if(((last_gpio_value ^ signal->gpio_value) & signal->gpio_mask) != signal->gpio_mask) {
signal->gpio_value ^= signal->gpio_mask;
return PULSE_READER_LOST_EDGE;
}
signal->timer_value = signal->timer_buffer[signal->pos];
signal->pos++;
signal->pos %= signal->size;
uint32_t delta_unit = 0;
/* probably larger values, so choose a wider data type */
if(signal->unit_divider > 1) {
delta_unit =
(uint32_t)((uint64_t)delta * (uint64_t)signal->unit_multiplier / signal->unit_divider);
} else {
delta_unit = delta * signal->unit_multiplier;
}
/* if to be scaled to bit times, save a few instructions. should be faster */
if(signal->bit_time > 1) {
return (delta_unit + signal->bit_time / 2) / signal->bit_time;
}
return delta_unit;
}
/* check for timeout */
uint32_t elapsed = DWT->CYCCNT - start_time;
if(elapsed > timeout_ticks) {
return PULSE_READER_NO_EDGE;
}
} while(true);
}
-130
View File
@@ -1,130 +0,0 @@
#pragma once
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stm32wbxx_ll_dma.h>
#include <stm32wbxx_ll_dmamux.h>
#include <stm32wbxx_ll_tim.h>
#include <stm32wbxx_ll_exti.h>
#include <furi_hal_gpio.h>
#ifdef __cplusplus
extern "C" {
#endif
#define PULSE_READER_NO_EDGE 0xFFFFFFFFUL
#define PULSE_READER_LOST_EDGE 0xFFFFFFFEUL
#define F_TIM2 64000000UL
/**
* unit of the edge durations to return
*/
typedef enum {
PulseReaderUnit64MHz,
PulseReaderUnitPicosecond,
PulseReaderUnitNanosecond,
PulseReaderUnitMicrosecond,
} PulseReaderUnit;
typedef struct {
bool start_level;
uint32_t* timer_buffer;
uint32_t* gpio_buffer;
uint32_t size;
uint32_t pos;
uint32_t timer_value;
uint32_t gpio_value;
uint32_t gpio_mask;
uint32_t unit_multiplier;
uint32_t unit_divider;
uint32_t bit_time;
uint32_t dma_channel;
const GpioPin* gpio;
LL_DMA_InitTypeDef dma_config_timer;
LL_DMA_InitTypeDef dma_config_gpio;
} PulseReader;
/** Allocate a PulseReader object
*
* Allocates memory for a ringbuffer and initalizes the object
*
* @param[in] gpio the GPIO to use. will get configured as input.
* @param[in] size number of edges to buffer
*/
PulseReader* pulse_reader_alloc(const GpioPin* gpio, uint32_t size);
/** Free a PulseReader object
*
* Frees all memory of the given object
*
* @param[in] signal previously allocated PulseReader object.
*/
void pulse_reader_free(PulseReader* signal);
/** Start signal capturing
*
* Initializes DMA1, TIM2 and DMAMUX_REQ_GEN_0 to automatically capture timer values
*
* @param[in] signal previously allocated PulseReader object.
*/
void pulse_reader_start(PulseReader* signal);
/** Stop signal capturing
*
* Frees DMA1, TIM2 and DMAMUX_REQ_GEN_0
*
* @param[in] signal previously allocated PulseReader object.
*/
void pulse_reader_stop(PulseReader* signal);
/** Recevie a sample from ringbuffer
*
* Waits for the specified time until a new edge gets detected.
* If not configured otherwise, the pulse duration will be in picosecond resolution.
* If a bittime was configured, the return value will contain the properly rounded
* number of bit times measured.
*
* @param[in] signal previously allocated PulseReader object.
* @param[in] timeout_us time to wait for a signal [µs]
*
* @returns the scaled value of the pulse duration
*/
uint32_t pulse_reader_receive(PulseReader* signal, int timeout_us);
/** Get available samples
*
* Get the number of available samples in the ringbuffer
*
* @param[in] signal previously allocated PulseReader object.
*
* @returns the number of samples in buffer
*/
uint32_t pulse_reader_samples(PulseReader* signal);
/** Set timebase
*
* Set the timebase to be used when returning pulse duration.
*
* @param[in] signal previously allocated PulseReader object.
* @param[in] unit PulseReaderUnit64MHz or PulseReaderUnitPicosecond
*/
void pulse_reader_set_timebase(PulseReader* signal, PulseReaderUnit unit);
/** Set bit time
*
* Set the number of timebase units per bit.
* When set, the pulse_reader_receive() will return an already rounded
* bit count value instead of the raw duration.
*
* Set to 1 to return duration again.
*
* @param[in] signal previously allocated PulseReader object.
* @param[in] bit_time
*/
void pulse_reader_set_bittime(PulseReader* signal, uint32_t bit_time);
#ifdef __cplusplus
}
#endif