Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FL-2245] Introduce Mifare Classic Emulation #1242

Merged
merged 25 commits into from
May 24, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
277b5cb
digital signal: introduce digital signal
gornekich May 16, 2022
e61cedf
nfca: add nfca signal encoder
gornekich May 17, 2022
94abd13
nfc: add mifare classic emulation scene
gornekich May 17, 2022
0696728
nfca: add classic emulation support to lib and hal
gornekich May 17, 2022
5b9d7b2
mifare classic: support basic read commands
gornekich May 17, 2022
1505e15
nfc: add mifare classic menu scene
gornekich May 17, 2022
56d287a
mifare classic: start parsing commands in emulation
gornekich May 19, 2022
571132a
mifare classic: add nested auth
gornekich May 19, 2022
e223ec3
nfc: fix errors
gornekich May 19, 2022
6562c6d
mifare classic: add encrypt function
gornekich May 20, 2022
5c54aae
nfc: fix mifare classic save
gornekich May 20, 2022
5ce1f73
lib hex: add hex uint64_t ASCII parser
gornekich May 20, 2022
0323e82
flipper format: add uint64 hex format support
gornekich May 20, 2022
677d21e
nfc: add mifare classic key map
gornekich May 20, 2022
c78f890
nfc: hide mifare classic keys on emulation
gornekich May 20, 2022
55b0124
mifare classic: add NACK responce
gornekich May 20, 2022
22050dd
nfc: add partial bytes support in transparent mode
gornekich May 20, 2022
7ac896e
nfc: mifare classic add shadow file support
gornekich May 20, 2022
2302273
digital signal: move arr buffer from BSS to heap
gornekich May 20, 2022
eaf2632
mifare classic: process access bits more careful
gornekich May 23, 2022
a81bb61
Merge branch 'dev' into gornek/2245_mifare_classic_emulation
gornekich May 23, 2022
9857a8c
nfca: fix memory leack
gornekich May 23, 2022
790e78e
nfc: format sources
gornekich May 23, 2022
5e98ae5
mifare classic: cleun up
gornekich May 23, 2022
1dbedb1
Merge branch 'dev' into gornek/2245_mifare_classic_emulation
skotopes May 24, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions applications/nfc/nfc.c
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,8 @@ int32_t nfc_app(void* p) {
if(nfc_device_load(nfc->dev, p)) {
if(nfc->dev->format == NfcDeviceSaveFormatMifareUl) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateMifareUl);
} else if(nfc->dev->format == NfcDeviceSaveFormatMifareClassic) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateMifareClassic);
} else {
scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid);
}
Expand Down
29 changes: 28 additions & 1 deletion applications/nfc/nfc_device.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
static const char* nfc_file_header = "Flipper NFC device";
static const uint32_t nfc_file_version = 2;

// Protocols format versions
static const uint32_t nfc_mifare_classic_data_format_version = 1;

