Skip to content

Commit 070dc19

Browse files
gornekichskotopes
authored and
A. Rager
committed
[FL-2245] Introduce Mifare Classic Emulation (flipperdevices#1242)
* digital signal: introduce digital signal * nfca: add nfca signal encoder * nfc: add mifare classic emulation scene * nfca: add classic emulation support to lib and hal * mifare classic: support basic read commands * nfc: add mifare classic menu scene * mifare classic: start parsing commands in emulation * mifare classic: add nested auth * nfc: fix errors * mifare classic: add encrypt function * nfc: fix mifare classic save * lib hex: add hex uint64_t ASCII parser * flipper format: add uint64 hex format support * nfc: add mifare classic key map * nfc: hide mifare classic keys on emulation * mifare classic: add NACK responce * nfc: add partial bytes support in transparent mode * nfc: mifare classic add shadow file support * digital signal: move arr buffer from BSS to heap * mifare classic: process access bits more careful * nfca: fix memory leack * nfc: format sources * mifare classic: cleun up Co-authored-by: あく <alleteam@gmail.com>
1 parent c9d0c07 commit 070dc19

28 files changed

+1150
-28
lines changed

applications/nfc/nfc.c

+2
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,8 @@ int32_t nfc_app(void* p) {
173173
if(nfc_device_load(nfc->dev, p)) {
174174
if(nfc->dev->format == NfcDeviceSaveFormatMifareUl) {
175175
scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateMifareUl);
176+
} else if(nfc->dev->format == NfcDeviceSaveFormatMifareClassic) {
177+
scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateMifareClassic);
176178
} else {
177179
scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid);
178180
}

applications/nfc/nfc_device.c

+28-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
static const char* nfc_file_header = "Flipper NFC device";
88
static const uint32_t nfc_file_version = 2;
99

