Explorar o código

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

Willy-JL hai 8 meses
pai
achega
2db78dea3b

+ 5 - 4
seos/.catalog/README.md

@@ -1,11 +1,12 @@
 ## Keys
 ## Keys
 
 
-**The app uses all zero keys by default**. If you'd like to use your own keys, use the format of the 'keys-example.txt' to specify them, and place into 'SD Card/apps_data/seos/keys.txt'
+**The app uses all zero keys by default**. It uses an ADF OID of 030107090000000000 ("0.3.1.7.9.0.0.0.0.0").
+If you'd like to use your own keys/ADF OID, use the format of the 'keys-example.txt' to specify them, and place into 'SD Card/apps_data/seos/keys.txt'
 
 
 ## Note
 ## Note
 
 
-This software incorporates a third-party implementation of Seos technology. It is not developed, authorized, licensed, or endorsed by HID Global, ASSA ABLOY, or any of their affiliates. References to Seos are solely for descriptive and compatibility purposes.
+This software incorporates a third-party implementation of Seos®️  technology. It is not developed, authorized, licensed, or endorsed by HID Global, ASSA ABLOY, or any of their affiliates. References to Seos®️  are solely for descriptive and compatibility purposes.
 
 
-No guarantee of compatibility or functionality is made. This implementation may not work with all Seos-enabled systems, and its performance, security, and reliability are not assured. Users assume all risks associated with its use.
+No guarantee of compatibility or functionality is made. This implementation may not work with all Seos®️ -enabled systems, and its performance, security, and reliability are not assured. Users assume all risks associated with its use.
 
 
-Seos, HID Global, and ASSA ABLOY are trademarks or registered trademarks of their respective owners. This software is not associated with or sponsored by them in any way.
+Seos®️ , HID Global, and ASSA ABLOY are trademarks or registered trademarks of their respective owners. This software is not associated with or sponsored by them in any way.

+ 8 - 7
seos/README.md

@@ -4,13 +4,18 @@ Flipper app for reading and emulating Seos-compatible cards/fobs/mobile credenti
 
 
 ![Demo Video](demo.mp4)
 ![Demo Video](demo.mp4)
 
 
+## Keys
+
+**The app uses all 00 keys by default.**  It uses an ADF OID of 030107090000000000 ("0.3.1.7.9.0.0.0.0.0").
+If you'd like to use your own keys/ADF OID, use the format of the `keys-example.txt` to specify them, and place into `SD Card/apps_data/seos/keys.txt`
+
 ## Note
 ## Note
 
 
-This software incorporates a third-party implementation of Seos™ technology. It is not developed, authorized, licensed, or endorsed by HID Global, ASSA ABLOY, or any of their affiliates. References to Seos™ are solely for descriptive and compatibility purposes.
+This software incorporates a third-party implementation of Seos®️  technology. It is not developed, authorized, licensed, or endorsed by HID Global, ASSA ABLOY, or any of their affiliates. References to Seos®️  are solely for descriptive and compatibility purposes.
 
 
-No guarantee of compatibility or functionality is made. This implementation may not work with all Seos™-enabled systems, and its performance, security, and reliability are not assured. Users assume all risks associated with its use.
+No guarantee of compatibility or functionality is made. This implementation may not work with all Seos®️ -enabled systems, and its performance, security, and reliability are not assured. Users assume all risks associated with its use.
 
 
-Seos, HID Global, and ASSA ABLOY are trademarks or registered trademarks of their respective owners. This software is not associated with or sponsored by them in any way.
+Seos®️ , HID Global, and ASSA ABLOY are trademarks or registered trademarks of their respective owners. This software is not associated with or sponsored by them in any way.
 
 
 ## To do:
 ## To do:
 
 
@@ -20,10 +25,6 @@ Seos™, HID Global, and ASSA ABLOY are trademarks or registered trademarks of t
 - When parsing incoming data, use buffer + len instead of BitBuffer so I can increment buffer pointer as I parse header(s)
 - When parsing incoming data, use buffer + len instead of BitBuffer so I can increment buffer pointer as I parse header(s)
 - CMAC checking where I missed it
 - CMAC checking where I missed it
 
 
