Просмотр исходного кода

NFC user dict list, delete, and de-duplication. (#1533)

* Add MFC user keys list
* Leakey submenu fix
* Set next target for Save/Delete success scenes
* Delete individual user keys
* Update count of total keys
* Fix memory leak
* Check for duplicate keys
* Remove a submodule that I never added?
* Swap and position icons
* Revamp according to design doc
* Rename icons to include size and replace keychain icon with smaller variant
* Fix typos
* Final fixes
* Fufill requested changes
* Cleanup comments
* Merge dev after SD app loading
* Fixing icon names
* Revert merge mistakes and API version
* Scene switching adjustments
* F7: add/change/remove some nfc icons in api_symbols.csv

Co-authored-by: あく <alleteam@gmail.com>
David 3 лет назад
Родитель
Сommit
f5ff6438d1
28 измененных файлов с 454 добавлено и 53 удалено
  1. 1 1
      .vscode/example/settings.json
  2. 1 1
      applications/main/lfrfid/views/lfrfid_view_read.c
  3. 5 0
      applications/main/nfc/nfc_i.h
  4. 3 0
      applications/main/nfc/scenes/nfc_scene_config.h
  5. 7 2
      applications/main/nfc/scenes/nfc_scene_delete_success.c
  6. 4 1
      applications/main/nfc/scenes/nfc_scene_dict_not_found.c
  7. 1 1
      applications/main/nfc/scenes/nfc_scene_extra_actions.c
  8. 20 4
      applications/main/nfc/scenes/nfc_scene_mf_classic_keys.c
  9. 7 6
      applications/main/nfc/scenes/nfc_scene_mf_classic_keys_add.c
  10. 77 0
      applications/main/nfc/scenes/nfc_scene_mf_classic_keys_delete.c
  11. 60 0
      applications/main/nfc/scenes/nfc_scene_mf_classic_keys_list.c
  12. 47 0
      applications/main/nfc/scenes/nfc_scene_mf_classic_keys_warn_duplicate.c
  13. 1 1
      applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_auth.c
  14. 1 1
      applications/main/nfc/scenes/nfc_scene_read.c
  15. 1 1
      applications/main/nfc/scenes/nfc_scene_restore_original_confirm.c
  16. 4 1
      applications/main/nfc/scenes/nfc_scene_save_success.c
  17. 1 1
      applications/plugins/bt_hid_app/views/bt_hid_keyboard.c
  18. 1 1
      applications/plugins/bt_hid_app/views/bt_hid_mouse.c
  19. 1 1
      applications/services/desktop/views/desktop_view_pin_input.c
  20. BIN
      assets/icons/NFC/Keychain.png
  21. BIN
      assets/icons/NFC/Keychain_39x36.png
  22. 0 0
      assets/icons/NFC/NFC_manual_60x50.png
  23. BIN
      assets/icons/NFC/Reader_detect_43x40.png
  24. 0 0
      assets/icons/NFC/Restoring_38x32.png
  25. 0 0
      assets/icons/PIN/Pin_arrow_up_7x9.png
  26. 6 5
      firmware/targets/f7/api_symbols.csv
  27. 186 24
      lib/nfc/helpers/mf_classic_dict.c
  28. 19 1
      lib/nfc/helpers/mf_classic_dict.h

+ 1 - 1
.vscode/example/settings.json

@@ -22,4 +22,4 @@
         "SConstruct": "python",
         "SConstruct": "python",
         "*.fam": "python",
         "*.fam": "python",
     }
     }
-}
+}

+ 1 - 1
applications/main/lfrfid/views/lfrfid_view_read.c

