mirror of
https://github.com/bettse/seader.git
synced 2026-05-05 13:45:10 +00:00
save as MFC
This commit is contained in:
@@ -5,6 +5,7 @@ enum SubmenuIndex {
|
||||
SubmenuIndexSavePicopass,
|
||||
SubmenuIndexSaveRFID,
|
||||
SubmenuIndexSaveSR,
|
||||
SubmenuIndexSaveMFC,
|
||||
};
|
||||
|
||||
void seader_scene_card_menu_submenu_callback(void* context, uint32_t index) {
|
||||
@@ -42,6 +43,12 @@ void seader_scene_card_menu_on_enter(void* context) {
|
||||
seader_scene_card_menu_submenu_callback,
|
||||
seader);
|
||||
}
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
"Save MFC",
|
||||
SubmenuIndexSaveMFC,
|
||||
seader_scene_card_menu_submenu_callback,
|
||||
seader);
|
||||
}
|
||||
|
||||
submenu_set_selected_item(
|
||||
@@ -79,6 +86,12 @@ bool seader_scene_card_menu_on_event(void* context, SceneManagerEvent event) {
|
||||
seader->credential->save_format = SeaderCredentialSaveFormatSR;
|
||||
scene_manager_next_scene(seader->scene_manager, SeaderSceneSaveName);
|
||||
consumed = true;
|
||||
} else if(event.event == SubmenuIndexSaveMFC) {
|
||||
scene_manager_set_scene_state(
|
||||
seader->scene_manager, SeaderSceneCardMenu, SubmenuIndexSaveMFC);
|
||||
seader->credential->save_format = SeaderCredentialSaveFormatMFC;
|
||||
scene_manager_next_scene(seader->scene_manager, SeaderSceneSaveName);
|
||||
consumed = true;
|
||||
}
|
||||
} else if(event.type == SceneManagerEventTypeBack) {
|
||||
consumed = scene_manager_search_and_switch_to_previous_scene(
|
||||
|
||||
@@ -103,6 +103,242 @@ static bool seader_credential_load(SeaderCredential* cred, FuriString* path, boo
|
||||
return parsed;
|
||||
}
|
||||
|
||||
bool seader_credential_save_mfc(SeaderCredential* cred, const char* name) {
|
||||
furi_assert(cred);
|
||||
|
||||
static const char* nfc_file_header = "Flipper NFC device";
|
||||
static const uint32_t nfc_file_version = 3;
|
||||
static const uint32_t nfc_mifare_classic_data_format_version = 2;
|
||||
|
||||
uint8_t uid[4] = {0xDF, 0xC6, 0x9C, 0x05};
|
||||
uint8_t atqa[2] = {0x00, 0x04};
|
||||
uint8_t sak = 0x08;
|
||||
uint8_t manuf_block[16] = {
|
||||
0xDF,
|
||||
0xC6,
|
||||
0x9C,
|
||||
0x05,
|
||||
0x80,
|
||||
0x08,
|
||||
0x04,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x73,
|
||||
0x65,
|
||||
0x61,
|
||||
0x64,
|
||||
0x65,
|
||||
0x72};
|
||||
uint8_t sector0_trailer[16] = {
|
||||
0xa0,
|
||||
0xa1,
|
||||
0xa2,
|
||||
0xa3,
|
||||
0xa4,
|
||||
0xa5,
|
||||
0x78,
|
||||
0x77,
|
||||
0x88,
|
||||
0xc1,
|
||||
0x89,
|
||||
0xec,
|
||||
0xa9,
|
||||
0x7f,
|
||||
0x8c,
|
||||
0x2a};
|
||||
uint8_t sector1_trailer[16] = {
|
||||
0x48,
|
||||
0x49,
|
||||
0x44,
|
||||
0x20,
|
||||
0x49,
|
||||
0x53,
|
||||
0x78,
|
||||
0x77,
|
||||
0x88,
|
||||
0xaa,
|
||||
0x20,
|
||||
0x47,
|
||||
0x52,
|
||||
0x45,
|
||||
0x41,
|
||||
0x54};
|
||||
uint8_t sectorn_trailer[16] = {
|
||||
0xff,
|
||||
0xff,
|
||||
0xff,
|
||||
0xff,
|
||||
0xff,
|
||||
0xff,
|
||||
0xff,
|
||||
0x07,
|
||||
0x80,
|
||||
0x69,
|
||||
0xff,
|
||||
0xff,
|
||||
0xff,
|
||||
0xff,
|
||||
0xff,
|
||||
0xff};
|
||||
uint8_t mad_block[16] = {
|
||||
0x1b,
|
||||
0x01,
|
||||
0x4d,
|
||||
0x48,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00};
|
||||
uint8_t empty_block[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||
uint8_t pacs_block[16] = {0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||
|
||||
bool use_load_path = true;
|
||||
bool saved = false;
|
||||
FlipperFormat* file = flipper_format_file_alloc(cred->storage);
|
||||
FuriString* temp_str;
|
||||
temp_str = furi_string_alloc();
|
||||
|
||||
uint64_t sentinel = 1ULL << cred->bit_length;
|
||||
uint64_t swapped = __builtin_bswap64(cred->credential | sentinel);
|
||||
memcpy(pacs_block + 8, &swapped, sizeof(swapped));
|
||||
|
||||
do {
|
||||
if(use_load_path && !furi_string_empty(cred->load_path)) {
|
||||
// Get directory name
|
||||
path_extract_dirname(furi_string_get_cstr(cred->load_path), temp_str);
|
||||
// Make path to file to save
|
||||
furi_string_cat_printf(temp_str, "/%s%s", name, SEADER_APP_MFC_EXTENSION);
|
||||
} else {
|
||||
furi_string_printf(
|
||||
temp_str, "%s/%s%s", SEADER_APP_MFC_FOLDER, name, SEADER_APP_MFC_EXTENSION);
|
||||
}
|
||||
|
||||
FURI_LOG_D(TAG, "Save as MFC [%s]", furi_string_get_cstr(temp_str));
|
||||
|
||||
// Open file
|
||||
if(!flipper_format_file_open_always(file, furi_string_get_cstr(temp_str))) break;
|
||||
if(!flipper_format_write_header_cstr(file, nfc_file_header, nfc_file_version)) break;
|
||||
// Write nfc device type
|
||||
if(!flipper_format_write_comment_cstr(
|
||||
file, "Nfc device type can be UID, Mifare Ultralight, Mifare Classic or ISO15693"))
|
||||
break;
|
||||
furi_string_set(temp_str, "Mifare Classic");
|
||||
if(!flipper_format_write_string(file, "Device type", temp_str)) break;
|
||||
// Write UID
|
||||
if(!flipper_format_write_comment_cstr(file, "UID is common for all formats")) break;
|
||||
if(!flipper_format_write_hex(file, "UID", uid, 4)) break;
|
||||
// Write ATQA, SAK
|
||||
if(!flipper_format_write_comment_cstr(file, "ISO14443 specific fields")) break;
|
||||
// Save ATQA in MSB order for correct companion apps display
|
||||
if(!flipper_format_write_hex(file, "ATQA", atqa, 2)) break;
|
||||
if(!flipper_format_write_hex(file, "SAK", &sak, 1)) break;
|
||||
if(!flipper_format_write_comment_cstr(file, "Mifare Classic specific data")) break;
|
||||
if(!flipper_format_write_comment_cstr(file, "Made with Seader")) break;
|
||||
if(!flipper_format_write_string_cstr(file, "Mifare Classic type", "1K")) break;
|
||||
uint8_t blocks = 64;
|
||||
|
||||
if(!flipper_format_write_uint32(
|
||||
file, "Data format version", &nfc_mifare_classic_data_format_version, 1))
|
||||
break;
|
||||
if(!flipper_format_write_comment_cstr(
|
||||
file, "Mifare Classic blocks, \'??\' means unknown data"))
|
||||
break;
|
||||
bool block_saved = true;
|
||||
FuriString* block_str;
|
||||
block_str = furi_string_alloc();
|
||||
for(size_t i = 0; i < blocks; i++) {
|
||||
furi_string_printf(temp_str, "Block %d", i);
|
||||
switch(i) {
|
||||
case 0:
|
||||
if(!flipper_format_write_hex(
|
||||
file, furi_string_get_cstr(temp_str), manuf_block, sizeof(manuf_block))) {
|
||||
block_saved = false;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
if(!flipper_format_write_hex(
|
||||
file, furi_string_get_cstr(temp_str), mad_block, sizeof(mad_block))) {
|
||||
block_saved = false;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
if(!flipper_format_write_hex(
|
||||
file,
|
||||
furi_string_get_cstr(temp_str),
|
||||
sector0_trailer,
|
||||
sizeof(sector0_trailer))) {
|
||||
block_saved = false;
|
||||
}
|
||||
break;
|
||||
case 5:
|
||||
if(!flipper_format_write_hex(
|
||||
file, furi_string_get_cstr(temp_str), pacs_block, sizeof(pacs_block))) {
|
||||
block_saved = false;
|
||||
}
|
||||
break;
|
||||
case 7:
|
||||
if(!flipper_format_write_hex(
|
||||
file,
|
||||
furi_string_get_cstr(temp_str),
|
||||
sector1_trailer,
|
||||
sizeof(sector1_trailer))) {
|
||||
block_saved = false;
|
||||
}
|
||||
break;
|
||||
// Trailers
|
||||
case 11:
|
||||
case 15:
|
||||
case 19:
|
||||
case 23:
|
||||
case 27:
|
||||
case 31:
|
||||
case 35:
|
||||
case 39:
|
||||
case 43:
|
||||
case 47:
|
||||
case 51:
|
||||
case 55:
|
||||
case 59:
|
||||
case 63:
|
||||
if(!flipper_format_write_hex(
|
||||
file,
|
||||
furi_string_get_cstr(temp_str),
|
||||
sectorn_trailer,
|
||||
sizeof(sectorn_trailer))) {
|
||||
block_saved = false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if(!flipper_format_write_hex(
|
||||
file, furi_string_get_cstr(temp_str), empty_block, sizeof(empty_block))) {
|
||||
block_saved = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
furi_string_free(block_str);
|
||||
if(!block_saved) break;
|
||||
|
||||
saved = true;
|
||||
} while(false);
|
||||
|
||||
if(!saved) {
|
||||
dialog_message_show_storage_error(cred->dialogs, "Can not save\nfile");
|
||||
}
|
||||
furi_string_free(temp_str);
|
||||
flipper_format_free(file);
|
||||
return saved;
|
||||
}
|
||||
|
||||
bool seader_credential_save_agnostic(SeaderCredential* cred, const char* name) {
|
||||
furi_assert(cred);
|
||||
|
||||
@@ -160,6 +396,8 @@ bool seader_credential_save(SeaderCredential* cred, const char* name) {
|
||||
|
||||
if(cred->save_format == SeaderCredentialSaveFormatAgnostic) {
|
||||
return seader_credential_save_agnostic(cred, name);
|
||||
} else if(cred->save_format == SeaderCredentialSaveFormatMFC) {
|
||||
return seader_credential_save_mfc(cred, name);
|
||||
} else if(
|
||||
cred->save_format == SeaderCredentialSaveFormatPicopass ||
|
||||
cred->save_format == SeaderCredentialSaveFormatSR) {
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
|
||||
#define SEADER_CRED_NAME_MAX_LEN 22
|
||||
#define SEADER_APP_EXTENSION ".credential"
|
||||
#define SEADER_APP_MFC_EXTENSION ".nfc"
|
||||
#define SEADER_APP_MFC_FOLDER ANY_PATH("nfc")
|
||||
|
||||
typedef void (*SeaderLoadingCallback)(void* context, bool state);
|
||||
|
||||
@@ -23,6 +25,7 @@ typedef enum {
|
||||
SeaderCredentialSaveFormatPicopass,
|
||||
SeaderCredentialSaveFormatRFID,
|
||||
SeaderCredentialSaveFormatSR,
|
||||
SeaderCredentialSaveFormatMFC,
|
||||
} SeaderCredentialSaveFormat;
|
||||
|
||||
typedef struct {
|
||||
|
||||
Reference in New Issue
Block a user