NfcDevice* nfc_device_alloc() {
NfcDevice* nfc_dev = malloc(sizeof(NfcDevice));
nfc_dev->storage = furi_record_open("storage");
Expand Down Expand Up @@ -624,15 +627,25 @@ static bool nfc_device_save_mifare_classic_data(FlipperFormat* file, NfcDevice*
// Save Mifare Classic specific data
do {
if(!flipper_format_write_comment_cstr(file, "Mifare Classic specific data")) break;

if(data->type == MfClassicType1k) {
if(!flipper_format_write_string_cstr(file, "Mifare Classic type", "1K")) break;
blocks = 64;
} else if(data->type == MfClassicType4k) {
if(!flipper_format_write_string_cstr(file, "Mifare Classic type", "4K")) break;
blocks = 256;
}
if(!flipper_format_write_comment_cstr(file, "Mifare Classic blocks")) break;
if(!flipper_format_write_uint32(
file, "Data format version", &nfc_mifare_classic_data_format_version, 1))
break;

if(!flipper_format_write_comment_cstr(
file, "Key map is the bit mask indicating valid key in each sector"))
break;
if(!flipper_format_write_hex_uint64(file, "Key A map", &data->key_a_mask, 1)) break;
if(!flipper_format_write_hex_uint64(file, "Key B map", &data->key_b_mask, 1)) break;

if(!flipper_format_write_comment_cstr(file, "Mifare Classic blocks")) break;
bool block_saved = true;
for(size_t i = 0; i < blocks; i++) {
string_printf(temp_str, "Block %d", i);
Expand All @@ -654,6 +667,7 @@ static bool nfc_device_load_mifare_classic_data(FlipperFormat* file, NfcDevice*
bool parsed = false;
MfClassicData* data = &dev->dev_data.mf_classic_data;
string_t temp_str;
uint32_t data_format_version = 0;
string_init(temp_str);
uint16_t data_blocks = 0;

Expand All @@ -669,6 +683,19 @@ static bool nfc_device_load_mifare_classic_data(FlipperFormat* file, NfcDevice*
} else {
break;
}

// Read Mifare Classic format version
if(!flipper_format_read_uint32(file, "Data format version", &data_format_version, 1)) {
// Load unread sectors with zero keys access for backward compatability
if(!flipper_format_rewind(file)) break;
data->key_a_mask = 0xffffffffffffffff;
data->key_b_mask = 0xffffffffffffffff;
} else {
if(data_format_version != nfc_mifare_classic_data_format_version) break;
if(!flipper_format_read_hex_uint64(file, "Key A map", &data->key_a_mask, 1)) break;
if(!flipper_format_read_hex_uint64(file, "Key B map", &data->key_b_mask, 1)) break;
}

// Read Mifare Classic blocks
bool block_read = true;
for(size_t i = 0; i < data_blocks; i++) {
Expand Down
31 changes: 31 additions & 0 deletions applications/nfc/nfc_worker.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <lib/nfc_protocols/mifare_ultralight.h>
#include <lib/nfc_protocols/mifare_classic.h>
#include <lib/nfc_protocols/mifare_desfire.h>
#include <lib/nfc_protocols/nfca.h>

#include "helpers/nfc_mf_classic_dict.h"

Expand Down Expand Up @@ -104,6 +105,8 @@ int32_t nfc_worker_task(void* context) {
nfc_worker_emulate_mifare_ul(nfc_worker);
} else if(nfc_worker->state == NfcWorkerStateReadMifareClassic) {
nfc_worker_mifare_classic_dict_attack(nfc_worker);
} else if(nfc_worker->state == NfcWorkerStateEmulateMifareClassic) {
nfc_worker_emulate_mifare_classic(nfc_worker);
} else if(nfc_worker->state == NfcWorkerStateReadMifareDesfire) {
nfc_worker_read_mifare_desfire(nfc_worker);
}
Expand Down Expand Up @@ -474,6 +477,34 @@ void nfc_worker_mifare_classic_dict_attack(NfcWorker* nfc_worker) {
stream_free(nfc_worker->dict_stream);
}

void nfc_worker_emulate_mifare_classic(NfcWorker* nfc_worker) {
FuriHalNfcTxRxContext tx_rx;
FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data;
MfClassicEmulator emulator = {
.cuid = nfc_util_bytes2num(&nfc_data->uid[nfc_data->uid_len - 4], 4),
.data = nfc_worker->dev_data->mf_classic_data,
.data_changed = false,
};
NfcaSignal* nfca_signal = nfca_signal_alloc();
tx_rx.nfca_signal = nfca_signal;

while(nfc_worker->state == NfcWorkerStateEmulateMifareClassic) {
if(furi_hal_nfc_listen(
nfc_data->uid, nfc_data->uid_len, nfc_data->atqa, nfc_data->sak, true, 300)) {
mf_classic_emulator(&emulator, &tx_rx);
}
}
if(emulator.data_changed) {
nfc_worker->dev_data->mf_classic_data = emulator.data;
if(nfc_worker->callback) {
nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context);
}
emulator.data_changed = false;
}

nfca_signal_free(nfca_signal);
}

void nfc_worker_read_mifare_desfire(NfcWorker* nfc_worker) {
ReturnCode err;
uint8_t tx_buff[64] = {};
Expand Down
1 change: 1 addition & 0 deletions applications/nfc/nfc_worker.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ typedef enum {
NfcWorkerStateReadMifareUltralight,
NfcWorkerStateEmulateMifareUltralight,
NfcWorkerStateReadMifareClassic,
NfcWorkerStateEmulateMifareClassic,
NfcWorkerStateReadMifareDesfire,
// Transition
NfcWorkerStateStop,
Expand Down
2 changes: 2 additions & 0 deletions applications/nfc/scenes/nfc_scene_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,6 @@ ADD_SCENE(nfc, restore_original, RestoreOriginal)
ADD_SCENE(nfc, debug, Debug)
ADD_SCENE(nfc, field, Field)
ADD_SCENE(nfc, read_mifare_classic, ReadMifareClassic)
ADD_SCENE(nfc, emulate_mifare_classic, EmulateMifareClassic)
ADD_SCENE(nfc, mifare_classic_menu, MifareClassicMenu)
ADD_SCENE(nfc, dict_not_found, DictNotFound)
64 changes: 64 additions & 0 deletions applications/nfc/scenes/nfc_scene_emulate_mifare_classic.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
#include "../nfc_i.h"
#include <dolphin/dolphin.h>

#define NFC_MF_CLASSIC_DATA_NOT_CHANGED (0UL)
#define NFC_MF_CLASSIC_DATA_CHANGED (1UL)

void nfc_emulate_mifare_classic_worker_callback(NfcWorkerEvent event, void* context) {
UNUSED(event);
Nfc* nfc = context;

scene_manager_set_scene_state(
nfc->scene_manager, NfcSceneEmulateMifareClassic, NFC_MF_CLASSIC_DATA_CHANGED);
}

void nfc_scene_emulate_mifare_classic_on_enter(void* context) {
Nfc* nfc = context;
DOLPHIN_DEED(DolphinDeedNfcEmulate);

// Setup view
Popup* popup = nfc->popup;
if(strcmp(nfc->dev->dev_name, "")) {
nfc_text_store_set(nfc, "%s", nfc->dev->dev_name);
}
popup_set_icon(popup, 0, 3, &I_RFIDDolphinSend_97x61);
popup_set_header(popup, "Emulating\nMf Classic", 56, 31, AlignLeft, AlignTop);

// Setup and start worker
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup);
nfc_worker_start(
nfc->worker,
NfcWorkerStateEmulateMifareClassic,
&nfc->dev->dev_data,
nfc_emulate_mifare_classic_worker_callback,
nfc);
}

bool nfc_scene_emulate_mifare_classic_on_event(void* context, SceneManagerEvent event) {
Nfc* nfc = context;
bool consumed = false;

if(event.type == SceneManagerEventTypeTick) {
notification_message(nfc->notifications, &sequence_blink_blue_10);
consumed = true;
} else if(event.type == SceneManagerEventTypeBack) {
// Stop worker
nfc_worker_stop(nfc->worker);
// Check if data changed and save in shadow file
if(scene_manager_get_scene_state(nfc->scene_manager, NfcSceneEmulateMifareClassic) ==
NFC_MF_CLASSIC_DATA_CHANGED) {
scene_manager_set_scene_state(
nfc->scene_manager, NfcSceneEmulateMifareClassic, NFC_MF_CLASSIC_DATA_NOT_CHANGED);
nfc_device_save_shadow(nfc->dev, nfc->dev->dev_name);
}
consumed = false;
}
return consumed;
}

void nfc_scene_emulate_mifare_classic_on_exit(void* context) {
Nfc* nfc = context;

// Clear view
popup_reset(nfc->popup);
}
64 changes: 64 additions & 0 deletions applications/nfc/scenes/nfc_scene_mifare_classic_menu.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
#include "../nfc_i.h"

enum SubmenuIndex {
SubmenuIndexSave,
SubmenuIndexEmulate,
};

void nfc_scene_mifare_classic_menu_submenu_callback(void* context, uint32_t index) {
Nfc* nfc = context;

view_dispatcher_send_custom_event(nfc->view_dispatcher, index);
}

void nfc_scene_mifare_classic_menu_on_enter(void* context) {
Nfc* nfc = context;
Submenu* submenu = nfc->submenu;

submenu_add_item(
submenu, "Save", SubmenuIndexSave, nfc_scene_mifare_classic_menu_submenu_callback, nfc);
submenu_add_item(
submenu,
"Emulate",
SubmenuIndexEmulate,
nfc_scene_mifare_classic_menu_submenu_callback,
nfc);
submenu_set_selected_item(
nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMifareUlMenu));

view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu);
}

bool nfc_scene_mifare_classic_menu_on_event(void* context, SceneManagerEvent event) {
Nfc* nfc = context;
bool consumed = false;

if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SubmenuIndexSave) {
scene_manager_set_scene_state(
nfc->scene_manager, NfcSceneMifareUlMenu, SubmenuIndexSave);
nfc->dev->format = NfcDeviceSaveFormatMifareClassic;
// 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_set_scene_state(
nfc->scene_manager, NfcSceneMifareUlMenu, SubmenuIndexEmulate);
scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateMifareClassic);
consumed = true;
}
} else if(event.type == SceneManagerEventTypeBack) {
consumed =
scene_manager_search_and_switch_to_previous_scene(nfc->scene_manager, NfcSceneStart);
}

return consumed;
}