10+
// Protocols format versions
11+
static const uint32_t nfc_mifare_classic_data_format_version = 1;
12+
1013
NfcDevice* nfc_device_alloc() {
1114
NfcDevice* nfc_dev = malloc(sizeof(NfcDevice));
1215
nfc_dev->storage = furi_record_open("storage");
@@ -624,15 +627,25 @@ static bool nfc_device_save_mifare_classic_data(FlipperFormat* file, NfcDevice*
624627
// Save Mifare Classic specific data
625628
do {
626629
if(!flipper_format_write_comment_cstr(file, "Mifare Classic specific data")) break;
630+
627631
if(data->type == MfClassicType1k) {
628632
if(!flipper_format_write_string_cstr(file, "Mifare Classic type", "1K")) break;
629633
blocks = 64;
630634
} else if(data->type == MfClassicType4k) {
631635
if(!flipper_format_write_string_cstr(file, "Mifare Classic type", "4K")) break;
632636
blocks = 256;
633637
}
634-
if(!flipper_format_write_comment_cstr(file, "Mifare Classic blocks")) break;
638+
if(!flipper_format_write_uint32(
639+
file, "Data format version", &nfc_mifare_classic_data_format_version, 1))
640+
break;
641+
642+
if(!flipper_format_write_comment_cstr(
643+
file, "Key map is the bit mask indicating valid key in each sector"))
644+
break;
645+
if(!flipper_format_write_hex_uint64(file, "Key A map", &data->key_a_mask, 1)) break;
646+
if(!flipper_format_write_hex_uint64(file, "Key B map", &data->key_b_mask, 1)) break;
635647

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

@@ -669,6 +683,19 @@ static bool nfc_device_load_mifare_classic_data(FlipperFormat* file, NfcDevice*
669683
} else {
670684
break;
671685
}
686+
687+
// Read Mifare Classic format version
688+
if(!flipper_format_read_uint32(file, "Data format version", &data_format_version, 1)) {
689+
// Load unread sectors with zero keys access for backward compatability
690+
if(!flipper_format_rewind(file)) break;
691+
data->key_a_mask = 0xffffffffffffffff;
692+
data->key_b_mask = 0xffffffffffffffff;
693+
} else {
694+
if(data_format_version != nfc_mifare_classic_data_format_version) break;
695+
if(!flipper_format_read_hex_uint64(file, "Key A map", &data->key_a_mask, 1)) break;
696+
if(!flipper_format_read_hex_uint64(file, "Key B map", &data->key_b_mask, 1)) break;
697+
}
698+
672699
// Read Mifare Classic blocks
673700
bool block_read = true;
674701
for(size_t i = 0; i < data_blocks; i++) {

applications/nfc/nfc_worker.c

+31
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include <lib/nfc_protocols/mifare_ultralight.h>
88
#include <lib/nfc_protocols/mifare_classic.h>
99
#include <lib/nfc_protocols/mifare_desfire.h>
10+
#include <lib/nfc_protocols/nfca.h>
1011

1112
#include "helpers/nfc_mf_classic_dict.h"
1213

@@ -104,6 +105,8 @@ int32_t nfc_worker_task(void* context) {
104105
nfc_worker_emulate_mifare_ul(nfc_worker);
105106
} else if(nfc_worker->state == NfcWorkerStateReadMifareClassic) {
106107
nfc_worker_mifare_classic_dict_attack(nfc_worker);
108+
} else if(nfc_worker->state == NfcWorkerStateEmulateMifareClassic) {
109+
nfc_worker_emulate_mifare_classic(nfc_worker);
107110
} else if(nfc_worker->state == NfcWorkerStateReadMifareDesfire) {
108111
nfc_worker_read_mifare_desfire(nfc_worker);
109112
}
@@ -474,6 +477,34 @@ void nfc_worker_mifare_classic_dict_attack(NfcWorker* nfc_worker) {
474477
stream_free(nfc_worker->dict_stream);
475478
}
476479

480+
void nfc_worker_emulate_mifare_classic(NfcWorker* nfc_worker) {
481+
FuriHalNfcTxRxContext tx_rx;
482+
FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data;
483+
MfClassicEmulator emulator = {
484+
.cuid = nfc_util_bytes2num(&nfc_data->uid[nfc_data->uid_len - 4], 4),
485+
.data = nfc_worker->dev_data->mf_classic_data,
486+
.data_changed = false,
487+
};
488+
NfcaSignal* nfca_signal = nfca_signal_alloc();
489+
tx_rx.nfca_signal = nfca_signal;
490+
491+
while(nfc_worker->state == NfcWorkerStateEmulateMifareClassic) {
492+
if(furi_hal_nfc_listen(
493+
nfc_data->uid, nfc_data->uid_len, nfc_data->atqa, nfc_data->sak, true, 300)) {
494+
mf_classic_emulator(&emulator, &tx_rx);
495+
}
496+
}
497+
if(emulator.data_changed) {
498+
nfc_worker->dev_data->mf_classic_data = emulator.data;
499+
if(nfc_worker->callback) {
500+
nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context);
501+
}
502+
emulator.data_changed = false;
503+
}
504+
505+
nfca_signal_free(nfca_signal);
506+
}
507+
477508
void nfc_worker_read_mifare_desfire(NfcWorker* nfc_worker) {
478509
ReturnCode err;
479510
uint8_t tx_buff[64] = {};

applications/nfc/nfc_worker.h

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ typedef enum {
1919
NfcWorkerStateReadMifareUltralight,
2020
NfcWorkerStateEmulateMifareUltralight,
2121
NfcWorkerStateReadMifareClassic,
22+
NfcWorkerStateEmulateMifareClassic,
2223
NfcWorkerStateReadMifareDesfire,
2324
// Transition
2425
NfcWorkerStateStop,

applications/nfc/scenes/nfc_scene_config.h

+2
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,6 @@ ADD_SCENE(nfc, restore_original, RestoreOriginal)
3434
ADD_SCENE(nfc, debug, Debug)
3535
ADD_SCENE(nfc, field, Field)
3636
ADD_SCENE(nfc, read_mifare_classic, ReadMifareClassic)
37+
ADD_SCENE(nfc, emulate_mifare_classic, EmulateMifareClassic)
38+
ADD_SCENE(nfc, mifare_classic_menu, MifareClassicMenu)
3739
ADD_SCENE(nfc, dict_not_found, DictNotFound)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
#include "../nfc_i.h"
2+
#include <dolphin/dolphin.h>
3+
4+
#define NFC_MF_CLASSIC_DATA_NOT_CHANGED (0UL)
5+
#define NFC_MF_CLASSIC_DATA_CHANGED (1UL)
6+
7+
void nfc_emulate_mifare_classic_worker_callback(NfcWorkerEvent event, void* context) {
8+
UNUSED(event);
9+
Nfc* nfc = context;
10+
11+
scene_manager_set_scene_state(
12+
nfc->scene_manager, NfcSceneEmulateMifareClassic, NFC_MF_CLASSIC_DATA_CHANGED);
13+
}
14+
15+
void nfc_scene_emulate_mifare_classic_on_enter(void* context) {
16+
Nfc* nfc = context;
17+
DOLPHIN_DEED(DolphinDeedNfcEmulate);
18+
19+
// Setup view
20+
Popup* popup = nfc->popup;
21+
if(strcmp(nfc->dev->dev_name, "")) {
22+
nfc_text_store_set(nfc, "%s", nfc->dev->dev_name);
23+
}
24+
popup_set_icon(popup, 0, 3, &I_RFIDDolphinSend_97x61);
25+
popup_set_header(popup, "Emulating\nMf Classic", 56, 31, AlignLeft, AlignTop);
26+
27+
// Setup and start worker
28+
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup);
29+
nfc_worker_start(
30+
nfc->worker,
31+
NfcWorkerStateEmulateMifareClassic,
32+
&nfc->dev->dev_data,
33+
nfc_emulate_mifare_classic_worker_callback,
34+
nfc);
35+
}
36+
37+
bool nfc_scene_emulate_mifare_classic_on_event(void* context, SceneManagerEvent event) {
38+
Nfc* nfc = context;
39+
bool consumed = false;
40+
41+
if(event.type == SceneManagerEventTypeTick) {
42+
notification_message(nfc->notifications, &sequence_blink_blue_10);
43+
consumed = true;
44+
} else if(event.type == SceneManagerEventTypeBack) {
45+
// Stop worker
46+
nfc_worker_stop(nfc->worker);
47+
// Check if data changed and save in shadow file
48+
if(scene_manager_get_scene_state(nfc->scene_manager, NfcSceneEmulateMifareClassic) ==
49+
NFC_MF_CLASSIC_DATA_CHANGED) {
50+
scene_manager_set_scene_state(
51+
nfc->scene_manager, NfcSceneEmulateMifareClassic, NFC_MF_CLASSIC_DATA_NOT_CHANGED);
52+
nfc_device_save_shadow(nfc->dev, nfc->dev->dev_name);
53+
}
54+
consumed = false;
55+
}
56+
return consumed;
57+
}
58+
59+
void nfc_scene_emulate_mifare_classic_on_exit(void* context) {
60+
Nfc* nfc = context;
61+
62+
// Clear view
63+
popup_reset(nfc->popup);
64+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
#include "../nfc_i.h"
2+
3+
enum SubmenuIndex {
4+
SubmenuIndexSave,
5+
SubmenuIndexEmulate,
6+
};
7+
8+
void nfc_scene_mifare_classic_menu_submenu_callback(void* context, uint32_t index) {
9+
Nfc* nfc = context;
10+
11+
view_dispatcher_send_custom_event(nfc->view_dispatcher, index);
12+
}
13+
14+
void nfc_scene_mifare_classic_menu_on_enter(void* context) {
15+
Nfc* nfc = context;
16+
Submenu* submenu = nfc->submenu;
17+
18+
submenu_add_item(
19+
submenu, "Save", SubmenuIndexSave, nfc_scene_mifare_classic_menu_submenu_callback, nfc);
20+
submenu_add_item(
21+
submenu,
22+
"Emulate",
23+
SubmenuIndexEmulate,
24+
nfc_scene_mifare_classic_menu_submenu_callback,
25+
nfc);
26+
submenu_set_selected_item(
27+
nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMifareUlMenu));
28+
29+
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu);
30+
}
31+
32+
bool nfc_scene_mifare_classic_menu_on_event(void* context, SceneManagerEvent event) {
33+
Nfc* nfc = context;
34+
bool consumed = false;
35+
36+
if(event.type == SceneManagerEventTypeCustom) {
37+
if(event.event == SubmenuIndexSave) {
38+
scene_manager_set_scene_state(
39+
nfc->scene_manager, NfcSceneMifareUlMenu, SubmenuIndexSave);
40+
nfc->dev->format = NfcDeviceSaveFormatMifareClassic;
41+
// Clear device name
42+
nfc_device_set_name(nfc->dev, "");
43+
scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName);
44+
consumed = true;
45+
} else if(event.event == SubmenuIndexEmulate) {
46+
scene_manager_set_scene_state(
47+
nfc->scene_manager, NfcSceneMifareUlMenu, SubmenuIndexEmulate);
48+
scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateMifareClassic);
49+
consumed = true;
50+
}
51+
} else if(event.type == SceneManagerEventTypeBack) {
52+
consumed =
53+
scene_manager_search_and_switch_to_previous_scene(nfc->scene_manager, NfcSceneStart);
54+
}
55+
56+
return consumed;
57+
}
58+
59+
void nfc_scene_mifare_classic_menu_on_exit(void* context) {
60+
Nfc* nfc = context;
61+
62+
// Clear view
63+
submenu_reset(nfc->submenu);
64+
}

applications/nfc/scenes/nfc_scene_read_mifare_classic.c

+1-2
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ bool nfc_scene_read_mifare_classic_on_event(void* context, SceneManagerEvent eve
4747
consumed = true;
4848
} else if(event.type == SceneManagerEventTypeCustom) {
4949
if(event.event == NfcCustomEventDictAttackDone) {
50-
scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName);
50+
scene_manager_next_scene(nfc->scene_manager, NfcSceneMifareClassicMenu);
5151
consumed = true;
5252
} else if(event.event == NfcWorkerEventDetectedClassic1k) {
5353
dict_attack_card_detected(nfc->dict_attack, MfClassicType1k);
@@ -71,7 +71,6 @@ bool nfc_scene_read_mifare_classic_on_event(void* context, SceneManagerEvent eve
7171
scene_manager_set_scene_state(
7272
nfc->scene_manager, NfcSceneReadMifareClassic, NfcSceneReadMifareClassicStateDone);
7373
notification_message(nfc->notifications, &sequence_success);
74-
nfc->dev->format = NfcDeviceSaveFormatMifareClassic;
7574
dict_attack_set_result(nfc->dict_attack, true);
7675
consumed = true;
7776
} else if(event.event == NfcWorkerEventFail) {

applications/nfc/scenes/nfc_scene_saved_menu.c

+6-6
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,11 @@ void nfc_scene_saved_menu_on_enter(void* context) {
2727
SubmenuIndexEmulate,
2828
nfc_scene_saved_menu_submenu_callback,
2929
nfc);
30-
} else if(nfc->dev->format == NfcDeviceSaveFormatMifareUl) {
30+
} else if(
31+
nfc->dev->format == NfcDeviceSaveFormatMifareUl ||
32+
nfc->dev->format == NfcDeviceSaveFormatMifareClassic) {
3133
submenu_add_item(
32-
submenu,
33-
"Emulate Ultralight",
34-
SubmenuIndexEmulate,
35-
nfc_scene_saved_menu_submenu_callback,
36-
nfc);
34+
submenu, "Emulate", SubmenuIndexEmulate, nfc_scene_saved_menu_submenu_callback, nfc);
3735
}
3836
submenu_add_item(
3937
submenu, "Edit UID and Name", SubmenuIndexEdit, nfc_scene_saved_menu_submenu_callback, nfc);
@@ -64,6 +62,8 @@ bool nfc_scene_saved_menu_on_event(void* context, SceneManagerEvent event) {
6462
if(event.event == SubmenuIndexEmulate) {
6563
if(nfc->dev->format == NfcDeviceSaveFormatMifareUl) {
6664
scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateMifareUl);
65+
} else if(nfc->dev->format == NfcDeviceSaveFormatMifareClassic) {
66+
scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateMifareClassic);
6767
} else {
6868
scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid);
6969
}

applications/nfc/views/dict_attack.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ static void dict_attack_draw_callback(Canvas* canvas, void* model) {
4646
canvas_draw_str_aligned(canvas, 64, 2, AlignCenter, AlignTop, draw_str);
4747
} else if(m->state == DictAttackStateSuccess) {
4848
canvas_draw_str_aligned(canvas, 64, 2, AlignCenter, AlignTop, "Complete!");
49-
elements_button_right(canvas, "Save");
49+
elements_button_right(canvas, "More");
5050
} else if(m->state == DictAttackStateFail) {
5151
canvas_draw_str_aligned(
5252
canvas, 64, 2, AlignCenter, AlignTop, "Failed to read any sector");

core/furi/common_defines.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ extern "C" {
8484
#endif
8585

8686
#ifndef FURI_BIT
87-
#define FURI_BIT(x, n) ((x) >> (n)&1)
87+
#define FURI_BIT(x, n) (((x) >> (n)) & 1)
8888
#endif
8989

9090
#ifndef FURI_IS_IRQ_MASKED

0 commit comments

Comments
 (0)