-## Keys
-
-The app uses all 00 keys by default. If you'd like to use your own keys, use the format of the `keys-example.txt` to specify them, and place into `SD Card/apps_data/seos/keys.txt`
-
 ## Hardware for BLE support (experimental)
 ## Hardware for BLE support (experimental)
 
 
 1. Install/setup nordic SDK
 1. Install/setup nordic SDK

+ 2 - 2
seos/keys-example.txt

@@ -1,7 +1,7 @@
 Filetype: Seos keys
 Filetype: Seos keys
 Version: 1
 Version: 1
-SEOS_ADF_OID_LEN: 17
-SEOS_ADF_OID: 2b 06 01 04 01 81 e4 38 01 01 02 01 18 01 01 02 02
+SEOS_ADF_OID_LEN: 9
+SEOS_ADF_OID: 03 01 07 09 00 00 00 00 00
 SEOS_ADF1_PRIV_ENC: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
 SEOS_ADF1_PRIV_ENC: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
 SEOS_ADF1_PRIV_MAC: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
 SEOS_ADF1_PRIV_MAC: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
 SEOS_ADF1_READ: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
 SEOS_ADF1_READ: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

+ 2 - 19
seos/keys.c

@@ -1,25 +1,8 @@
 #include <stdint.h>
 #include <stdint.h>
 #include <stddef.h>
 #include <stddef.h>
 
 
