Explorar o código

Merge seos from https://gitlab.com/bettse/flipper_seos

WillyJL hai 8 meses
pai
achega
544d08ab99

+ 3 - 0
seos/CHANGELOG.md

@@ -1,3 +1,6 @@
+## 1.2
+ - Add support for reading Seader files that have SIO
+ - Add custom zero key ADF OID (0.3.1.7.9.0.0.0.0.0/030107090000000000)
 ## 1.1
  - Add native BLE
 

+ 2 - 0
seos/example.seos

@@ -8,3 +8,5 @@ SIO: 30 34 ...
 Priv Key: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
 Auth Key: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
 ADF Response: cd 02 ....
+ADF OID Length: 9
+ADF OID: 03 01 07 09 00 00 00 00 00

+ 1 - 1
seos/scenes/seos_scene_ble_central.c

@@ -56,7 +56,7 @@ bool seos_scene_ble_central_on_event(void* context, SceneManagerEvent event) {
             consumed = true;
         }
     } else if(event.type == SceneManagerEventTypeBack) {
-        if(seos->credential.sio_len > 0) {
+        if(seos->credential->sio_len > 0) {
             scene_manager_search_and_switch_to_previous_scene(
                 seos->scene_manager, SeosSceneSavedMenu);
         } else {

+ 1 - 1
seos/scenes/seos_scene_ble_peripheral.c

@@ -59,7 +59,7 @@ bool seos_scene_ble_peripheral_on_event(void* context, SceneManagerEvent event)
             consumed = true;
         }
     } else if(event.type == SceneManagerEventTypeBack) {
-        if(seos->credential.sio_len > 0) {
+        if(seos->credential->sio_len > 0) {
             scene_manager_search_and_switch_to_previous_scene(
                 seos->scene_manager, SeosSceneSavedMenu);
         } else {

+ 4 - 4
seos/scenes/seos_scene_delete.c

@@ -9,11 +9,11 @@ void seos_scene_delete_widget_callback(GuiButtonType result, InputType type, voi
 
 void seos_scene_delete_on_enter(void* context) {
     Seos* seos = context;
-    SeosEmulator* seos_emulator = seos->seos_emulator;
+    SeosCredential* seos_credential = seos->credential;
 
     // Setup Custom Widget view
     char temp_str[141];
-    snprintf(temp_str, sizeof(temp_str), "\e#Delete %s?\e#", seos_emulator->name);
+    snprintf(temp_str, sizeof(temp_str), "\e#Delete %s?\e#", seos_credential->name);
     widget_add_text_box_element(
         seos->widget, 0, 0, 128, 23, AlignCenter, AlignCenter, temp_str, false);
     widget_add_button_element(
@@ -26,14 +26,14 @@ void seos_scene_delete_on_enter(void* context) {
 
 bool seos_scene_delete_on_event(void* context, SceneManagerEvent event) {
     Seos* seos = context;
-    SeosEmulator* seos_emulator = seos->seos_emulator;
+    SeosCredential* seos_credential = seos->credential;
     bool consumed = false;
 
     if(event.type == SceneManagerEventTypeCustom) {
         if(event.event == GuiButtonTypeLeft) {
             return scene_manager_previous_scene(seos->scene_manager);
         } else if(event.event == GuiButtonTypeRight) {
-            if(seos_emulator_delete(seos_emulator, true)) {
+            if(seos_credential_delete(seos_credential, true)) {
                 scene_manager_next_scene(seos->scene_manager, SeosSceneDeleteSuccess);
             } else {
                 scene_manager_search_and_switch_to_previous_scene(

+ 5 - 5
seos/scenes/seos_scene_file_select.c

@@ -1,18 +1,18 @@
 #include "../seos_i.h"
-#include "../seos_emulator.h"
+#include "../seos_credential.h"
 
 void seos_scene_file_select_on_enter(void* context) {
     Seos* seos = context;
-    SeosEmulator* seos_emulator = seos->seos_emulator;
+    SeosCredential* seos_credential = seos->credential;
     // Process file_select return
-    seos_emulator_set_loading_callback(seos_emulator, seos_show_loading_popup, seos);
-    if(seos_emulator_file_select(seos_emulator)) {
+    seos_credential_set_loading_callback(seos_credential, seos_show_loading_popup, seos);
+    if(seos_credential_file_select(seos_credential)) {
         seos->flow_mode = FLOW_CRED;
         scene_manager_next_scene(seos->scene_manager, SeosSceneSavedMenu);
     } else {
         scene_manager_search_and_switch_to_previous_scene(seos->scene_manager, SeosSceneMainMenu);
     }
-    seos_emulator_set_loading_callback(seos_emulator, NULL, seos);
+    seos_credential_set_loading_callback(seos_credential, NULL, seos);
 }
 
 bool seos_scene_file_select_on_event(void* context, SceneManagerEvent event) {

+ 8 - 8
seos/scenes/seos_scene_info.c

@@ -18,7 +18,7 @@ void seos_scene_info_widget_callback(GuiButtonType result, InputType type, void*
 void seos_scene_info_on_enter(void* context) {
     Seos* seos = context;
     Widget* widget = seos->widget;
-    SeosCredential credential = seos->credential;
+    SeosCredential* credential = seos->credential;
 
     FuriString* primary_str = furi_string_alloc_set("Info");
     FuriString* secondary_str_label = furi_string_alloc();
@@ -27,18 +27,18 @@ void seos_scene_info_on_enter(void* context) {
     FuriString* keys_str = furi_string_alloc();
 
     furi_string_set(secondary_str_label, "Diversifier:");
-    for(size_t i = 0; i < credential.diversifier_len; i++) {
-        furi_string_cat_printf(secondary_str_value, "%02X", credential.diversifier[i]);
+    for(size_t i = 0; i < credential->diversifier_len; i++) {
+        furi_string_cat_printf(secondary_str_value, "%02X", credential->diversifier[i]);
     }
 
     // RID
-    if(credential.sio_len > 3 && credential.sio[2] == 0x81) {
-        size_t len = credential.sio[3];
+    if(credential->sio_len > 3 && credential->sio[2] == 0x81) {
+        size_t len = credential->sio[3];
         furi_string_set(details_str, "RID:");
         for(size_t i = 0; i < len; i++) {
-            furi_string_cat_printf(details_str, "%02X", credential.sio[4 + i]);
+            furi_string_cat_printf(details_str, "%02X", credential->sio[4 + i]);
         }
-        if(len >= 4 && credential.sio[3 + 1] == 0x01) {
+        if(len >= 4 && credential->sio[3 + 1] == 0x01) {
             furi_string_cat_printf(details_str, "(retail)");
         } else if(len == 2) {
             furi_string_cat_printf(details_str, "(ER)");
@@ -48,7 +48,7 @@ void seos_scene_info_on_enter(void* context) {
     }
 
     // keys
-    if(memcmp(credential.priv_key, empty, sizeof(empty)) != 0) {
+    if(memcmp(credential->priv_key, empty, sizeof(empty)) != 0) {
         furi_string_cat_printf(keys_str, "+keys");
     }
 

+ 3 - 3
seos/scenes/seos_scene_main_menu.c

@@ -58,7 +58,7 @@ void seos_scene_main_menu_on_enter(void* context) {
     submenu_add_item(
         submenu, "About", SubmenuIndexAbout, seos_scene_main_menu_submenu_callback, seos);
 
-    if(storage_dir_exists(seos->storage, SEADER_PATH)) {
+    if(storage_dir_exists(seos->credential->storage, SEADER_PATH)) {
         submenu_add_item(
             submenu,
             "Saved (Seader)",
@@ -103,13 +103,13 @@ bool seos_scene_main_menu_on_event(void* context, SceneManagerEvent event) {
         } else if(event.event == SubmenuIndexSaved) {
             scene_manager_set_scene_state(
                 seos->scene_manager, SeosSceneMainMenu, SubmenuIndexSaved);
-            seos->seos_emulator->load_type = SeosLoadSeos;
+            seos->credential->load_type = SeosLoadSeos;
             scene_manager_next_scene(seos->scene_manager, SeosSceneFileSelect);
             consumed = true;
         } else if(event.event == SubmenuIndexSavedSeader) {
             scene_manager_set_scene_state(
                 seos->scene_manager, SeosSceneMainMenu, SubmenuIndexSavedSeader);
-            seos->seos_emulator->load_type = SeosLoadSeader;
+            seos->credential->load_type = SeosLoadSeader;
             scene_manager_next_scene(seos->scene_manager, SeosSceneFileSelect);
             consumed = true;
         } else if(event.event == SubmenuIndexInspect) {

+ 7 - 7
seos/scenes/seos_scene_read_success.c

@@ -14,7 +14,7 @@ void seos_scene_read_success_widget_callback(GuiButtonType result, InputType typ
 
 void seos_scene_read_success_on_enter(void* context) {
     Seos* seos = context;
-    SeosCredential credential = seos->credential;
+    SeosCredential* credential = seos->credential;
     Widget* widget = seos->widget;
 
     dolphin_deed(DolphinDeedNfcReadSuccess);
@@ -25,18 +25,18 @@ void seos_scene_read_success_on_enter(void* context) {
     FuriString* details_str = furi_string_alloc();
 
     furi_string_set(secondary_str_label, "Diversifier:");
-    for(size_t i = 0; i < credential.diversifier_len; i++) {
-        furi_string_cat_printf(secondary_str_value, "%02X", credential.diversifier[i]);
+    for(size_t i = 0; i < credential->diversifier_len; i++) {
+        furi_string_cat_printf(secondary_str_value, "%02X", credential->diversifier[i]);
     }
 
     // RID
-    if(credential.sio_len > 3 && credential.sio[2] == 0x81) {
-        size_t len = credential.sio[3];
+    if(credential->sio_len > 3 && credential->sio[2] == 0x81) {
+        size_t len = credential->sio[3];
         furi_string_set(details_str, "RID:");
         for(size_t i = 0; i < len; i++) {
-            furi_string_cat_printf(details_str, "%02X", credential.sio[4 + i]);
+            furi_string_cat_printf(details_str, "%02X", credential->sio[4 + i]);
         }
-        if(len >= 4 && credential.sio[3 + 1] == 0x01) {
+        if(len >= 4 && credential->sio[3 + 1] == 0x01) {
             furi_string_cat_printf(details_str, "(retail)");
         } else if(len == 2) {
             furi_string_cat_printf(details_str, "(ER)");

+ 7 - 7
seos/scenes/seos_scene_save_name.c

@@ -17,11 +17,11 @@ void seos_scene_save_name_on_enter(void* context) {
     // Setup view
     TextInput* text_input = seos->text_input;
     bool dev_name_empty = false;
-    if(!strcmp(seos->dev_name, "")) {
+    if(!strcmp(seos->credential->name, "")) {
         name_generator_make_auto(seos->text_store, sizeof(seos->text_store), SEOS_APP_FILE_PREFIX);
         dev_name_empty = true;
     } else {
-        seos_text_store_set(seos, seos->dev_name);
+        seos_text_store_set(seos, seos->credential->name);
     }
     text_input_set_header_text(text_input, "Name the card");
     text_input_set_result_callback(
@@ -35,12 +35,12 @@ void seos_scene_save_name_on_enter(void* context) {
     FuriString* folder_path;
     folder_path = furi_string_alloc_set(STORAGE_APP_DATA_PATH_PREFIX);
 
-    if(furi_string_end_with(seos->load_path, SEOS_APP_EXTENSION)) {
-        path_extract_dirname(furi_string_get_cstr(seos->load_path), folder_path);
+    if(furi_string_end_with(seos->credential->load_path, SEOS_APP_EXTENSION)) {
+        path_extract_dirname(furi_string_get_cstr(seos->credential->load_path), folder_path);
     }
 
     ValidatorIsFile* validator_is_file = validator_is_file_alloc_init(
-        furi_string_get_cstr(folder_path), SEOS_APP_EXTENSION, seos->dev_name);
+        furi_string_get_cstr(folder_path), SEOS_APP_EXTENSION, seos->credential->name);
     text_input_set_validator(text_input, validator_is_file_callback, validator_is_file);
 
     view_dispatcher_switch_to_view(seos->view_dispatcher, SeosViewTextInput);
@@ -54,8 +54,8 @@ bool seos_scene_save_name_on_event(void* context, SceneManagerEvent event) {
 
     if(event.type == SceneManagerEventTypeCustom) {
         if(event.event == SeosCustomEventTextInputDone) {
-            strlcpy(seos->dev_name, seos->text_store, strlen(seos->text_store) + 1);
-            if(seos_credential_save(seos, seos->text_store)) {
+            strlcpy(seos->credential->name, seos->text_store, strlen(seos->text_store) + 1);
+            if(seos_credential_save(seos->credential, seos->text_store)) {
                 scene_manager_next_scene(seos->scene_manager, SeosSceneSaveSuccess);
                 consumed = true;
             } else {

+ 1 - 1
seos/scenes/seos_scene_saved_menu.c

@@ -83,7 +83,7 @@ bool seos_scene_saved_menu_on_event(void* context, SceneManagerEvent event) {
             consumed = true;
         }
     } else if(event.type == SceneManagerEventTypeBack) {
-        memset((void*)&seos->credential, 0, sizeof(seos->credential));
+        seos_credential_clear(seos->credential);
         scene_manager_search_and_switch_to_previous_scene(seos->scene_manager, SeosSceneMainMenu);
         consumed = true;
     }

+ 4 - 56
seos/seos.c

@@ -8,7 +8,7 @@ bool seos_load_keys(Seos* seos) {
     const char* file_header = "Seos keys";
     const uint32_t file_version = 1;
     bool parsed = false;
-    FlipperFormat* file = flipper_format_file_alloc(seos->storage);
+    FlipperFormat* file = flipper_format_file_alloc(seos->credential->storage);
     FuriString* path = furi_string_alloc();
     FuriString* temp_str = furi_string_alloc();
     uint32_t version = 0;
@@ -119,11 +119,8 @@ Seos* seos_alloc() {
     seos->widget = widget_alloc();
     view_dispatcher_add_view(seos->view_dispatcher, SeosViewWidget, widget_get_view(seos->widget));
 
-    seos->storage = furi_record_open(RECORD_STORAGE);
-    seos->dialogs = furi_record_open(RECORD_DIALOGS);
-    seos->load_path = furi_string_alloc();
-
-    seos->seos_emulator = seos_emulator_alloc(&seos->credential);
+    seos->credential = seos_credential_alloc();
+    seos->seos_emulator = seos_emulator_alloc(seos->credential);
 
     seos->keys_loaded = seos_load_keys(seos);
 
@@ -179,9 +176,7 @@ void seos_free(Seos* seos) {
     furi_record_close(RECORD_NOTIFICATION);
     seos->notifications = NULL;
 
-    furi_string_free(seos->load_path);
-    furi_record_close(RECORD_STORAGE);
-    furi_record_close(RECORD_DIALOGS);
+    seos_credential_free(seos->credential);
 
     if(seos->seos_emulator) {
         seos_emulator_free(seos->seos_emulator);
@@ -204,53 +199,6 @@ void seos_text_store_clear(Seos* seos) {
     memset(seos->text_store, 0, sizeof(seos->text_store));
 }
 
-bool seos_credential_save(Seos* seos, const char* dev_name) {
-    bool saved = false;
-    FlipperFormat* file = flipper_format_file_alloc(seos->storage);
-    FuriString* temp_str = furi_string_alloc();
-    bool use_load_path = true;
-
-    do {
-        if(use_load_path && !furi_string_empty(seos->load_path)) {
-            // Get directory name
-            path_extract_dirname(furi_string_get_cstr(seos->load_path), temp_str);
-            // Make path to file to save
-            furi_string_cat_printf(temp_str, "/%s%s", dev_name, SEOS_APP_EXTENSION);
-        } else {
-            // First remove file if it was saved
-            furi_string_printf(
-                temp_str, "%s/%s%s", STORAGE_APP_DATA_PATH_PREFIX, dev_name, SEOS_APP_EXTENSION);
-        }
-
-        // Open file
-        if(!flipper_format_file_open_always(file, furi_string_get_cstr(temp_str))) break;
-
-        // Write header
-        if(!flipper_format_write_header_cstr(file, seos_file_header, seos_file_version)) break;
-
-        if(!flipper_format_write_uint32(
-               file, "Diversifier Length", (uint32_t*)&(seos->credential.diversifier_len), 1))
-            break;
-        if(!flipper_format_write_hex(
-               file, "Diversifier", seos->credential.diversifier, seos->credential.diversifier_len))
-            break;
-        if(!flipper_format_write_uint32(
-               file, "SIO Length", (uint32_t*)&(seos->credential.sio_len), 1))
-            break;
-        if(!flipper_format_write_hex(file, "SIO", seos->credential.sio, seos->credential.sio_len))
-            break;
-
-        saved = true;
-    } while(false);
-
-    if(!saved) {
-        dialog_message_show_storage_error(seos->dialogs, "Can not save\nfile");
-    }
-    furi_string_free(temp_str);
-    flipper_format_free(file);
-    return saved;
-}
-
 static const NotificationSequence seos_sequence_blink_start_blue = {
     &message_blink_start_10,
     &message_blink_set_color_blue,

+ 0 - 2
seos/seos.h

@@ -1,5 +1,3 @@
 #pragma once
 
 typedef struct Seos Seos;
-
-bool seos_credential_save(Seos* seos, const char* dev_name);

+ 6 - 13
seos/seos_central.c

@@ -17,7 +17,7 @@ SeosCentral* seos_central_alloc(Seos* seos) {
     SeosCentral* seos_central = malloc(sizeof(SeosCentral));
     memset(seos_central, 0, sizeof(SeosCentral));
     seos_central->seos = seos;
-    seos_central->credential = &seos->credential;
+    seos_central->credential = seos->credential;
 
     seos_central->phase = SELECT_AID;
     // Using DES for greater compatibilty
@@ -77,20 +77,13 @@ void seos_central_notify(void* context, const uint8_t* buffer, size_t buffer_len
             bit_buffer_append_bytes(response, (uint8_t*)file_not_found, sizeof(file_not_found));
         }
     } else if(memcmp(apdu, select_adf_header, sizeof(select_adf_header)) == 0) {
-        // is our adf in the list?
-        // +1 to skip APDU length byte
-        void* p = memmem(
-            apdu + sizeof(select_adf_header) + 1,
-            apdu[sizeof(select_adf_header)],
-            SEOS_ADF_OID,
-            SEOS_ADF_OID_LEN);
-        if(p) {
-            seos_log_buffer(TAG, "Matched ADF", p, SEOS_ADF_OID_LEN);
+        const uint8_t* oid_list = apdu + sizeof(select_adf_header) + 1;
+        size_t oid_list_len = apdu[sizeof(select_adf_header)];
 
-            bit_buffer_append_byte(response, BLE_START);
-
-            seos_emulator_select_adf(&seos_central->params, seos_central->credential, response);
+        bit_buffer_append_byte(response, BLE_START);
 
+        if(seos_emulator_select_adf(
+               oid_list, oid_list_len, &seos_central->params, seos_central->credential, response)) {
             bit_buffer_append_bytes(response, (uint8_t*)success, sizeof(success));
             seos_central->phase = GENERAL_AUTHENTICATION_1;
         } else {

+ 1 - 0
seos/seos_central.h

@@ -8,6 +8,7 @@
 
 #include "secure_messaging.h"
 #include "seos_common.h"
+#include "seos_credential.h"
 #include "seos.h"
 #include "seos_att.h"
 #include "keys.h"

+ 10 - 13
seos/seos_characteristic.c

@@ -22,7 +22,7 @@ SeosCharacteristic* seos_characteristic_alloc(Seos* seos) {
     SeosCharacteristic* seos_characteristic = malloc(sizeof(SeosCharacteristic));
     memset(seos_characteristic, 0, sizeof(SeosCharacteristic));
     seos_characteristic->seos = seos;
-    seos_characteristic->credential = &seos->credential;
+    seos_characteristic->credential = seos->credential;
 
     seos_characteristic->phase = SELECT_AID;
     seos_characteristic->secure_messaging = NULL;
@@ -196,18 +196,15 @@ void seos_characteristic_cred_flow(
             bit_buffer_append_bytes(payload, (uint8_t*)file_not_found, sizeof(file_not_found));
         }
     } else if(memcmp(apdu, select_adf_header, sizeof(select_adf_header)) == 0) {
-        // is our adf in the list?
-        // +1 to skip APDU length byte
-        void* p = memmem(
-            apdu + sizeof(select_adf_header) + 1,
-            apdu[sizeof(select_adf_header)],
-            SEOS_ADF_OID,
-            SEOS_ADF_OID_LEN);
-        if(p) {
-            seos_log_buffer(TAG, "Matched ADF", p, SEOS_ADF_OID_LEN);
-
-            seos_emulator_select_adf(
-                &seos_characteristic->params, seos_characteristic->credential, payload);
+        const uint8_t* oid_list = apdu + sizeof(select_adf_header) + 1;
+        size_t oid_list_len = apdu[sizeof(select_adf_header)];
+
+        if(seos_emulator_select_adf(
+               oid_list,
+               oid_list_len,
+               &seos_characteristic->params,
+               seos_characteristic->credential,
+               payload)) {
             bit_buffer_append_bytes(payload, (uint8_t*)success, sizeof(success));
         } else {
             FURI_LOG_W(TAG, "Failed to match any ADF OID");

+ 1 - 0
seos/seos_characteristic.h

@@ -8,6 +8,7 @@
 
 #include "secure_messaging.h"
 #include "seos_common.h"
+#include "seos_credential.h"
 #include "seos.h"
 #include "seos_att.h"
 #include "keys.h"

+ 0 - 10
seos/seos_common.c

@@ -38,16 +38,6 @@ void seos_log_bitbuffer(char* TAG, char* prefix, BitBuffer* buffer) {
     }
 }
 
-void seos_common_copy_credential(const SeosCredential* src, SeosCredential* dst) {
-    furi_assert(src);
-    furi_assert(dst);
-
-    dst->diversifier_len = src->diversifier_len;
-    memcpy(dst->diversifier, src->diversifier, dst->diversifier_len);
-    dst->sio_len = src->sio_len;
-    memcpy(dst->sio, src->sio, dst->sio_len);
-}
-
 void seos_worker_diversify_key(
     uint8_t master_key_value[16],
     uint8_t* diversifier,

+ 0 - 12
seos/seos_common.h

@@ -50,16 +50,6 @@ typedef enum {
     REQUEST_SIO,
 } SeosPhase;
 
-typedef struct {
-    uint8_t diversifier[16];
-    size_t diversifier_len;
-    uint8_t sio[128];
-    size_t sio_len;
-    uint8_t priv_key[16];
-    uint8_t auth_key[16];
-    uint8_t adf_response[72];
-} SeosCredential;
-
 typedef struct {
     uint8_t rndICC[8];
     uint8_t UID[8];
@@ -75,8 +65,6 @@ typedef struct {
 void seos_log_bitbuffer(char* TAG, char* prefix, BitBuffer* buffer);
 void seos_log_buffer(char* TAG, char* prefix, uint8_t* buffer, size_t buffer_len);
 
-void seos_common_copy_credential(const SeosCredential* src, SeosCredential* dst);
-
 void seos_worker_diversify_key(
     uint8_t master_key_value[16],
     uint8_t* diversifier,

+ 379 - 0
seos/seos_credential.c

@@ -0,0 +1,379 @@
+#include "seos_credential_i.h"
+
+#define SEADER_PATH          "/ext/apps_data/seader"
+#define SEADER_APP_EXTENSION ".credential"
+
+#define TAG "SeosCredential"
+
+static uint8_t empty[16] =
+    {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+SeosCredential* seos_credential_alloc() {
+    SeosCredential* seos_credential = malloc(sizeof(SeosCredential));
+    memset(seos_credential, 0, sizeof(SeosCredential));
+
+    seos_credential->load_path = furi_string_alloc();
+    seos_credential->storage = furi_record_open(RECORD_STORAGE);
+    seos_credential->dialogs = furi_record_open(RECORD_DIALOGS);
+    seos_credential->use_hardcoded = false;
+
+    return seos_credential;
+}
+
+bool seos_credential_clear(SeosCredential* seos_credential) {
+    memset(seos_credential->diversifier, 0, sizeof(seos_credential->diversifier));
+    seos_credential->diversifier_len = 0;
+    memset(seos_credential->sio, 0, sizeof(seos_credential->sio));
+    seos_credential->sio_len = 0;
+    memset(seos_credential->priv_key, 0, sizeof(seos_credential->priv_key));
+    memset(seos_credential->auth_key, 0, sizeof(seos_credential->auth_key));
+    seos_credential->adf_oid_len = 0;
+    memset(seos_credential->adf_oid, 0, sizeof(seos_credential->adf_oid));
+    memset(seos_credential->adf_response, 0, sizeof(seos_credential->adf_response));
+    memset(seos_credential->name, 0, sizeof(seos_credential->name));
+    return true;
+}
+
+void seos_credential_free(SeosCredential* seos_credential) {
+    furi_assert(seos_credential);
+
+    furi_string_free(seos_credential->load_path);
+    furi_record_close(RECORD_STORAGE);
+    furi_record_close(RECORD_DIALOGS);
+
+    free(seos_credential);
+}
+
+bool seos_credential_save(SeosCredential* seos_credential, const char* dev_name) {
+    bool saved = false;
+    FlipperFormat* file = flipper_format_file_alloc(seos_credential->storage);
+    FuriString* temp_str = furi_string_alloc();
+    bool use_load_path = true;
+
+    do {
+        if(use_load_path && !furi_string_empty(seos_credential->load_path)) {
+            // Get directory name
+            path_extract_dirname(furi_string_get_cstr(seos_credential->load_path), temp_str);
+            // Make path to file to save
+            furi_string_cat_printf(temp_str, "/%s%s", dev_name, SEOS_APP_EXTENSION);
+        } else {
+            // First remove file if it was saved
+            furi_string_printf(
+                temp_str, "%s/%s%s", STORAGE_APP_DATA_PATH_PREFIX, dev_name, SEOS_APP_EXTENSION);
+        }
+
+        // Open file
+        if(!flipper_format_file_open_always(file, furi_string_get_cstr(temp_str))) break;
+
+        // Write header
+        if(!flipper_format_write_header_cstr(file, seos_file_header, seos_file_version)) break;
+
+        if(!flipper_format_write_uint32(
+               file, "Diversifier Length", (uint32_t*)&(seos_credential->diversifier_len), 1))
+            break;
+        if(!flipper_format_write_hex(
+               file, "Diversifier", seos_credential->diversifier, seos_credential->diversifier_len))
+            break;
+        if(!flipper_format_write_uint32(
+               file, "SIO Length", (uint32_t*)&(seos_credential->sio_len), 1))
+            break;
+        if(!flipper_format_write_hex(file, "SIO", seos_credential->sio, seos_credential->sio_len))
+            break;
+        if(!flipper_format_write_hex(
+               file, "Priv Key", seos_credential->priv_key, sizeof(seos_credential->priv_key)))
+            break;
+        if(!flipper_format_write_hex(
+               file, "Auth Key", seos_credential->auth_key, sizeof(seos_credential->auth_key)))
+            break;
+        if(seos_credential->adf_response[0] != 0) {
+            flipper_format_write_hex(
+                file,
+                "ADF Response",
+                seos_credential->adf_response,
+                sizeof(seos_credential->adf_response));
+        }
+        if(seos_credential->adf_oid_len > 0) {
+            flipper_format_write_uint32(
+                file, "ADF OID Length", (uint32_t*)&(seos_credential->adf_oid_len), 1);
+            flipper_format_write_hex(
+                file, "ADF OID", seos_credential->adf_oid, seos_credential->adf_oid_len);
+        }
+
+        saved = true;
+    } while(false);
+
+    if(!saved) {
+        dialog_message_show_storage_error(seos_credential->dialogs, "Can not save\nfile");
+    }
+    furi_string_free(temp_str);
+    flipper_format_free(file);
+    return saved;
+}
+
+static bool
+    seos_credential_file_load(SeosCredential* seos_credential, FuriString* path, bool show_dialog) {
+    bool parsed = false;
+    FlipperFormat* file = flipper_format_file_alloc(seos_credential->storage);
+    FuriString* temp_str;
+    temp_str = furi_string_alloc();
+    bool deprecated_version = false;
+
+    if(seos_credential->loading_cb) {
+        seos_credential->loading_cb(seos_credential->loading_cb_ctx, true);
+    }
+
+    memset(seos_credential->diversifier, 0, sizeof(seos_credential->diversifier));
+    memset(seos_credential->sio, 0, sizeof(seos_credential->sio));
+    do {
+        if(!flipper_format_file_open_existing(file, furi_string_get_cstr(path))) break;
+
+        // Read and verify file header
+        uint32_t version = 0;
+        if(!flipper_format_read_header(file, temp_str, &version)) break;
+        if(furi_string_cmp_str(temp_str, seos_file_header) || (version != seos_file_version)) {
+            deprecated_version = true;
+            break;
+        }
+
+        if(!flipper_format_read_uint32(
+               file, "Diversifier Length", (uint32_t*)&(seos_credential->diversifier_len), 1))
+            break;
+        if(!flipper_format_read_hex(
+               file, "Diversifier", seos_credential->diversifier, seos_credential->diversifier_len))
+            break;
+
+        if(!flipper_format_read_uint32(
+               file, "SIO Length", (uint32_t*)&(seos_credential->sio_len), 1))
+            break;
+        if(!flipper_format_read_hex(file, "SIO", seos_credential->sio, seos_credential->sio_len))
+            break;
+
+        // optional
+        memset(seos_credential->priv_key, 0, sizeof(seos_credential->priv_key));
+        memset(seos_credential->auth_key, 0, sizeof(seos_credential->auth_key));
+        memset(seos_credential->adf_response, 0, sizeof(seos_credential->adf_response));
+        flipper_format_read_hex(
+            file, "Priv Key", seos_credential->priv_key, sizeof(seos_credential->priv_key));
+        flipper_format_read_hex(
+            file, "Auth Key", seos_credential->auth_key, sizeof(seos_credential->auth_key));
+        if(memcmp(seos_credential->priv_key, empty, sizeof(empty)) != 0) {
+            FURI_LOG_I(TAG, "+ Priv Key");
+        }
+        if(memcmp(seos_credential->priv_key, empty, sizeof(empty)) != 0) {
+            FURI_LOG_I(TAG, "+ Auth Key");
+        }
+        flipper_format_read_hex(
+            file,
+            "ADF Response",
+            seos_credential->adf_response,
+            sizeof(seos_credential->adf_response));
+
+        flipper_format_read_uint32(
+            file, "ADF OID Length", (uint32_t*)&(seos_credential->adf_oid_len), 1);
+        flipper_format_read_hex(
+            file, "ADF OID", seos_credential->adf_oid, seos_credential->adf_oid_len);
+
+        parsed = true;
+    } while(false);
+
+    if(seos_credential->loading_cb) {
+        seos_credential->loading_cb(seos_credential->loading_cb_ctx, false);
+    }
+
+    if((!parsed) && (show_dialog)) {
+        if(deprecated_version) {
+            dialog_message_show_storage_error(seos_credential->dialogs, "File format deprecated");
+        } else {
+            dialog_message_show_storage_error(seos_credential->dialogs, "Can not parse\nfile");
+        }
+    }
+
+    furi_string_free(temp_str);
+    flipper_format_free(file);
+
+    return parsed;
+}
+
+bool seos_credential_file_select_seos(SeosCredential* seos_credential) {
+    furi_assert(seos_credential);
+    bool res = false;
+
+    FuriString* seos_app_folder = furi_string_alloc_set(STORAGE_APP_DATA_PATH_PREFIX);
+
+    DialogsFileBrowserOptions browser_options;
+    dialog_file_browser_set_basic_options(&browser_options, SEOS_APP_EXTENSION, &I_Nfc_10px);
+    browser_options.base_path = STORAGE_APP_DATA_PATH_PREFIX;
+
+    res = dialog_file_browser_show(
+        seos_credential->dialogs, seos_credential->load_path, seos_app_folder, &browser_options);
+
+    furi_string_free(seos_app_folder);
+    if(res) {
+        FuriString* filename;
+        filename = furi_string_alloc();
+        path_extract_filename(seos_credential->load_path, filename, true);
+        strncpy(seos_credential->name, furi_string_get_cstr(filename), SEOS_FILE_NAME_MAX_LENGTH);
+        res = seos_credential_file_load(seos_credential, seos_credential->load_path, true);
+        furi_string_free(filename);
+    }
+
+    return res;
+}
+
+static bool seos_credential_file_load_seader(
+    SeosCredential* seos_credential,
+    FuriString* path,
+    bool show_dialog) {
+    bool parsed = false;
+    FlipperFormat* file = flipper_format_file_alloc(seos_credential->storage);
+    FuriString* reason = furi_string_alloc_set("Couldn't load file");
+    FuriString* temp_str;
+    temp_str = furi_string_alloc();
+    const char* seader_file_header = "Flipper Seader Credential";
+    const uint32_t seader_file_version = 1;
+
+    if(seos_credential->loading_cb) {
+        seos_credential->loading_cb(seos_credential->loading_cb_ctx, true);
+    }
+
+    memset(seos_credential->diversifier, 0, sizeof(seos_credential->diversifier));
+    memset(seos_credential->sio, 0, sizeof(seos_credential->sio));
+    do {
+        if(!flipper_format_file_open_existing(file, furi_string_get_cstr(path))) break;
+
+        // Read and verify file header
+        uint32_t version = 0;
+        if(!flipper_format_read_header(file, temp_str, &version)) break;
+        if(furi_string_cmp_str(temp_str, seader_file_header) || (version != seader_file_version)) {
+            furi_string_printf(reason, "Deprecated file format");
+            break;
+        }
+        // Don't forget, order of keys is important
+
+        if(!flipper_format_key_exist(file, "SIO")) {
+            furi_string_printf(reason, "Missing SIO");
+            break;
+        }
+        seos_credential->sio_len = 64; // Seader SIO size
+        // We can't check the return status because it will be false if less than the requested length of bytes was read.
+        flipper_format_read_hex(file, "SIO", seos_credential->sio, seos_credential->sio_len);
+        seos_credential->sio_len =
+            seos_credential->sio[1] + 4; // 2 for type and length, 2 for null after SIO data
+
+        // -------------
+        if(!flipper_format_key_exist(file, "Diversifier")) {
+            furi_string_printf(reason, "Missing Diversifier");
+            break;
+        }
+        seos_credential->diversifier_len = 8; //Seader diversifier size
+        flipper_format_read_hex(
+            file, "Diversifier", seos_credential->diversifier, seos_credential->diversifier_len);
+        uint8_t* end = memchr(seos_credential->diversifier, 0, 8);
+        if(end) {
+            seos_credential->diversifier_len = end - seos_credential->diversifier;
+        } // Returns NULL if char cannot be found
+
+        SeosCredential* cred = seos_credential;
+        char display[128 * 2 + 1];
+
+        memset(display, 0, sizeof(display));
+        for(uint8_t i = 0; i < cred->sio_len; i++) {
+            snprintf(display + (i * 2), sizeof(display), "%02x", cred->sio[i]);
+        }
+        FURI_LOG_D(TAG, "SIO: %s", display);
+
+        memset(display, 0, sizeof(display));
+        for(uint8_t i = 0; i < cred->diversifier_len; i++) {
+            snprintf(display + (i * 2), sizeof(display), "%02x", cred->diversifier[i]);
+        }
+        FURI_LOG_D(TAG, "Diversifier: %s", display);
+
+        parsed = true;
+    } while(false);
+
+    if(seos_credential->loading_cb) {
+        seos_credential->loading_cb(seos_credential->loading_cb_ctx, false);
+    }
+
+    if((!parsed) && (show_dialog)) {
+        dialog_message_show_storage_error(seos_credential->dialogs, furi_string_get_cstr(reason));
+    }
+
+    furi_string_free(reason);
+    furi_string_free(temp_str);
+    flipper_format_free(file);
+
+    return parsed;
+}
+
+bool seos_credential_file_select_seader(SeosCredential* seos_credential) {
+    furi_assert(seos_credential);
+    bool res = false;
+
+    FuriString* app_folder = furi_string_alloc_set(SEADER_PATH);
+
+    DialogsFileBrowserOptions browser_options;
+    dialog_file_browser_set_basic_options(&browser_options, SEADER_APP_EXTENSION, &I_Nfc_10px);
+    browser_options.base_path = SEADER_PATH;
+
+    res = dialog_file_browser_show(
+        seos_credential->dialogs, seos_credential->load_path, app_folder, &browser_options);
+
+    furi_string_free(app_folder);
+    if(res) {
+        FuriString* filename;
+        filename = furi_string_alloc();
+        path_extract_filename(seos_credential->load_path, filename, true);
+        strncpy(seos_credential->name, furi_string_get_cstr(filename), SEOS_FILE_NAME_MAX_LENGTH);
+        res = seos_credential_file_load_seader(seos_credential, seos_credential->load_path, true);
+        furi_string_free(filename);
+    }
+
+    return res;
+}
+
+bool seos_credential_file_select(SeosCredential* seos_credential) {
+    if(seos_credential->load_type == SeosLoadSeos) {
+        return seos_credential_file_select_seos(seos_credential);
+    } else if(seos_credential->load_type == SeosLoadSeader) {
+        return seos_credential_file_select_seader(seos_credential);
+    }
+    return false;
+}
+
+bool seos_credential_delete(SeosCredential* seos_credential, bool use_load_path) {
+    furi_assert(seos_credential);
+    bool deleted = false;
+    FuriString* file_path;
+    file_path = furi_string_alloc();
+
+    do {
+        // Delete original file
+        if(use_load_path && !furi_string_empty(seos_credential->load_path)) {
+            furi_string_set(file_path, seos_credential->load_path);
+        } else {
+            furi_string_printf(
+                file_path, APP_DATA_PATH("%s%s"), seos_credential->name, SEOS_APP_EXTENSION);
+        }
+        if(!storage_simply_remove(seos_credential->storage, furi_string_get_cstr(file_path)))
+            break;
+        deleted = true;
+    } while(0);
+
+    if(!deleted) {
+        dialog_message_show_storage_error(seos_credential->dialogs, "Can not remove file");
+    }
+
+    furi_string_free(file_path);
+    return deleted;
+}
+
+void seos_credential_set_loading_callback(
+    SeosCredential* seos_credential,
+    SeosLoadingCallback callback,
+    void* context) {
+    furi_assert(seos_credential);
+
+    seos_credential->loading_cb = callback;
+    seos_credential->loading_cb_ctx = context;
+}

+ 53 - 0
seos/seos_credential.h

@@ -0,0 +1,53 @@
+#pragma once
+
+#include <furi.h>
+#include <storage/storage.h>
+#include <dialogs/dialogs.h>
+#include <lib/toolbox/path.h>
+#include <lib/flipper_format/flipper_format.h>
+
+/* generated by fbt from .png files in images folder */
+#include <seos_icons.h>
+
+#include "seos.h"
+#include "seos_common.h"
+
+typedef void (*SeosLoadingCallback)(void* context, bool state);
+
+typedef struct {
+    uint8_t diversifier[16];
+    size_t diversifier_len;
+    uint8_t sio[128];
+    size_t sio_len;
+    uint8_t priv_key[16];
+    uint8_t auth_key[16];
+    size_t adf_oid_len;
+    uint8_t adf_oid[32];
+    uint8_t adf_response[80];
+    bool use_hardcoded;
+
+    char name[SEOS_FILE_NAME_MAX_LENGTH + 1];
+    FuriString* load_path;
+    SeosLoadingCallback loading_cb;
+    void* loading_cb_ctx;
+    Storage* storage;
+    DialogsApp* dialogs;
+    enum {
+        SeosLoadSeos,
+        SeosLoadSeader
+    } load_type;
+
+} SeosCredential;
+
+SeosCredential* seos_credential_alloc();
+void seos_credential_free(SeosCredential* seos_credential);
+
+bool seos_credential_save(SeosCredential* seos_credential, const char* dev_name);
+bool seos_credential_clear(SeosCredential* seos_credential);
+bool seos_credential_file_select(SeosCredential* seos_credential);
+void seos_credential_set_loading_callback(
+    SeosCredential* seos_credential,
+    SeosLoadingCallback callback,
+    void* context);
+
+bool seos_credential_delete(SeosCredential* seos_credential, bool use_load_path);

+ 4 - 0
seos/seos_credential_i.h

@@ -0,0 +1,4 @@
+#pragma once
+
+#include "seos_i.h"
+#include "seos_credential.h"

+ 48 - 328
seos/seos_emulator.c

@@ -2,9 +2,7 @@
 
 #define TAG "SeosEmulator"
 
-#define NAD_MASK             0x08
-#define SEADER_PATH          "/ext/apps_data/seader"
-#define SEADER_APP_EXTENSION ".credential"
+#define NAD_MASK 0x08
 
 static uint8_t select_header[] = {0x00, 0xa4, 0x04, 0x00};
 static uint8_t standard_seos_aid[] = {0xa0, 0x00, 0x00, 0x04, 0x40, 0x00, 0x01, 0x01, 0x00, 0x01};
@@ -25,8 +23,6 @@ static uint8_t general_authenticate_1[] =
 static uint8_t general_authenticate_1_response_header[] = {0x7c, 0x0a, 0x81, 0x08};
 static uint8_t general_authenticate_2_header[] = {0x00, 0x87, 0x00, 0x01};
 static uint8_t secure_messaging_header[] = {0x0c, 0xcb, 0x3f, 0xff};
-static uint8_t empty[16] =
-    {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
 
 SeosEmulator* seos_emulator_alloc(SeosCredential* credential) {
     SeosEmulator* seos_emulator = malloc(sizeof(SeosEmulator));
@@ -42,9 +38,6 @@ SeosEmulator* seos_emulator_alloc(SeosCredential* credential) {
 
     seos_emulator->secure_messaging = NULL;
 
-    seos_emulator->storage = furi_record_open(RECORD_STORAGE);
-    seos_emulator->dialogs = furi_record_open(RECORD_DIALOGS);
-    seos_emulator->load_path = furi_string_alloc();
     seos_emulator->tx_buffer = bit_buffer_alloc(SEOS_WORKER_MAX_BUFFER_SIZE);
 
     return seos_emulator;
@@ -57,299 +50,10 @@ void seos_emulator_free(SeosEmulator* seos_emulator) {
         secure_messaging_free(seos_emulator->secure_messaging);
     }
 
-    furi_record_close(RECORD_STORAGE);
-    furi_record_close(RECORD_DIALOGS);
-    furi_string_free(seos_emulator->load_path);
     bit_buffer_free(seos_emulator->tx_buffer);
     free(seos_emulator);
 }
 
-void seos_emulator_set_loading_callback(
-    SeosEmulator* seos_emulator,
-    SeosLoadingCallback callback,
-    void* context) {
-    furi_assert(seos_emulator);
-
-    seos_emulator->loading_cb = callback;
-    seos_emulator->loading_cb_ctx = context;
-}
-
-static bool
-    seos_emulator_file_load(SeosEmulator* seos_emulator, FuriString* path, bool show_dialog) {
-    bool parsed = false;
-    FlipperFormat* file = flipper_format_file_alloc(seos_emulator->storage);
-    FuriString* temp_str;
-    temp_str = furi_string_alloc();
-    bool deprecated_version = false;
-
-    if(seos_emulator->loading_cb) {
-        seos_emulator->loading_cb(seos_emulator->loading_cb_ctx, true);
-    }
-
-    memset(
-        seos_emulator->credential->diversifier, 0, sizeof(seos_emulator->credential->diversifier));
-    memset(seos_emulator->credential->sio, 0, sizeof(seos_emulator->credential->sio));
-    do {
-        if(!flipper_format_file_open_existing(file, furi_string_get_cstr(path))) break;
-
-        // Read and verify file header
-        uint32_t version = 0;
-        if(!flipper_format_read_header(file, temp_str, &version)) break;
-        if(furi_string_cmp_str(temp_str, seos_file_header) || (version != seos_file_version)) {
-            deprecated_version = true;
-            break;
-        }
-
-        if(!flipper_format_read_uint32(
-               file,
-               "Diversifier Length",
-               (uint32_t*)&(seos_emulator->credential->diversifier_len),
-               1))
-            break;
-        if(!flipper_format_read_hex(
-               file,
-               "Diversifier",
-               seos_emulator->credential->diversifier,
-               seos_emulator->credential->diversifier_len))
-            break;
-
-        if(!flipper_format_read_uint32(
-               file, "SIO Length", (uint32_t*)&(seos_emulator->credential->sio_len), 1))
-            break;
-        if(!flipper_format_read_hex(
-               file, "SIO", seos_emulator->credential->sio, seos_emulator->credential->sio_len))
-            break;
-
-        // optional
-        memset(
-            seos_emulator->credential->priv_key, 0, sizeof(seos_emulator->credential->priv_key));
-        memset(
-            seos_emulator->credential->auth_key, 0, sizeof(seos_emulator->credential->auth_key));
-        memset(
-            seos_emulator->credential->adf_response,
-            0,
-            sizeof(seos_emulator->credential->adf_response));
-        flipper_format_read_hex(
-            file,
-            "Priv Key",
-            seos_emulator->credential->priv_key,
-            sizeof(seos_emulator->credential->priv_key));
-        flipper_format_read_hex(
-            file,
-            "Auth Key",
-            seos_emulator->credential->auth_key,
-            sizeof(seos_emulator->credential->auth_key));
-        if(memcmp(seos_emulator->credential->priv_key, empty, sizeof(empty)) != 0) {
-            FURI_LOG_I(TAG, "+ Priv Key");
-        }
-        if(memcmp(seos_emulator->credential->priv_key, empty, sizeof(empty)) != 0) {
-            FURI_LOG_I(TAG, "+ Auth Key");
-        }
-        flipper_format_read_hex(
-            file,
-            "ADF Response",
-            seos_emulator->credential->adf_response,
-            sizeof(seos_emulator->credential->adf_response));
-        parsed = true;
-    } while(false);
-
-    if(seos_emulator->loading_cb) {
-        seos_emulator->loading_cb(seos_emulator->loading_cb_ctx, false);
-    }
-
-    if((!parsed) && (show_dialog)) {
-        if(deprecated_version) {
-            dialog_message_show_storage_error(seos_emulator->dialogs, "File format deprecated");
-        } else {
-            dialog_message_show_storage_error(seos_emulator->dialogs, "Can not parse\nfile");
-        }
-    }
-
-    furi_string_free(temp_str);
-    flipper_format_free(file);
-
-    return parsed;
-}
-
-static bool seos_emulator_file_load_seader(
-    SeosEmulator* seos_emulator,
-    FuriString* path,
-    bool show_dialog) {
-    bool parsed = false;
-    FlipperFormat* file = flipper_format_file_alloc(seos_emulator->storage);
-    FuriString* reason = furi_string_alloc_set("Couldn't load file");
-    FuriString* temp_str;
-    temp_str = furi_string_alloc();
-    const char* seader_file_header = "Flipper Seader Credential";
-    const uint32_t seader_file_version = 1;
-
-    if(seos_emulator->loading_cb) {
-        seos_emulator->loading_cb(seos_emulator->loading_cb_ctx, true);
-    }
-
-    memset(
-        seos_emulator->credential->diversifier, 0, sizeof(seos_emulator->credential->diversifier));
-    memset(seos_emulator->credential->sio, 0, sizeof(seos_emulator->credential->sio));
-    do {
-        if(!flipper_format_file_open_existing(file, furi_string_get_cstr(path))) break;
-
-        // Read and verify file header
-        uint32_t version = 0;
-        if(!flipper_format_read_header(file, temp_str, &version)) break;
-        if(furi_string_cmp_str(temp_str, seader_file_header) || (version != seader_file_version)) {
-            furi_string_printf(reason, "Deprecated file format");
-            break;
-        }
-        // Don't forget, order of keys is important
-
-        if(!flipper_format_key_exist(file, "SIO")) {
-            furi_string_printf(reason, "Missing SIO");
-            break;
-        }
-        seos_emulator->credential->sio_len = 64; // Seader SIO size
-        // We can't check the return status because it will be false if less than the requested length of bytes was read.
-        flipper_format_read_hex(
-            file, "SIO", seos_emulator->credential->sio, seos_emulator->credential->sio_len);
-        seos_emulator->credential->sio_len = seos_emulator->credential->sio[1] +
-                                             4; // 2 for type and length, 2 for null after SIO data
-
-        // -------------
-        if(!flipper_format_key_exist(file, "Diversifier")) {
-            furi_string_printf(reason, "Missing Diversifier");
-            break;
-        }
-        seos_emulator->credential->diversifier_len = 8; //Seader diversifier size
-        flipper_format_read_hex(
-            file,
-            "Diversifier",
-            seos_emulator->credential->diversifier,
-            seos_emulator->credential->diversifier_len);
-        uint8_t* end = memchr(seos_emulator->credential->diversifier, 0, 8);
-        if(end) {
-            seos_emulator->credential->diversifier_len =
-                end - seos_emulator->credential->diversifier;
-        } // Returns NULL if char cannot be found
-
-        SeosCredential* cred = seos_emulator->credential;
-        char display[128 * 2 + 1];
-
-        memset(display, 0, sizeof(display));
-        for(uint8_t i = 0; i < cred->sio_len; i++) {
-            snprintf(display + (i * 2), sizeof(display), "%02x", cred->sio[i]);
-        }
-        FURI_LOG_D(TAG, "SIO: %s", display);
-
-        memset(display, 0, sizeof(display));
-        for(uint8_t i = 0; i < cred->diversifier_len; i++) {
-            snprintf(display + (i * 2), sizeof(display), "%02x", cred->diversifier[i]);
-        }
-        FURI_LOG_D(TAG, "Diversifier: %s", display);
-
-        parsed = true;
-    } while(false);
-
-    if(seos_emulator->loading_cb) {
-        seos_emulator->loading_cb(seos_emulator->loading_cb_ctx, false);
-    }
-
-    if((!parsed) && (show_dialog)) {
-        dialog_message_show_storage_error(seos_emulator->dialogs, furi_string_get_cstr(reason));
-    }
-
-    furi_string_free(reason);
-    furi_string_free(temp_str);
-    flipper_format_free(file);
-
-    return parsed;
-}
-
-bool seos_emulator_file_select_seader(SeosEmulator* seos_emulator) {
-    furi_assert(seos_emulator);
-    bool res = false;
-
-    FuriString* app_folder = furi_string_alloc_set(SEADER_PATH);
-
-    DialogsFileBrowserOptions browser_options;
-    dialog_file_browser_set_basic_options(&browser_options, SEADER_APP_EXTENSION, &I_Nfc_10px);
-    browser_options.base_path = SEADER_PATH;
-
-    res = dialog_file_browser_show(
-        seos_emulator->dialogs, seos_emulator->load_path, app_folder, &browser_options);
-
-    furi_string_free(app_folder);
-    if(res) {
-        FuriString* filename;
-        filename = furi_string_alloc();
-        path_extract_filename(seos_emulator->load_path, filename, true);
-        strncpy(seos_emulator->name, furi_string_get_cstr(filename), SEOS_FILE_NAME_MAX_LENGTH);
-        res = seos_emulator_file_load_seader(seos_emulator, seos_emulator->load_path, true);
-        furi_string_free(filename);
-    }
-
-    return res;
-}
-
-bool seos_emulator_file_select_seos(SeosEmulator* seos_emulator) {
-    furi_assert(seos_emulator);
-    bool res = false;
-
-    FuriString* seos_app_folder = furi_string_alloc_set(STORAGE_APP_DATA_PATH_PREFIX);
-
-    DialogsFileBrowserOptions browser_options;
-    dialog_file_browser_set_basic_options(&browser_options, SEOS_APP_EXTENSION, &I_Nfc_10px);
-    browser_options.base_path = STORAGE_APP_DATA_PATH_PREFIX;
-
-    res = dialog_file_browser_show(
-        seos_emulator->dialogs, seos_emulator->load_path, seos_app_folder, &browser_options);
-
-    furi_string_free(seos_app_folder);
-    if(res) {
-        FuriString* filename;
-        filename = furi_string_alloc();
-        path_extract_filename(seos_emulator->load_path, filename, true);
-        strncpy(seos_emulator->name, furi_string_get_cstr(filename), SEOS_FILE_NAME_MAX_LENGTH);
-        res = seos_emulator_file_load(seos_emulator, seos_emulator->load_path, true);
-        furi_string_free(filename);
-    }
-
-    return res;
-}
-
-bool seos_emulator_file_select(SeosEmulator* seos_emulator) {
-    if(seos_emulator->load_type == SeosLoadSeos) {
-        return seos_emulator_file_select_seos(seos_emulator);
-    } else if(seos_emulator->load_type == SeosLoadSeader) {
-        return seos_emulator_file_select_seader(seos_emulator);
-    }
-    return false;
-}
-
-bool seos_emulator_delete(SeosEmulator* seos_emulator, bool use_load_path) {
-    furi_assert(seos_emulator);
-    bool deleted = false;
-    FuriString* file_path;
-    file_path = furi_string_alloc();
-
-    do {
-        // Delete original file
-        if(use_load_path && !furi_string_empty(seos_emulator->load_path)) {
-            furi_string_set(file_path, seos_emulator->load_path);
-        } else {
-            furi_string_printf(
-                file_path, APP_DATA_PATH("%s%s"), seos_emulator->name, SEOS_APP_EXTENSION);
-        }
-        if(!storage_simply_remove(seos_emulator->storage, furi_string_get_cstr(file_path))) break;
-        deleted = true;
-    } while(0);
-
-    if(!deleted) {
-        dialog_message_show_storage_error(seos_emulator->dialogs, "Can not remove file");
-    }
-
-    furi_string_free(file_path);
-    return deleted;
-}
-
 void seos_emulator_select_aid(BitBuffer* tx_buffer) {
     FURI_LOG_D(TAG, "Select AID");
     bit_buffer_append_bytes(tx_buffer, SEOS_APPLET_FCI, sizeof(SEOS_APPLET_FCI));
@@ -381,7 +85,10 @@ bool seos_emulator_general_authenticate_2(
 
     params->key_no = rx_data[3];
 
-    if(memcmp(credential->priv_key, empty, sizeof(empty)) == 0) {
+    if(credential->use_hardcoded) {
+        memcpy(params->priv_key, credential->priv_key, sizeof(params->priv_key));
+        memcpy(params->auth_key, credential->auth_key, sizeof(params->auth_key));
+    } else {
         seos_worker_diversify_key(
             SEOS_ADF1_READ,
             credential->diversifier,
@@ -393,10 +100,6 @@ bool seos_emulator_general_authenticate_2(
             params->key_no,
             true,
             params->priv_key);
-    } else {
-        memcpy(params->priv_key, credential->priv_key, sizeof(params->priv_key));
-    }
-    if(memcmp(credential->auth_key, empty, sizeof(empty)) == 0) {
         seos_worker_diversify_key(
             SEOS_ADF1_READ,
             credential->diversifier,
@@ -408,8 +111,6 @@ bool seos_emulator_general_authenticate_2(
             params->key_no,
             false,
             params->auth_key);
-    } else {
-        memcpy(params->auth_key, credential->auth_key, sizeof(params->auth_key));
     }
 
     uint8_t cmac[16];
@@ -551,18 +252,42 @@ void seos_emulator_aes_adf_payload(SeosCredential* credential, uint8_t* buffer)
     mbedtls_aes_free(&ctx);
 }
 
-void seos_emulator_select_adf(
+bool seos_emulator_select_adf(
+    const uint8_t* oid_list,
+    size_t oid_list_len,
     AuthParameters* params,
     SeosCredential* credential,
     BitBuffer* tx_buffer) {
     FURI_LOG_D(TAG, "Select ADF");
-    // Shortcut if the credential file contained the hardcoded response
-    if(credential->adf_response[2] != 0x00 && credential->adf_response[2] == params->cipher) {
-        FURI_LOG_I(TAG, "Using hardcoded ADF Response");
-        bit_buffer_append_bytes(
-            tx_buffer, credential->adf_response, sizeof(credential->adf_response));
-        seos_log_bitbuffer(TAG, "Select ADF (0xcd02...)", tx_buffer);
-        return;
+
+    void* p = NULL;
+    if(credential->adf_oid_len > 0) {
+        p = memmem(oid_list, oid_list_len, credential->adf_oid, credential->adf_oid_len);
+        if(p) {
+            seos_log_buffer(TAG, "Select ADF OID(credential)", p, credential->adf_oid_len);
+
+            if(credential->adf_response[0] == 0xCD) {
+                FURI_LOG_I(TAG, "Using hardcoded ADF Response");
+                // 4 byte cipher/hash
+                // 2 byte cryptogram header
+                // x bytes of cryptogram
+                // 10 bytes for mac (2 byte header + 8 byte cmac)
+                size_t adf_response_len = 4 + 2 + credential->adf_response[5] + 10;
+                bit_buffer_append_bytes(tx_buffer, credential->adf_response, adf_response_len);
+
+                params->cipher = credential->adf_response[2];
+                params->hash = credential->adf_response[3];
+                credential->use_hardcoded = true;
+                return true;
+            }
+        }
+    }
+    // Next we try to match the ADF OID from the keys file
+    p = memmem(oid_list, oid_list_len, SEOS_ADF_OID, SEOS_ADF_OID_LEN);
+    if(p) {
+        seos_log_buffer(TAG, "Select ADF OID(keys)", p, SEOS_ADF_OID_LEN);
+    } else {
+        return false;
     }
 
     size_t prefix_len = bit_buffer_get_size_bytes(tx_buffer);
@@ -608,8 +333,7 @@ void seos_emulator_select_adf(
     uint8_t cmac_prefix[] = {0x8e, 0x08};
     bit_buffer_append_bytes(tx_buffer, cmac_prefix, sizeof(cmac_prefix));
     bit_buffer_append_bytes(tx_buffer, cmac, SEOS_WORKER_CMAC_SIZE);
-
-    seos_log_bitbuffer(TAG, "Select ADF (0xcd02...)", tx_buffer);
+    return true;
 }
 
 NfcCommand seos_worker_listener_inspect_reader(Seos* seos) {
@@ -657,6 +381,7 @@ NfcCommand seos_worker_listener_process_message(Seos* seos) {
     const uint8_t* apdu = rx_data + offset;
 
     if(memcmp(apdu, select_header, sizeof(select_header)) == 0) {
+        seos_emulator->credential->use_hardcoded = false;
         if(memcmp(apdu + sizeof(select_header) + 1, standard_seos_aid, sizeof(standard_seos_aid)) ==
            0) {
             seos_emulator_select_aid(seos_emulator->tx_buffer);
@@ -692,22 +417,17 @@ NfcCommand seos_worker_listener_process_message(Seos* seos) {
                 seos_emulator->tx_buffer, (uint8_t*)FILE_NOT_FOUND, sizeof(FILE_NOT_FOUND));
         }
     } else if(memcmp(apdu, select_adf_header, sizeof(select_adf_header)) == 0) {
-        // is our adf in the list?
         // +1 to skip APDU length byte
-        void* p = memmem(
-            apdu + sizeof(select_adf_header) + 1,
-            apdu[sizeof(select_adf_header)],
-            SEOS_ADF_OID,
-            SEOS_ADF_OID_LEN);
-        if(p) {
-            BitBuffer* tmp = bit_buffer_alloc(SEOS_ADF_OID_LEN);
-            bit_buffer_append_bytes(tmp, p, SEOS_ADF_OID_LEN);
-            seos_log_bitbuffer(TAG, "Matched ADF", tmp);
-            bit_buffer_free(tmp);
-            view_dispatcher_send_custom_event(seos->view_dispatcher, SeosCustomEventADFMatched);
+        const uint8_t* oid_list = apdu + sizeof(select_adf_header) + 1;
+        size_t oid_list_len = apdu[sizeof(select_adf_header)];
 
-            seos_emulator_select_adf(
-                &seos_emulator->params, seos_emulator->credential, seos_emulator->tx_buffer);
+        if(seos_emulator_select_adf(
+               oid_list,
+               oid_list_len,
+               &seos_emulator->params,
+               seos_emulator->credential,
+               seos_emulator->tx_buffer)) {
+            view_dispatcher_send_custom_event(seos->view_dispatcher, SeosCustomEventADFMatched);
         } else {
             FURI_LOG_W(TAG, "Failed to match any ADF OID");
         }

+ 4 - 25
seos/seos_emulator.h

@@ -1,18 +1,14 @@
 #pragma once
 
-#include <storage/storage.h>
-#include <dialogs/dialogs.h>
-#include <lib/toolbox/path.h>
 #include <lib/nfc/protocols/nfc_generic_event.h>
 #include <lib/nfc/protocols/iso14443_4a/iso14443_4a_listener.h>
 #include <lib/nfc/helpers/iso14443_crc.h>
 #include <mbedtls/des.h>
 #include <mbedtls/aes.h>
 
+#include "seos_credential.h"
 #include "secure_messaging.h"
 
-typedef void (*SeosLoadingCallback)(void* context, bool state);
-
 typedef struct {
     Iso14443_3aListener* iso14443_listener;
     BitBuffer* tx_buffer;
@@ -23,17 +19,6 @@ typedef struct {
     SecureMessaging* secure_messaging;
 
     SeosCredential* credential;
-
-    char name[SEOS_FILE_NAME_MAX_LENGTH + 1];
-    FuriString* load_path;
-    SeosLoadingCallback loading_cb;
-    void* loading_cb_ctx;
-    Storage* storage;
-    DialogsApp* dialogs;
-    enum {
-        SeosLoadSeos,
-        SeosLoadSeader
-    } load_type;
 } SeosEmulator;
 
 NfcCommand seos_worker_listener_callback(NfcGenericEvent event, void* context);
@@ -42,14 +27,6 @@ SeosEmulator* seos_emulator_alloc(SeosCredential* credential);
 
 void seos_emulator_free(SeosEmulator* seos_emulator);
 
-void seos_emulator_set_loading_callback(
-    SeosEmulator* seos_emulator,
-    SeosLoadingCallback callback,
-    void* context);
-
-bool seos_emulator_file_select(SeosEmulator* seos_emulator);
-bool seos_emulator_delete(SeosEmulator* seos_emulator, bool use_load_path);
-
 void seos_emulator_general_authenticate_1(BitBuffer* tx_buffer, AuthParameters params);
 bool seos_emulator_general_authenticate_2(
     const uint8_t* buffer,
@@ -59,7 +36,9 @@ bool seos_emulator_general_authenticate_2(
     BitBuffer* tx_buffer);
 
 void seos_emulator_select_aid(BitBuffer* tx_buffer);
-void seos_emulator_select_adf(
+bool seos_emulator_select_adf(
+    const uint8_t* oid_list,
+    size_t oid_list_len,
     AuthParameters* params,
     SeosCredential* credential,
     BitBuffer* tx_buffer);

+ 2 - 7
seos/seos_i.h

@@ -6,7 +6,6 @@
 #include <gui/view_dispatcher.h>
 #include <gui/scene_manager.h>
 #include <notification/notification_messages.h>
-#include <storage/storage.h>
 
 #include <gui/modules/submenu.h>
 #include <gui/modules/popup.h>
@@ -28,6 +27,7 @@
 #include "seos.h"
 #include "keys.h"
 #include "seos_hci.h"
+#include "seos_credential.h"
 #include "seos_characteristic.h"
 #include "seos_native_peripheral.h"
 #include "seos_central.h"
@@ -69,7 +69,6 @@ struct Seos {
     Gui* gui;
     NotificationApp* notifications;
     SceneManager* scene_manager;
-    Storage* storage;
 
     char text_store[SEOS_TEXT_STORE_SIZE + 1];
     FuriString* text_box_store;
@@ -87,7 +86,7 @@ struct Seos {
     NfcPoller* poller;
     NfcDevice* nfc_device;
 
-    SeosCredential credential;
+    SeosCredential* credential;
 
     // NFC
     SeosEmulator* seos_emulator;
@@ -99,10 +98,6 @@ struct Seos {
     SeosCentral* seos_central;
     FlowMode flow_mode;
 
-    char dev_name[SEOS_FILE_NAME_MAX_LENGTH + 1];
-    FuriString* load_path;
-    DialogsApp* dialogs;
-
     bool keys_loaded;
     Bt* bt;
     FuriHalBleProfileBase* ble_profile;

+ 24 - 22
seos/seos_native_peripheral.c

@@ -71,7 +71,7 @@ SeosNativePeripheral* seos_native_peripheral_alloc(Seos* seos) {
     memset(seos_native_peripheral, 0, sizeof(SeosNativePeripheral));
 
     seos_native_peripheral->seos = seos;
-    seos_native_peripheral->credential = &seos->credential;
+    seos_native_peripheral->credential = seos->credential;
     seos_native_peripheral->bt = furi_record_open(RECORD_BT);
 
     seos_native_peripheral->phase = SELECT_AID;
@@ -173,6 +173,7 @@ void seos_native_peripheral_stop(SeosNativePeripheral* seos_native_peripheral) {
 void seos_native_peripheral_process_message_cred(
     SeosNativePeripheral* seos_native_peripheral,
     NativePeripheralMessage message) {
+    Seos* seos = seos_native_peripheral->seos;
     BitBuffer* response = bit_buffer_alloc(128); // TODO: MTU
 
     uint8_t* data = message.buf;
@@ -191,19 +192,17 @@ void seos_native_peripheral_process_message_cred(
             bit_buffer_append_bytes(response, (uint8_t*)file_not_found, sizeof(file_not_found));
         }
     } else if(memcmp(apdu, select_adf_header, sizeof(select_adf_header)) == 0) {
-        // is our adf in the list?
         // +1 to skip APDU length byte
-        void* p = memmem(
-            apdu + sizeof(select_adf_header) + 1,
-            apdu[sizeof(select_adf_header)],
-            SEOS_ADF_OID,
-            SEOS_ADF_OID_LEN);
-        if(p) {
-            seos_log_buffer(TAG, "Matched ADF", p, SEOS_ADF_OID_LEN);
-
-            seos_emulator_select_adf(
-                &seos_native_peripheral->params, seos_native_peripheral->credential, response);
-            bit_buffer_append_bytes(response, (uint8_t*)success, sizeof(success));
+        const uint8_t* oid_list = apdu + sizeof(select_adf_header) + 1;
+        size_t oid_list_len = apdu[sizeof(select_adf_header)];
+
+        if(seos_emulator_select_adf(
+               oid_list,
+               oid_list_len,
+               &seos_native_peripheral->params,
+               seos_native_peripheral->credential,
+               response)) {
+            view_dispatcher_send_custom_event(seos->view_dispatcher, SeosCustomEventADFMatched);
         } else {
             FURI_LOG_W(TAG, "Failed to match any ADF OID");
         }
@@ -315,7 +314,6 @@ void seos_native_peripheral_process_message_reader(
         bit_buffer_append_bytes(response, select_adf_header, sizeof(select_adf_header));
         bit_buffer_append_bytes(response, SEOS_ADF_OID, SEOS_ADF_OID_LEN);
         seos_native_peripheral->phase = SELECT_ADF;
-
     } else if(memcmp(data + 1, cd02, sizeof(cd02)) == 0) {
         BitBuffer* attribute_value = bit_buffer_alloc(message.len);
         bit_buffer_append_bytes(attribute_value, message.buf, message.len);
@@ -380,7 +378,10 @@ void seos_native_peripheral_process_message_reader(
         view_dispatcher_send_custom_event(
             seos_native_peripheral->seos->view_dispatcher, SeosCustomEventSIORequested);
     } else if(seos_native_peripheral->phase == REQUEST_SIO) {
+        // TODO: consider seos_reader_request_sio
         SecureMessaging* secure_messaging = seos_native_peripheral->secure_messaging;
+        SeosCredential* credential = seos_native_peripheral->credential;
+        AuthParameters* params = &seos_native_peripheral->params;
 
         BitBuffer* rx_buffer = bit_buffer_alloc(message.len - 1);
         bit_buffer_append_bytes(rx_buffer, rx_data, message.len - 1);
@@ -389,18 +390,19 @@ void seos_native_peripheral_process_message_reader(
         seos_log_bitbuffer(TAG, "BLE response(clear)", rx_buffer);
 
         // Skip fileId
-        seos_native_peripheral->credential->sio_len = bit_buffer_get_byte(rx_buffer, 2);
-        if(seos_native_peripheral->credential->sio_len >
-           sizeof(seos_native_peripheral->credential->sio)) {
+        credential->sio_len = bit_buffer_get_byte(rx_buffer, 2);
+        if(credential->sio_len > sizeof(credential->sio)) {
             FURI_LOG_W(TAG, "SIO too long to save");
             bit_buffer_free(response);
             return;
         }
-        memcpy(
-            seos_native_peripheral->credential->sio,
-            bit_buffer_get_data(rx_buffer) + 3,
-            seos_native_peripheral->credential->sio_len);
-        FURI_LOG_I(TAG, "SIO Captured, %d bytes", seos_native_peripheral->credential->sio_len);
+        memcpy(credential->sio, bit_buffer_get_data(rx_buffer) + 3, credential->sio_len);
+        memcpy(credential->priv_key, params->priv_key, sizeof(credential->priv_key));
+        memcpy(credential->auth_key, params->auth_key, sizeof(credential->auth_key));
+        credential->adf_oid_len = SEOS_ADF_OID_LEN;
+        memcpy(credential->adf_oid, SEOS_ADF_OID, sizeof(credential->adf_oid));
+
+        FURI_LOG_I(TAG, "SIO Captured, %d bytes", credential->sio_len);
 
         Seos* seos = seos_native_peripheral->seos;
         view_dispatcher_send_custom_event(seos->view_dispatcher, SeosCustomEventReaderSuccess);

+ 36 - 9
seos/seos_reader.c

@@ -261,24 +261,39 @@ bool seos_reader_select_adf_response(
     params->cipher = rx_data[2];
     params->hash = rx_data[3];
 
+    memset(credential->adf_response, 0, sizeof(credential->adf_response));
+    size_t response_length = bit_buffer_get_size_bytes(rx_buffer) - offset - sizeof(success);
+    if(response_length > sizeof(credential->adf_response)) {
+        FURI_LOG_W(
+            TAG,
+            "adf_response too large %zu > %zu",
+            response_length,
+            sizeof(credential->adf_response));
+        response_length = sizeof(credential->adf_response);
+    }
+    memcpy(credential->adf_response, rx_data, response_length);
+
     size_t bufLen = 0;
     uint8_t clear[0x40];
+    memset(clear, 0, sizeof(clear));
 
+    // Copy IV because mbedtls methods mutate it
     if(params->cipher == AES_128_CBC) {
-        size_t ivLen = 16;
-        bufLen = rx_data[5] - ivLen;
-        uint8_t* iv = (uint8_t*)rx_data + 6;
-        uint8_t* enc = (uint8_t*)rx_data + 6 + ivLen;
+        uint8_t iv[16];
+        memcpy(iv, rx_data + 6, sizeof(iv));
+        bufLen = rx_data[5] - sizeof(iv);
+        uint8_t* enc = (uint8_t*)rx_data + 6 + sizeof(iv);
+
         mbedtls_aes_context ctx;
         mbedtls_aes_init(&ctx);
         mbedtls_aes_setkey_dec(&ctx, SEOS_ADF1_PRIV_ENC, sizeof(SEOS_ADF1_PRIV_ENC) * 8);
         mbedtls_aes_crypt_cbc(&ctx, MBEDTLS_AES_DECRYPT, bufLen, iv, enc, clear);
         mbedtls_aes_free(&ctx);
     } else if(params->cipher == TWO_KEY_3DES_CBC_MODE) {
-        size_t ivLen = 8;
-        bufLen = rx_data[5] - ivLen;
-        uint8_t* iv = (uint8_t*)rx_data + 6;
-        uint8_t* enc = (uint8_t*)rx_data + 6 + ivLen;
+        uint8_t iv[8];
+        memcpy(iv, rx_data + 6, sizeof(iv));
+        bufLen = rx_data[5] - sizeof(iv);
+        uint8_t* enc = (uint8_t*)rx_data + 6 + sizeof(iv);
 
         mbedtls_des3_context ctx;
         mbedtls_des3_init(&ctx);
@@ -286,6 +301,7 @@ bool seos_reader_select_adf_response(
         mbedtls_des3_crypt_cbc(&ctx, MBEDTLS_DES_DECRYPT, bufLen, iv, enc, clear);
         mbedtls_des3_free(&ctx);
     }
+    seos_log_buffer(TAG, "clear", clear, sizeof(clear));
 
     // 06112b0601040181e438010102011801010202 cf 07 3d4c010c71cfa7 e2d0b41a00cc5e494c8d52b6e562592399fe614a
     if(clear[0] != 0x06) {
@@ -403,7 +419,7 @@ NfcCommand seos_state_machine(Seos* seos, Iso14443_4aPoller* iso14443_4a_poller)
     furi_assert(seos);
     NfcCommand ret = NfcCommandContinue;
 
-    SeosReader* seos_reader = seos_reader_alloc(&seos->credential, iso14443_4a_poller);
+    SeosReader* seos_reader = seos_reader_alloc(seos->credential, iso14443_4a_poller);
     seos->seos_reader = seos_reader;
 
     do {
@@ -418,13 +434,24 @@ NfcCommand seos_state_machine(Seos* seos, Iso14443_4aPoller* iso14443_4a_poller)
             break;
         }
 
+        FURI_LOG_D(TAG, "General Authenticate 1");
         ret = seos_reader_general_authenticate_1(seos_reader);
         if(ret == NfcCommandStop) break;
 
+        FURI_LOG_D(TAG, "General Authenticate 2");
         ret = seos_reader_general_authenticate_2(seos_reader);
         if(ret == NfcCommandStop) break;
 
+        FURI_LOG_D(TAG, "Request SIO");
         if(seos_reader_request_sio(seos_reader)) {
+            SeosCredential* credential = seos_reader->credential;
+            AuthParameters* params = &seos_reader->params;
+
+            memcpy(credential->priv_key, params->priv_key, sizeof(credential->priv_key));
+            memcpy(credential->auth_key, params->auth_key, sizeof(credential->auth_key));
+            credential->adf_oid_len = SEOS_ADF_OID_LEN;
+            memcpy(credential->adf_oid, SEOS_ADF_OID, sizeof(credential->adf_oid));
+
             view_dispatcher_send_custom_event(seos->view_dispatcher, SeosCustomEventReaderSuccess);
         }