void nfc_scene_mifare_classic_menu_on_exit(void* context) {
Nfc* nfc = context;

// Clear view
submenu_reset(nfc->submenu);
}
3 changes: 1 addition & 2 deletions applications/nfc/scenes/nfc_scene_read_mifare_classic.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ bool nfc_scene_read_mifare_classic_on_event(void* context, SceneManagerEvent eve
consumed = true;
} else if(event.type == SceneManagerEventTypeCustom) {
if(event.event == NfcCustomEventDictAttackDone) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName);
scene_manager_next_scene(nfc->scene_manager, NfcSceneMifareClassicMenu);
consumed = true;
} else if(event.event == NfcWorkerEventDetectedClassic1k) {
dict_attack_card_detected(nfc->dict_attack, MfClassicType1k);
Expand All @@ -71,7 +71,6 @@ bool nfc_scene_read_mifare_classic_on_event(void* context, SceneManagerEvent eve
scene_manager_set_scene_state(
nfc->scene_manager, NfcSceneReadMifareClassic, NfcSceneReadMifareClassicStateDone);
notification_message(nfc->notifications, &sequence_success);
nfc->dev->format = NfcDeviceSaveFormatMifareClassic;
dict_attack_set_result(nfc->dict_attack, true);
consumed = true;
} else if(event.event == NfcWorkerEventFail) {
Expand Down
12 changes: 6 additions & 6 deletions applications/nfc/scenes/nfc_scene_saved_menu.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,11 @@ void nfc_scene_saved_menu_on_enter(void* context) {
SubmenuIndexEmulate,
nfc_scene_saved_menu_submenu_callback,
nfc);
} else if(nfc->dev->format == NfcDeviceSaveFormatMifareUl) {
} else if(
nfc->dev->format == NfcDeviceSaveFormatMifareUl ||
nfc->dev->format == NfcDeviceSaveFormatMifareClassic) {
submenu_add_item(
submenu,
"Emulate Ultralight",
SubmenuIndexEmulate,
nfc_scene_saved_menu_submenu_callback,
nfc);
submenu, "Emulate", SubmenuIndexEmulate, nfc_scene_saved_menu_submenu_callback, nfc);
}
submenu_add_item(
submenu, "Edit UID and Name", SubmenuIndexEdit, nfc_scene_saved_menu_submenu_callback, nfc);
Expand Down Expand Up @@ -64,6 +62,8 @@ bool nfc_scene_saved_menu_on_event(void* context, SceneManagerEvent event) {
if(event.event == SubmenuIndexEmulate) {
if(nfc->dev->format == NfcDeviceSaveFormatMifareUl) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateMifareUl);
} else if(nfc->dev->format == NfcDeviceSaveFormatMifareClassic) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateMifareClassic);
} else {
scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid);
}
Expand Down
2 changes: 1 addition & 1 deletion applications/nfc/views/dict_attack.c
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ static void dict_attack_draw_callback(Canvas* canvas, void* model) {
canvas_draw_str_aligned(canvas, 64, 2, AlignCenter, AlignTop, draw_str);
} else if(m->state == DictAttackStateSuccess) {
canvas_draw_str_aligned(canvas, 64, 2, AlignCenter, AlignTop, "Complete!");
elements_button_right(canvas, "Save");
elements_button_right(canvas, "More");
} else if(m->state == DictAttackStateFail) {
canvas_draw_str_aligned(
canvas, 64, 2, AlignCenter, AlignTop, "Failed to read any sector");
Expand Down
2 changes: 1 addition & 1 deletion core/furi/common_defines.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ extern "C" {
#endif

#ifndef FURI_BIT
#define FURI_BIT(x, n) ((x) >> (n)&1)
#define FURI_BIT(x, n) (((x) >> (n)) & 1)
#endif

#ifndef FURI_IS_IRQ_MASKED
Expand Down
Loading