@@ -16,7 +16,7 @@ static void lfrfid_view_read_draw_callback(Canvas* canvas, void* _model) {
     LfRfidReadViewModel* model = _model;
     LfRfidReadViewModel* model = _model;
     canvas_set_color(canvas, ColorBlack);
     canvas_set_color(canvas, ColorBlack);
 
 
-    canvas_draw_icon(canvas, 0, 8, &I_NFC_manual);
+    canvas_draw_icon(canvas, 0, 8, &I_NFC_manual_60x50);
 
 
     canvas_set_font(canvas, FontPrimary);
     canvas_set_font(canvas, FontPrimary);
 
 

+ 5 - 0
applications/main/nfc/nfc_i.h

@@ -37,6 +37,10 @@
 
 
 #include "rpc/rpc_app.h"
 #include "rpc/rpc_app.h"
 
 
+#include <m-array.h>
+
+ARRAY_DEF(MfClassicUserKeys, char*, M_PTR_OPLIST);
+
 #define NFC_TEXT_STORE_SIZE 128
 #define NFC_TEXT_STORE_SIZE 128
 
 
 typedef enum {
 typedef enum {
@@ -60,6 +64,7 @@ struct Nfc {
     char text_store[NFC_TEXT_STORE_SIZE + 1];
     char text_store[NFC_TEXT_STORE_SIZE + 1];
     string_t text_box_store;
     string_t text_box_store;
     uint8_t byte_input_store[6];
     uint8_t byte_input_store[6];
+    MfClassicUserKeys_t mfc_key_strs; // Used in MFC key listing
 
 
     void* rpc_ctx;
     void* rpc_ctx;
     NfcRpcState rpc_state;
     NfcRpcState rpc_state;

+ 3 - 0
applications/main/nfc/scenes/nfc_scene_config.h

@@ -32,6 +32,9 @@ ADD_SCENE(nfc, mf_classic_menu, MfClassicMenu)
 ADD_SCENE(nfc, mf_classic_emulate, MfClassicEmulate)
 ADD_SCENE(nfc, mf_classic_emulate, MfClassicEmulate)
 ADD_SCENE(nfc, mf_classic_keys, MfClassicKeys)
 ADD_SCENE(nfc, mf_classic_keys, MfClassicKeys)
 ADD_SCENE(nfc, mf_classic_keys_add, MfClassicKeysAdd)
 ADD_SCENE(nfc, mf_classic_keys_add, MfClassicKeysAdd)
+ADD_SCENE(nfc, mf_classic_keys_list, MfClassicKeysList)
+ADD_SCENE(nfc, mf_classic_keys_delete, MfClassicKeysDelete)
+ADD_SCENE(nfc, mf_classic_keys_warn_duplicate, MfClassicKeysWarnDuplicate)
 ADD_SCENE(nfc, mf_classic_dict_attack, MfClassicDictAttack)
 ADD_SCENE(nfc, mf_classic_dict_attack, MfClassicDictAttack)
 ADD_SCENE(nfc, emv_read_success, EmvReadSuccess)
 ADD_SCENE(nfc, emv_read_success, EmvReadSuccess)
 ADD_SCENE(nfc, emv_menu, EmvMenu)
 ADD_SCENE(nfc, emv_menu, EmvMenu)

+ 7 - 2
applications/main/nfc/scenes/nfc_scene_delete_success.c

@@ -25,8 +25,13 @@ bool nfc_scene_delete_success_on_event(void* context, SceneManagerEvent event) {
 
 
     if(event.type == SceneManagerEventTypeCustom) {
     if(event.type == SceneManagerEventTypeCustom) {
         if(event.event == NfcCustomEventViewExit) {
         if(event.event == NfcCustomEventViewExit) {
-            consumed = scene_manager_search_and_switch_to_previous_scene(
-                nfc->scene_manager, NfcSceneFileSelect);
+            if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneMfClassicKeys)) {
+                consumed = scene_manager_search_and_switch_to_previous_scene(
+                    nfc->scene_manager, NfcSceneMfClassicKeys);
+            } else {
+                consumed = scene_manager_search_and_switch_to_previous_scene(
+                    nfc->scene_manager, NfcSceneStart);
+            }
         }
         }
     }
     }
     return consumed;
     return consumed;

+ 4 - 1
applications/main/nfc/scenes/nfc_scene_dict_not_found.c

@@ -31,7 +31,10 @@ bool nfc_scene_dict_not_found_on_event(void* context, SceneManagerEvent event) {
 
 
     if(event.type == SceneManagerEventTypeCustom) {
     if(event.type == SceneManagerEventTypeCustom) {
         if(event.event == NfcCustomEventViewExit) {
         if(event.event == NfcCustomEventViewExit) {
-            if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneExtraActions)) {
+            if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneMfClassicKeys)) {
+                consumed = scene_manager_search_and_switch_to_previous_scene(
+                    nfc->scene_manager, NfcSceneMfClassicKeys);
+            } else if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneExtraActions)) {
                 consumed = scene_manager_search_and_switch_to_previous_scene(
                 consumed = scene_manager_search_and_switch_to_previous_scene(
                     nfc->scene_manager, NfcSceneExtraActions);
                     nfc->scene_manager, NfcSceneExtraActions);
             } else {
             } else {

+ 1 - 1
applications/main/nfc/scenes/nfc_scene_extra_actions.c

@@ -17,7 +17,7 @@ void nfc_scene_extra_actions_on_enter(void* context) {
 
 
     submenu_add_item(
     submenu_add_item(
         submenu,
         submenu,
-        "Mf Classic Keys",
+        "Mifare Classic Keys",
         SubmenuIndexMfClassicKeys,
         SubmenuIndexMfClassicKeys,
         nfc_scene_extra_actions_submenu_callback,
         nfc_scene_extra_actions_submenu_callback,
         nfc);
         nfc);

+ 20 - 4
applications/main/nfc/scenes/nfc_scene_mf_classic_keys.c

@@ -26,15 +26,25 @@ void nfc_scene_mf_classic_keys_on_enter(void* context) {
     }
     }
 
 
     widget_add_string_element(
     widget_add_string_element(
-        nfc->widget, 0, 0, AlignLeft, AlignTop, FontPrimary, "MF Classic Keys");
+        nfc->widget, 0, 0, AlignLeft, AlignTop, FontPrimary, "Mifare Classic Keys");
     char temp_str[32];
     char temp_str[32];
-    snprintf(temp_str, sizeof(temp_str), "Flipper dict: %ld", flipper_dict_keys_total);
+    snprintf(temp_str, sizeof(temp_str), "Flipper list: %ld", flipper_dict_keys_total);
     widget_add_string_element(nfc->widget, 0, 20, AlignLeft, AlignTop, FontSecondary, temp_str);
     widget_add_string_element(nfc->widget, 0, 20, AlignLeft, AlignTop, FontSecondary, temp_str);
-    snprintf(temp_str, sizeof(temp_str), "User dict: %ld", user_dict_keys_total);
+    snprintf(temp_str, sizeof(temp_str), "User list: %ld", user_dict_keys_total);
     widget_add_string_element(nfc->widget, 0, 32, AlignLeft, AlignTop, FontSecondary, temp_str);
     widget_add_string_element(nfc->widget, 0, 32, AlignLeft, AlignTop, FontSecondary, temp_str);
     widget_add_button_element(
     widget_add_button_element(
         nfc->widget, GuiButtonTypeCenter, "Add", nfc_scene_mf_classic_keys_widget_callback, nfc);
         nfc->widget, GuiButtonTypeCenter, "Add", nfc_scene_mf_classic_keys_widget_callback, nfc);
-    widget_add_icon_element(nfc->widget, 90, 12, &I_Keychain);
+    widget_add_button_element(
+        nfc->widget, GuiButtonTypeLeft, "Back", nfc_scene_mf_classic_keys_widget_callback, nfc);
+    widget_add_icon_element(nfc->widget, 87, 13, &I_Keychain_39x36);
+    if(user_dict_keys_total > 0) {
+        widget_add_button_element(
+            nfc->widget,
+            GuiButtonTypeRight,
+            "List",
+            nfc_scene_mf_classic_keys_widget_callback,
+            nfc);
+    }
 
 
     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
 }
 }
@@ -47,6 +57,12 @@ bool nfc_scene_mf_classic_keys_on_event(void* context, SceneManagerEvent event)
         if(event.event == GuiButtonTypeCenter) {
         if(event.event == GuiButtonTypeCenter) {
             scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicKeysAdd);
             scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicKeysAdd);
             consumed = true;
             consumed = true;
+        } else if(event.event == GuiButtonTypeLeft) {
+            scene_manager_previous_scene(nfc->scene_manager);
+            consumed = true;
+        } else if(event.event == GuiButtonTypeRight) {
+            scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicKeysList);
+            consumed = true;
         }
         }
     }
     }
 
 

