MX 2 سال پیش
والد
کامیت
ca25c7e43d
7فایلهای تغییر یافته به همراه227 افزوده شده و 191 حذف شده
  1. 1 2
      ccid.c
  2. 2 1
      readme.md
  3. 29 5
      sam_api.c
  4. 2 1
      scenes/seader_scene_read_card_success.c
  5. 5 1
      scenes/seader_scene_save_name.c
  6. 187 180
      seader_credential.c
  7. 1 1
      seader_credential.h

+ 1 - 2
ccid.c

@@ -52,6 +52,7 @@ void seader_ccid_check_for_sam(SeaderUartBridge* seader_uart) {
     hasSAM = false; // If someone is calling this, reset sam state
     powered[0] = false;
     powered[1] = false;
+    retries = 3;
     seader_ccid_GetSlotStatus(seader_uart, 0);
 }
 
@@ -154,7 +155,6 @@ size_t seader_ccid_process(Seader* seader, uint8_t* cmd, size_t cmd_len) {
                 if(hasSAM && sam_slot == 0) {
                     break;
                 }
-                retries = 0;
                 sequence[0] = 0;
                 seader_ccid_IccPowerOn(seader_uart, 0);
                 break;
@@ -178,7 +178,6 @@ size_t seader_ccid_process(Seader* seader, uint8_t* cmd, size_t cmd_len) {
                 if(hasSAM && sam_slot == 1) {
                     break;
                 }
-                retries = 0;
                 sequence[1] = 0;
                 seader_ccid_IccPowerOn(seader_uart, 1);
                 break;

+ 2 - 1
readme.md

@@ -1,6 +1,7 @@
 # Seader
 
-[Flipper Zero](https://flipperzero.one/) application (aka "fap"). Versioning TBD.
+
+A [Flipper Zero](https://flipperzero.one/) application (aka "fap") to interface with a SAM from the Flipper Zero over UART.  Latest release on the [App Catalog](https://lab.flipper.net/apps/seader).
 
 ## Bugs
 

+ 29 - 5
sam_api.c

@@ -18,6 +18,8 @@ uint8_t read4Block6[] = {RFAL_PICOPASS_CMD_READ4, 0x06, 0x45, 0x56};
 uint8_t read4Block9[] = {RFAL_PICOPASS_CMD_READ4, 0x09, 0xB2, 0xAE};
 uint8_t read4Block10[] = {RFAL_PICOPASS_CMD_READ4, 0x0A, 0x29, 0x9C};
 uint8_t read4Block13[] = {RFAL_PICOPASS_CMD_READ4, 0x0D, 0x96, 0xE8};
+//uint8_t read4Block14[] = {RFAL_PICOPASS_CMD_READ4, 0x0E, 0x0d, 0xda};
+
 uint8_t updateBlock2[] = {RFAL_PICOPASS_CMD_UPDATE, 0x02};
 
 void* calloc(size_t count, size_t size) {
@@ -54,9 +56,11 @@ void seader_picopass_state_machine(Seader* seader, uint8_t* buffer, size_t len)
     bit_buffer_append_bytes(tx_buffer, buffer, len);
     BitBuffer* rx_buffer = bit_buffer_alloc(SEADER_POLLER_MAX_BUFFER_SIZE);
 
+    uint8_t config[PICOPASS_BLOCK_LEN] = {0x12, 0xff, 0xff, 0xff, 0x7f, 0x1f, 0xff, 0x3c};
     uint8_t sr_aia[PICOPASS_BLOCK_LEN] = {0xFF, 0xff, 0xff, 0xff, 0xFF, 0xFf, 0xff, 0xFF};
     uint8_t epurse[PICOPASS_BLOCK_LEN] = {0xff, 0xff, 0xff, 0xff, 0xe3, 0xff, 0xff, 0xff};
     uint8_t pacs_sr_cfg[PICOPASS_BLOCK_LEN] = {0xA3, 0x03, 0x03, 0x03, 0x00, 0x03, 0xe0, 0x14};
+    uint8_t zeroes[PICOPASS_BLOCK_LEN] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
 
     uint8_t tmac[4] = {};
     uint8_t cc_p[12] = {};
@@ -70,6 +74,12 @@ void seader_picopass_state_machine(Seader* seader, uint8_t* buffer, size_t len)
                 bit_buffer_append_bytes(rx_buffer, sr_aia, sizeof(sr_aia));
             } else if(buffer[1] == PACS_CFG_INDEX) {
                 bit_buffer_append_bytes(rx_buffer, pacs_sr_cfg, sizeof(pacs_sr_cfg));
+            } else { // What i've seen is 0c 12
+                offset = buffer[1] - SEADER_ICLASS_SR_SIO_BASE_BLOCK;
+                bit_buffer_append_bytes(
+                    rx_buffer,
+                    seader->credential->sio + (PICOPASS_BLOCK_LEN * offset),
+                    PICOPASS_BLOCK_LEN);
             }
             iso13239_crc_append(Iso13239CrcTypePicopass, rx_buffer);
             break;
@@ -90,11 +100,25 @@ void seader_picopass_state_machine(Seader* seader, uint8_t* buffer, size_t len)
             bit_buffer_append_bytes(rx_buffer, tmac, sizeof(tmac));
             break;
         case RFAL_PICOPASS_CMD_READ4:
-            offset = buffer[1] - SEADER_ICLASS_SR_SIO_BASE_BLOCK;
-            bit_buffer_append_bytes(
-                rx_buffer,
-                seader->credential->sio + (PICOPASS_BLOCK_LEN * offset),
-                PICOPASS_BLOCK_LEN * 4);
+            if(buffer[1] < SEADER_ICLASS_SR_SIO_BASE_BLOCK) {
+                if(buffer[1] == PACS_CFG_INDEX) {
+                    bit_buffer_append_bytes(rx_buffer, pacs_sr_cfg, sizeof(pacs_sr_cfg));
+                    bit_buffer_append_bytes(rx_buffer, zeroes, sizeof(zeroes));
+                    bit_buffer_append_bytes(rx_buffer, zeroes, sizeof(zeroes));
+                    bit_buffer_append_bytes(rx_buffer, zeroes, sizeof(zeroes));
+                }
+            } else {
+                offset = buffer[1] - SEADER_ICLASS_SR_SIO_BASE_BLOCK;
+                bit_buffer_append_bytes(
+                    rx_buffer,
+                    seader->credential->sio + (PICOPASS_BLOCK_LEN * offset),
+                    PICOPASS_BLOCK_LEN * 4);
+            }
+            iso13239_crc_append(Iso13239CrcTypePicopass, rx_buffer);
+            break;
+        case RFAL_PICOPASS_CMD_PAGESEL:
+            // this should be considered an attempt, but realisticly not working
+            bit_buffer_append_bytes(rx_buffer, config, sizeof(config));
             iso13239_crc_append(Iso13239CrcTypePicopass, rx_buffer);
             break;
         }

+ 2 - 1
scenes/seader_scene_read_card_success.c

@@ -100,7 +100,8 @@ bool seader_scene_read_card_success_on_event(void* context, SceneManagerEvent ev
             consumed = true;
         }
     } else if(event.type == SceneManagerEventTypeBack) {
-        scene_manager_search_and_switch_to_previous_scene(seader->scene_manager, SeaderSceneStart);
+        scene_manager_search_and_switch_to_previous_scene(
+            seader->scene_manager, SeaderSceneSamPresent);
         consumed = true;
     }
     return consumed;

+ 5 - 1
scenes/seader_scene_save_name.c

@@ -3,6 +3,8 @@
 #include <gui/modules/validators.h>
 #include <toolbox/path.h>
 
+#define TAG "SeaderSceneSaveName"
+
 void seader_scene_save_name_text_input_callback(void* context) {
     Seader* seader = context;
 
@@ -52,7 +54,9 @@ bool seader_scene_save_name_on_event(void* context, SceneManagerEvent event) {
 
     if(event.type == SceneManagerEventTypeCustom) {
         if(event.event == SeaderCustomEventTextInputDone) {
-            if(strcmp(seader->credential->name, "") != 0) {
+            if(seader->credential->save_format == SeaderCredentialSaveFormatAgnostic &&
+               strcmp(seader->credential->name, "") != 0) {
+                FURI_LOG_D(TAG, "Delete existing named credential [%s]", seader->credential->name);
                 seader_credential_delete(seader->credential, true);
             }
             strlcpy(seader->credential->name, seader->text_store, strlen(seader->text_store) + 1);

+ 187 - 180
seader_credential.c

@@ -17,6 +17,7 @@ SeaderCredential* seader_credential_alloc() {
     SeaderCredential* seader_dev = malloc(sizeof(SeaderCredential));
     seader_dev->credential = 0;
     seader_dev->bit_length = 0;
+    memset(seader_dev->sio, 0xff, sizeof(seader_dev->sio));
     seader_dev->storage = furi_record_open(RECORD_STORAGE);
     seader_dev->dialogs = furi_record_open(RECORD_DIALOGS);
     seader_dev->load_path = furi_string_alloc();
@@ -378,7 +379,7 @@ bool seader_credential_save_agnostic(SeaderCredential* cred, const char* name) {
     return saved;
 }
 
-bool seader_credential_save(SeaderCredential* cred, const char* name) {
+bool seader_credential_save_picopass(SeaderCredential* cred, const char* name) {
     uint8_t zero[PICOPASS_BLOCK_LEN] = {0};
     uint8_t csn[PICOPASS_BLOCK_LEN] = {0x7a, 0xf5, 0x31, 0x13, 0xfe, 0xff, 0x12, 0xe0};
     uint8_t cfg[PICOPASS_BLOCK_LEN] = {0x12, 0xff, 0xff, 0xff, 0x7f, 0x1f, 0xff, 0x3c};
@@ -387,214 +388,220 @@ bool seader_credential_save(SeaderCredential* cred, const char* name) {
     uint8_t aia[PICOPASS_BLOCK_LEN] = {0xFF, 0xff, 0xff, 0xff, 0xFF, 0xFf, 0xff, 0xFF};
     uint8_t pacs_cfg[PICOPASS_BLOCK_LEN] = {0x03, 0x03, 0x03, 0x03, 0x00, 0x03, 0xe0, 0x14};
 
-    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) {
-        bool use_load_path = true;
-        bool saved = false;
-        bool withSIO = cred->save_format == SeaderCredentialSaveFormatSR;
-        FlipperFormat* file = flipper_format_file_alloc(cred->storage);
-        FuriString* temp_str = furi_string_alloc();
-
-        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, ".picopass");
-        } else {
-            furi_string_printf(
-                temp_str, "%s/%s%s", EXT_PATH("apps_data/picopass"), name, ".picopass");
-        }
+    bool use_load_path = true;
+    bool saved = false;
+    bool withSIO = cred->save_format == SeaderCredentialSaveFormatSR;
+    FlipperFormat* file = flipper_format_file_alloc(cred->storage);
+    FuriString* temp_str = furi_string_alloc();
+
+    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, ".picopass");
+    } else {
+        furi_string_printf(temp_str, "%s/%s%s", EXT_PATH("apps_data/picopass"), name, ".picopass");
+    }
 
-        FURI_LOG_D(TAG, "Save as Picopass [%s]", furi_string_get_cstr(temp_str));
-        uint64_t sentinel = 1ULL << cred->bit_length;
-        uint64_t swapped = __builtin_bswap64(cred->credential | sentinel);
-        // FURI_LOG_D(TAG, "PACS: (%d) %016llx | %016llx => %016llx", cred->bit_length, cred->credential, sentinel, swapped);
-        do {
-            if(!flipper_format_file_open_always(file, furi_string_get_cstr(temp_str))) break;
-            if(!flipper_format_write_header_cstr(file, "Flipper Picopass device", 1)) break;
-            if(!flipper_format_write_comment_cstr(
-                   file, "Picopass blocks generated from Seader app"))
-                break;
+    FURI_LOG_D(TAG, "Save as Picopass [%s]", furi_string_get_cstr(temp_str));
+    uint64_t sentinel = 1ULL << cred->bit_length;
+    uint64_t swapped = __builtin_bswap64(cred->credential | sentinel);
+    // FURI_LOG_D(TAG, "PACS: (%d) %016llx | %016llx => %016llx", cred->bit_length, cred->credential, sentinel, swapped);
+    do {
+        if(!flipper_format_file_open_always(file, furi_string_get_cstr(temp_str))) break;
+        if(!flipper_format_write_header_cstr(file, "Flipper Picopass device", 1)) break;
+        if(!flipper_format_write_comment_cstr(file, "Picopass blocks generated from Seader app"))
+            break;
 
-            bool block_saved = true;
-            for(size_t i = 0; i < 20; i++) {
-                furi_string_printf(temp_str, "Block %d", i);
-                switch(i) {
-                case CSN_INDEX:
-                    if(withSIO) {
-                        if(!flipper_format_write_hex(
-                               file,
-                               furi_string_get_cstr(temp_str),
-                               cred->diversifier,
-                               PICOPASS_BLOCK_LEN)) {
-                            block_saved = false;
-                        }
-                    } else {
-                        if(!flipper_format_write_hex(
-                               file, furi_string_get_cstr(temp_str), csn, sizeof(csn))) {
-                            block_saved = false;
-                        }
-                    }
-                    break;
-                case EPURSE_INDEX:
-                    if(!flipper_format_write_hex(
-                           file, furi_string_get_cstr(temp_str), epurse, PICOPASS_BLOCK_LEN)) {
-                        block_saved = false;
-                    }
-                    break;
-                case KD_INDEX:
-                    if(!flipper_format_write_hex(
-                           file, furi_string_get_cstr(temp_str), debit_key, PICOPASS_BLOCK_LEN)) {
-                        block_saved = false;
-                    }
-                    break;
-                case AIA_INDEX:
-                    if(!flipper_format_write_hex(
-                           file, furi_string_get_cstr(temp_str), aia, PICOPASS_BLOCK_LEN)) {
-                        block_saved = false;
-                    }
-                    break;
-                case CFG_INDEX:
+        bool block_saved = true;
+        for(size_t i = 0; i < 20; i++) {
+            furi_string_printf(temp_str, "Block %d", i);
+            switch(i) {
+            case CSN_INDEX:
+                if(withSIO) {
                     if(!flipper_format_write_hex(
-                           file, furi_string_get_cstr(temp_str), cfg, sizeof(cfg))) {
+                           file,
+                           furi_string_get_cstr(temp_str),
+                           cred->diversifier,
+                           PICOPASS_BLOCK_LEN)) {
                         block_saved = false;
                     }
-                    break;
-                case PACS_CFG_INDEX:
-                    if(withSIO) {
-                        pacs_cfg[0] = 0xA3;
-                    }
+                } else {
                     if(!flipper_format_write_hex(
-                           file, furi_string_get_cstr(temp_str), pacs_cfg, sizeof(pacs_cfg))) {
+                           file, furi_string_get_cstr(temp_str), csn, sizeof(csn))) {
                         block_saved = false;
                     }
-                    break;
-                case PACS_INDEX:
+                }
+                break;
+            case EPURSE_INDEX:
+                if(!flipper_format_write_hex(
+                       file, furi_string_get_cstr(temp_str), epurse, PICOPASS_BLOCK_LEN)) {
+                    block_saved = false;
+                }
+                break;
+            case KD_INDEX:
+                if(!flipper_format_write_hex(
+                       file, furi_string_get_cstr(temp_str), debit_key, PICOPASS_BLOCK_LEN)) {
+                    block_saved = false;
+                }
+                break;
+            case AIA_INDEX:
+                if(!flipper_format_write_hex(
+                       file, furi_string_get_cstr(temp_str), aia, PICOPASS_BLOCK_LEN)) {
+                    block_saved = false;
+                }
+                break;
+            case CFG_INDEX:
+                if(!flipper_format_write_hex(
+                       file, furi_string_get_cstr(temp_str), cfg, sizeof(cfg))) {
+                    block_saved = false;
+                }
+                break;
+            case PACS_CFG_INDEX:
+                if(withSIO) {
+                    pacs_cfg[0] = 0xA3;
+                }
+                if(!flipper_format_write_hex(
+                       file, furi_string_get_cstr(temp_str), pacs_cfg, sizeof(pacs_cfg))) {
+                    block_saved = false;
+                }
+                break;
+            case PACS_INDEX:
+                if(!flipper_format_write_hex(
+                       file,
+                       furi_string_get_cstr(temp_str),
+                       (uint8_t*)&swapped,
+                       PICOPASS_BLOCK_LEN)) {
+                    block_saved = false;
+                }
+                break;
+            case SR_SIO_INDEX:
+            case SR_SIO_INDEX + 1:
+            case SR_SIO_INDEX + 2:
+            case SR_SIO_INDEX + 3:
+            case SR_SIO_INDEX + 4:
+            case SR_SIO_INDEX + 5:
+            case SR_SIO_INDEX + 6:
+            case SR_SIO_INDEX + 7:
+                if(withSIO) {
                     if(!flipper_format_write_hex(
                            file,
                            furi_string_get_cstr(temp_str),
-                           (uint8_t*)&swapped,
+                           cred->sio + ((i - SR_SIO_INDEX) * PICOPASS_BLOCK_LEN),
                            PICOPASS_BLOCK_LEN)) {
                         block_saved = false;
                     }
-                    break;
-                case SR_SIO_INDEX:
-                case SR_SIO_INDEX + 1:
-                case SR_SIO_INDEX + 2:
-                case SR_SIO_INDEX + 3:
-                case SR_SIO_INDEX + 4:
-                case SR_SIO_INDEX + 5:
-                case SR_SIO_INDEX + 6:
-                case SR_SIO_INDEX + 7:
-                    if(withSIO) {
-                        if(!flipper_format_write_hex(
-                               file,
-                               furi_string_get_cstr(temp_str),
-                               cred->sio + ((i - SR_SIO_INDEX) * PICOPASS_BLOCK_LEN),
-                               PICOPASS_BLOCK_LEN)) {
-                            block_saved = false;
-                        }
-                    } else {
-                        if(!flipper_format_write_hex(
-                               file, furi_string_get_cstr(temp_str), zero, sizeof(zero))) {
-                            block_saved = false;
-                        }
-                    }
-                    break;
-                default:
+                } else {
                     if(!flipper_format_write_hex(
                            file, furi_string_get_cstr(temp_str), zero, sizeof(zero))) {
                         block_saved = false;
                     }
-                    break;
-                };
-                if(!block_saved) {
-                    break;
                 }
+                break;
+            default:
+                if(!flipper_format_write_hex(
+                       file, furi_string_get_cstr(temp_str), zero, sizeof(zero))) {
+                    block_saved = false;
+                }
+                break;
+            };
+            if(!block_saved) {
+                break;
             }
-            saved = true;
-        } while(false);
-
-        if(!saved) {
-            dialog_message_show_storage_error(cred->dialogs, "Can not save\nfile");
         }
+        saved = true;
+    } while(false);
 
-        furi_string_free(temp_str);
-        flipper_format_free(file);
-        return saved;
-    } else if(cred->save_format == SeaderCredentialSaveFormatRFID) {
-        bool result = false;
-        FuriString* file_path = furi_string_alloc();
-        furi_string_printf(file_path, "%s/%s%s", ANY_PATH("lfrfid"), name, ".rfid");
-        ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax);
-        ProtocolId protocol = LFRFIDProtocolHidGeneric;
-
-        FURI_LOG_D(TAG, "Original (%d): %016llx", cred->bit_length, cred->credential);
-        uint64_t target = 0;
-        if(cred->bit_length == 26) {
-            //3 bytes
-            protocol = LFRFIDProtocolH10301;
-            // Remove parity
-            target = (cred->credential >> 1) & 0xFFFFFF;
-            // Reverse order since it'll get reversed again
-            target = __builtin_bswap64(target) >> (64 - 24);
-        } else if(cred->bit_length < 44) {
-            // https://gist.github.com/blark/e8f125e402f576bdb7e2d7b3428bdba6
-            protocol = LFRFIDProtocolHidGeneric;
-            uint64_t sentinel = 1ULL << cred->bit_length;
-            if(cred->bit_length <= 36) {
-                uint64_t header = 1ULL << 37;
-                FURI_LOG_D(
-                    TAG,
-                    "Prox Format (%d): %011llx",
-                    cred->bit_length,
-                    cred->credential | sentinel | header);
-                target = __builtin_bswap64((cred->credential | sentinel | header) << 4) >>
-                         (64 - 48);
-            } else {
-                target = __builtin_bswap64(cred->credential << 4) >> (64 - 48);
-            }
-        } else {
-            //8 bytes
-            protocol = LFRFIDProtocolHidExGeneric;
-            target = cred->credential;
-            target = __builtin_bswap64(target);
-        }
+    if(!saved) {
+        dialog_message_show_storage_error(cred->dialogs, "Can not save\nfile");
+    }
 
-        FURI_LOG_D(TAG, "LFRFID (%d): %016llx", cred->bit_length, target);
-        size_t data_size = protocol_dict_get_data_size(dict, protocol);
-        uint8_t* data = malloc(data_size);
-        if(data_size < 8) {
-            memcpy(data, (void*)&target, data_size);
+    furi_string_free(temp_str);
+    flipper_format_free(file);
+    return saved;
+}
+
+bool seader_credential_save_rfid(SeaderCredential* cred, const char* name) {
+    bool result = false;
+    FuriString* file_path = furi_string_alloc();
+    furi_string_printf(file_path, "%s/%s%s", ANY_PATH("lfrfid"), name, ".rfid");
+    ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax);
+    ProtocolId protocol = LFRFIDProtocolHidGeneric;
+
+    FURI_LOG_D(TAG, "Original (%d): %016llx", cred->bit_length, cred->credential);
+    uint64_t target = 0;
+    if(cred->bit_length == 26) {
+        //3 bytes
+        protocol = LFRFIDProtocolH10301;
+        // Remove parity
+        target = (cred->credential >> 1) & 0xFFFFFF;
+        // Reverse order since it'll get reversed again
+        target = __builtin_bswap64(target) >> (64 - 24);
+    } else if(cred->bit_length < 44) {
+        // https://gist.github.com/blark/e8f125e402f576bdb7e2d7b3428bdba6
+        protocol = LFRFIDProtocolHidGeneric;
+        uint64_t sentinel = 1ULL << cred->bit_length;
+        if(cred->bit_length <= 36) {
+            uint64_t header = 1ULL << 37;
+            FURI_LOG_D(
+                TAG,
+                "Prox Format (%d): %011llx",
+                cred->bit_length,
+                cred->credential | sentinel | header);
+            target = __builtin_bswap64((cred->credential | sentinel | header) << 4) >> (64 - 48);
         } else {
-            // data_size 12 for LFRFIDProtocolHidExGeneric
-            memcpy(data + 4, (void*)&target, 8);
+            target = __builtin_bswap64(cred->credential << 4) >> (64 - 48);
         }
-        protocol_dict_set_data(dict, protocol, data, data_size);
-        free(data);
+    } else {
+        //8 bytes
+        protocol = LFRFIDProtocolHidExGeneric;
+        target = cred->credential;
+        target = __builtin_bswap64(target);
+    }
 
-        result = lfrfid_dict_file_save(dict, protocol, furi_string_get_cstr(file_path));
+    FURI_LOG_D(TAG, "LFRFID (%d): %016llx", cred->bit_length, target);
+    size_t data_size = protocol_dict_get_data_size(dict, protocol);
+    uint8_t* data = malloc(data_size);
+    if(data_size < 8) {
+        memcpy(data, (void*)&target, data_size);
+    } else {
+        // data_size 12 for LFRFIDProtocolHidExGeneric
+        memcpy(data + 4, (void*)&target, 8);
+    }
+    protocol_dict_set_data(dict, protocol, data, data_size);
+    free(data);
 
-        FuriString* briefStr;
-        briefStr = furi_string_alloc();
-        protocol_dict_render_brief_data(dict, briefStr, protocol);
-        FURI_LOG_D(TAG, "LFRFID Brief: %s", furi_string_get_cstr(briefStr));
-        furi_string_free(briefStr);
+    result = lfrfid_dict_file_save(dict, protocol, furi_string_get_cstr(file_path));
 
-        if(result) {
-            FURI_LOG_D(TAG, "Written: %d", result);
-        } else {
-            FURI_LOG_D(TAG, "Failed to write");
-        }
+    FuriString* briefStr;
+    briefStr = furi_string_alloc();
+    protocol_dict_render_brief_data(dict, briefStr, protocol);
+    FURI_LOG_D(TAG, "LFRFID Brief: %s", furi_string_get_cstr(briefStr));
+    furi_string_free(briefStr);
 
-        furi_string_free(file_path);
-        protocol_dict_free(dict);
-        return result;
+    if(result) {
+        FURI_LOG_D(TAG, "Written: %d", result);
+    } else {
+        FURI_LOG_D(TAG, "Failed to write");
+    }
+
+    furi_string_free(file_path);
+    protocol_dict_free(dict);
+
+    return result;
+}
+
+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) {
+        return seader_credential_save_picopass(cred, name);
+    } else if(cred->save_format == SeaderCredentialSaveFormatRFID) {
+        return seader_credential_save_rfid(cred, name);
     }
     return false;
 }

+ 1 - 1
seader_credential.h

@@ -35,7 +35,7 @@ typedef struct {
     DialogsApp* dialogs;
     uint64_t credential;
     size_t bit_length;
-    uint8_t sio[64];
+    uint8_t sio[128];
     uint8_t diversifier[8];
     SeaderCredentialType type;
     SeaderCredentialSaveFormat save_format;