浏览代码

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

Willy-JL 1 年之前
父节点
当前提交
2db78dea3b
共有 9 个文件被更改,包括 182 次插入 和 44 次删除
  1. 5 4
      seos/.catalog/README.md
  2. 8 7
      seos/README.md
  3. 2 2
      seos/keys-example.txt
  4. 2 19
      seos/keys.c
  5. 19 0
      seos/scenes/seos_scene_main_menu.c
  6. 1 4
      seos/seos.c
  7. 131 2
      seos/seos_emulator.c
  8. 4 0
      seos/seos_emulator.h
  9. 10 6
      seos/seos_reader.c

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

@@ -1,11 +1,12 @@
 ## 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
 
-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)
 
+## 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
 
-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:
 
@@ -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)
 - 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)
 
 1. Install/setup nordic SDK

+ 2 - 2
seos/keys-example.txt

@@ -1,7 +1,7 @@
 Filetype: Seos keys
 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_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

+ 2 - 19
seos/keys.c

@@ -1,25 +1,8 @@
 #include <stdint.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[] =
     {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
 uint8_t SEOS_ADF1_PRIV_MAC[] =

+ 19 - 0
seos/scenes/seos_scene_main_menu.c

@@ -2,6 +2,8 @@
 
 #define TAG "SceneMainMenu"
 
+#define SEADER_PATH "/ext/apps_data/seader"
+
 enum SubmenuIndex {
     SubmenuIndexSaved,
     SubmenuIndexRead,
@@ -10,6 +12,7 @@ enum SubmenuIndex {
     SubmenuIndexBLECredInterrogate,
     SubmenuIndexAbout,
     SubmenuIndexInspect,
+    SubmenuIndexSavedSeader,
 };
 
 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, "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(
         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) {
             scene_manager_set_scene_state(
                 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);
             consumed = true;
         } else if(event.event == SubmenuIndexInspect) {

+ 1 - 4
seos/seos.c

@@ -35,10 +35,7 @@ bool seos_load_keys(Seos* seos) {
 
     if(parsed) {
         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 {
         FURI_LOG_I(TAG, "Using default keys");
     }

+ 131 - 2
seos/seos_emulator.c

@@ -2,7 +2,9 @@
 
 #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 standard_seos_aid[] = {0xa0, 0x00, 0x00, 0x04, 0x40, 0x00, 0x01, 0x01, 0x00, 0x01};
@@ -169,7 +171,125 @@ static bool
     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);
     bool res = false;
 
@@ -195,6 +315,15 @@ bool seos_emulator_file_select(SeosEmulator* seos_emulator) {
     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;

+ 4 - 0
seos/seos_emulator.h

@@ -30,6 +30,10 @@ typedef struct {
     void* loading_cb_ctx;
     Storage* storage;
     DialogsApp* dialogs;
+    enum {
+        SeosLoadSeos,
+        SeosLoadSeader
+    } load_type;
 } SeosEmulator;
 
 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[] =
     {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[] =
     {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;
 
     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);
     if(error != Iso14443_4aErrorNone) {
         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;
     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);
     if(error != Iso14443_4aErrorNone) {
         FURI_LOG_W(TAG, "iso14443_4a_poller_send_block error %d", error);
         return NfcCommandStop;
     }
+    seos_log_bitbuffer(TAG, "NFC response", rx_buffer);
     bit_buffer_reset(tx_buffer);
     return ret;
 }