+ 7 - 6
applications/main/nfc/scenes/nfc_scene_mf_classic_keys_add.c

@@ -29,15 +29,16 @@ bool nfc_scene_mf_classic_keys_add_on_event(void* context, SceneManagerEvent eve
     if(event.type == SceneManagerEventTypeCustom) {
     if(event.type == SceneManagerEventTypeCustom) {
         if(event.event == NfcCustomEventByteInputDone) {
         if(event.event == NfcCustomEventByteInputDone) {
             // Add key to dict
             // Add key to dict
-            bool key_added = false;
             MfClassicDict* dict = mf_classic_dict_alloc(MfClassicDictTypeUser);
             MfClassicDict* dict = mf_classic_dict_alloc(MfClassicDictTypeUser);
             if(dict) {
             if(dict) {
-                if(mf_classic_dict_add_key(dict, nfc->byte_input_store)) {
-                    key_added = true;
+                if(mf_classic_dict_is_key_present(dict, nfc->byte_input_store)) {
+                    scene_manager_next_scene(
+                        nfc->scene_manager, NfcSceneMfClassicKeysWarnDuplicate);
+                } else if(mf_classic_dict_add_key(dict, nfc->byte_input_store)) {
+                    scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveSuccess);
+                } else {
+                    scene_manager_next_scene(nfc->scene_manager, NfcSceneDictNotFound);
                 }
                 }
-            }
-            if(key_added) {
-                scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveSuccess);
             } else {
             } else {
                 scene_manager_next_scene(nfc->scene_manager, NfcSceneDictNotFound);
                 scene_manager_next_scene(nfc->scene_manager, NfcSceneDictNotFound);
             }
             }

+ 77 - 0
applications/main/nfc/scenes/nfc_scene_mf_classic_keys_delete.c

@@ -0,0 +1,77 @@
+#include "../nfc_i.h"
+
+void nfc_scene_mf_classic_keys_delete_widget_callback(
+    GuiButtonType result,
+    InputType type,
+    void* context) {
+    Nfc* nfc = context;
+    if(type == InputTypeShort) {
+        view_dispatcher_send_custom_event(nfc->view_dispatcher, result);
+    }
+}
+
+void nfc_scene_mf_classic_keys_delete_on_enter(void* context) {
+    Nfc* nfc = context;
+    MfClassicDict* dict = mf_classic_dict_alloc(MfClassicDictTypeUser);
+    uint32_t key_index =
+        scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfClassicKeysDelete);
+    // Setup Custom Widget view
+    string_t key_str;
+    string_init(key_str);
+
+    widget_add_string_element(
+        nfc->widget, 64, 0, AlignCenter, AlignTop, FontPrimary, "Delete this key?");
+    widget_add_button_element(
+        nfc->widget,
+        GuiButtonTypeLeft,
+        "Cancel",
+        nfc_scene_mf_classic_keys_delete_widget_callback,
+        nfc);
+    widget_add_button_element(
+        nfc->widget,
+        GuiButtonTypeRight,
+        "Delete",
+        nfc_scene_mf_classic_keys_delete_widget_callback,
+        nfc);
+
+    mf_classic_dict_get_key_at_index_str(dict, key_str, key_index);
+    widget_add_string_element(
+        nfc->widget, 64, 32, AlignCenter, AlignCenter, FontSecondary, string_get_cstr(key_str));
+
+    string_clear(key_str);
+    mf_classic_dict_free(dict);
+
+    view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
+}
+
+bool nfc_scene_mf_classic_keys_delete_on_event(void* context, SceneManagerEvent event) {
+    Nfc* nfc = context;
+    bool consumed = false;
+    uint32_t key_index =
+        scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfClassicKeysDelete);
+
+    if(event.type == SceneManagerEventTypeCustom) {
+        if(event.event == GuiButtonTypeLeft) {
+            consumed = scene_manager_search_and_switch_to_previous_scene(
+                nfc->scene_manager, NfcSceneMfClassicKeys);
+        } else if(event.event == GuiButtonTypeRight) {
+            MfClassicDict* dict = mf_classic_dict_alloc(MfClassicDictTypeUser);
+            if(mf_classic_dict_delete_index(dict, key_index)) {
+                scene_manager_next_scene(nfc->scene_manager, NfcSceneDeleteSuccess);
+            } else {
+                scene_manager_search_and_switch_to_previous_scene(
+                    nfc->scene_manager, NfcSceneMfClassicKeys);
+            }
+            mf_classic_dict_free(dict);
+            consumed = true;
+        }
+    }
+
+    return consumed;
+}
+
+void nfc_scene_mf_classic_keys_delete_on_exit(void* context) {
+    Nfc* nfc = context;
+
+    widget_reset(nfc->widget);
+}

+ 60 - 0
applications/main/nfc/scenes/nfc_scene_mf_classic_keys_list.c

@@ -0,0 +1,60 @@
+#include "../nfc_i.h"
+
+void nfc_scene_mf_classic_keys_list_submenu_callback(void* context, uint32_t index) {
+    Nfc* nfc = context;
+
+    view_dispatcher_send_custom_event(nfc->view_dispatcher, index);
+}
+
+void nfc_scene_mf_classic_keys_list_on_enter(void* context) {
+    Nfc* nfc = context;
+    Submenu* submenu = nfc->submenu;
+    MfClassicDict* dict = mf_classic_dict_alloc(MfClassicDictTypeUser);
+    uint32_t index = 0;
+    string_t temp_key;
+    MfClassicUserKeys_init(nfc->mfc_key_strs);
+    string_init(temp_key);
+    if(dict) {
+        mf_classic_dict_rewind(dict);
+        while(mf_classic_dict_get_next_key_str(dict, temp_key)) {
+            char* current_key = (char*)malloc(sizeof(char) * 13);
+            strncpy(current_key, string_get_cstr(temp_key), 12);
+            MfClassicUserKeys_push_back(nfc->mfc_key_strs, current_key);
+            FURI_LOG_D("ListKeys", "Key %d: %s", index, current_key);
+            submenu_add_item(
+                submenu,
+                current_key,
+                index++,
+                nfc_scene_mf_classic_keys_list_submenu_callback,
+                nfc);
+        }
+    }
+    submenu_set_header(submenu, "Select key to delete:");
+    mf_classic_dict_free(dict);
+    string_clear(temp_key);
+    view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu);
+}
+
+bool nfc_scene_mf_classic_keys_list_on_event(void* context, SceneManagerEvent event) {
+    Nfc* nfc = context;
+    bool consumed = false;
+    if(event.type == SceneManagerEventTypeCustom) {
+        scene_manager_set_scene_state(
+            nfc->scene_manager, NfcSceneMfClassicKeysDelete, event.event);
+        scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicKeysDelete);
+        consumed = true;
+    }
+    return consumed;
+}
+
+void nfc_scene_mf_classic_keys_list_on_exit(void* context) {
+    Nfc* nfc = context;
+
+    MfClassicUserKeys_it_t it;
+    for(MfClassicUserKeys_it(it, nfc->mfc_key_strs); !MfClassicUserKeys_end_p(it);
+        MfClassicUserKeys_next(it)) {
+        free(*MfClassicUserKeys_ref(it));
+    }
+    MfClassicUserKeys_clear(nfc->mfc_key_strs);
+    submenu_reset(nfc->submenu);
+}