-size_t SEOS_ADF_OID_LEN = 17;
-uint8_t SEOS_ADF_OID[] = {
-    0x2b,
-    0x06,
-    0x01,
-    0x04,
-    0x01,
-    0x81,
-    0xe4,
-    0x38,
-    0x01,
-    0x01,
-    0x02,
-    0x01,
-    0x18,
-    0x01,
-    0x01,
-    0x02,
-    0x02};
+size_t SEOS_ADF_OID_LEN = 9;
+uint8_t SEOS_ADF_OID[32] = {0x03, 0x01, 0x07, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00};
 uint8_t SEOS_ADF1_PRIV_ENC[] =
 uint8_t SEOS_ADF1_PRIV_ENC[] =
     {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
     {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
 uint8_t SEOS_ADF1_PRIV_MAC[] =
 uint8_t SEOS_ADF1_PRIV_MAC[] =

+ 19 - 0
seos/scenes/seos_scene_main_menu.c

@@ -2,6 +2,8 @@
 
 
 #define TAG "SceneMainMenu"
 #define TAG "SceneMainMenu"
 
 
+#define SEADER_PATH "/ext/apps_data/seader"
+
 enum SubmenuIndex {
 enum SubmenuIndex {
     SubmenuIndexSaved,
     SubmenuIndexSaved,
     SubmenuIndexRead,
     SubmenuIndexRead,
@@ -10,6 +12,7 @@ enum SubmenuIndex {
     SubmenuIndexBLECredInterrogate,
     SubmenuIndexBLECredInterrogate,
     SubmenuIndexAbout,
     SubmenuIndexAbout,
     SubmenuIndexInspect,
     SubmenuIndexInspect,
+    SubmenuIndexSavedSeader,
 };
 };
 
 
 void seos_scene_main_menu_submenu_callback(void* context, uint32_t index) {
 void seos_scene_main_menu_submenu_callback(void* context, uint32_t index) {
@@ -55,6 +58,15 @@ void seos_scene_main_menu_on_enter(void* context) {
     submenu_add_item(
     submenu_add_item(
         submenu, "About", SubmenuIndexAbout, seos_scene_main_menu_submenu_callback, seos);
         submenu, "About", SubmenuIndexAbout, seos_scene_main_menu_submenu_callback, seos);
 
 
+    if(storage_dir_exists(seos->storage, SEADER_PATH)) {
+        submenu_add_item(
+            submenu,
+            "Saved (Seader)",
+            SubmenuIndexSavedSeader,
+            seos_scene_main_menu_submenu_callback,
+            seos);
+    }
+
     submenu_set_selected_item(
     submenu_set_selected_item(
         seos->submenu, scene_manager_get_scene_state(seos->scene_manager, SeosSceneMainMenu));
         seos->submenu, scene_manager_get_scene_state(seos->scene_manager, SeosSceneMainMenu));
 
 
@@ -91,6 +103,13 @@ bool seos_scene_main_menu_on_event(void* context, SceneManagerEvent event) {
         } else if(event.event == SubmenuIndexSaved) {
         } else if(event.event == SubmenuIndexSaved) {
             scene_manager_set_scene_state(
             scene_manager_set_scene_state(
                 seos->scene_manager, SeosSceneMainMenu, SubmenuIndexSaved);
                 seos->scene_manager, SeosSceneMainMenu, SubmenuIndexSaved);
+            seos->seos_emulator->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;
             scene_manager_next_scene(seos->scene_manager, SeosSceneFileSelect);
             scene_manager_next_scene(seos->scene_manager, SeosSceneFileSelect);
             consumed = true;
             consumed = true;
         } else if(event.event == SubmenuIndexInspect) {
         } else if(event.event == SubmenuIndexInspect) {

+ 1 - 4
seos/seos.c

@@ -35,10 +35,7 @@ bool seos_load_keys(Seos* seos) {
 
 
     if(parsed) {
     if(parsed) {
         FURI_LOG_I(TAG, "Keys loaded");
         FURI_LOG_I(TAG, "Keys loaded");
-        BitBuffer* tmp = bit_buffer_alloc(SEOS_ADF_OID_LEN);
-        bit_buffer_append_bytes(tmp, SEOS_ADF_OID, SEOS_ADF_OID_LEN);
-        seos_log_bitbuffer(TAG, "Keys for ADF OID loaded", tmp);
-        bit_buffer_free(tmp);
+        seos_log_buffer(TAG, "Keys for ADF OID loaded", SEOS_ADF_OID, SEOS_ADF_OID_LEN);
     } else {
     } else {
         FURI_LOG_I(TAG, "Using default keys");
         FURI_LOG_I(TAG, "Using default keys");
     }
     }

+ 131 - 2
seos/seos_emulator.c

@@ -2,7 +2,9 @@
 
 
 #define TAG "SeosEmulator"
 #define TAG "SeosEmulator"
 
 
-#define NAD_MASK 0x08
+#define NAD_MASK             0x08
+#define SEADER_PATH          "/ext/apps_data/seader"
+#define SEADER_APP_EXTENSION ".credential"
 
 
 static uint8_t select_header[] = {0x00, 0xa4, 0x04, 0x00};
 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};
 static uint8_t standard_seos_aid[] = {0xa0, 0x00, 0x00, 0x04, 0x40, 0x00, 0x01, 0x01, 0x00, 0x01};
@@ -169,7 +171,125 @@ static bool
     return parsed;
     return parsed;
 }
 }
 
 
-bool seos_emulator_file_select(SeosEmulator* seos_emulator) {
+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);
     furi_assert(seos_emulator);
     bool res = false;
     bool res = false;
 
 
@@ -195,6 +315,15 @@ bool seos_emulator_file_select(SeosEmulator* seos_emulator) {
     return res;
     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) {
 bool seos_emulator_delete(SeosEmulator* seos_emulator, bool use_load_path) {
     furi_assert(seos_emulator);
     furi_assert(seos_emulator);
     bool deleted = false;
     bool deleted = false;

+ 4 - 0
seos/seos_emulator.h

@@ -30,6 +30,10 @@ typedef struct {
     void* loading_cb_ctx;
     void* loading_cb_ctx;
     Storage* storage;
     Storage* storage;
     DialogsApp* dialogs;
     DialogsApp* dialogs;
+    enum {
+        SeosLoadSeos,
+        SeosLoadSeader
+    } load_type;
 } SeosEmulator;
 } SeosEmulator;
 
 
 NfcCommand seos_worker_listener_callback(NfcGenericEvent event, void* context);
 NfcCommand seos_worker_listener_callback(NfcGenericEvent event, void* context);

+ 10 - 6
seos/seos_reader.c

@@ -8,11 +8,6 @@ static uint8_t select[] =
 static uint8_t SEOS_APPLET_FCI[] =
 static uint8_t SEOS_APPLET_FCI[] =
     {0x6F, 0x0C, 0x84, 0x0A, 0xA0, 0x00, 0x00, 0x04, 0x40, 0x00, 0x01, 0x01, 0x00, 0x01};
     {0x6F, 0x0C, 0x84, 0x0A, 0xA0, 0x00, 0x00, 0x04, 0x40, 0x00, 0x01, 0x01, 0x00, 0x01};
 
 
-// TODO: support value from keys file
-static uint8_t select_adf[] = {0x80, 0xa5, 0x04, 0x00, 0x13, 0x06, 0x11, 0x2b, 0x06,
-                               0x01, 0x04, 0x01, 0x81, 0xe4, 0x38, 0x01, 0x01, 0x02,
-                               0x01, 0x18, 0x01, 0x01, 0x02, 0x02, 0x00};
-
 static uint8_t general_authenticate_1[] =
 static uint8_t general_authenticate_1[] =
     {0x00, 0x87, 0x00, 0x01, 0x04, 0x7c, 0x02, 0x81, 0x00, 0x00};
     {0x00, 0x87, 0x00, 0x01, 0x04, 0x7c, 0x02, 0x81, 0x00, 0x00};
 
 
@@ -187,6 +182,7 @@ NfcCommand seos_reader_select_aid(SeosReader* seos_reader) {
     Iso14443_4aError error;
     Iso14443_4aError error;
 
 
     bit_buffer_append_bytes(tx_buffer, select, sizeof(select));
     bit_buffer_append_bytes(tx_buffer, select, sizeof(select));
+    seos_log_bitbuffer(TAG, "NFC transmit", tx_buffer);
     error = iso14443_4a_poller_send_block(iso14443_4a_poller, tx_buffer, rx_buffer);
     error = iso14443_4a_poller_send_block(iso14443_4a_poller, tx_buffer, rx_buffer);
     if(error != Iso14443_4aErrorNone) {
     if(error != Iso14443_4aErrorNone) {
         FURI_LOG_W(TAG, "iso14443_4a_poller_send_block error %d", error);
         FURI_LOG_W(TAG, "iso14443_4a_poller_send_block error %d", error);
@@ -222,12 +218,20 @@ NfcCommand seos_reader_select_adf(SeosReader* seos_reader) {
     NfcCommand ret = NfcCommandContinue;
     NfcCommand ret = NfcCommandContinue;
     Iso14443_4aError error;
     Iso14443_4aError error;
 
 
-    bit_buffer_append_bytes(tx_buffer, select_adf, sizeof(select_adf));
+    uint8_t select_adf_header[] = {
+        0x80, 0xa5, 0x04, 0x00, (uint8_t)SEOS_ADF_OID_LEN + 2, 0x06, (uint8_t)SEOS_ADF_OID_LEN};
+
+    bit_buffer_append_bytes(tx_buffer, select_adf_header, sizeof(select_adf_header));
+    bit_buffer_append_bytes(tx_buffer, SEOS_ADF_OID, SEOS_ADF_OID_LEN);
+    bit_buffer_append_byte(tx_buffer, 0x00); // Le
+
+    seos_log_bitbuffer(TAG, "NFC transmit", tx_buffer);
     error = iso14443_4a_poller_send_block(iso14443_4a_poller, tx_buffer, rx_buffer);
     error = iso14443_4a_poller_send_block(iso14443_4a_poller, tx_buffer, rx_buffer);
     if(error != Iso14443_4aErrorNone) {
     if(error != Iso14443_4aErrorNone) {
         FURI_LOG_W(TAG, "iso14443_4a_poller_send_block error %d", error);
         FURI_LOG_W(TAG, "iso14443_4a_poller_send_block error %d", error);
         return NfcCommandStop;
         return NfcCommandStop;
     }
     }
+    seos_log_bitbuffer(TAG, "NFC response", rx_buffer);
     bit_buffer_reset(tx_buffer);
     bit_buffer_reset(tx_buffer);
     return ret;
     return ret;
 }
 }