[FL-2933] Mf Classic initial write, update, detect reader (#1941)
* nfc: introduce nfc write * nfc: add write logic * nfc worker: add write state * nfc: add mfc update logic * nfc: add update success logic * nfc: add custom card for detect reader * nfc: update write logic * nfc: add halt command, add notifications * nfc: add write fail scene * nfc: fixes and clean up * nfc: fix navigation ad notifications * nfc: fix detect reader nfc data setter Co-authored-by: あく <alleteam@gmail.com>
This commit is contained in:
parent
09b622d4ae
commit
93a6e17ce5
@ -36,6 +36,12 @@ ADD_SCENE(nfc, mf_classic_keys_list, MfClassicKeysList)
|
|||||||
ADD_SCENE(nfc, mf_classic_keys_delete, MfClassicKeysDelete)
|
ADD_SCENE(nfc, mf_classic_keys_delete, MfClassicKeysDelete)
|
||||||
ADD_SCENE(nfc, mf_classic_keys_warn_duplicate, MfClassicKeysWarnDuplicate)
|
ADD_SCENE(nfc, mf_classic_keys_warn_duplicate, MfClassicKeysWarnDuplicate)
|
||||||
ADD_SCENE(nfc, mf_classic_dict_attack, MfClassicDictAttack)
|
ADD_SCENE(nfc, mf_classic_dict_attack, MfClassicDictAttack)
|
||||||
|
ADD_SCENE(nfc, mf_classic_write, MfClassicWrite)
|
||||||
|
ADD_SCENE(nfc, mf_classic_write_success, MfClassicWriteSuccess)
|
||||||
|
ADD_SCENE(nfc, mf_classic_write_fail, MfClassicWriteFail)
|
||||||
|
ADD_SCENE(nfc, mf_classic_update, MfClassicUpdate)
|
||||||
|
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_read_success, EmvReadSuccess)
|
||||||
ADD_SCENE(nfc, emv_menu, EmvMenu)
|
ADD_SCENE(nfc, emv_menu, EmvMenu)
|
||||||
ADD_SCENE(nfc, emulate_apdu_sequence, EmulateApduSequence)
|
ADD_SCENE(nfc, emulate_apdu_sequence, EmulateApduSequence)
|
||||||
|
@ -28,6 +28,11 @@ void nfc_scene_detect_reader_on_enter(void* context) {
|
|||||||
|
|
||||||
detect_reader_set_callback(nfc->detect_reader, nfc_scene_detect_reader_callback, nfc);
|
detect_reader_set_callback(nfc->detect_reader, nfc_scene_detect_reader_callback, nfc);
|
||||||
detect_reader_set_nonces_max(nfc->detect_reader, NFC_SCENE_DETECT_READER_PAIR_NONCES_MAX);
|
detect_reader_set_nonces_max(nfc->detect_reader, NFC_SCENE_DETECT_READER_PAIR_NONCES_MAX);
|
||||||
|
NfcDeviceData* dev_data = &nfc->dev->dev_data;
|
||||||
|
if(dev_data->nfc_data.uid_len) {
|
||||||
|
detect_reader_set_uid(
|
||||||
|
nfc->detect_reader, dev_data->nfc_data.uid, dev_data->nfc_data.uid_len);
|
||||||
|
}
|
||||||
|
|
||||||
// Store number of collected nonces in scene state
|
// Store number of collected nonces in scene state
|
||||||
scene_manager_set_scene_state(nfc->scene_manager, NfcSceneDetectReader, 0);
|
scene_manager_set_scene_state(nfc->scene_manager, NfcSceneDetectReader, 0);
|
||||||
|
98
applications/main/nfc/scenes/nfc_scene_mf_classic_update.c
Normal file
98
applications/main/nfc/scenes/nfc_scene_mf_classic_update.c
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
#include "../nfc_i.h"
|
||||||
|
#include <dolphin/dolphin.h>
|
||||||
|
|
||||||
|
enum {
|
||||||
|
NfcSceneMfClassicUpdateStateCardSearch,
|
||||||
|
NfcSceneMfClassicUpdateStateCardFound,
|
||||||
|
};
|
||||||
|
|
||||||
|
bool nfc_mf_classic_update_worker_callback(NfcWorkerEvent event, void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
|
||||||
|
Nfc* nfc = context;
|
||||||
|
view_dispatcher_send_custom_event(nfc->view_dispatcher, event);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nfc_scene_mf_classic_update_setup_view(Nfc* nfc) {
|
||||||
|
Popup* popup = nfc->popup;
|
||||||
|
popup_reset(popup);
|
||||||
|
uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfClassicUpdate);
|
||||||
|
|
||||||
|
if(state == NfcSceneMfClassicUpdateStateCardSearch) {
|
||||||
|
popup_set_text(
|
||||||
|
nfc->popup, "Apply the initial\ncard only", 128, 32, AlignRight, AlignCenter);
|
||||||
|
popup_set_icon(nfc->popup, 0, 8, &I_NFC_manual_60x50);
|
||||||
|
} else {
|
||||||
|
popup_set_header(popup, "Updating\nDon't move...", 52, 32, AlignLeft, AlignCenter);
|
||||||
|
popup_set_icon(popup, 12, 23, &A_Loading_24);
|
||||||
|
}
|
||||||
|
|
||||||
|
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup);
|
||||||
|
}
|
||||||
|
|
||||||
|
void nfc_scene_mf_classic_update_on_enter(void* context) {
|
||||||
|
Nfc* nfc = context;
|
||||||
|
DOLPHIN_DEED(DolphinDeedNfcEmulate);
|
||||||
|
|
||||||
|
scene_manager_set_scene_state(
|
||||||
|
nfc->scene_manager, NfcSceneMfClassicUpdate, NfcSceneMfClassicUpdateStateCardSearch);
|
||||||
|
nfc_scene_mf_classic_update_setup_view(nfc);
|
||||||
|
|
||||||
|
// Setup and start worker
|
||||||
|
nfc_worker_start(
|
||||||
|
nfc->worker,
|
||||||
|
NfcWorkerStateMfClassicUpdate,
|
||||||
|
&nfc->dev->dev_data,
|
||||||
|
nfc_mf_classic_update_worker_callback,
|
||||||
|
nfc);
|
||||||
|
nfc_blink_emulate_start(nfc);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool nfc_scene_mf_classic_update_on_event(void* context, SceneManagerEvent event) {
|
||||||
|
Nfc* nfc = context;
|
||||||
|
bool consumed = false;
|
||||||
|
|
||||||
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
|
if(event.event == NfcWorkerEventSuccess) {
|
||||||
|
nfc_worker_stop(nfc->worker);
|
||||||
|
if(nfc_device_save_shadow(nfc->dev, nfc->dev->dev_name)) {
|
||||||
|
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicUpdateSuccess);
|
||||||
|
} else {
|
||||||
|
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicWrongCard);
|
||||||
|
}
|
||||||
|
consumed = true;
|
||||||
|
} else if(event.event == NfcWorkerEventWrongCard) {
|
||||||
|
nfc_worker_stop(nfc->worker);
|
||||||
|
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicWrongCard);
|
||||||
|
consumed = true;
|
||||||
|
} else if(event.event == NfcWorkerEventCardDetected) {
|
||||||
|
scene_manager_set_scene_state(
|
||||||
|
nfc->scene_manager,
|
||||||
|
NfcSceneMfClassicUpdate,
|
||||||
|
NfcSceneMfClassicUpdateStateCardFound);
|
||||||
|
nfc_scene_mf_classic_update_setup_view(nfc);
|
||||||
|
consumed = true;
|
||||||
|
} else if(event.event == NfcWorkerEventNoCardDetected) {
|
||||||
|
scene_manager_set_scene_state(
|
||||||
|
nfc->scene_manager,
|
||||||
|
NfcSceneMfClassicUpdate,
|
||||||
|
NfcSceneMfClassicUpdateStateCardSearch);
|
||||||
|
nfc_scene_mf_classic_update_setup_view(nfc);
|
||||||
|
consumed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return consumed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nfc_scene_mf_classic_update_on_exit(void* context) {
|
||||||
|
Nfc* nfc = context;
|
||||||
|
nfc_worker_stop(nfc->worker);
|
||||||
|
scene_manager_set_scene_state(
|
||||||
|
nfc->scene_manager, NfcSceneMfClassicUpdate, NfcSceneMfClassicUpdateStateCardSearch);
|
||||||
|
// Clear view
|
||||||
|
popup_reset(nfc->popup);
|
||||||
|
|
||||||
|
nfc_blink_stop(nfc);
|
||||||
|
}
|
@ -0,0 +1,44 @@
|
|||||||
|
#include "../nfc_i.h"
|
||||||
|
#include <dolphin/dolphin.h>
|
||||||
|
|
||||||
|
void nfc_scene_mf_classic_update_success_popup_callback(void* context) {
|
||||||
|
Nfc* nfc = context;
|
||||||
|
view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit);
|
||||||
|
}
|
||||||
|
|
||||||
|
void nfc_scene_mf_classic_update_success_on_enter(void* context) {
|
||||||
|
Nfc* nfc = context;
|
||||||
|
DOLPHIN_DEED(DolphinDeedNfcSave);
|
||||||
|
|
||||||
|
notification_message(nfc->notifications, &sequence_success);
|
||||||
|
|
||||||
|
Popup* popup = nfc->popup;
|
||||||
|
popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59);
|
||||||
|
popup_set_header(popup, "Updated!", 11, 20, AlignLeft, AlignBottom);
|
||||||
|
popup_set_timeout(popup, 1500);
|
||||||
|
popup_set_context(popup, nfc);
|
||||||
|
popup_set_callback(popup, nfc_scene_mf_classic_update_success_popup_callback);
|
||||||
|
popup_enable_timeout(popup);
|
||||||
|
|
||||||
|
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool nfc_scene_mf_classic_update_success_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, NfcSceneFileSelect);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return consumed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nfc_scene_mf_classic_update_success_on_exit(void* context) {
|
||||||
|
Nfc* nfc = context;
|
||||||
|
|
||||||
|
// Clear view
|
||||||
|
popup_reset(nfc->popup);
|
||||||
|
}
|
92
applications/main/nfc/scenes/nfc_scene_mf_classic_write.c
Normal file
92
applications/main/nfc/scenes/nfc_scene_mf_classic_write.c
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
#include "../nfc_i.h"
|
||||||
|
#include <dolphin/dolphin.h>
|
||||||
|
|
||||||
|
enum {
|
||||||
|
NfcSceneMfClassicWriteStateCardSearch,
|
||||||
|
NfcSceneMfClassicWriteStateCardFound,
|
||||||
|
};
|
||||||
|
|
||||||
|
bool nfc_mf_classic_write_worker_callback(NfcWorkerEvent event, void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
|
||||||
|
Nfc* nfc = context;
|
||||||
|
view_dispatcher_send_custom_event(nfc->view_dispatcher, event);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nfc_scene_mf_classic_write_setup_view(Nfc* nfc) {
|
||||||
|
Popup* popup = nfc->popup;
|
||||||
|
popup_reset(popup);
|
||||||
|
uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfClassicWrite);
|
||||||
|
|
||||||
|
if(state == NfcSceneMfClassicWriteStateCardSearch) {
|
||||||
|
popup_set_text(
|
||||||
|
nfc->popup, "Apply the initial\ncard only", 128, 32, AlignRight, AlignCenter);
|
||||||
|
popup_set_icon(nfc->popup, 0, 8, &I_NFC_manual_60x50);
|
||||||
|
} else {
|
||||||
|
popup_set_header(popup, "Writing\nDon't move...", 52, 32, AlignLeft, AlignCenter);
|
||||||
|
popup_set_icon(popup, 12, 23, &A_Loading_24);
|
||||||
|
}
|
||||||
|
|
||||||
|
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup);
|
||||||
|
}
|
||||||
|
|
||||||
|
void nfc_scene_mf_classic_write_on_enter(void* context) {
|
||||||
|
Nfc* nfc = context;
|
||||||
|
DOLPHIN_DEED(DolphinDeedNfcEmulate);
|
||||||
|
|
||||||
|
scene_manager_set_scene_state(
|
||||||
|
nfc->scene_manager, NfcSceneMfClassicWrite, NfcSceneMfClassicWriteStateCardSearch);
|
||||||
|
nfc_scene_mf_classic_write_setup_view(nfc);
|
||||||
|
|
||||||
|
// Setup and start worker
|
||||||
|
nfc_worker_start(
|
||||||
|
nfc->worker,
|
||||||
|
NfcWorkerStateMfClassicWrite,
|
||||||
|
&nfc->dev->dev_data,
|
||||||
|
nfc_mf_classic_write_worker_callback,
|
||||||
|
nfc);
|
||||||
|
nfc_blink_emulate_start(nfc);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool nfc_scene_mf_classic_write_on_event(void* context, SceneManagerEvent event) {
|
||||||
|
Nfc* nfc = context;
|
||||||
|
bool consumed = false;
|
||||||
|
|
||||||
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
|
if(event.event == NfcWorkerEventSuccess) {
|
||||||
|
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicWriteSuccess);
|
||||||
|
consumed = true;
|
||||||
|
} else if(event.event == NfcWorkerEventFail) {
|
||||||
|
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicWriteFail);
|
||||||
|
consumed = true;
|
||||||
|
} else if(event.event == NfcWorkerEventWrongCard) {
|
||||||
|
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicWrongCard);
|
||||||
|
consumed = true;
|
||||||
|
} else if(event.event == NfcWorkerEventCardDetected) {
|
||||||
|
scene_manager_set_scene_state(
|
||||||
|
nfc->scene_manager, NfcSceneMfClassicWrite, NfcSceneMfClassicWriteStateCardFound);
|
||||||
|
nfc_scene_mf_classic_write_setup_view(nfc);
|
||||||
|
consumed = true;
|
||||||
|
} else if(event.event == NfcWorkerEventNoCardDetected) {
|
||||||
|
scene_manager_set_scene_state(
|
||||||
|
nfc->scene_manager, NfcSceneMfClassicWrite, NfcSceneMfClassicWriteStateCardSearch);
|
||||||
|
nfc_scene_mf_classic_write_setup_view(nfc);
|
||||||
|
consumed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return consumed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nfc_scene_mf_classic_write_on_exit(void* context) {
|
||||||
|
Nfc* nfc = context;
|
||||||
|
|
||||||
|
nfc_worker_stop(nfc->worker);
|
||||||
|
scene_manager_set_scene_state(
|
||||||
|
nfc->scene_manager, NfcSceneMfClassicWrite, NfcSceneMfClassicWriteStateCardSearch);
|
||||||
|
// Clear view
|
||||||
|
popup_reset(nfc->popup);
|
||||||
|
|
||||||
|
nfc_blink_stop(nfc);
|
||||||
|
}
|
@ -0,0 +1,58 @@
|
|||||||
|
#include "../nfc_i.h"
|
||||||
|
|
||||||
|
void nfc_scene_mf_classic_write_fail_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_mf_classic_write_fail_on_enter(void* context) {
|
||||||
|
Nfc* nfc = context;
|
||||||
|
Widget* widget = nfc->widget;
|
||||||
|
|
||||||
|
notification_message(nfc->notifications, &sequence_error);
|
||||||
|
|
||||||
|
widget_add_icon_element(widget, 72, 17, &I_DolphinCommon_56x48);
|
||||||
|
widget_add_string_element(
|
||||||
|
widget, 7, 4, AlignLeft, AlignTop, FontPrimary, "Writing gone wrong!");
|
||||||
|
widget_add_string_multiline_element(
|
||||||
|
widget,
|
||||||
|
7,
|
||||||
|
17,
|
||||||
|
AlignLeft,
|
||||||
|
AlignTop,
|
||||||
|
FontSecondary,
|
||||||
|
"Not all sectors\nwere written\ncorrectly.");
|
||||||
|
|
||||||
|
widget_add_button_element(
|
||||||
|
widget, GuiButtonTypeLeft, "Finish", nfc_scene_mf_classic_write_fail_widget_callback, nfc);
|
||||||
|
|
||||||
|
// Setup and start worker
|
||||||
|
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool nfc_scene_mf_classic_write_fail_on_event(void* context, SceneManagerEvent event) {
|
||||||
|
Nfc* nfc = context;
|
||||||
|
bool consumed = false;
|
||||||
|
|
||||||
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
|
if(event.event == GuiButtonTypeLeft) {
|
||||||
|
consumed = scene_manager_search_and_switch_to_previous_scene(
|
||||||
|
nfc->scene_manager, NfcSceneFileSelect);
|
||||||
|
}
|
||||||
|
} else if(event.type == SceneManagerEventTypeBack) {
|
||||||
|
consumed = scene_manager_search_and_switch_to_previous_scene(
|
||||||
|
nfc->scene_manager, NfcSceneSavedMenu);
|
||||||
|
}
|
||||||
|
return consumed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nfc_scene_mf_classic_write_fail_on_exit(void* context) {
|
||||||
|
Nfc* nfc = context;
|
||||||
|
|
||||||
|
widget_reset(nfc->widget);
|
||||||
|
}
|
@ -0,0 +1,44 @@
|
|||||||
|
#include "../nfc_i.h"
|
||||||
|
#include <dolphin/dolphin.h>
|
||||||
|
|
||||||
|
void nfc_scene_mf_classic_write_success_popup_callback(void* context) {
|
||||||
|
Nfc* nfc = context;
|
||||||
|
view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit);
|
||||||
|
}
|
||||||
|
|
||||||
|
void nfc_scene_mf_classic_write_success_on_enter(void* context) {
|
||||||
|
Nfc* nfc = context;
|
||||||
|
DOLPHIN_DEED(DolphinDeedNfcSave);
|
||||||
|
|
||||||
|
notification_message(nfc->notifications, &sequence_success);
|
||||||
|
|
||||||
|
Popup* popup = nfc->popup;
|
||||||
|
popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59);
|
||||||
|
popup_set_header(popup, "Successfully\nwritten", 13, 22, AlignLeft, AlignBottom);
|
||||||
|
popup_set_timeout(popup, 1500);
|
||||||
|
popup_set_context(popup, nfc);
|
||||||
|
popup_set_callback(popup, nfc_scene_mf_classic_write_success_popup_callback);
|
||||||
|
popup_enable_timeout(popup);
|
||||||
|
|
||||||
|
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool nfc_scene_mf_classic_write_success_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, NfcSceneFileSelect);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return consumed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nfc_scene_mf_classic_write_success_on_exit(void* context) {
|
||||||
|
Nfc* nfc = context;
|
||||||
|
|
||||||
|
// Clear view
|
||||||
|
popup_reset(nfc->popup);
|
||||||
|
}
|
@ -0,0 +1,53 @@
|
|||||||
|
#include "../nfc_i.h"
|
||||||
|
|
||||||
|
void nfc_scene_mf_classic_wrong_card_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_mf_classic_wrong_card_on_enter(void* context) {
|
||||||
|
Nfc* nfc = context;
|
||||||
|
Widget* widget = nfc->widget;
|
||||||
|
|
||||||
|
notification_message(nfc->notifications, &sequence_error);
|
||||||
|
|
||||||
|
widget_add_icon_element(widget, 73, 17, &I_DolphinCommon_56x48);
|
||||||
|
widget_add_string_element(
|
||||||
|
widget, 3, 4, AlignLeft, AlignTop, FontPrimary, "This is wrong card");
|
||||||
|
widget_add_string_multiline_element(
|
||||||
|
widget,
|
||||||
|
4,
|
||||||
|
17,
|
||||||
|
AlignLeft,
|
||||||
|
AlignTop,
|
||||||
|
FontSecondary,
|
||||||
|
"Data management\nis only possible\nwith initial card");
|
||||||
|
widget_add_button_element(
|
||||||
|
widget, GuiButtonTypeLeft, "Retry", nfc_scene_mf_classic_wrong_card_widget_callback, nfc);
|
||||||
|
|
||||||
|
// Setup and start worker
|
||||||
|
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool nfc_scene_mf_classic_wrong_card_on_event(void* context, SceneManagerEvent event) {
|
||||||
|
Nfc* nfc = context;
|
||||||
|
bool consumed = false;
|
||||||
|
|
||||||
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
|
if(event.event == GuiButtonTypeLeft) {
|
||||||
|
consumed = scene_manager_previous_scene(nfc->scene_manager);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return consumed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nfc_scene_mf_classic_wrong_card_on_exit(void* context) {
|
||||||
|
Nfc* nfc = context;
|
||||||
|
|
||||||
|
widget_reset(nfc->widget);
|
||||||
|
}
|
@ -4,6 +4,9 @@
|
|||||||
enum SubmenuIndex {
|
enum SubmenuIndex {
|
||||||
SubmenuIndexEmulate,
|
SubmenuIndexEmulate,
|
||||||
SubmenuIndexEditUid,
|
SubmenuIndexEditUid,
|
||||||
|
SubmenuIndexDetectReader,
|
||||||
|
SubmenuIndexWrite,
|
||||||
|
SubmenuIndexUpdate,
|
||||||
SubmenuIndexRename,
|
SubmenuIndexRename,
|
||||||
SubmenuIndexDelete,
|
SubmenuIndexDelete,
|
||||||
SubmenuIndexInfo,
|
SubmenuIndexInfo,
|
||||||
@ -42,6 +45,28 @@ void nfc_scene_saved_menu_on_enter(void* context) {
|
|||||||
submenu_add_item(
|
submenu_add_item(
|
||||||
submenu, "Emulate", SubmenuIndexEmulate, nfc_scene_saved_menu_submenu_callback, nfc);
|
submenu, "Emulate", SubmenuIndexEmulate, nfc_scene_saved_menu_submenu_callback, nfc);
|
||||||
}
|
}
|
||||||
|
if(nfc->dev->format == NfcDeviceSaveFormatMifareClassic) {
|
||||||
|
if(!mf_classic_is_card_read(&nfc->dev->dev_data.mf_classic_data)) {
|
||||||
|
submenu_add_item(
|
||||||
|
submenu,
|
||||||
|
"Detect reader",
|
||||||
|
SubmenuIndexDetectReader,
|
||||||
|
nfc_scene_saved_menu_submenu_callback,
|
||||||
|
nfc);
|
||||||
|
}
|
||||||
|
submenu_add_item(
|
||||||
|
submenu,
|
||||||
|
"Write To Initial Card",
|
||||||
|
SubmenuIndexWrite,
|
||||||
|
nfc_scene_saved_menu_submenu_callback,
|
||||||
|
nfc);
|
||||||
|
submenu_add_item(
|
||||||
|
submenu,
|
||||||
|
"Update From Initial Card",
|
||||||
|
SubmenuIndexUpdate,
|
||||||
|
nfc_scene_saved_menu_submenu_callback,
|
||||||
|
nfc);
|
||||||
|
}
|
||||||
submenu_add_item(
|
submenu_add_item(
|
||||||
submenu, "Info", SubmenuIndexInfo, nfc_scene_saved_menu_submenu_callback, nfc);
|
submenu, "Info", SubmenuIndexInfo, nfc_scene_saved_menu_submenu_callback, nfc);
|
||||||
if(nfc->dev->shadow_file_exist) {
|
if(nfc->dev->shadow_file_exist) {
|
||||||
@ -79,6 +104,15 @@ bool nfc_scene_saved_menu_on_event(void* context, SceneManagerEvent event) {
|
|||||||
}
|
}
|
||||||
DOLPHIN_DEED(DolphinDeedNfcEmulate);
|
DOLPHIN_DEED(DolphinDeedNfcEmulate);
|
||||||
consumed = true;
|
consumed = true;
|
||||||
|
} else if(event.event == SubmenuIndexDetectReader) {
|
||||||
|
scene_manager_next_scene(nfc->scene_manager, NfcSceneDetectReader);
|
||||||
|
consumed = true;
|
||||||
|
} else if(event.event == SubmenuIndexWrite) {
|
||||||
|
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicWrite);
|
||||||
|
consumed = true;
|
||||||
|
} else if(event.event == SubmenuIndexUpdate) {
|
||||||
|
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicUpdate);
|
||||||
|
consumed = true;
|
||||||
} else if(event.event == SubmenuIndexRename) {
|
} else if(event.event == SubmenuIndexRename) {
|
||||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName);
|
scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName);
|
||||||
consumed = true;
|
consumed = true;
|
||||||
|
@ -53,6 +53,7 @@ bool nfc_scene_start_on_event(void* context, SceneManagerEvent event) {
|
|||||||
} else if(event.event == SubmenuIndexDetectReader) {
|
} else if(event.event == SubmenuIndexDetectReader) {
|
||||||
bool sd_exist = storage_sd_status(nfc->dev->storage) == FSE_OK;
|
bool sd_exist = storage_sd_status(nfc->dev->storage) == FSE_OK;
|
||||||
if(sd_exist) {
|
if(sd_exist) {
|
||||||
|
nfc_device_data_clear(&nfc->dev->dev_data);
|
||||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneDetectReader);
|
scene_manager_next_scene(nfc->scene_manager, NfcSceneDetectReader);
|
||||||
DOLPHIN_DEED(DolphinDeedNfcDetectReader);
|
DOLPHIN_DEED(DolphinDeedNfcDetectReader);
|
||||||
} else {
|
} else {
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
#include <assets_icons.h>
|
#include <assets_icons.h>
|
||||||
#include <gui/elements.h>
|
#include <gui/elements.h>
|
||||||
|
|
||||||
|
#define DETECT_READER_UID_MAX_LEN (10)
|
||||||
|
|
||||||
struct DetectReader {
|
struct DetectReader {
|
||||||
View* view;
|
View* view;
|
||||||
DetectReaderDoneCallback callback;
|
DetectReaderDoneCallback callback;
|
||||||
@ -12,6 +14,7 @@ typedef struct {
|
|||||||
uint16_t nonces;
|
uint16_t nonces;
|
||||||
uint16_t nonces_max;
|
uint16_t nonces_max;
|
||||||
DetectReaderState state;
|
DetectReaderState state;
|
||||||
|
FuriString* uid_str;
|
||||||
} DetectReaderViewModel;
|
} DetectReaderViewModel;
|
||||||
|
|
||||||
static void detect_reader_draw_callback(Canvas* canvas, void* model) {
|
static void detect_reader_draw_callback(Canvas* canvas, void* model) {
|
||||||
@ -23,6 +26,10 @@ static void detect_reader_draw_callback(Canvas* canvas, void* model) {
|
|||||||
if(m->state == DetectReaderStateStart) {
|
if(m->state == DetectReaderStateStart) {
|
||||||
snprintf(text, sizeof(text), "Touch the reader");
|
snprintf(text, sizeof(text), "Touch the reader");
|
||||||
canvas_draw_icon(canvas, 21, 13, &I_Move_flipper_26x39);
|
canvas_draw_icon(canvas, 21, 13, &I_Move_flipper_26x39);
|
||||||
|
if(furi_string_size(m->uid_str)) {
|
||||||
|
elements_multiline_text_aligned(
|
||||||
|
canvas, 64, 64, AlignCenter, AlignBottom, furi_string_get_cstr(m->uid_str));
|
||||||
|
}
|
||||||
} else if(m->state == DetectReaderStateReaderDetected) {
|
} else if(m->state == DetectReaderStateReaderDetected) {
|
||||||
snprintf(text, sizeof(text), "Move the Flipper away");
|
snprintf(text, sizeof(text), "Move the Flipper away");
|
||||||
canvas_draw_icon(canvas, 24, 25, &I_Release_arrow_18x15);
|
canvas_draw_icon(canvas, 24, 25, &I_Release_arrow_18x15);
|
||||||
@ -86,12 +93,24 @@ DetectReader* detect_reader_alloc() {
|
|||||||
view_set_input_callback(detect_reader->view, detect_reader_input_callback);
|
view_set_input_callback(detect_reader->view, detect_reader_input_callback);
|
||||||
view_set_context(detect_reader->view, detect_reader);
|
view_set_context(detect_reader->view, detect_reader);
|
||||||
|
|
||||||
|
with_view_model(
|
||||||
|
detect_reader->view,
|
||||||
|
DetectReaderViewModel * model,
|
||||||
|
{ model->uid_str = furi_string_alloc(); },
|
||||||
|
false);
|
||||||
|
|
||||||
return detect_reader;
|
return detect_reader;
|
||||||
}
|
}
|
||||||
|
|
||||||
void detect_reader_free(DetectReader* detect_reader) {
|
void detect_reader_free(DetectReader* detect_reader) {
|
||||||
furi_assert(detect_reader);
|
furi_assert(detect_reader);
|
||||||
|
|
||||||
|
with_view_model(
|
||||||
|
detect_reader->view,
|
||||||
|
DetectReaderViewModel * model,
|
||||||
|
{ furi_string_free(model->uid_str); },
|
||||||
|
false);
|
||||||
|
|
||||||
view_free(detect_reader->view);
|
view_free(detect_reader->view);
|
||||||
free(detect_reader);
|
free(detect_reader);
|
||||||
}
|
}
|
||||||
@ -106,6 +125,7 @@ void detect_reader_reset(DetectReader* detect_reader) {
|
|||||||
model->nonces = 0;
|
model->nonces = 0;
|
||||||
model->nonces_max = 0;
|
model->nonces_max = 0;
|
||||||
model->state = DetectReaderStateStart;
|
model->state = DetectReaderStateStart;
|
||||||
|
furi_string_reset(model->uid_str);
|
||||||
},
|
},
|
||||||
false);
|
false);
|
||||||
}
|
}
|
||||||
@ -152,3 +172,19 @@ void detect_reader_set_state(DetectReader* detect_reader, DetectReaderState stat
|
|||||||
with_view_model(
|
with_view_model(
|
||||||
detect_reader->view, DetectReaderViewModel * model, { model->state = state; }, true);
|
detect_reader->view, DetectReaderViewModel * model, { model->state = state; }, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void detect_reader_set_uid(DetectReader* detect_reader, uint8_t* uid, uint8_t uid_len) {
|
||||||
|
furi_assert(detect_reader);
|
||||||
|
furi_assert(uid);
|
||||||
|
furi_assert(uid_len < DETECT_READER_UID_MAX_LEN);
|
||||||
|
with_view_model(
|
||||||
|
detect_reader->view,
|
||||||
|
DetectReaderViewModel * model,
|
||||||
|
{
|
||||||
|
furi_string_set_str(model->uid_str, "UID:");
|
||||||
|
for(size_t i = 0; i < uid_len; i++) {
|
||||||
|
furi_string_cat_printf(model->uid_str, " %02X", uid[i]);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
true);
|
||||||
|
}
|
||||||
|
@ -32,3 +32,5 @@ void detect_reader_set_nonces_max(DetectReader* detect_reader, uint16_t nonces_m
|
|||||||
void detect_reader_set_nonces_collected(DetectReader* detect_reader, uint16_t nonces_collected);
|
void detect_reader_set_nonces_collected(DetectReader* detect_reader, uint16_t nonces_collected);
|
||||||
|
|
||||||
void detect_reader_set_state(DetectReader* detect_reader, DetectReaderState state);
|
void detect_reader_set_state(DetectReader* detect_reader, DetectReaderState state);
|
||||||
|
|
||||||
|
void detect_reader_set_uid(DetectReader* detect_reader, uint8_t* uid, uint8_t uid_len);
|
||||||
|
@ -620,6 +620,10 @@ uint16_t furi_hal_nfc_bitstream_to_data_and_parity(
|
|||||||
uint16_t in_buff_bits,
|
uint16_t in_buff_bits,
|
||||||
uint8_t* out_data,
|
uint8_t* out_data,
|
||||||
uint8_t* out_parity) {
|
uint8_t* out_parity) {
|
||||||
|
if(in_buff_bits < 8) {
|
||||||
|
out_data[0] = in_buff[0];
|
||||||
|
return in_buff_bits;
|
||||||
|
}
|
||||||
if(in_buff_bits % 9 != 0) {
|
if(in_buff_bits % 9 != 0) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -635,7 +639,7 @@ uint16_t furi_hal_nfc_bitstream_to_data_and_parity(
|
|||||||
bit_processed += 9;
|
bit_processed += 9;
|
||||||
curr_byte++;
|
curr_byte++;
|
||||||
}
|
}
|
||||||
return curr_byte;
|
return curr_byte * 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool furi_hal_nfc_tx_rx(FuriHalNfcTxRxContext* tx_rx, uint16_t timeout_ms) {
|
bool furi_hal_nfc_tx_rx(FuriHalNfcTxRxContext* tx_rx, uint16_t timeout_ms) {
|
||||||
@ -692,8 +696,8 @@ bool furi_hal_nfc_tx_rx(FuriHalNfcTxRxContext* tx_rx, uint16_t timeout_ms) {
|
|||||||
|
|
||||||
if(tx_rx->tx_rx_type == FuriHalNfcTxRxTypeRaw ||
|
if(tx_rx->tx_rx_type == FuriHalNfcTxRxTypeRaw ||
|
||||||
tx_rx->tx_rx_type == FuriHalNfcTxRxTypeRxRaw) {
|
tx_rx->tx_rx_type == FuriHalNfcTxRxTypeRxRaw) {
|
||||||
tx_rx->rx_bits = 8 * furi_hal_nfc_bitstream_to_data_and_parity(
|
tx_rx->rx_bits = furi_hal_nfc_bitstream_to_data_and_parity(
|
||||||
temp_rx_buff, *temp_rx_bits, tx_rx->rx_data, tx_rx->rx_parity);
|
temp_rx_buff, *temp_rx_bits, tx_rx->rx_data, tx_rx->rx_parity);
|
||||||
} else {
|
} else {
|
||||||
memcpy(tx_rx->rx_data, temp_rx_buff, MIN(*temp_rx_bits / 8, FURI_HAL_NFC_DATA_BUFF_SIZE));
|
memcpy(tx_rx->rx_data, temp_rx_buff, MIN(*temp_rx_bits / 8, FURI_HAL_NFC_DATA_BUFF_SIZE));
|
||||||
tx_rx->rx_bits = *temp_rx_bits;
|
tx_rx->rx_bits = *temp_rx_bits;
|
||||||
|
@ -201,10 +201,17 @@ NfcProtocol
|
|||||||
|
|
||||||
FuriHalNfcDevData* reader_analyzer_get_nfc_data(ReaderAnalyzer* instance) {
|
FuriHalNfcDevData* reader_analyzer_get_nfc_data(ReaderAnalyzer* instance) {
|
||||||
furi_assert(instance);
|
furi_assert(instance);
|
||||||
|
instance->nfc_data = reader_analyzer_nfc_data[ReaderAnalyzerNfcDataMfClassic];
|
||||||
return &instance->nfc_data;
|
return &instance->nfc_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void reader_analyzer_set_nfc_data(ReaderAnalyzer* instance, FuriHalNfcDevData* nfc_data) {
|
||||||
|
furi_assert(instance);
|
||||||
|
furi_assert(nfc_data);
|
||||||
|
|
||||||
|
memcpy(&instance->nfc_data, nfc_data, sizeof(FuriHalNfcDevData));
|
||||||
|
}
|
||||||
|
|
||||||
static void reader_analyzer_write(
|
static void reader_analyzer_write(
|
||||||
ReaderAnalyzer* instance,
|
ReaderAnalyzer* instance,
|
||||||
uint8_t* data,
|
uint8_t* data,
|
||||||
|
@ -35,6 +35,8 @@ NfcProtocol
|
|||||||
|
|
||||||
FuriHalNfcDevData* reader_analyzer_get_nfc_data(ReaderAnalyzer* instance);
|
FuriHalNfcDevData* reader_analyzer_get_nfc_data(ReaderAnalyzer* instance);
|
||||||
|
|
||||||
|
void reader_analyzer_set_nfc_data(ReaderAnalyzer* instance, FuriHalNfcDevData* nfc_data);
|
||||||
|
|
||||||
void reader_analyzer_prepare_tx_rx(
|
void reader_analyzer_prepare_tx_rx(
|
||||||
ReaderAnalyzer* instance,
|
ReaderAnalyzer* instance,
|
||||||
FuriHalNfcTxRxContext* tx_rx,
|
FuriHalNfcTxRxContext* tx_rx,
|
||||||
|
@ -1122,6 +1122,13 @@ static bool nfc_device_load_data(NfcDevice* dev, FuriString* path, bool show_dia
|
|||||||
if(!flipper_format_read_hex(file, "UID", data->uid, data->uid_len)) break;
|
if(!flipper_format_read_hex(file, "UID", data->uid, data->uid_len)) break;
|
||||||
if(!flipper_format_read_hex(file, "ATQA", data->atqa, 2)) break;
|
if(!flipper_format_read_hex(file, "ATQA", data->atqa, 2)) break;
|
||||||
if(!flipper_format_read_hex(file, "SAK", &data->sak, 1)) break;
|
if(!flipper_format_read_hex(file, "SAK", &data->sak, 1)) break;
|
||||||
|
// Load CUID
|
||||||
|
uint8_t* cuid_start = data->uid;
|
||||||
|
if(data->uid_len == 7) {
|
||||||
|
cuid_start = &data->uid[3];
|
||||||
|
}
|
||||||
|
data->cuid = (cuid_start[0] << 24) | (cuid_start[1] << 16) | (cuid_start[2] << 8) |
|
||||||
|
(cuid_start[3]);
|
||||||
// Parse other data
|
// Parse other data
|
||||||
if(dev->format == NfcDeviceSaveFormatMifareUl) {
|
if(dev->format == NfcDeviceSaveFormatMifareUl) {
|
||||||
if(!nfc_device_load_mifare_ul_data(file, dev)) break;
|
if(!nfc_device_load_mifare_ul_data(file, dev)) break;
|
||||||
|
@ -99,6 +99,10 @@ int32_t nfc_worker_task(void* context) {
|
|||||||
nfc_worker_emulate_mf_ultralight(nfc_worker);
|
nfc_worker_emulate_mf_ultralight(nfc_worker);
|
||||||
} else if(nfc_worker->state == NfcWorkerStateMfClassicEmulate) {
|
} else if(nfc_worker->state == NfcWorkerStateMfClassicEmulate) {
|
||||||
nfc_worker_emulate_mf_classic(nfc_worker);
|
nfc_worker_emulate_mf_classic(nfc_worker);
|
||||||
|
} else if(nfc_worker->state == NfcWorkerStateMfClassicWrite) {
|
||||||
|
nfc_worker_write_mf_classic(nfc_worker);
|
||||||
|
} else if(nfc_worker->state == NfcWorkerStateMfClassicUpdate) {
|
||||||
|
nfc_worker_update_mf_classic(nfc_worker);
|
||||||
} else if(nfc_worker->state == NfcWorkerStateReadMfUltralightReadAuth) {
|
} else if(nfc_worker->state == NfcWorkerStateReadMfUltralightReadAuth) {
|
||||||
nfc_worker_mf_ultralight_read_auth(nfc_worker);
|
nfc_worker_mf_ultralight_read_auth(nfc_worker);
|
||||||
} else if(nfc_worker->state == NfcWorkerStateMfClassicDictAttack) {
|
} else if(nfc_worker->state == NfcWorkerStateMfClassicDictAttack) {
|
||||||
@ -666,6 +670,144 @@ void nfc_worker_emulate_mf_classic(NfcWorker* nfc_worker) {
|
|||||||
rfal_platform_spi_release();
|
rfal_platform_spi_release();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void nfc_worker_write_mf_classic(NfcWorker* nfc_worker) {
|
||||||
|
FuriHalNfcTxRxContext tx_rx = {};
|
||||||
|
bool card_found_notified = false;
|
||||||
|
FuriHalNfcDevData nfc_data = {};
|
||||||
|
MfClassicData* src_data = &nfc_worker->dev_data->mf_classic_data;
|
||||||
|
MfClassicData dest_data = *src_data;
|
||||||
|
|
||||||
|
while(nfc_worker->state == NfcWorkerStateMfClassicWrite) {
|
||||||
|
if(furi_hal_nfc_detect(&nfc_data, 200)) {
|
||||||
|
if(!card_found_notified) {
|
||||||
|
nfc_worker->callback(NfcWorkerEventCardDetected, nfc_worker->context);
|
||||||
|
card_found_notified = true;
|
||||||
|
}
|
||||||
|
furi_hal_nfc_sleep();
|
||||||
|
|
||||||
|
FURI_LOG_I(TAG, "Check low level nfc data");
|
||||||
|
if(memcmp(&nfc_data, &nfc_worker->dev_data->nfc_data, sizeof(FuriHalNfcDevData))) {
|
||||||
|
FURI_LOG_E(TAG, "Wrong card");
|
||||||
|
nfc_worker->callback(NfcWorkerEventWrongCard, nfc_worker->context);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
FURI_LOG_I(TAG, "Check mf classic type");
|
||||||
|
MfClassicType type =
|
||||||
|
mf_classic_get_classic_type(nfc_data.atqa[0], nfc_data.atqa[1], nfc_data.sak);
|
||||||
|
if(type != nfc_worker->dev_data->mf_classic_data.type) {
|
||||||
|
FURI_LOG_E(TAG, "Wrong mf classic type");
|
||||||
|
nfc_worker->callback(NfcWorkerEventWrongCard, nfc_worker->context);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set blocks not read
|
||||||
|
mf_classic_set_sector_data_not_read(&dest_data);
|
||||||
|
FURI_LOG_I(TAG, "Updating card sectors");
|
||||||
|
uint8_t total_sectors = mf_classic_get_total_sectors_num(type);
|
||||||
|
bool write_success = true;
|
||||||
|
for(uint8_t i = 0; i < total_sectors; i++) {
|
||||||
|
FURI_LOG_I(TAG, "Reading sector %d", i);
|
||||||
|
mf_classic_read_sector(&tx_rx, &dest_data, i);
|
||||||
|
bool old_data_read = mf_classic_is_sector_data_read(src_data, i);
|
||||||
|
bool new_data_read = mf_classic_is_sector_data_read(&dest_data, i);
|
||||||
|
if(old_data_read != new_data_read) {
|
||||||
|
FURI_LOG_E(TAG, "Failed to update sector %d", i);
|
||||||
|
write_success = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(nfc_worker->state != NfcWorkerStateMfClassicWrite) break;
|
||||||
|
if(!mf_classic_write_sector(&tx_rx, &dest_data, src_data, i)) {
|
||||||
|
FURI_LOG_E(TAG, "Failed to write %d sector", i);
|
||||||
|
write_success = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(nfc_worker->state != NfcWorkerStateMfClassicWrite) break;
|
||||||
|
if(write_success) {
|
||||||
|
nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context);
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
nfc_worker->callback(NfcWorkerEventFail, nfc_worker->context);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if(card_found_notified) {
|
||||||
|
nfc_worker->callback(NfcWorkerEventNoCardDetected, nfc_worker->context);
|
||||||
|
card_found_notified = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
furi_delay_ms(300);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void nfc_worker_update_mf_classic(NfcWorker* nfc_worker) {
|
||||||
|
FuriHalNfcTxRxContext tx_rx = {};
|
||||||
|
bool card_found_notified = false;
|
||||||
|
FuriHalNfcDevData nfc_data = {};
|
||||||
|
MfClassicData* old_data = &nfc_worker->dev_data->mf_classic_data;
|
||||||
|
MfClassicData new_data = *old_data;
|
||||||
|
|
||||||
|
while(nfc_worker->state == NfcWorkerStateMfClassicUpdate) {
|
||||||
|
if(furi_hal_nfc_detect(&nfc_data, 200)) {
|
||||||
|
if(!card_found_notified) {
|
||||||
|
nfc_worker->callback(NfcWorkerEventCardDetected, nfc_worker->context);
|
||||||
|
card_found_notified = true;
|
||||||
|
}
|
||||||
|
furi_hal_nfc_sleep();
|
||||||
|
|
||||||
|
FURI_LOG_I(TAG, "Check low level nfc data");
|
||||||
|
if(memcmp(&nfc_data, &nfc_worker->dev_data->nfc_data, sizeof(FuriHalNfcDevData))) {
|
||||||
|
FURI_LOG_E(TAG, "Low level nfc data mismatch");
|
||||||
|
nfc_worker->callback(NfcWorkerEventWrongCard, nfc_worker->context);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
FURI_LOG_I(TAG, "Check MF classic type");
|
||||||
|
MfClassicType type =
|
||||||
|
mf_classic_get_classic_type(nfc_data.atqa[0], nfc_data.atqa[1], nfc_data.sak);
|
||||||
|
if(type != nfc_worker->dev_data->mf_classic_data.type) {
|
||||||
|
FURI_LOG_E(TAG, "MF classic type mismatch");
|
||||||
|
nfc_worker->callback(NfcWorkerEventWrongCard, nfc_worker->context);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set blocks not read
|
||||||
|
mf_classic_set_sector_data_not_read(&new_data);
|
||||||
|
FURI_LOG_I(TAG, "Updating card sectors");
|
||||||
|
uint8_t total_sectors = mf_classic_get_total_sectors_num(type);
|
||||||
|
bool update_success = true;
|
||||||
|
for(uint8_t i = 0; i < total_sectors; i++) {
|
||||||
|
FURI_LOG_I(TAG, "Reading sector %d", i);
|
||||||
|
mf_classic_read_sector(&tx_rx, &new_data, i);
|
||||||
|
bool old_data_read = mf_classic_is_sector_data_read(old_data, i);
|
||||||
|
bool new_data_read = mf_classic_is_sector_data_read(&new_data, i);
|
||||||
|
if(old_data_read != new_data_read) {
|
||||||
|
FURI_LOG_E(TAG, "Failed to update sector %d", i);
|
||||||
|
update_success = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(nfc_worker->state != NfcWorkerStateMfClassicUpdate) break;
|
||||||
|
}
|
||||||
|
if(nfc_worker->state != NfcWorkerStateMfClassicUpdate) break;
|
||||||
|
|
||||||
|
// Check updated data
|
||||||
|
if(update_success) {
|
||||||
|
*old_data = new_data;
|
||||||
|
nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if(card_found_notified) {
|
||||||
|
nfc_worker->callback(NfcWorkerEventNoCardDetected, nfc_worker->context);
|
||||||
|
card_found_notified = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
furi_delay_ms(300);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void nfc_worker_mf_ultralight_read_auth(NfcWorker* nfc_worker) {
|
void nfc_worker_mf_ultralight_read_auth(NfcWorker* nfc_worker) {
|
||||||
furi_assert(nfc_worker);
|
furi_assert(nfc_worker);
|
||||||
furi_assert(nfc_worker->callback);
|
furi_assert(nfc_worker->callback);
|
||||||
@ -758,7 +900,13 @@ void nfc_worker_analyze_reader(NfcWorker* nfc_worker) {
|
|||||||
FuriHalNfcTxRxContext tx_rx = {};
|
FuriHalNfcTxRxContext tx_rx = {};
|
||||||
|
|
||||||
ReaderAnalyzer* reader_analyzer = nfc_worker->reader_analyzer;
|
ReaderAnalyzer* reader_analyzer = nfc_worker->reader_analyzer;
|
||||||
FuriHalNfcDevData* nfc_data = reader_analyzer_get_nfc_data(reader_analyzer);
|
FuriHalNfcDevData* nfc_data = NULL;
|
||||||
|
if(nfc_worker->dev_data->protocol == NfcDeviceProtocolMifareClassic) {
|
||||||
|
nfc_data = &nfc_worker->dev_data->nfc_data;
|
||||||
|
reader_analyzer_set_nfc_data(reader_analyzer, nfc_data);
|
||||||
|
} else {
|
||||||
|
nfc_data = reader_analyzer_get_nfc_data(reader_analyzer);
|
||||||
|
}
|
||||||
MfClassicEmulator emulator = {
|
MfClassicEmulator emulator = {
|
||||||
.cuid = nfc_util_bytes2num(&nfc_data->uid[nfc_data->uid_len - 4], 4),
|
.cuid = nfc_util_bytes2num(&nfc_data->uid[nfc_data->uid_len - 4], 4),
|
||||||
.data = nfc_worker->dev_data->mf_classic_data,
|
.data = nfc_worker->dev_data->mf_classic_data,
|
||||||
|
@ -14,6 +14,8 @@ typedef enum {
|
|||||||
NfcWorkerStateUidEmulate,
|
NfcWorkerStateUidEmulate,
|
||||||
NfcWorkerStateMfUltralightEmulate,
|
NfcWorkerStateMfUltralightEmulate,
|
||||||
NfcWorkerStateMfClassicEmulate,
|
NfcWorkerStateMfClassicEmulate,
|
||||||
|
NfcWorkerStateMfClassicWrite,
|
||||||
|
NfcWorkerStateMfClassicUpdate,
|
||||||
NfcWorkerStateReadMfUltralightReadAuth,
|
NfcWorkerStateReadMfUltralightReadAuth,
|
||||||
NfcWorkerStateMfClassicDictAttack,
|
NfcWorkerStateMfClassicDictAttack,
|
||||||
NfcWorkerStateAnalyzeReader,
|
NfcWorkerStateAnalyzeReader,
|
||||||
@ -48,13 +50,16 @@ typedef enum {
|
|||||||
NfcWorkerEventNoCardDetected,
|
NfcWorkerEventNoCardDetected,
|
||||||
NfcWorkerEventWrongCardDetected,
|
NfcWorkerEventWrongCardDetected,
|
||||||
|
|
||||||
// Mifare Classic events
|
// Read Mifare Classic events
|
||||||
NfcWorkerEventNoDictFound,
|
NfcWorkerEventNoDictFound,
|
||||||
NfcWorkerEventNewSector,
|
NfcWorkerEventNewSector,
|
||||||
NfcWorkerEventNewDictKeyBatch,
|
NfcWorkerEventNewDictKeyBatch,
|
||||||
NfcWorkerEventFoundKeyA,
|
NfcWorkerEventFoundKeyA,
|
||||||
NfcWorkerEventFoundKeyB,
|
NfcWorkerEventFoundKeyB,
|
||||||
|
|
||||||
|
// Write Mifare Classic events
|
||||||
|
NfcWorkerEventWrongCard,
|
||||||
|
|
||||||
// Detect Reader events
|
// Detect Reader events
|
||||||
NfcWorkerEventDetectReaderDetected,
|
NfcWorkerEventDetectReaderDetected,
|
||||||
NfcWorkerEventDetectReaderLost,
|
NfcWorkerEventDetectReaderLost,
|
||||||
|
@ -41,6 +41,10 @@ void nfc_worker_emulate_mf_ultralight(NfcWorker* nfc_worker);
|
|||||||
|
|
||||||
void nfc_worker_emulate_mf_classic(NfcWorker* nfc_worker);
|
void nfc_worker_emulate_mf_classic(NfcWorker* nfc_worker);
|
||||||
|
|
||||||
|
void nfc_worker_write_mf_classic(NfcWorker* nfc_worker);
|
||||||
|
|
||||||
|
void nfc_worker_update_mf_classic(NfcWorker* nfc_worker);
|
||||||
|
|
||||||
void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker);
|
void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker);
|
||||||
|
|
||||||
void nfc_worker_mf_ultralight_read_auth(NfcWorker* nfc_worker);
|
void nfc_worker_mf_ultralight_read_auth(NfcWorker* nfc_worker);
|
||||||
|
@ -73,3 +73,55 @@ uint32_t prng_successor(uint32_t x, uint32_t n) {
|
|||||||
|
|
||||||
return SWAPENDIAN(x);
|
return SWAPENDIAN(x);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void crypto1_decrypt(
|
||||||
|
Crypto1* crypto,
|
||||||
|
uint8_t* encrypted_data,
|
||||||
|
uint16_t encrypted_data_bits,
|
||||||
|
uint8_t* decrypted_data) {
|
||||||
|
furi_assert(crypto);
|
||||||
|
furi_assert(encrypted_data);
|
||||||
|
furi_assert(decrypted_data);
|
||||||
|
|
||||||
|
if(encrypted_data_bits < 8) {
|
||||||
|
uint8_t decrypted_byte = 0;
|
||||||
|
decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_data[0], 0)) << 0;
|
||||||
|
decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_data[0], 1)) << 1;
|
||||||
|
decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_data[0], 2)) << 2;
|
||||||
|
decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_data[0], 3)) << 3;
|
||||||
|
decrypted_data[0] = decrypted_byte;
|
||||||
|
} else {
|
||||||
|
for(size_t i = 0; i < encrypted_data_bits / 8; i++) {
|
||||||
|
decrypted_data[i] = crypto1_byte(crypto, 0, 0) ^ encrypted_data[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void crypto1_encrypt(
|
||||||
|
Crypto1* crypto,
|
||||||
|
uint8_t* keystream,
|
||||||
|
uint8_t* plain_data,
|
||||||
|
uint16_t plain_data_bits,
|
||||||
|
uint8_t* encrypted_data,
|
||||||
|
uint8_t* encrypted_parity) {
|
||||||
|
furi_assert(crypto);
|
||||||
|
furi_assert(plain_data);
|
||||||
|
furi_assert(encrypted_data);
|
||||||
|
furi_assert(encrypted_parity);
|
||||||
|
|
||||||
|
if(plain_data_bits < 8) {
|
||||||
|
encrypted_data[0] = 0;
|
||||||
|
for(size_t i = 0; i < plain_data_bits; i++) {
|
||||||
|
encrypted_data[0] |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(plain_data[0], i)) << i;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
memset(encrypted_parity, 0, plain_data_bits / 8 + 1);
|
||||||
|
for(uint8_t i = 0; i < plain_data_bits / 8; i++) {
|
||||||
|
encrypted_data[i] = crypto1_byte(crypto, keystream ? keystream[i] : 0, 0) ^
|
||||||
|
plain_data[i];
|
||||||
|
encrypted_parity[i / 8] |=
|
||||||
|
(((crypto1_filter(crypto->odd) ^ nfc_util_odd_parity8(plain_data[i])) & 0x01)
|
||||||
|
<< (7 - (i & 0x0007)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -21,3 +21,17 @@ uint32_t crypto1_word(Crypto1* crypto1, uint32_t in, int is_encrypted);
|
|||||||
uint32_t crypto1_filter(uint32_t in);
|
uint32_t crypto1_filter(uint32_t in);
|
||||||
|
|
||||||
uint32_t prng_successor(uint32_t x, uint32_t n);
|
uint32_t prng_successor(uint32_t x, uint32_t n);
|
||||||
|
|
||||||
|
void crypto1_decrypt(
|
||||||
|
Crypto1* crypto,
|
||||||
|
uint8_t* encrypted_data,
|
||||||
|
uint16_t encrypted_data_bits,
|
||||||
|
uint8_t* decrypted_data);
|
||||||
|
|
||||||
|
void crypto1_encrypt(
|
||||||
|
Crypto1* crypto,
|
||||||
|
uint8_t* keystream,
|
||||||
|
uint8_t* plain_data,
|
||||||
|
uint16_t plain_data_bits,
|
||||||
|
uint8_t* encrypted_data,
|
||||||
|
uint8_t* encrypted_parity);
|
||||||
|
@ -9,21 +9,8 @@
|
|||||||
|
|
||||||
#define MF_CLASSIC_AUTH_KEY_A_CMD (0x60U)
|
#define MF_CLASSIC_AUTH_KEY_A_CMD (0x60U)
|
||||||
#define MF_CLASSIC_AUTH_KEY_B_CMD (0x61U)
|
#define MF_CLASSIC_AUTH_KEY_B_CMD (0x61U)
|
||||||
#define MF_CLASSIC_READ_SECT_CMD (0x30)
|
#define MF_CLASSIC_READ_BLOCK_CMD (0x30)
|
||||||
|
#define MF_CLASSIC_WRITE_BLOCK_CMD (0xA0)
|
||||||
typedef enum {
|
|
||||||
MfClassicActionDataRead,
|
|
||||||
MfClassicActionDataWrite,
|
|
||||||
MfClassicActionDataInc,
|
|
||||||
MfClassicActionDataDec,
|
|
||||||
|
|
||||||
MfClassicActionKeyARead,
|
|
||||||
MfClassicActionKeyAWrite,
|
|
||||||
MfClassicActionKeyBRead,
|
|
||||||
MfClassicActionKeyBWrite,
|
|
||||||
MfClassicActionACRead,
|
|
||||||
MfClassicActionACWrite,
|
|
||||||
} MfClassicAction;
|
|
||||||
|
|
||||||
const char* mf_classic_get_type_str(MfClassicType type) {
|
const char* mf_classic_get_type_str(MfClassicType type) {
|
||||||
if(type == MfClassicType1k) {
|
if(type == MfClassicType1k) {
|
||||||
@ -122,6 +109,24 @@ void mf_classic_set_block_read(MfClassicData* data, uint8_t block_num, MfClassic
|
|||||||
FURI_BIT_SET(data->block_read_mask[block_num / 32], block_num % 32);
|
FURI_BIT_SET(data->block_read_mask[block_num / 32], block_num % 32);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool mf_classic_is_sector_data_read(MfClassicData* data, uint8_t sector_num) {
|
||||||
|
furi_assert(data);
|
||||||
|
|
||||||
|
uint8_t first_block = mf_classic_get_first_block_num_of_sector(sector_num);
|
||||||
|
uint8_t total_blocks = mf_classic_get_blocks_num_in_sector(sector_num);
|
||||||
|
bool data_read = true;
|
||||||
|
for(size_t i = first_block; i < first_block + total_blocks; i++) {
|
||||||
|
data_read &= mf_classic_is_block_read(data, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
return data_read;
|
||||||
|
}
|
||||||
|
|
||||||
|
void mf_classic_set_sector_data_not_read(MfClassicData* data) {
|
||||||
|
furi_assert(data);
|
||||||
|
memset(data->block_read_mask, 0, sizeof(data->block_read_mask));
|
||||||
|
}
|
||||||
|
|
||||||
bool mf_classic_is_key_found(MfClassicData* data, uint8_t sector_num, MfClassicKey key_type) {
|
bool mf_classic_is_key_found(MfClassicData* data, uint8_t sector_num, MfClassicKey key_type) {
|
||||||
furi_assert(data);
|
furi_assert(data);
|
||||||
|
|
||||||
@ -190,6 +195,9 @@ void mf_classic_get_read_sectors_and_keys(
|
|||||||
uint8_t* sectors_read,
|
uint8_t* sectors_read,
|
||||||
uint8_t* keys_found) {
|
uint8_t* keys_found) {
|
||||||
furi_assert(data);
|
furi_assert(data);
|
||||||
|
furi_assert(sectors_read);
|
||||||
|
furi_assert(keys_found);
|
||||||
|
|
||||||
*sectors_read = 0;
|
*sectors_read = 0;
|
||||||
*keys_found = 0;
|
*keys_found = 0;
|
||||||
uint8_t sectors_total = mf_classic_get_total_sectors_num(data->type);
|
uint8_t sectors_total = mf_classic_get_total_sectors_num(data->type);
|
||||||
@ -225,12 +233,12 @@ bool mf_classic_is_card_read(MfClassicData* data) {
|
|||||||
return card_read;
|
return card_read;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool mf_classic_is_allowed_access_sector_trailer(
|
bool mf_classic_is_allowed_access_sector_trailer(
|
||||||
MfClassicEmulator* emulator,
|
MfClassicData* data,
|
||||||
uint8_t block_num,
|
uint8_t block_num,
|
||||||
MfClassicKey key,
|
MfClassicKey key,
|
||||||
MfClassicAction action) {
|
MfClassicAction action) {
|
||||||
uint8_t* sector_trailer = emulator->data.block[block_num].value;
|
uint8_t* sector_trailer = data->block[block_num].value;
|
||||||
uint8_t AC = ((sector_trailer[7] >> 5) & 0x04) | ((sector_trailer[8] >> 2) & 0x02) |
|
uint8_t AC = ((sector_trailer[7] >> 5) & 0x04) | ((sector_trailer[8] >> 2) & 0x02) |
|
||||||
((sector_trailer[8] >> 7) & 0x01);
|
((sector_trailer[8] >> 7) & 0x01);
|
||||||
switch(action) {
|
switch(action) {
|
||||||
@ -266,13 +274,13 @@ static bool mf_classic_is_allowed_access_sector_trailer(
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool mf_classic_is_allowed_access_data_block(
|
bool mf_classic_is_allowed_access_data_block(
|
||||||
MfClassicEmulator* emulator,
|
MfClassicData* data,
|
||||||
uint8_t block_num,
|
uint8_t block_num,
|
||||||
MfClassicKey key,
|
MfClassicKey key,
|
||||||
MfClassicAction action) {
|
MfClassicAction action) {
|
||||||
uint8_t* sector_trailer =
|
uint8_t* sector_trailer =
|
||||||
emulator->data.block[mf_classic_get_sector_trailer_num_by_block(block_num)].value;
|
data->block[mf_classic_get_sector_trailer_num_by_block(block_num)].value;
|
||||||
|
|
||||||
uint8_t sector_block;
|
uint8_t sector_block;
|
||||||
if(block_num <= 128) {
|
if(block_num <= 128) {
|
||||||
@ -336,9 +344,10 @@ static bool mf_classic_is_allowed_access(
|
|||||||
MfClassicKey key,
|
MfClassicKey key,
|
||||||
MfClassicAction action) {
|
MfClassicAction action) {
|
||||||
if(mf_classic_is_sector_trailer(block_num)) {
|
if(mf_classic_is_sector_trailer(block_num)) {
|
||||||
return mf_classic_is_allowed_access_sector_trailer(emulator, block_num, key, action);
|
return mf_classic_is_allowed_access_sector_trailer(
|
||||||
|
&emulator->data, block_num, key, action);
|
||||||
} else {
|
} else {
|
||||||
return mf_classic_is_allowed_access_data_block(emulator, block_num, key, action);
|
return mf_classic_is_allowed_access_data_block(&emulator->data, block_num, key, action);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -514,25 +523,17 @@ bool mf_classic_read_block(
|
|||||||
furi_assert(block);
|
furi_assert(block);
|
||||||
|
|
||||||
bool read_block_success = false;
|
bool read_block_success = false;
|
||||||
uint8_t plain_cmd[4] = {MF_CLASSIC_READ_SECT_CMD, block_num, 0x00, 0x00};
|
uint8_t plain_cmd[4] = {MF_CLASSIC_READ_BLOCK_CMD, block_num, 0x00, 0x00};
|
||||||
nfca_append_crc16(plain_cmd, 2);
|
nfca_append_crc16(plain_cmd, 2);
|
||||||
memset(tx_rx->tx_data, 0, sizeof(tx_rx->tx_data));
|
|
||||||
memset(tx_rx->tx_parity, 0, sizeof(tx_rx->tx_parity));
|
|
||||||
|
|
||||||
for(uint8_t i = 0; i < 4; i++) {
|
crypto1_encrypt(crypto, NULL, plain_cmd, 4 * 8, tx_rx->tx_data, tx_rx->tx_parity);
|
||||||
tx_rx->tx_data[i] = crypto1_byte(crypto, 0x00, 0) ^ plain_cmd[i];
|
|
||||||
tx_rx->tx_parity[0] |=
|
|
||||||
((crypto1_filter(crypto->odd) ^ nfc_util_odd_parity8(plain_cmd[i])) & 0x01) << (7 - i);
|
|
||||||
}
|
|
||||||
tx_rx->tx_bits = 4 * 9;
|
tx_rx->tx_bits = 4 * 9;
|
||||||
tx_rx->tx_rx_type = FuriHalNfcTxRxTypeRaw;
|
tx_rx->tx_rx_type = FuriHalNfcTxRxTypeRaw;
|
||||||
|
|
||||||
if(furi_hal_nfc_tx_rx(tx_rx, 50)) {
|
if(furi_hal_nfc_tx_rx(tx_rx, 50)) {
|
||||||
if(tx_rx->rx_bits == 8 * (MF_CLASSIC_BLOCK_SIZE + 2)) {
|
if(tx_rx->rx_bits == 8 * (MF_CLASSIC_BLOCK_SIZE + 2)) {
|
||||||
uint8_t block_received[MF_CLASSIC_BLOCK_SIZE + 2];
|
uint8_t block_received[MF_CLASSIC_BLOCK_SIZE + 2];
|
||||||
for(uint8_t i = 0; i < MF_CLASSIC_BLOCK_SIZE + 2; i++) {
|
crypto1_decrypt(crypto, tx_rx->rx_data, tx_rx->rx_bits, block_received);
|
||||||
block_received[i] = crypto1_byte(crypto, 0, 0) ^ tx_rx->rx_data[i];
|
|
||||||
}
|
|
||||||
uint16_t crc_calc = nfca_get_crc16(block_received, MF_CLASSIC_BLOCK_SIZE);
|
uint16_t crc_calc = nfca_get_crc16(block_received, MF_CLASSIC_BLOCK_SIZE);
|
||||||
uint16_t crc_received = (block_received[MF_CLASSIC_BLOCK_SIZE + 1] << 8) |
|
uint16_t crc_received = (block_received[MF_CLASSIC_BLOCK_SIZE + 1] << 8) |
|
||||||
block_received[MF_CLASSIC_BLOCK_SIZE];
|
block_received[MF_CLASSIC_BLOCK_SIZE];
|
||||||
@ -754,49 +755,6 @@ uint8_t mf_classic_update_card(FuriHalNfcTxRxContext* tx_rx, MfClassicData* data
|
|||||||
return sectors_read;
|
return sectors_read;
|
||||||
}
|
}
|
||||||
|
|
||||||
void mf_crypto1_decrypt(
|
|
||||||
Crypto1* crypto,
|
|
||||||
uint8_t* encrypted_data,
|
|
||||||
uint16_t encrypted_data_bits,
|
|
||||||
uint8_t* decrypted_data) {
|
|
||||||
if(encrypted_data_bits < 8) {
|
|
||||||
uint8_t decrypted_byte = 0;
|
|
||||||
decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_data[0], 0)) << 0;
|
|
||||||
decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_data[0], 1)) << 1;
|
|
||||||
decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_data[0], 2)) << 2;
|
|
||||||
decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_data[0], 3)) << 3;
|
|
||||||
decrypted_data[0] = decrypted_byte;
|
|
||||||
} else {
|
|
||||||
for(size_t i = 0; i < encrypted_data_bits / 8; i++) {
|
|
||||||
decrypted_data[i] = crypto1_byte(crypto, 0, 0) ^ encrypted_data[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void mf_crypto1_encrypt(
|
|
||||||
Crypto1* crypto,
|
|
||||||
uint8_t* keystream,
|
|
||||||
uint8_t* plain_data,
|
|
||||||
uint16_t plain_data_bits,
|
|
||||||
uint8_t* encrypted_data,
|
|
||||||
uint8_t* encrypted_parity) {
|
|
||||||
if(plain_data_bits < 8) {
|
|
||||||
encrypted_data[0] = 0;
|
|
||||||
for(size_t i = 0; i < plain_data_bits; i++) {
|
|
||||||
encrypted_data[0] |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(plain_data[0], i)) << i;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
memset(encrypted_parity, 0, plain_data_bits / 8 + 1);
|
|
||||||
for(uint8_t i = 0; i < plain_data_bits / 8; i++) {
|
|
||||||
encrypted_data[i] = crypto1_byte(crypto, keystream ? keystream[i] : 0, 0) ^
|
|
||||||
plain_data[i];
|
|
||||||
encrypted_parity[i / 8] |=
|
|
||||||
(((crypto1_filter(crypto->odd) ^ nfc_util_odd_parity8(plain_data[i])) & 0x01)
|
|
||||||
<< (7 - (i & 0x0007)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_rx) {
|
bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_rx) {
|
||||||
furi_assert(emulator);
|
furi_assert(emulator);
|
||||||
furi_assert(tx_rx);
|
furi_assert(tx_rx);
|
||||||
@ -819,7 +777,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_
|
|||||||
tx_rx->rx_bits);
|
tx_rx->rx_bits);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
mf_crypto1_decrypt(&emulator->crypto, tx_rx->rx_data, tx_rx->rx_bits, plain_data);
|
crypto1_decrypt(&emulator->crypto, tx_rx->rx_data, tx_rx->rx_bits, plain_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(plain_data[0] == 0x50 && plain_data[1] == 0x00) {
|
if(plain_data[0] == 0x50 && plain_data[1] == 0x00) {
|
||||||
@ -857,7 +815,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_
|
|||||||
tx_rx->tx_bits = sizeof(nt) * 8;
|
tx_rx->tx_bits = sizeof(nt) * 8;
|
||||||
tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent;
|
tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent;
|
||||||
} else {
|
} else {
|
||||||
mf_crypto1_encrypt(
|
crypto1_encrypt(
|
||||||
&emulator->crypto,
|
&emulator->crypto,
|
||||||
nt_keystream,
|
nt_keystream,
|
||||||
nt,
|
nt,
|
||||||
@ -904,7 +862,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_
|
|||||||
uint32_t ans = prng_successor(nonce, 96);
|
uint32_t ans = prng_successor(nonce, 96);
|
||||||
uint8_t responce[4] = {};
|
uint8_t responce[4] = {};
|
||||||
nfc_util_num2bytes(ans, 4, responce);
|
nfc_util_num2bytes(ans, 4, responce);
|
||||||
mf_crypto1_encrypt(
|
crypto1_encrypt(
|
||||||
&emulator->crypto,
|
&emulator->crypto,
|
||||||
NULL,
|
NULL,
|
||||||
responce,
|
responce,
|
||||||
@ -938,7 +896,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_
|
|||||||
// Send NACK
|
// Send NACK
|
||||||
uint8_t nack = 0x04;
|
uint8_t nack = 0x04;
|
||||||
if(is_encrypted) {
|
if(is_encrypted) {
|
||||||
mf_crypto1_encrypt(
|
crypto1_encrypt(
|
||||||
&emulator->crypto, NULL, &nack, 4, tx_rx->tx_data, tx_rx->tx_parity);
|
&emulator->crypto, NULL, &nack, 4, tx_rx->tx_data, tx_rx->tx_parity);
|
||||||
} else {
|
} else {
|
||||||
tx_rx->tx_data[0] = nack;
|
tx_rx->tx_data[0] = nack;
|
||||||
@ -951,7 +909,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_
|
|||||||
}
|
}
|
||||||
nfca_append_crc16(block_data, 16);
|
nfca_append_crc16(block_data, 16);
|
||||||
|
|
||||||
mf_crypto1_encrypt(
|
crypto1_encrypt(
|
||||||
&emulator->crypto,
|
&emulator->crypto,
|
||||||
NULL,
|
NULL,
|
||||||
block_data,
|
block_data,
|
||||||
@ -967,14 +925,14 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_
|
|||||||
}
|
}
|
||||||
// Send ACK
|
// Send ACK
|
||||||
uint8_t ack = 0x0A;
|
uint8_t ack = 0x0A;
|
||||||
mf_crypto1_encrypt(&emulator->crypto, NULL, &ack, 4, tx_rx->tx_data, tx_rx->tx_parity);
|
crypto1_encrypt(&emulator->crypto, NULL, &ack, 4, tx_rx->tx_data, tx_rx->tx_parity);
|
||||||
tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent;
|
tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent;
|
||||||
tx_rx->tx_bits = 4;
|
tx_rx->tx_bits = 4;
|
||||||
|
|
||||||
if(!furi_hal_nfc_tx_rx(tx_rx, 300)) break;
|
if(!furi_hal_nfc_tx_rx(tx_rx, 300)) break;
|
||||||
if(tx_rx->rx_bits != 18 * 8) break;
|
if(tx_rx->rx_bits != 18 * 8) break;
|
||||||
|
|
||||||
mf_crypto1_decrypt(&emulator->crypto, tx_rx->rx_data, tx_rx->rx_bits, plain_data);
|
crypto1_decrypt(&emulator->crypto, tx_rx->rx_data, tx_rx->rx_bits, plain_data);
|
||||||
uint8_t block_data[16] = {};
|
uint8_t block_data[16] = {};
|
||||||
memcpy(block_data, emulator->data.block[block].value, MF_CLASSIC_BLOCK_SIZE);
|
memcpy(block_data, emulator->data.block[block].value, MF_CLASSIC_BLOCK_SIZE);
|
||||||
if(mf_classic_is_sector_trailer(block)) {
|
if(mf_classic_is_sector_trailer(block)) {
|
||||||
@ -1002,7 +960,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_
|
|||||||
}
|
}
|
||||||
// Send ACK
|
// Send ACK
|
||||||
ack = 0x0A;
|
ack = 0x0A;
|
||||||
mf_crypto1_encrypt(&emulator->crypto, NULL, &ack, 4, tx_rx->tx_data, tx_rx->tx_parity);
|
crypto1_encrypt(&emulator->crypto, NULL, &ack, 4, tx_rx->tx_data, tx_rx->tx_parity);
|
||||||
tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent;
|
tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent;
|
||||||
tx_rx->tx_bits = 4;
|
tx_rx->tx_bits = 4;
|
||||||
} else {
|
} else {
|
||||||
@ -1015,8 +973,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_
|
|||||||
// Send NACK
|
// Send NACK
|
||||||
uint8_t nack = 0x04;
|
uint8_t nack = 0x04;
|
||||||
if(is_encrypted) {
|
if(is_encrypted) {
|
||||||
mf_crypto1_encrypt(
|
crypto1_encrypt(&emulator->crypto, NULL, &nack, 4, tx_rx->tx_data, tx_rx->tx_parity);
|
||||||
&emulator->crypto, NULL, &nack, 4, tx_rx->tx_data, tx_rx->tx_parity);
|
|
||||||
} else {
|
} else {
|
||||||
tx_rx->tx_data[0] = nack;
|
tx_rx->tx_data[0] = nack;
|
||||||
}
|
}
|
||||||
@ -1027,3 +984,143 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool mf_classic_write_block(
|
||||||
|
FuriHalNfcTxRxContext* tx_rx,
|
||||||
|
MfClassicBlock* src_block,
|
||||||
|
uint8_t block_num,
|
||||||
|
MfClassicKey key_type,
|
||||||
|
uint64_t key) {
|
||||||
|
furi_assert(tx_rx);
|
||||||
|
furi_assert(src_block);
|
||||||
|
|
||||||
|
Crypto1 crypto = {};
|
||||||
|
uint8_t plain_data[18] = {};
|
||||||
|
uint8_t resp = 0;
|
||||||
|
bool write_success = false;
|
||||||
|
|
||||||
|
do {
|
||||||
|
furi_hal_nfc_sleep();
|
||||||
|
if(!mf_classic_auth(tx_rx, block_num, key, key_type, &crypto)) {
|
||||||
|
FURI_LOG_D(TAG, "Auth fail");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Send write command
|
||||||
|
plain_data[0] = MF_CLASSIC_WRITE_BLOCK_CMD;
|
||||||
|
plain_data[1] = block_num;
|
||||||
|
nfca_append_crc16(plain_data, 2);
|
||||||
|
crypto1_encrypt(&crypto, NULL, plain_data, 4 * 8, tx_rx->tx_data, tx_rx->tx_parity);
|
||||||
|
tx_rx->tx_bits = 4 * 8;
|
||||||
|
tx_rx->tx_rx_type = FuriHalNfcTxRxTypeRaw;
|
||||||
|
|
||||||
|
if(furi_hal_nfc_tx_rx(tx_rx, 50)) {
|
||||||
|
if(tx_rx->rx_bits == 4) {
|
||||||
|
crypto1_decrypt(&crypto, tx_rx->rx_data, 4, &resp);
|
||||||
|
if(resp != 0x0A) {
|
||||||
|
FURI_LOG_D(TAG, "NACK received on write cmd: %02X", resp);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
FURI_LOG_D(TAG, "Not ACK received");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
FURI_LOG_D(TAG, "Failed to send write cmd");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send data
|
||||||
|
memcpy(plain_data, src_block->value, MF_CLASSIC_BLOCK_SIZE);
|
||||||
|
nfca_append_crc16(plain_data, MF_CLASSIC_BLOCK_SIZE);
|
||||||
|
crypto1_encrypt(
|
||||||
|
&crypto,
|
||||||
|
NULL,
|
||||||
|
plain_data,
|
||||||
|
(MF_CLASSIC_BLOCK_SIZE + 2) * 8,
|
||||||
|
tx_rx->tx_data,
|
||||||
|
tx_rx->tx_parity);
|
||||||
|
tx_rx->tx_bits = (MF_CLASSIC_BLOCK_SIZE + 2) * 8;
|
||||||
|
tx_rx->tx_rx_type = FuriHalNfcTxRxTypeRaw;
|
||||||
|
if(furi_hal_nfc_tx_rx(tx_rx, 50)) {
|
||||||
|
if(tx_rx->rx_bits == 4) {
|
||||||
|
crypto1_decrypt(&crypto, tx_rx->rx_data, 4, &resp);
|
||||||
|
if(resp != 0x0A) {
|
||||||
|
FURI_LOG_D(TAG, "NACK received on sending data");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
FURI_LOG_D(TAG, "Not ACK received");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
FURI_LOG_D(TAG, "Failed to send data");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
write_success = true;
|
||||||
|
|
||||||
|
// Send Halt
|
||||||
|
plain_data[0] = 0x50;
|
||||||
|
plain_data[1] = 0x00;
|
||||||
|
nfca_append_crc16(plain_data, 2);
|
||||||
|
crypto1_encrypt(&crypto, NULL, plain_data, 2 * 8, tx_rx->tx_data, tx_rx->tx_parity);
|
||||||
|
tx_rx->tx_bits = 2 * 8;
|
||||||
|
tx_rx->tx_rx_type = FuriHalNfcTxRxTypeRaw;
|
||||||
|
// No response is expected
|
||||||
|
furi_hal_nfc_tx_rx(tx_rx, 50);
|
||||||
|
} while(false);
|
||||||
|
|
||||||
|
return write_success;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool mf_classic_write_sector(
|
||||||
|
FuriHalNfcTxRxContext* tx_rx,
|
||||||
|
MfClassicData* dest_data,
|
||||||
|
MfClassicData* src_data,
|
||||||
|
uint8_t sec_num) {
|
||||||
|
furi_assert(tx_rx);
|
||||||
|
furi_assert(dest_data);
|
||||||
|
furi_assert(src_data);
|
||||||
|
|
||||||
|
uint8_t first_block = mf_classic_get_first_block_num_of_sector(sec_num);
|
||||||
|
uint8_t total_blocks = mf_classic_get_blocks_num_in_sector(sec_num);
|
||||||
|
MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(dest_data, sec_num);
|
||||||
|
bool key_a_found = mf_classic_is_key_found(dest_data, sec_num, MfClassicKeyA);
|
||||||
|
bool key_b_found = mf_classic_is_key_found(dest_data, sec_num, MfClassicKeyB);
|
||||||
|
|
||||||
|
bool write_success = true;
|
||||||
|
for(size_t i = first_block; i < first_block + total_blocks; i++) {
|
||||||
|
// Compare blocks
|
||||||
|
if(memcmp(dest_data->block[i].value, src_data->block[i].value, MF_CLASSIC_BLOCK_SIZE)) {
|
||||||
|
bool key_a_write_allowed = mf_classic_is_allowed_access_data_block(
|
||||||
|
dest_data, i, MfClassicKeyA, MfClassicActionDataWrite);
|
||||||
|
bool key_b_write_allowed = mf_classic_is_allowed_access_data_block(
|
||||||
|
dest_data, i, MfClassicKeyB, MfClassicActionDataWrite);
|
||||||
|
|
||||||
|
if(key_a_found && key_a_write_allowed) {
|
||||||
|
FURI_LOG_I(TAG, "Writing block %d with key A", i);
|
||||||
|
uint64_t key = nfc_util_bytes2num(sec_tr->key_a, 6);
|
||||||
|
if(!mf_classic_write_block(tx_rx, &src_data->block[i], i, MfClassicKeyA, key)) {
|
||||||
|
FURI_LOG_E(TAG, "Failed to write block %d", i);
|
||||||
|
write_success = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else if(key_b_found && key_b_write_allowed) {
|
||||||
|
FURI_LOG_I(TAG, "Writing block %d with key A", i);
|
||||||
|
uint64_t key = nfc_util_bytes2num(sec_tr->key_b, 6);
|
||||||
|
if(!mf_classic_write_block(tx_rx, &src_data->block[i], i, MfClassicKeyB, key)) {
|
||||||
|
FURI_LOG_E(TAG, "Failed to write block %d", i);
|
||||||
|
write_success = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
FURI_LOG_E(TAG, "Failed to find key with write access");
|
||||||
|
write_success = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
FURI_LOG_D(TAG, "Blocks %d are equal", i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return write_success;
|
||||||
|
}
|
||||||
|
@ -27,6 +27,20 @@ typedef enum {
|
|||||||
MfClassicKeyB,
|
MfClassicKeyB,
|
||||||
} MfClassicKey;
|
} MfClassicKey;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
MfClassicActionDataRead,
|
||||||
|
MfClassicActionDataWrite,
|
||||||
|
MfClassicActionDataInc,
|
||||||
|
MfClassicActionDataDec,
|
||||||
|
|
||||||
|
MfClassicActionKeyARead,
|
||||||
|
MfClassicActionKeyAWrite,
|
||||||
|
MfClassicActionKeyBRead,
|
||||||
|
MfClassicActionKeyBWrite,
|
||||||
|
MfClassicActionACRead,
|
||||||
|
MfClassicActionACWrite,
|
||||||
|
} MfClassicAction;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint8_t value[MF_CLASSIC_BLOCK_SIZE];
|
uint8_t value[MF_CLASSIC_BLOCK_SIZE];
|
||||||
} MfClassicBlock;
|
} MfClassicBlock;
|
||||||
@ -90,6 +104,18 @@ bool mf_classic_is_sector_trailer(uint8_t block);
|
|||||||
|
|
||||||
uint8_t mf_classic_get_sector_by_block(uint8_t block);
|
uint8_t mf_classic_get_sector_by_block(uint8_t block);
|
||||||
|
|
||||||
|
bool mf_classic_is_allowed_access_sector_trailer(
|
||||||
|
MfClassicData* data,
|
||||||
|
uint8_t block_num,
|
||||||
|
MfClassicKey key,
|
||||||
|
MfClassicAction action);
|
||||||
|
|
||||||
|
bool mf_classic_is_allowed_access_data_block(
|
||||||
|
MfClassicData* data,
|
||||||
|
uint8_t block_num,
|
||||||
|
MfClassicKey key,
|
||||||
|
MfClassicAction action);
|
||||||
|
|
||||||
bool mf_classic_is_key_found(MfClassicData* data, uint8_t sector_num, MfClassicKey key_type);
|
bool mf_classic_is_key_found(MfClassicData* data, uint8_t sector_num, MfClassicKey key_type);
|
||||||
|
|
||||||
void mf_classic_set_key_found(
|
void mf_classic_set_key_found(
|
||||||
@ -104,6 +130,10 @@ bool mf_classic_is_block_read(MfClassicData* data, uint8_t block_num);
|
|||||||
|
|
||||||
void mf_classic_set_block_read(MfClassicData* data, uint8_t block_num, MfClassicBlock* block_data);
|
void mf_classic_set_block_read(MfClassicData* data, uint8_t block_num, MfClassicBlock* block_data);
|
||||||
|
|
||||||
|
bool mf_classic_is_sector_data_read(MfClassicData* data, uint8_t sector_num);
|
||||||
|
|
||||||
|
void mf_classic_set_sector_data_not_read(MfClassicData* data);
|
||||||
|
|
||||||
bool mf_classic_is_sector_read(MfClassicData* data, uint8_t sector_num);
|
bool mf_classic_is_sector_read(MfClassicData* data, uint8_t sector_num);
|
||||||
|
|
||||||
bool mf_classic_is_card_read(MfClassicData* data);
|
bool mf_classic_is_card_read(MfClassicData* data);
|
||||||
@ -145,3 +175,16 @@ uint8_t mf_classic_read_card(
|
|||||||
uint8_t mf_classic_update_card(FuriHalNfcTxRxContext* tx_rx, MfClassicData* data);
|
uint8_t mf_classic_update_card(FuriHalNfcTxRxContext* tx_rx, MfClassicData* data);
|
||||||
|
|
||||||
bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_rx);
|
bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_rx);
|
||||||
|
|
||||||
|
bool mf_classic_write_block(
|
||||||
|
FuriHalNfcTxRxContext* tx_rx,
|
||||||
|
MfClassicBlock* src_block,
|
||||||
|
uint8_t block_num,
|
||||||
|
MfClassicKey key_type,
|
||||||
|
uint64_t key);
|
||||||
|
|
||||||
|
bool mf_classic_write_sector(
|
||||||
|
FuriHalNfcTxRxContext* tx_rx,
|
||||||
|
MfClassicData* dest_data,
|
||||||
|
MfClassicData* src_data,
|
||||||
|
uint8_t sec_num);
|
||||||
|
Loading…
Reference in New Issue
Block a user