From d9d5c63ddffc64f28ea879bd6dcff99a55b76ce8 Mon Sep 17 00:00:00 2001 From: Eric Betts Date: Wed, 6 Dec 2023 18:53:22 -0800 Subject: [PATCH] Picopass save as seader (#81) --- picopass/.catalog/changelog.md | 5 ++ picopass/application.fam | 2 +- picopass/picopass_device.c | 62 +++++++++++++++++++++- picopass/picopass_device.h | 1 + picopass/protocol/picopass_poller.c | 10 +++- picopass/scenes/picopass_scene_card_menu.c | 23 +++++++- 6 files changed, 96 insertions(+), 7 deletions(-) diff --git a/picopass/.catalog/changelog.md b/picopass/.catalog/changelog.md index 22e0da95..6008b87f 100644 --- a/picopass/.catalog/changelog.md +++ b/picopass/.catalog/changelog.md @@ -1,3 +1,8 @@ +## 1.9 + - Fix bug (#77) with loclass + - Better loclass notes + - Read card using nr-mac + - Save as Seader format ## 1.8 - Minimal changes for recent API updates ## 1.7 diff --git a/picopass/application.fam b/picopass/application.fam index a4def0c7..5343fdb2 100644 --- a/picopass/application.fam +++ b/picopass/application.fam @@ -10,7 +10,7 @@ App( ], stack_size=4 * 1024, fap_description="App to communicate with NFC tags using the PicoPass(iClass) format", - fap_version="1.8", + fap_version="1.9", fap_icon="125_10px.png", fap_category="NFC", fap_libs=["mbedtls"], diff --git a/picopass/picopass_device.c b/picopass/picopass_device.c index 0d5c74f4..f7b8fa8f 100644 --- a/picopass/picopass_device.c +++ b/picopass/picopass_device.c @@ -34,6 +34,59 @@ void picopass_device_set_name(PicopassDevice* dev, const char* name) { strlcpy(dev->dev_name, name, PICOPASS_DEV_NAME_MAX_LEN); } +// For use with Seader's virtual card processing. +static bool picopass_device_save_file_seader( + PicopassDevice* dev, + FlipperFormat* file, + FuriString* file_path) { + furi_assert(dev); + PicopassPacs* pacs = &dev->dev_data.pacs; + PicopassBlock* AA1 = dev->dev_data.AA1; + bool result = false; + + const char* seader_file_header = "Flipper Seader Credential"; + const uint32_t seader_file_version = 1; + + do { + FURI_LOG_D( + TAG, + "Save %s %ld to %s", + seader_file_header, + seader_file_version, + furi_string_get_cstr(file_path)); + if(!flipper_format_file_open_always(file, furi_string_get_cstr(file_path))) break; + if(!flipper_format_write_header_cstr(file, seader_file_header, seader_file_version)) break; + if(!flipper_format_write_uint32(file, "Bits", (uint32_t*)&pacs->bitLength, 1)) break; + if(!flipper_format_write_hex(file, "Credential", pacs->credential, PICOPASS_BLOCK_LEN)) + break; + + FURI_LOG_D(TAG, "Pre-sio"); + // Seader only captures 64 byte SIO so I'm going to leave it at that + uint8_t sio[64]; + + // TODO: save SR vs SE more properly + if(pacs->sio) { // SR + for(uint8_t i = 0; i < 8; i++) { + memcpy(sio + (i * 8), AA1[10 + i].data, PICOPASS_BLOCK_LEN); + } + if(!flipper_format_write_hex(file, "SIO", sio, sizeof(sio))) break; + } else if(pacs->se_enabled) { //SE + for(uint8_t i = 0; i < 8; i++) { + memcpy(sio + (i * 8), AA1[6 + i].data, PICOPASS_BLOCK_LEN); + } + if(!flipper_format_write_hex(file, "SIO", sio, sizeof(sio))) break; + } + FURI_LOG_D(TAG, "post sio"); + if(!flipper_format_write_hex( + file, "Diversifier", AA1[PICOPASS_CSN_BLOCK_INDEX].data, PICOPASS_BLOCK_LEN)) + break; + + result = true; + } while(false); + + return result; +} + static bool picopass_device_save_file_lfrfid(PicopassDevice* dev, FuriString* file_path) { furi_assert(dev); PicopassPacs* pacs = &dev->dev_data.pacs; @@ -151,11 +204,13 @@ static bool picopass_device_save_file( } } if(!block_saved) break; + saved = true; } else if(dev->format == PicopassDeviceSaveFormatLF) { saved = picopass_device_save_file_lfrfid(dev, temp_str); + } else if(dev->format == PicopassDeviceSaveFormatSeader) { + saved = picopass_device_save_file_seader(dev, file, temp_str); } - saved = true; - } while(0); + } while(false); if(!saved) { dialog_message_show_storage_error(dev->dialogs, "Can not save\nfile"); @@ -171,6 +226,9 @@ bool picopass_device_save(PicopassDevice* dev, const char* dev_name) { dev, dev_name, STORAGE_APP_DATA_PATH_PREFIX, PICOPASS_APP_EXTENSION, true); } else if(dev->format == PicopassDeviceSaveFormatLF) { return picopass_device_save_file(dev, dev_name, ANY_PATH("lfrfid"), ".rfid", true); + } else if(dev->format == PicopassDeviceSaveFormatSeader) { + return picopass_device_save_file( + dev, dev_name, EXT_PATH("apps_data/seader"), ".credential", true); } return false; diff --git a/picopass/picopass_device.h b/picopass/picopass_device.h index b46f2e6e..cace5648 100644 --- a/picopass/picopass_device.h +++ b/picopass/picopass_device.h @@ -70,6 +70,7 @@ typedef enum { typedef enum { PicopassDeviceSaveFormatHF, PicopassDeviceSaveFormatLF, + PicopassDeviceSaveFormatSeader, } PicopassDeviceSaveFormat; typedef enum { diff --git a/picopass/protocol/picopass_poller.c b/picopass/protocol/picopass_poller.c index 0ca614e1..da6a6188 100644 --- a/picopass/protocol/picopass_poller.c +++ b/picopass/protocol/picopass_poller.c @@ -286,8 +286,14 @@ NfcCommand picopass_poller_nr_mac_auth(PicopassPoller* instance) { picopass_poller_prepare_read(instance); instance->state = PicopassPollerStateReadBlock; // Set to non-zero keys to allow emulation - memset(instance->data->AA1[PICOPASS_SECURE_KD_BLOCK_INDEX].data, 0xff, PICOPASS_BLOCK_LEN); - memset(instance->data->AA1[PICOPASS_SECURE_KC_BLOCK_INDEX].data, 0xff, PICOPASS_BLOCK_LEN); + memset( + instance->data->AA1[PICOPASS_SECURE_KD_BLOCK_INDEX].data, + 0xff, + PICOPASS_BLOCK_LEN); + memset( + instance->data->AA1[PICOPASS_SECURE_KC_BLOCK_INDEX].data, + 0xff, + PICOPASS_BLOCK_LEN); } } diff --git a/picopass/scenes/picopass_scene_card_menu.c b/picopass/scenes/picopass_scene_card_menu.c index 981a90d0..9d4521e9 100644 --- a/picopass/scenes/picopass_scene_card_menu.c +++ b/picopass/scenes/picopass_scene_card_menu.c @@ -3,6 +3,7 @@ enum SubmenuIndex { SubmenuIndexSave, SubmenuIndexSaveAsLF, + SubmenuIndexSaveAsSeader, SubmenuIndexChangeKey, SubmenuIndexWrite, SubmenuIndexEmulate, @@ -31,7 +32,12 @@ void picopass_scene_card_menu_on_enter(void* context) { SubmenuIndexSave, picopass_scene_card_menu_submenu_callback, picopass); - + submenu_add_item( + submenu, + "Save in Seader fmt", + SubmenuIndexSaveAsSeader, + picopass_scene_card_menu_submenu_callback, + picopass); } else { submenu_add_item( submenu, @@ -49,7 +55,14 @@ void picopass_scene_card_menu_on_enter(void* context) { SubmenuIndexSaveAsLF, picopass_scene_card_menu_submenu_callback, picopass); - + if(pacs->sio) { // SR + submenu_add_item( + submenu, + "Save in Seader fmt", + SubmenuIndexSaveAsSeader, + picopass_scene_card_menu_submenu_callback, + picopass); + } submenu_add_item( submenu, "Write", @@ -94,6 +107,12 @@ bool picopass_scene_card_menu_on_event(void* context, SceneManagerEvent event) { scene_manager_next_scene(picopass->scene_manager, PicopassSceneSaveName); picopass->dev->format = PicopassDeviceSaveFormatHF; consumed = true; + } else if(event.event == SubmenuIndexSaveAsSeader) { + scene_manager_set_scene_state( + picopass->scene_manager, PicopassSceneCardMenu, event.event); + scene_manager_next_scene(picopass->scene_manager, PicopassSceneSaveName); + picopass->dev->format = PicopassDeviceSaveFormatSeader; + consumed = true; } else if(event.event == SubmenuIndexSaveAsLF) { scene_manager_set_scene_state( picopass->scene_manager, PicopassSceneCardMenu, SubmenuIndexSaveAsLF);