+ 47 - 0
applications/main/nfc/scenes/nfc_scene_mf_classic_keys_warn_duplicate.c

@@ -0,0 +1,47 @@
+#include "../nfc_i.h"
+
+void nfc_scene_mf_classic_keys_warn_duplicate_popup_callback(void* context) {
+    Nfc* nfc = context;
+    view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit);
+}
+
+void nfc_scene_mf_classic_keys_warn_duplicate_on_enter(void* context) {
+    Nfc* nfc = context;
+
+    // Setup view
+    Popup* popup = nfc->popup;
+    popup_set_icon(popup, 72, 16, &I_DolphinCommon_56x48);
+    popup_set_header(popup, "Key already exists!", 64, 3, AlignCenter, AlignTop);
+    popup_set_text(
+        popup,
+        "Please enter a\n"
+        "different key.",
+        4,
+        24,
+        AlignLeft,
+        AlignTop);
+    popup_set_timeout(popup, 5000);
+    popup_set_context(popup, nfc);
+    popup_set_callback(popup, nfc_scene_mf_classic_keys_warn_duplicate_popup_callback);
+    popup_enable_timeout(popup);
+    view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup);
+}
+
+bool nfc_scene_mf_classic_keys_warn_duplicate_on_event(void* context, SceneManagerEvent event) {
+    Nfc* nfc = context;
+    bool consumed = false;
+
+    if(event.type == SceneManagerEventTypeCustom) {
+        if(event.event == NfcCustomEventViewExit) {
+            consumed = scene_manager_search_and_switch_to_previous_scene(
+                nfc->scene_manager, NfcSceneMfClassicKeysAdd);
+        }
+    }
+    return consumed;
+}
+
+void nfc_scene_mf_classic_keys_warn_duplicate_on_exit(void* context) {
+    Nfc* nfc = context;
+
+    popup_reset(nfc->popup);
+}

+ 1 - 1
applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_auth.c

