forked from luu176/Metroflip
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmetroflip.c
384 lines (316 loc) · 13.7 KB
/
metroflip.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
#include "metroflip_i.h"
#define TAG "Metroflip"
#include "api/metroflip/metroflip_api.h"
#include "api/metroflip/metroflip_api_interface.h"
#include "metroflip_plugins.h"
struct MfClassicKeyCache {
MfClassicDeviceKeys keys;
MfClassicKeyType current_key_type;
uint8_t current_sector;
};
static bool metroflip_custom_event_callback(void* context, uint32_t event) {
furi_assert(context);
Metroflip* app = context;
return scene_manager_handle_custom_event(app->scene_manager, event);
}
static bool metroflip_back_event_callback(void* context) {
furi_assert(context);
Metroflip* app = context;
return scene_manager_handle_back_event(app->scene_manager);
}
Metroflip* metroflip_alloc() {
Metroflip* app = malloc(sizeof(Metroflip));
app->gui = furi_record_open(RECORD_GUI);
//nfc device
app->nfc = nfc_alloc();
app->nfc_device = nfc_device_alloc();
app->detected_protocols = nfc_detected_protocols_alloc();
// key cache
app->mfc_key_cache = mf_classic_key_cache_alloc();
// notifs
app->notifications = furi_record_open(RECORD_NOTIFICATION);
// View Dispatcher and Scene Manager
app->view_dispatcher = view_dispatcher_alloc();
app->scene_manager = scene_manager_alloc(&metroflip_scene_handlers, app);
view_dispatcher_set_event_callback_context(app->view_dispatcher, app);
view_dispatcher_set_custom_event_callback(
app->view_dispatcher, metroflip_custom_event_callback);
view_dispatcher_set_navigation_event_callback(
app->view_dispatcher, metroflip_back_event_callback);
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
// Custom Widget
app->widget = widget_alloc();
view_dispatcher_add_view(
app->view_dispatcher, MetroflipViewWidget, widget_get_view(app->widget));
// Gui Modules
app->submenu = submenu_alloc();
view_dispatcher_add_view(
app->view_dispatcher, MetroflipViewSubmenu, submenu_get_view(app->submenu));
app->text_input = text_input_alloc();
view_dispatcher_add_view(
app->view_dispatcher, MetroflipViewTextInput, text_input_get_view(app->text_input));
app->byte_input = byte_input_alloc();
view_dispatcher_add_view(
app->view_dispatcher, MetroflipViewByteInput, byte_input_get_view(app->byte_input));
app->popup = popup_alloc();
view_dispatcher_add_view(app->view_dispatcher, MetroflipViewPopup, popup_get_view(app->popup));
app->nfc_device = nfc_device_alloc();
// TextBox
app->text_box = text_box_alloc();
view_dispatcher_add_view(
app->view_dispatcher, MetroflipViewTextBox, text_box_get_view(app->text_box));
app->text_box_store = furi_string_alloc();
return app;
}
void metroflip_free(Metroflip* app) {
furi_assert(app);
//nfc device
nfc_free(app->nfc);
nfc_device_free(app->nfc_device);
nfc_detected_protocols_free(app->detected_protocols);
// key cache
mf_classic_key_cache_free(app->mfc_key_cache);
//notifs
furi_record_close(RECORD_NOTIFICATION);
app->notifications = NULL;
// Gui modules
view_dispatcher_remove_view(app->view_dispatcher, MetroflipViewSubmenu);
submenu_free(app->submenu);
view_dispatcher_remove_view(app->view_dispatcher, MetroflipViewTextInput);
text_input_free(app->text_input);
view_dispatcher_remove_view(app->view_dispatcher, MetroflipViewByteInput);
byte_input_free(app->byte_input);
view_dispatcher_remove_view(app->view_dispatcher, MetroflipViewPopup);
popup_free(app->popup);
// Custom Widget
view_dispatcher_remove_view(app->view_dispatcher, MetroflipViewWidget);
widget_free(app->widget);
// TextBox
view_dispatcher_remove_view(app->view_dispatcher, MetroflipViewTextBox);
text_box_free(app->text_box);
furi_string_free(app->text_box_store);
// View Dispatcher and Scene Manager
view_dispatcher_free(app->view_dispatcher);
scene_manager_free(app->scene_manager);
// Records
furi_record_close(RECORD_GUI);
free(app);
}
static const NotificationSequence metroflip_app_sequence_blink_start_blue = {
&message_blink_start_10,
&message_blink_set_color_blue,
&message_do_not_reset,
NULL,
};
static const NotificationSequence metroflip_app_sequence_blink_stop = {
&message_blink_stop,
NULL,
};
void metroflip_app_blink_start(Metroflip* metroflip) {
notification_message(metroflip->notifications, &metroflip_app_sequence_blink_start_blue);
}
void metroflip_app_blink_stop(Metroflip* metroflip) {
notification_message(metroflip->notifications, &metroflip_app_sequence_blink_stop);
}
void metroflip_exit_widget_callback(GuiButtonType result, InputType type, void* context) {
Metroflip* app = context;
UNUSED(result);
if(type == InputTypeShort) {
scene_manager_search_and_switch_to_previous_scene(app->scene_manager, MetroflipSceneStart);
}
}
// Calypso
void byte_to_binary(uint8_t byte, char* bits) {
for(int i = 7; i >= 0; i--) {
bits[7 - i] = (byte & (1 << i)) ? '1' : '0';
}
bits[8] = '\0';
}
int binary_to_decimal(const char binary[]) {
int decimal = 0;
int length = strlen(binary);
for(int i = 0; i < length; i++) {
decimal = decimal * 2 + (binary[i] - '0');
}
return decimal;
}
void locale_format_datetime_cat(FuriString* out, const DateTime* dt, bool time) {
// helper to print datetimes
FuriString* s = furi_string_alloc();
LocaleDateFormat date_format = locale_get_date_format();
const char* separator = (date_format == LocaleDateFormatDMY) ? "." : "/";
locale_format_date(s, dt, date_format, separator);
furi_string_cat(out, s);
if(time) {
locale_format_time(s, dt, locale_get_time_format(), false);
furi_string_cat_printf(out, " ");
furi_string_cat(out, s);
}
furi_string_free(s);
}
// read file
uint8_t read_file[5] = {0x94, 0xb2, 0x01, 0x04, 0x1D};
// ^^^
// |||
// FID
// select app
uint8_t select_app[8] = {0x94, 0xA4, 0x00, 0x00, 0x02, 0x20, 0x00, 0x00};
// ^^^^^^^^^
// |||||||||
// AID: 20XX
uint8_t apdu_success[2] = {0x90, 0x00};
char* bit_slice(const char* bit_representation, int start, int end) {
static char bit_slice[32 * 8 + 1];
strncpy(bit_slice, bit_representation + start, end - start + 1);
bit_slice[end - start + 1] = '\0';
return bit_slice;
}
int bit_slice_to_dec(const char* bit_representation, int start, int end) {
return binary_to_decimal(bit_slice(bit_representation, start, end));
}
extern int32_t metroflip(void* p) {
UNUSED(p);
/*
plugin_manager_load_single(PluginManager * manager, const char* path)
uint32_t plugin_count = plugin_manager_get_count(manager);
FURI_LOG_I(TAG, "Loaded %lu plugin(s)", plugin_count);
for(uint32_t i = 0; i < plugin_count; i++) {
const MetroflipPlugin* plugin = plugin_manager_get_ep(manager, i);
FURI_LOG_I(TAG, "plugin name: %s", plugin->name);
}
*/
Metroflip* app = metroflip_alloc();
scene_manager_set_scene_state(app->scene_manager, MetroflipSceneStart, MetroflipSceneAuto);
scene_manager_next_scene(app->scene_manager, MetroflipSceneStart);
view_dispatcher_run(app->view_dispatcher);
metroflip_free(app);
return 0;
}
KeyfileManager manage_keyfiles(
char uid_str[],
const uint8_t* uid,
size_t uid_len,
MfClassicKeyCache* instance,
uint64_t key_mask_a_required,
uint64_t key_mask_b_required) {
Storage* storage = furi_record_open(RECORD_STORAGE);
File* source = storage_file_alloc(storage);
char source_path[64];
UNUSED(key_mask_b_required);
FURI_LOG_I("TAG", "%s", uid_str);
size_t source_required_size =
strlen("/ext/nfc/.cache/") + strlen(uid_str) + strlen(".keys") + 1;
snprintf(source_path, source_required_size, "/ext/nfc/.cache/%s.keys", uid_str);
bool cache_file = storage_file_open(source, source_path, FSAM_READ, FSOM_OPEN_EXISTING);
/*-----------------Open assets cache file (if exists)------------*/
File* dest = storage_file_alloc(storage);
char dest_path[64];
size_t dest_required_size =
strlen("/ext/nfc/assets/.") + strlen(uid_str) + strlen(".keys") + 1;
snprintf(dest_path, dest_required_size, "/ext/nfc/assets/.%s.keys", uid_str);
bool dest_cache_file = storage_file_open(dest, dest_path, FSAM_READ, FSOM_OPEN_EXISTING);
/*-----------------Check cache file------------*/
if(!cache_file) {
/*-----------------Check assets cache file------------*/
FURI_LOG_I("TAG", "cache dont exist, checking assets");
if(!dest_cache_file) {
FURI_LOG_I("TAG", "assets dont exist, prompting user to fix..");
storage_file_close(source);
storage_file_close(dest);
return MISSING_KEYFILE;
} else {
size_t dest_file_length = storage_file_size(dest);
FURI_LOG_I(
"TAG", "assets exist, but cache doesnt, proceeding to copy assets to cache");
// Close, then open both files
storage_file_close(source);
storage_file_close(dest);
storage_file_open(
source, source_path, FSAM_WRITE, FSOM_OPEN_ALWAYS); // create new file
storage_file_open(
dest, dest_path, FSAM_READ, FSOM_OPEN_EXISTING); // open existing assets keyfile
FURI_LOG_I("TAG", "creating cache file at %s from %s", source_path, dest_path);
/*-----Clone keyfile from assets to cache (creates temporary buffer)----*/
uint8_t* cloned_buffer = malloc(dest_file_length);
storage_file_read(dest, cloned_buffer, dest_file_length);
storage_file_write(source, cloned_buffer, dest_file_length);
free(cloned_buffer);
storage_file_close(source);
storage_file_close(dest);
return SUCCESSFUL;
}
} else {
FURI_LOG_I("TAG", "testing 1");
size_t source_file_length = storage_file_size(source);
FURI_LOG_I("TAG", "testing 2");
storage_file_close(source);
mf_classic_key_cache_load(instance, uid, uid_len);
if(KEY_MASK_BIT_CHECK(key_mask_a_required, instance->keys.key_a_mask) &&
KEY_MASK_BIT_CHECK(key_mask_b_required, instance->keys.key_b_mask)) {
FURI_LOG_I("TAG", "cache exist, creating assets cache if not already exists");
storage_file_close(dest);
storage_file_close(source);
storage_file_open(dest, dest_path, FSAM_WRITE, FSOM_OPEN_ALWAYS);
storage_file_open(source, source_path, FSAM_READ, FSOM_OPEN_EXISTING);
FURI_LOG_I("TAG", "creating assets cache");
/*-----Clone keyfile from assets to cache (creates temporary buffer)----*/
uint8_t* cloned_buffer = malloc(source_file_length);
storage_file_read(source, cloned_buffer, source_file_length);
storage_file_write(dest, cloned_buffer, source_file_length);
free(cloned_buffer);
storage_file_close(source);
storage_file_close(dest);
return SUCCESSFUL;
} else {
FURI_LOG_I("TAG", "incomplete cache file, aborting.");
storage_file_close(source);
storage_file_close(dest);
return INCOMPLETE_KEYFILE;
}
}
FURI_LOG_I("TAG", "proceeding to read");
storage_file_close(source);
storage_file_close(dest);
}
void uid_to_string(const uint8_t* uid, size_t uid_len, char* uid_str, size_t max_len) {
size_t pos = 0;
for(size_t i = 0; i < uid_len && pos + 2 < max_len; ++i) {
pos += snprintf(&uid_str[pos], max_len - pos, "%02X", uid[i]);
}
uid_str[pos] = '\0'; // Null-terminate the string
}
void handle_keyfile_case(
Metroflip* app,
const char* message_title,
const char* log_message,
FuriString* parsed_data,
char card_type[]) {
FURI_LOG_I(card_type, log_message);
dolphin_deed(DolphinDeedNfcReadSuccess);
furi_string_reset(parsed_data);
furi_string_printf(
parsed_data,
"\e#%s\n\n"
"To read a %s, \nyou need to read \nit in NFC "
"app on \nthe flipper, and it\nneeds to show \n32/32 keys and\n"
"16/16 sectors read\n"
"Here is a guide to \nfollow to read \nMIFARE Classic:\n"
"https://flipper.wiki/mifareclassic/\n"
"Once completed, Scan again\n\n",
message_title,
card_type);
widget_add_text_scroll_element(app->widget, 0, 0, 128, 64, furi_string_get_cstr(parsed_data));
widget_add_button_element(
app->widget, GuiButtonTypeRight, "Exit", metroflip_exit_widget_callback, app);
view_dispatcher_switch_to_view(app->view_dispatcher, MetroflipViewWidget);
metroflip_app_blink_stop(app);
}
void metroflip_plugin_manager_alloc(Metroflip* app) {
app->resolver = composite_api_resolver_alloc();
composite_api_resolver_add(app->resolver, firmware_api_interface);
composite_api_resolver_add(app->resolver, metroflip_api_interface);
app->plugin_manager = plugin_manager_alloc(
METROFLIP_SUPPORTED_CARD_PLUGIN_APP_ID,
METROFLIP_SUPPORTED_CARD_PLUGIN_API_VERSION,
composite_api_resolver_get(app->resolver));
}