[FL-2809] Rework BLE key storage (#2154)
* bt: disconnect first on profile change * bt keys: rework bt keys * saved struct: add payload size getter to API * bt: rework bt with new key storage API * bt: add keys storage operation to bt API * hid: save bt keys on sd card * bt: add unit tests for key storage * bt: working profile switch * bt: cleanup * bt hid: change keys storage path Co-authored-by: あく <alleteam@gmail.com>
This commit is contained in:
		| @@ -117,6 +117,8 @@ Bt* bt_alloc() { | ||||
|     if(!bt_settings_load(&bt->bt_settings)) { | ||||
|         bt_settings_save(&bt->bt_settings); | ||||
|     } | ||||
|     // Keys storage | ||||
|     bt->keys_storage = bt_keys_storage_alloc(BT_KEYS_STORAGE_PATH); | ||||
|     // Alloc queue | ||||
|     bt->message_queue = furi_message_queue_alloc(8, sizeof(BtMessage)); | ||||
|  | ||||
| @@ -285,8 +287,10 @@ static bool bt_on_gap_event_callback(GapEvent event, void* context) { | ||||
| static void bt_on_key_storage_change_callback(uint8_t* addr, uint16_t size, void* context) { | ||||
|     furi_assert(context); | ||||
|     Bt* bt = context; | ||||
|     FURI_LOG_I(TAG, "Changed addr start: %p, size changed: %d", addr, size); | ||||
|     BtMessage message = {.type = BtMessageTypeKeysStorageUpdated}; | ||||
|     BtMessage message = { | ||||
|         .type = BtMessageTypeKeysStorageUpdated, | ||||
|         .data.key_storage_data.start_address = addr, | ||||
|         .data.key_storage_data.size = size}; | ||||
|     furi_check( | ||||
|         furi_message_queue_put(bt->message_queue, &message, FuriWaitForever) == FuriStatusOk); | ||||
| } | ||||
| @@ -331,6 +335,8 @@ static void bt_change_profile(Bt* bt, BtMessage* message) { | ||||
|             furi_profile = FuriHalBtProfileSerial; | ||||
|         } | ||||
|  | ||||
|         bt_keys_storage_load(bt->keys_storage); | ||||
|  | ||||
|         if(furi_hal_bt_change_app(furi_profile, bt_on_gap_event_callback, bt)) { | ||||
|             FURI_LOG_I(TAG, "Bt App started"); | ||||
|             if(bt->bt_settings.enabled) { | ||||
| @@ -358,6 +364,7 @@ static void bt_change_profile(Bt* bt, BtMessage* message) { | ||||
|  | ||||
| static void bt_close_connection(Bt* bt) { | ||||
|     bt_close_rpc_connection(bt); | ||||
|     furi_hal_bt_stop_advertising(); | ||||
|     furi_event_flag_set(bt->api_event, BT_API_UNLOCK_EVENT); | ||||
| } | ||||
|  | ||||
| @@ -372,8 +379,8 @@ int32_t bt_srv(void* p) { | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     // Read keys | ||||
|     if(!bt_keys_storage_load(bt)) { | ||||
|     // Load keys | ||||
|     if(!bt_keys_storage_load(bt->keys_storage)) { | ||||
|         FURI_LOG_W(TAG, "Failed to load bonding keys"); | ||||
|     } | ||||
|  | ||||
| @@ -418,13 +425,16 @@ int32_t bt_srv(void* p) { | ||||
|             // Display PIN code | ||||
|             bt_pin_code_show(bt, message.data.pin_code); | ||||
|         } else if(message.type == BtMessageTypeKeysStorageUpdated) { | ||||
|             bt_keys_storage_save(bt); | ||||
|             bt_keys_storage_update( | ||||
|                 bt->keys_storage, | ||||
|                 message.data.key_storage_data.start_address, | ||||
|                 message.data.key_storage_data.size); | ||||
|         } else if(message.type == BtMessageTypeSetProfile) { | ||||
|             bt_change_profile(bt, &message); | ||||
|         } else if(message.type == BtMessageTypeDisconnect) { | ||||
|             bt_close_connection(bt); | ||||
|         } else if(message.type == BtMessageTypeForgetBondedDevices) { | ||||
|             bt_keys_storage_delete(bt); | ||||
|             bt_keys_storage_delete(bt->keys_storage); | ||||
|         } | ||||
|     } | ||||
|     return 0; | ||||
|   | ||||
| @@ -56,6 +56,19 @@ void bt_set_status_changed_callback(Bt* bt, BtStatusChangedCallback callback, vo | ||||
|  */ | ||||
| void bt_forget_bonded_devices(Bt* bt); | ||||
|  | ||||
| /** Set keys storage file path | ||||
|  * | ||||
|  * @param bt                    Bt instance | ||||
|  * @param keys_storage_path     Path to file with saved keys | ||||
|  */ | ||||
| void bt_keys_storage_set_storage_path(Bt* bt, const char* keys_storage_path); | ||||
|  | ||||
| /** Set default keys storage file path | ||||
|  * | ||||
|  * @param bt                    Bt instance | ||||
|  */ | ||||
| void bt_keys_storage_set_default_path(Bt* bt); | ||||
|  | ||||
| #ifdef __cplusplus | ||||
| } | ||||
| #endif | ||||
|   | ||||
| @@ -39,3 +39,18 @@ void bt_forget_bonded_devices(Bt* bt) { | ||||
|     furi_check( | ||||
|         furi_message_queue_put(bt->message_queue, &message, FuriWaitForever) == FuriStatusOk); | ||||
| } | ||||
|  | ||||
| void bt_keys_storage_set_storage_path(Bt* bt, const char* keys_storage_path) { | ||||
|     furi_assert(bt); | ||||
|     furi_assert(bt->keys_storage); | ||||
|     furi_assert(keys_storage_path); | ||||
|  | ||||
|     bt_keys_storage_set_file_path(bt->keys_storage, keys_storage_path); | ||||
| } | ||||
|  | ||||
| void bt_keys_storage_set_default_path(Bt* bt) { | ||||
|     furi_assert(bt); | ||||
|     furi_assert(bt->keys_storage); | ||||
|  | ||||
|     bt_keys_storage_set_file_path(bt->keys_storage, BT_KEYS_STORAGE_PATH); | ||||
| } | ||||
|   | ||||
| @@ -13,8 +13,14 @@ | ||||
| #include <power/power_service/power.h> | ||||
| #include <rpc/rpc.h> | ||||
| #include <notification/notification.h> | ||||
| #include <storage/storage.h> | ||||
|  | ||||
| #include <bt/bt_settings.h> | ||||
| #include <bt/bt_service/bt_keys_storage.h> | ||||
|  | ||||
| #include "bt_keys_filename.h" | ||||
|  | ||||
| #define BT_KEYS_STORAGE_PATH INT_PATH(BT_KEYS_STORAGE_FILE_NAME) | ||||
|  | ||||
| #define BT_API_UNLOCK_EVENT (1UL << 0) | ||||
|  | ||||
| @@ -29,10 +35,16 @@ typedef enum { | ||||
|     BtMessageTypeForgetBondedDevices, | ||||
| } BtMessageType; | ||||
|  | ||||
| typedef struct { | ||||
|     uint8_t* start_address; | ||||
|     uint16_t size; | ||||
| } BtKeyStorageUpdateData; | ||||
|  | ||||
| typedef union { | ||||
|     uint32_t pin_code; | ||||
|     uint8_t battery_level; | ||||
|     BtProfile profile; | ||||
|     BtKeyStorageUpdateData key_storage_data; | ||||
| } BtMessageData; | ||||
|  | ||||
| typedef struct { | ||||
| @@ -46,6 +58,7 @@ struct Bt { | ||||
|     uint16_t bt_keys_size; | ||||
|     uint16_t max_packet_size; | ||||
|     BtSettings bt_settings; | ||||
|     BtKeysStorage* keys_storage; | ||||
|     BtStatus status; | ||||
|     BtProfile profile; | ||||
|     FuriMessageQueue* message_queue; | ||||
|   | ||||
| @@ -1,49 +1,24 @@ | ||||
| #include "bt_keys_storage.h" | ||||
|  | ||||
| #include <furi.h> | ||||
| #include <furi_hal_bt.h> | ||||
| #include <lib/toolbox/saved_struct.h> | ||||
| #include <storage/storage.h> | ||||
|  | ||||
| #define BT_KEYS_STORAGE_PATH INT_PATH(BT_KEYS_STORAGE_FILE_NAME) | ||||
| #define BT_KEYS_STORAGE_VERSION (0) | ||||
| #define BT_KEYS_STORAGE_MAGIC (0x18) | ||||
|  | ||||
| bool bt_keys_storage_load(Bt* bt) { | ||||
|     furi_assert(bt); | ||||
|     bool file_loaded = false; | ||||
| #define TAG "BtKeyStorage" | ||||
|  | ||||
|     furi_hal_bt_get_key_storage_buff(&bt->bt_keys_addr_start, &bt->bt_keys_size); | ||||
|     furi_hal_bt_nvm_sram_sem_acquire(); | ||||
|     file_loaded = saved_struct_load( | ||||
|         BT_KEYS_STORAGE_PATH, | ||||
|         bt->bt_keys_addr_start, | ||||
|         bt->bt_keys_size, | ||||
|         BT_KEYS_STORAGE_MAGIC, | ||||
|         BT_KEYS_STORAGE_VERSION); | ||||
|     furi_hal_bt_nvm_sram_sem_release(); | ||||
| struct BtKeysStorage { | ||||
|     uint8_t* nvm_sram_buff; | ||||
|     uint16_t nvm_sram_buff_size; | ||||
|     FuriString* file_path; | ||||
| }; | ||||
|  | ||||
|     return file_loaded; | ||||
| } | ||||
| bool bt_keys_storage_delete(BtKeysStorage* instance) { | ||||
|     furi_assert(instance); | ||||
|  | ||||
| bool bt_keys_storage_save(Bt* bt) { | ||||
|     furi_assert(bt); | ||||
|     furi_assert(bt->bt_keys_addr_start); | ||||
|     bool file_saved = false; | ||||
|  | ||||
|     furi_hal_bt_nvm_sram_sem_acquire(); | ||||
|     file_saved = saved_struct_save( | ||||
|         BT_KEYS_STORAGE_PATH, | ||||
|         bt->bt_keys_addr_start, | ||||
|         bt->bt_keys_size, | ||||
|         BT_KEYS_STORAGE_MAGIC, | ||||
|         BT_KEYS_STORAGE_VERSION); | ||||
|     furi_hal_bt_nvm_sram_sem_release(); | ||||
|  | ||||
|     return file_saved; | ||||
| } | ||||
|  | ||||
| bool bt_keys_storage_delete(Bt* bt) { | ||||
|     furi_assert(bt); | ||||
|     bool delete_succeed = false; | ||||
|     bool bt_is_active = furi_hal_bt_is_active(); | ||||
|  | ||||
| @@ -55,3 +30,117 @@ bool bt_keys_storage_delete(Bt* bt) { | ||||
|  | ||||
|     return delete_succeed; | ||||
| } | ||||
|  | ||||
| BtKeysStorage* bt_keys_storage_alloc(const char* keys_storage_path) { | ||||
|     furi_assert(keys_storage_path); | ||||
|  | ||||
|     BtKeysStorage* instance = malloc(sizeof(BtKeysStorage)); | ||||
|     // Set default nvm ram parameters | ||||
|     furi_hal_bt_get_key_storage_buff(&instance->nvm_sram_buff, &instance->nvm_sram_buff_size); | ||||
|     // Set key storage file | ||||
|     instance->file_path = furi_string_alloc(); | ||||
|     furi_string_set_str(instance->file_path, keys_storage_path); | ||||
|  | ||||
|     return instance; | ||||
| } | ||||
|  | ||||
| void bt_keys_storage_free(BtKeysStorage* instance) { | ||||
|     furi_assert(instance); | ||||
|  | ||||
|     furi_string_free(instance->file_path); | ||||
|     free(instance); | ||||
| } | ||||
|  | ||||
| void bt_keys_storage_set_file_path(BtKeysStorage* instance, const char* path) { | ||||
|     furi_assert(instance); | ||||
|     furi_assert(path); | ||||
|  | ||||
|     furi_string_set_str(instance->file_path, path); | ||||
| } | ||||
|  | ||||
| void bt_keys_storage_set_ram_params(BtKeysStorage* instance, uint8_t* buff, uint16_t size) { | ||||
|     furi_assert(instance); | ||||
|     furi_assert(buff); | ||||
|  | ||||
|     instance->nvm_sram_buff = buff; | ||||
|     instance->nvm_sram_buff_size = size; | ||||
| } | ||||
|  | ||||
| bool bt_keys_storage_load(BtKeysStorage* instance) { | ||||
|     furi_assert(instance); | ||||
|  | ||||
|     bool loaded = false; | ||||
|     do { | ||||
|         // Get payload size | ||||
|         size_t payload_size = 0; | ||||
|         if(!saved_struct_get_payload_size( | ||||
|                furi_string_get_cstr(instance->file_path), | ||||
|                BT_KEYS_STORAGE_MAGIC, | ||||
|                BT_KEYS_STORAGE_VERSION, | ||||
|                &payload_size)) { | ||||
|             FURI_LOG_E(TAG, "Failed to read payload size"); | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         if(payload_size > instance->nvm_sram_buff_size) { | ||||
|             FURI_LOG_E(TAG, "Saved data doesn't fit ram buffer"); | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         // Load saved data to ram | ||||
|         furi_hal_bt_nvm_sram_sem_acquire(); | ||||
|         bool data_loaded = saved_struct_load( | ||||
|             furi_string_get_cstr(instance->file_path), | ||||
|             instance->nvm_sram_buff, | ||||
|             payload_size, | ||||
|             BT_KEYS_STORAGE_MAGIC, | ||||
|             BT_KEYS_STORAGE_VERSION); | ||||
|         furi_hal_bt_nvm_sram_sem_release(); | ||||
|         if(!data_loaded) { | ||||
|             FURI_LOG_E(TAG, "Failed to load struct"); | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         loaded = true; | ||||
|     } while(false); | ||||
|  | ||||
|     return loaded; | ||||
| } | ||||
|  | ||||
| bool bt_keys_storage_update(BtKeysStorage* instance, uint8_t* start_addr, uint32_t size) { | ||||
|     furi_assert(instance); | ||||
|     furi_assert(start_addr); | ||||
|  | ||||
|     bool updated = false; | ||||
|  | ||||
|     FURI_LOG_I( | ||||
|         TAG, | ||||
|         "Base address: %p. Start update address: %p. Size changed: %ld", | ||||
|         (void*)instance->nvm_sram_buff, | ||||
|         start_addr, | ||||
|         size); | ||||
|  | ||||
|     do { | ||||
|         size_t new_size = start_addr - instance->nvm_sram_buff + size; | ||||
|         if(new_size > instance->nvm_sram_buff_size) { | ||||
|             FURI_LOG_E(TAG, "NVM RAM buffer overflow"); | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         furi_hal_bt_nvm_sram_sem_acquire(); | ||||
|         bool data_updated = saved_struct_save( | ||||
|             furi_string_get_cstr(instance->file_path), | ||||
|             instance->nvm_sram_buff, | ||||
|             new_size, | ||||
|             BT_KEYS_STORAGE_MAGIC, | ||||
|             BT_KEYS_STORAGE_VERSION); | ||||
|         furi_hal_bt_nvm_sram_sem_release(); | ||||
|         if(!data_updated) { | ||||
|             FURI_LOG_E(TAG, "Failed to update key storage"); | ||||
|             break; | ||||
|         } | ||||
|         updated = true; | ||||
|     } while(false); | ||||
|  | ||||
|     return updated; | ||||
| } | ||||
|   | ||||
| @@ -1,10 +1,20 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "bt_i.h" | ||||
| #include "bt_keys_filename.h" | ||||
| #include <stdint.h> | ||||
| #include <stdbool.h> | ||||
|  | ||||
| bool bt_keys_storage_load(Bt* bt); | ||||
| typedef struct BtKeysStorage BtKeysStorage; | ||||
|  | ||||
| bool bt_keys_storage_save(Bt* bt); | ||||
| BtKeysStorage* bt_keys_storage_alloc(const char* keys_storage_path); | ||||
|  | ||||
| bool bt_keys_storage_delete(Bt* bt); | ||||
| void bt_keys_storage_free(BtKeysStorage* instance); | ||||
|  | ||||
| void bt_keys_storage_set_file_path(BtKeysStorage* instance, const char* path); | ||||
|  | ||||
| void bt_keys_storage_set_ram_params(BtKeysStorage* instance, uint8_t* buff, uint16_t size); | ||||
|  | ||||
| bool bt_keys_storage_load(BtKeysStorage* instance); | ||||
|  | ||||
| bool bt_keys_storage_update(BtKeysStorage* instance, uint8_t* start_addr, uint32_t size); | ||||
|  | ||||
| bool bt_keys_storage_delete(BtKeysStorage* instance); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user