@@ -27,7 +27,7 @@ void nfc_scene_mf_ultralight_read_auth_set_state(Nfc* nfc, NfcSceneMfUlReadState
             popup_reset(nfc->popup);
             popup_reset(nfc->popup);
             popup_set_text(
             popup_set_text(
                 nfc->popup, "Apply card to\nFlipper's back", 97, 24, AlignCenter, AlignTop);
                 nfc->popup, "Apply card to\nFlipper's back", 97, 24, AlignCenter, AlignTop);
-            popup_set_icon(nfc->popup, 0, 8, &I_NFC_manual);
+            popup_set_icon(nfc->popup, 0, 8, &I_NFC_manual_60x50);
         } else if(state == NfcSceneMfUlReadStateReading) {
         } else if(state == NfcSceneMfUlReadStateReading) {
             popup_reset(nfc->popup);
             popup_reset(nfc->popup);
             popup_set_header(
             popup_set_header(

+ 1 - 1
applications/main/nfc/scenes/nfc_scene_read.c

@@ -26,7 +26,7 @@ void nfc_scene_read_set_state(Nfc* nfc, NfcSceneReadState state) {
             popup_reset(nfc->popup);
             popup_reset(nfc->popup);
             popup_set_text(
             popup_set_text(
                 nfc->popup, "Apply card to\nFlipper's back", 97, 24, AlignCenter, AlignTop);
                 nfc->popup, "Apply card to\nFlipper's back", 97, 24, AlignCenter, AlignTop);
-            popup_set_icon(nfc->popup, 0, 8, &I_NFC_manual);
+            popup_set_icon(nfc->popup, 0, 8, &I_NFC_manual_60x50);
         } else if(state == NfcSceneReadStateReading) {
         } else if(state == NfcSceneReadStateReading) {
             popup_reset(nfc->popup);
             popup_reset(nfc->popup);
             popup_set_header(
             popup_set_header(

+ 1 - 1
applications/main/nfc/scenes/nfc_scene_restore_original_confirm.c

@@ -11,7 +11,7 @@ void nfc_scene_restore_original_confirm_on_enter(void* context) {
     DialogEx* dialog_ex = nfc->dialog_ex;
     DialogEx* dialog_ex = nfc->dialog_ex;
 
 
     dialog_ex_set_header(dialog_ex, "Restore Card Data?", 64, 0, AlignCenter, AlignTop);
     dialog_ex_set_header(dialog_ex, "Restore Card Data?", 64, 0, AlignCenter, AlignTop);
-    dialog_ex_set_icon(dialog_ex, 5, 15, &I_Restoring);
+    dialog_ex_set_icon(dialog_ex, 5, 15, &I_Restoring_38x32);
     dialog_ex_set_text(
     dialog_ex_set_text(
         dialog_ex, "It will be returned\nto its original state.", 47, 21, AlignLeft, AlignTop);
         dialog_ex, "It will be returned\nto its original state.", 47, 21, AlignLeft, AlignTop);
     dialog_ex_set_left_button_text(dialog_ex, "Cancel");
     dialog_ex_set_left_button_text(dialog_ex, "Cancel");

+ 4 - 1
applications/main/nfc/scenes/nfc_scene_save_success.c

@@ -27,7 +27,10 @@ bool nfc_scene_save_success_on_event(void* context, SceneManagerEvent event) {
 
 
     if(event.type == SceneManagerEventTypeCustom) {
     if(event.type == SceneManagerEventTypeCustom) {
         if(event.event == NfcCustomEventViewExit) {
         if(event.event == NfcCustomEventViewExit) {
-            if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSavedMenu)) {
+            if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneMfClassicKeys)) {
+                consumed = scene_manager_search_and_switch_to_previous_scene(
+                    nfc->scene_manager, NfcSceneMfClassicKeys);
+            } else if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSavedMenu)) {
                 consumed = scene_manager_search_and_switch_to_previous_scene(
                 consumed = scene_manager_search_and_switch_to_previous_scene(
                     nfc->scene_manager, NfcSceneSavedMenu);
                     nfc->scene_manager, NfcSceneSavedMenu);
             } else {
             } else {

+ 1 - 1
applications/plugins/bt_hid_app/views/bt_hid_keyboard.c

@@ -109,7 +109,7 @@ const BtHidKeyboardKey bt_hid_keyboard_keyset[ROW_COUNT][COLUMN_COUNT] = {
         {.width = 1, .icon = NULL, .key = "-", .shift_key = "_", .value = HID_KEYBOARD_MINUS},
         {.width = 1, .icon = NULL, .key = "-", .shift_key = "_", .value = HID_KEYBOARD_MINUS},
     },
     },
     {
     {
-        {.width = 1, .icon = &I_Pin_arrow_up7x9, .value = HID_KEYBOARD_L_SHIFT},
+        {.width = 1, .icon = &I_Pin_arrow_up_7x9, .value = HID_KEYBOARD_L_SHIFT},
         {.width = 1, .icon = NULL, .key = ",", .shift_key = "<", .value = HID_KEYPAD_COMMA},
         {.width = 1, .icon = NULL, .key = ",", .shift_key = "<", .value = HID_KEYPAD_COMMA},
         {.width = 1, .icon = NULL, .key = ".", .shift_key = ">", .value = HID_KEYBOARD_DOT},
         {.width = 1, .icon = NULL, .key = ".", .shift_key = ">", .value = HID_KEYBOARD_DOT},
         {.width = 4, .icon = NULL, .key = " ", .value = HID_KEYBOARD_SPACEBAR},
         {.width = 4, .icon = NULL, .key = " ", .value = HID_KEYBOARD_SPACEBAR},

+ 1 - 1
applications/plugins/bt_hid_app/views/bt_hid_mouse.c

@@ -53,7 +53,7 @@ static void bt_hid_mouse_draw_callback(Canvas* canvas, void* context) {
         canvas_set_bitmap_mode(canvas, 0);
         canvas_set_bitmap_mode(canvas, 0);
         canvas_set_color(canvas, ColorWhite);
         canvas_set_color(canvas, ColorWhite);
     }
     }
-    canvas_draw_icon(canvas, 84, 10, &I_Pin_arrow_up7x9);
+    canvas_draw_icon(canvas, 84, 10, &I_Pin_arrow_up_7x9);
     canvas_set_color(canvas, ColorBlack);
     canvas_set_color(canvas, ColorBlack);
 
 
     // Down
     // Down

+ 1 - 1
applications/services/desktop/views/desktop_view_pin_input.c

@@ -117,7 +117,7 @@ static void desktop_view_pin_input_draw_cells(Canvas* canvas, DesktopViewPinInpu
                     canvas_draw_icon(canvas, x + 3, y + 2, &I_Pin_arrow_down_7x9);
                     canvas_draw_icon(canvas, x + 3, y + 2, &I_Pin_arrow_down_7x9);
                     break;
                     break;
                 case InputKeyUp:
                 case InputKeyUp:
-                    canvas_draw_icon(canvas, x + 3, y + 2, &I_Pin_arrow_up7x9);
+                    canvas_draw_icon(canvas, x + 3, y + 2, &I_Pin_arrow_up_7x9);
                     break;
                     break;
                 case InputKeyLeft:
                 case InputKeyLeft:
                     canvas_draw_icon(canvas, x + 2, y + 3, &I_Pin_arrow_left_9x7);
                     canvas_draw_icon(canvas, x + 2, y + 3, &I_Pin_arrow_left_9x7);

BIN
assets/icons/NFC/Keychain.png


BIN
assets/icons/NFC/Keychain_39x36.png


+ 0 - 0
assets/icons/NFC/NFC_manual.png → assets/icons/NFC/NFC_manual_60x50.png


BIN
assets/icons/NFC/Reader_detect_43x40.png


+ 0 - 0
assets/icons/NFC/Restoring.png → assets/icons/NFC/Restoring_38x32.png


+ 0 - 0
assets/icons/PIN/Pin_arrow_up7x9.png → assets/icons/PIN/Pin_arrow_up_7x9.png


+ 6 - 5
firmware/targets/f7/api_symbols.csv

@@ -1,5 +1,5 @@
 entry,status,name,type,params
 entry,status,name,type,params
-Version,+,1.5,,
+Version,+,1.6,,
 Header,+,applications/services/bt/bt_service/bt.h,,
 Header,+,applications/services/bt/bt_service/bt.h,,
 Header,+,applications/services/cli/cli.h,,
 Header,+,applications/services/cli/cli.h,,
 Header,+,applications/services/cli/cli_vcp.h,,
 Header,+,applications/services/cli/cli_vcp.h,,
@@ -2612,7 +2612,7 @@ Variable,+,I_KeyBackspaceSelected_16x9,const Icon,
 Variable,+,I_KeyBackspace_16x9,const Icon,
 Variable,+,I_KeyBackspace_16x9,const Icon,
 Variable,+,I_KeySaveSelected_24x11,const Icon,
 Variable,+,I_KeySaveSelected_24x11,const Icon,
 Variable,+,I_KeySave_24x11,const Icon,
 Variable,+,I_KeySave_24x11,const Icon,
-Variable,+,I_Keychain,const Icon,
+Variable,+,I_Keychain_39x36,const Icon,
 Variable,+,I_Left_mouse_icon_9x9,const Icon,
 Variable,+,I_Left_mouse_icon_9x9,const Icon,
 Variable,+,I_Lock_7x8,const Icon,
 Variable,+,I_Lock_7x8,const Icon,
 Variable,+,I_Lock_8x8,const Icon,
 Variable,+,I_Lock_8x8,const Icon,
@@ -2620,7 +2620,7 @@ Variable,+,I_MHz_25x11,const Icon,
 Variable,+,I_Medium_chip_22x21,const Icon,
 Variable,+,I_Medium_chip_22x21,const Icon,
 Variable,+,I_Mute_25x27,const Icon,
 Variable,+,I_Mute_25x27,const Icon,
 Variable,+,I_Mute_hvr_25x27,const Icon,
 Variable,+,I_Mute_hvr_25x27,const Icon,
-Variable,+,I_NFC_manual,const Icon,
+Variable,+,I_NFC_manual_60x50,const Icon,
 Variable,+,I_Nfc_10px,const Icon,
 Variable,+,I_Nfc_10px,const Icon,
 Variable,+,I_Ok_btn_9x9,const Icon,
 Variable,+,I_Ok_btn_9x9,const Icon,
 Variable,+,I_Ok_btn_pressed_13x13,const Icon,
 Variable,+,I_Ok_btn_pressed_13x13,const Icon,
@@ -2628,7 +2628,7 @@ Variable,+,I_Percent_10x14,const Icon,
 Variable,+,I_Pin_arrow_down_7x9,const Icon,
 Variable,+,I_Pin_arrow_down_7x9,const Icon,
 Variable,+,I_Pin_arrow_left_9x7,const Icon,
 Variable,+,I_Pin_arrow_left_9x7,const Icon,
 Variable,+,I_Pin_arrow_right_9x7,const Icon,
 Variable,+,I_Pin_arrow_right_9x7,const Icon,
-Variable,+,I_Pin_arrow_up7x9,const Icon,
+Variable,+,I_Pin_arrow_up_7x9,const Icon,
 Variable,+,I_Pin_attention_dpad_29x29,const Icon,
 Variable,+,I_Pin_attention_dpad_29x29,const Icon,
 Variable,+,I_Pin_back_arrow_10x8,const Icon,
 Variable,+,I_Pin_back_arrow_10x8,const Icon,
 Variable,+,I_Pin_back_full_40x8,const Icon,
 Variable,+,I_Pin_back_full_40x8,const Icon,
@@ -2642,7 +2642,8 @@ Variable,+,I_RFIDBigChip_37x36,const Icon,
 Variable,+,I_RFIDDolphinReceive_97x61,const Icon,
 Variable,+,I_RFIDDolphinReceive_97x61,const Icon,
 Variable,+,I_RFIDDolphinSend_97x61,const Icon,
 Variable,+,I_RFIDDolphinSend_97x61,const Icon,
 Variable,+,I_RFIDDolphinSuccess_108x57,const Icon,
 Variable,+,I_RFIDDolphinSuccess_108x57,const Icon,
-Variable,+,I_Restoring,const Icon,
+Variable,+,I_Reader_detect_43x40,const Icon,
+Variable,+,I_Restoring_38x32,const Icon,
 Variable,+,I_Right_mouse_icon_9x9,const Icon,
 Variable,+,I_Right_mouse_icon_9x9,const Icon,
 Variable,+,I_SDQuestion_35x43,const Icon,
 Variable,+,I_SDQuestion_35x43,const Icon,
 Variable,+,I_SDcardFail_11x8,const Icon,
 Variable,+,I_SDcardFail_11x8,const Icon,

+ 186 - 24
lib/nfc/helpers/mf_classic_dict.c

@@ -86,63 +86,225 @@ void mf_classic_dict_free(MfClassicDict* dict) {
     free(dict);
     free(dict);
 }
 }
 
 
+static void mf_classic_dict_int_to_str(uint8_t* key_int, string_t key_str) {
+    string_reset(key_str);
+    for(size_t i = 0; i < 6; i++) {
+        string_cat_printf(key_str, "%02X", key_int[i]);
+    }
+}
+
+static void mf_classic_dict_str_to_int(string_t key_str, uint64_t* key_int) {
+    uint8_t key_byte_tmp;
+
+    *key_int = 0ULL;
+    for(uint8_t i = 0; i < 12; i += 2) {
+        args_char_to_hex(
+            string_get_char(key_str, i), string_get_char(key_str, i + 1), &key_byte_tmp);
+        *key_int |= (uint8_t)key_byte_tmp << 8 * (5 - i / 2);
+    }
+}
+
 uint32_t mf_classic_dict_get_total_keys(MfClassicDict* dict) {
 uint32_t mf_classic_dict_get_total_keys(MfClassicDict* dict) {
     furi_assert(dict);
     furi_assert(dict);
 
 
     return dict->total_keys;
     return dict->total_keys;
 }
 }
 
 
-bool mf_classic_dict_get_next_key(MfClassicDict* dict, uint64_t* key) {
+bool mf_classic_dict_rewind(MfClassicDict* dict) {
     furi_assert(dict);
     furi_assert(dict);
     furi_assert(dict->stream);
     furi_assert(dict->stream);
 
 
-    uint8_t key_byte_tmp = 0;
-    string_t next_line;
-    string_init(next_line);
+    return stream_rewind(dict->stream);
+}
+
+bool mf_classic_dict_get_next_key_str(MfClassicDict* dict, string_t key) {
+    furi_assert(dict);
+    furi_assert(dict->stream);
 
 
     bool key_read = false;
     bool key_read = false;
-    *key = 0ULL;
+    string_reset(key);
     while(!key_read) {
     while(!key_read) {
-        if(!stream_read_line(dict->stream, next_line)) break;
-        if(string_get_char(next_line, 0) == '#') continue;
-        if(string_size(next_line) != NFC_MF_CLASSIC_KEY_LEN) continue;
-        for(uint8_t i = 0; i < 12; i += 2) {
-            args_char_to_hex(
-                string_get_char(next_line, i), string_get_char(next_line, i + 1), &key_byte_tmp);
-            *key |= (uint64_t)key_byte_tmp << 8 * (5 - i / 2);
-        }
+        if(!stream_read_line(dict->stream, key)) break;
+        if(string_get_char(key, 0) == '#') continue;
+        if(string_size(key) != NFC_MF_CLASSIC_KEY_LEN) continue;
+        string_left(key, 12);
         key_read = true;
         key_read = true;
     }
     }
 
 
-    string_clear(next_line);
     return key_read;
     return key_read;
 }
 }
 
 
-bool mf_classic_dict_rewind(MfClassicDict* dict) {
+bool mf_classic_dict_get_next_key(MfClassicDict* dict, uint64_t* key) {
     furi_assert(dict);
     furi_assert(dict);
     furi_assert(dict->stream);
     furi_assert(dict->stream);
 
 
-    return stream_rewind(dict->stream);
+    string_t temp_key;
+    string_init(temp_key);
+    bool key_read = mf_classic_dict_get_next_key_str(dict, temp_key);
+    if(key_read) {
+        mf_classic_dict_str_to_int(temp_key, key);
+    }
+    string_clear(temp_key);
+    return key_read;
 }
 }
 
 
-bool mf_classic_dict_add_key(MfClassicDict* dict, uint8_t* key) {
+bool mf_classic_dict_is_key_present_str(MfClassicDict* dict, string_t key) {
     furi_assert(dict);
     furi_assert(dict);
     furi_assert(dict->stream);
     furi_assert(dict->stream);
 
 
-    string_t key_str;
-    string_init(key_str);
-    for(size_t i = 0; i < 6; i++) {
-        string_cat_printf(key_str, "%02X", key[i]);
+    string_t next_line;
+    string_init(next_line);
+
+    bool key_found = false;
+    stream_rewind(dict->stream);
+    while(!key_found) {
+        if(!stream_read_line(dict->stream, next_line)) break;
+        if(string_get_char(next_line, 0) == '#') continue;
+        if(string_size(next_line) != NFC_MF_CLASSIC_KEY_LEN) continue;
+        string_left(next_line, 12);
+        if(!string_equal_p(key, next_line)) continue;
+        key_found = true;
     }
     }
-    string_cat_printf(key_str, "\n");
+
+    string_clear(next_line);
+    return key_found;
+}
+
+bool mf_classic_dict_is_key_present(MfClassicDict* dict, uint8_t* key) {
+    string_t temp_key;
+
+    string_init(temp_key);
+    mf_classic_dict_int_to_str(key, temp_key);
+    bool key_found = mf_classic_dict_is_key_present_str(dict, temp_key);
+    string_clear(temp_key);
+    return key_found;
+}
+
+bool mf_classic_dict_add_key_str(MfClassicDict* dict, string_t key) {
+    furi_assert(dict);
+    furi_assert(dict->stream);
+
+    string_cat_printf(key, "\n");
 
 
     bool key_added = false;
     bool key_added = false;
     do {
     do {
         if(!stream_seek(dict->stream, 0, StreamOffsetFromEnd)) break;
         if(!stream_seek(dict->stream, 0, StreamOffsetFromEnd)) break;
-        if(!stream_insert_string(dict->stream, key_str)) break;
+        if(!stream_insert_string(dict->stream, key)) break;
+        dict->total_keys++;
         key_added = true;
         key_added = true;
     } while(false);
     } while(false);
 
 
-    string_clear(key_str);
+    string_left(key, 12);
     return key_added;
     return key_added;
 }
 }
+
+bool mf_classic_dict_add_key(MfClassicDict* dict, uint8_t* key) {
+    furi_assert(dict);
+    furi_assert(dict->stream);
+
+    string_t temp_key;
+    string_init(temp_key);
+    mf_classic_dict_int_to_str(key, temp_key);
+    bool key_added = mf_classic_dict_add_key_str(dict, temp_key);
+
+    string_clear(temp_key);
+    return key_added;
+}
+
+bool mf_classic_dict_get_key_at_index_str(MfClassicDict* dict, string_t key, uint32_t target) {
+    furi_assert(dict);
+    furi_assert(dict->stream);
+
+    string_t next_line;
+    uint32_t index = 0;
+    string_init(next_line);
+    string_reset(key);
+
+    bool key_found = false;
+    while(!key_found) {
+        if(!stream_read_line(dict->stream, next_line)) break;
+        if(string_get_char(next_line, 0) == '#') continue;
+        if(string_size(next_line) != NFC_MF_CLASSIC_KEY_LEN) continue;
+        if(index++ != target) continue;
+        string_set_n(key, next_line, 0, 12);
+        key_found = true;
+    }
+
+    string_clear(next_line);
+    return key_found;
+}
+
+bool mf_classic_dict_get_key_at_index(MfClassicDict* dict, uint64_t* key, uint32_t target) {
+    furi_assert(dict);
+    furi_assert(dict->stream);
+
+    string_t temp_key;
+    string_init(temp_key);
+    bool key_found = mf_classic_dict_get_key_at_index_str(dict, temp_key, target);
+    if(key_found) {
+        mf_classic_dict_str_to_int(temp_key, key);
+    }
+    string_clear(temp_key);
+    return key_found;
+}
+
+bool mf_classic_dict_find_index_str(MfClassicDict* dict, string_t key, uint32_t* target) {
+    furi_assert(dict);
+    furi_assert(dict->stream);
+
+    string_t next_line;
+    string_init(next_line);
+
+    bool key_found = false;
+    uint32_t index = 0;
+    stream_rewind(dict->stream);
+    while(!key_found) {
+        if(!stream_read_line(dict->stream, next_line)) break;
+        if(string_get_char(next_line, 0) == '#') continue;
+        if(string_size(next_line) != NFC_MF_CLASSIC_KEY_LEN) continue;
+        string_left(next_line, 12);
+        if(!string_equal_p(key, next_line)) continue;
+        key_found = true;
+        *target = index;
+    }
+
+    string_clear(next_line);
+    return key_found;
+}
+
+bool mf_classic_dict_find_index(MfClassicDict* dict, uint8_t* key, uint32_t* target) {
+    furi_assert(dict);
+    furi_assert(dict->stream);
+
+    string_t temp_key;
+    string_init(temp_key);
+    mf_classic_dict_int_to_str(key, temp_key);
+    bool key_found = mf_classic_dict_find_index_str(dict, temp_key, target);
+
+    string_clear(temp_key);
+    return key_found;
+}
+
+bool mf_classic_dict_delete_index(MfClassicDict* dict, uint32_t target) {
+    furi_assert(dict);
+    furi_assert(dict->stream);
+
+    string_t next_line;
+    string_init(next_line);
+    uint32_t index = 0;
+
+    bool key_removed = false;
+    while(!key_removed) {
+        if(!stream_read_line(dict->stream, next_line)) break;
+        if(string_get_char(next_line, 0) == '#') continue;
+        if(string_size(next_line) != NFC_MF_CLASSIC_KEY_LEN) continue;
+        if(index++ != target) continue;
+        stream_seek(dict->stream, -NFC_MF_CLASSIC_KEY_LEN, StreamOffsetFromCurrent);
+        if(!stream_delete(dict->stream, NFC_MF_CLASSIC_KEY_LEN)) break;
+        dict->total_keys--;
+        key_removed = true;
+    }
+
+    string_clear(next_line);
+    return key_removed;
+}

+ 19 - 1
lib/nfc/helpers/mf_classic_dict.h

@@ -21,8 +21,26 @@ void mf_classic_dict_free(MfClassicDict* dict);
 
 
 uint32_t mf_classic_dict_get_total_keys(MfClassicDict* dict);
 uint32_t mf_classic_dict_get_total_keys(MfClassicDict* dict);
 
 
+bool mf_classic_dict_rewind(MfClassicDict* dict);
+
+bool mf_classic_dict_is_key_present(MfClassicDict* dict, uint8_t* key);
+
+bool mf_classic_dict_is_key_present_str(MfClassicDict* dict, string_t key);
+
 bool mf_classic_dict_get_next_key(MfClassicDict* dict, uint64_t* key);
 bool mf_classic_dict_get_next_key(MfClassicDict* dict, uint64_t* key);
 
 
-bool mf_classic_dict_rewind(MfClassicDict* dict);
+bool mf_classic_dict_get_next_key_str(MfClassicDict* dict, string_t key);
+
+bool mf_classic_dict_get_key_at_index(MfClassicDict* dict, uint64_t* key, uint32_t target);
+
+bool mf_classic_dict_get_key_at_index_str(MfClassicDict* dict, string_t key, uint32_t target);
 
 
 bool mf_classic_dict_add_key(MfClassicDict* dict, uint8_t* key);
 bool mf_classic_dict_add_key(MfClassicDict* dict, uint8_t* key);
+
+bool mf_classic_dict_add_key_str(MfClassicDict* dict, string_t key);
+
+bool mf_classic_dict_find_index(MfClassicDict* dict, uint8_t* key, uint32_t* target);
+
+bool mf_classic_dict_find_index_str(MfClassicDict* dict, string_t key, uint32_t* target);
+
+bool mf_classic_dict_delete_index(MfClassicDict* dict, uint32_t target);