[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:
		
							parent
							
								
									e7107e39f7
								
							
						
					
					
						commit
						4cee550cc6
					
				
							
								
								
									
										110
									
								
								applications/debug/unit_tests/bt/bt_test.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								applications/debug/unit_tests/bt/bt_test.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,110 @@ | |||||||
|  | #include <furi.h> | ||||||
|  | #include <furi_hal.h> | ||||||
|  | #include "../minunit.h" | ||||||
|  | 
 | ||||||
|  | #include <bt/bt_service/bt_keys_storage.h> | ||||||
|  | #include <storage/storage.h> | ||||||
|  | 
 | ||||||
|  | #define BT_TEST_KEY_STORAGE_FILE_PATH EXT_PATH("unit_tests/bt_test.keys") | ||||||
|  | #define BT_TEST_NVM_RAM_BUFF_SIZE (507 * 4) // The same as in ble NVM storage
 | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     Storage* storage; | ||||||
|  |     BtKeysStorage* bt_keys_storage; | ||||||
|  |     uint8_t* nvm_ram_buff_dut; | ||||||
|  |     uint8_t* nvm_ram_buff_ref; | ||||||
|  | } BtTest; | ||||||
|  | 
 | ||||||
|  | BtTest* bt_test = NULL; | ||||||
|  | 
 | ||||||
|  | void bt_test_alloc() { | ||||||
|  |     bt_test = malloc(sizeof(BtTest)); | ||||||
|  |     bt_test->storage = furi_record_open(RECORD_STORAGE); | ||||||
|  |     bt_test->nvm_ram_buff_dut = malloc(BT_TEST_NVM_RAM_BUFF_SIZE); | ||||||
|  |     bt_test->nvm_ram_buff_ref = malloc(BT_TEST_NVM_RAM_BUFF_SIZE); | ||||||
|  |     bt_test->bt_keys_storage = bt_keys_storage_alloc(BT_TEST_KEY_STORAGE_FILE_PATH); | ||||||
|  |     bt_keys_storage_set_ram_params( | ||||||
|  |         bt_test->bt_keys_storage, bt_test->nvm_ram_buff_dut, BT_TEST_NVM_RAM_BUFF_SIZE); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void bt_test_free() { | ||||||
|  |     furi_assert(bt_test); | ||||||
|  |     free(bt_test->nvm_ram_buff_ref); | ||||||
|  |     free(bt_test->nvm_ram_buff_dut); | ||||||
|  |     bt_keys_storage_free(bt_test->bt_keys_storage); | ||||||
|  |     furi_record_close(RECORD_STORAGE); | ||||||
|  |     free(bt_test); | ||||||
|  |     bt_test = NULL; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void bt_test_keys_storage_profile() { | ||||||
|  |     // Emulate nvm change on initial connection
 | ||||||
|  |     const int nvm_change_size_on_connection = 88; | ||||||
|  |     for(size_t i = 0; i < nvm_change_size_on_connection; i++) { | ||||||
|  |         bt_test->nvm_ram_buff_dut[i] = rand(); | ||||||
|  |         bt_test->nvm_ram_buff_ref[i] = bt_test->nvm_ram_buff_dut[i]; | ||||||
|  |     } | ||||||
|  |     // Emulate update storage on initial connect
 | ||||||
|  |     mu_assert( | ||||||
|  |         bt_keys_storage_update( | ||||||
|  |             bt_test->bt_keys_storage, bt_test->nvm_ram_buff_dut, nvm_change_size_on_connection), | ||||||
|  |         "Failed to update key storage on initial connect"); | ||||||
|  |     memset(bt_test->nvm_ram_buff_dut, 0, BT_TEST_NVM_RAM_BUFF_SIZE); | ||||||
|  |     mu_assert(bt_keys_storage_load(bt_test->bt_keys_storage), "Failed to load NVM"); | ||||||
|  |     mu_assert( | ||||||
|  |         memcmp( | ||||||
|  |             bt_test->nvm_ram_buff_ref, bt_test->nvm_ram_buff_dut, nvm_change_size_on_connection) == | ||||||
|  |             0, | ||||||
|  |         "Wrong buffer loaded"); | ||||||
|  | 
 | ||||||
|  |     const int nvm_disconnect_update_offset = 84; | ||||||
|  |     const int nvm_disconnect_update_size = 324; | ||||||
|  |     const int nvm_total_size = nvm_change_size_on_connection - | ||||||
|  |                                (nvm_change_size_on_connection - nvm_disconnect_update_offset) + | ||||||
|  |                                nvm_disconnect_update_size; | ||||||
|  |     // Emulate update storage on initial disconnect
 | ||||||
|  |     for(size_t i = nvm_disconnect_update_offset; | ||||||
|  |         i < nvm_disconnect_update_offset + nvm_disconnect_update_size; | ||||||
|  |         i++) { | ||||||
|  |         bt_test->nvm_ram_buff_dut[i] = rand(); | ||||||
|  |         bt_test->nvm_ram_buff_ref[i] = bt_test->nvm_ram_buff_dut[i]; | ||||||
|  |     } | ||||||
|  |     mu_assert( | ||||||
|  |         bt_keys_storage_update( | ||||||
|  |             bt_test->bt_keys_storage, | ||||||
|  |             &bt_test->nvm_ram_buff_dut[nvm_disconnect_update_offset], | ||||||
|  |             nvm_disconnect_update_size), | ||||||
|  |         "Failed to update key storage on initial disconnect"); | ||||||
|  |     memset(bt_test->nvm_ram_buff_dut, 0, BT_TEST_NVM_RAM_BUFF_SIZE); | ||||||
|  |     mu_assert(bt_keys_storage_load(bt_test->bt_keys_storage), "Failed to load NVM"); | ||||||
|  |     mu_assert( | ||||||
|  |         memcmp(bt_test->nvm_ram_buff_ref, bt_test->nvm_ram_buff_dut, nvm_total_size) == 0, | ||||||
|  |         "Wrong buffer loaded"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void bt_test_keys_remove_test_file() { | ||||||
|  |     mu_assert( | ||||||
|  |         storage_simply_remove(bt_test->storage, BT_TEST_KEY_STORAGE_FILE_PATH), | ||||||
|  |         "Can't remove test file"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | MU_TEST(bt_test_keys_storage_serial_profile) { | ||||||
|  |     furi_assert(bt_test); | ||||||
|  | 
 | ||||||
|  |     bt_test_keys_remove_test_file(); | ||||||
|  |     bt_test_keys_storage_profile(); | ||||||
|  |     bt_test_keys_remove_test_file(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | MU_TEST_SUITE(test_bt) { | ||||||
|  |     bt_test_alloc(); | ||||||
|  | 
 | ||||||
|  |     MU_RUN_TEST(bt_test_keys_storage_serial_profile); | ||||||
|  | 
 | ||||||
|  |     bt_test_free(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int run_minunit_test_bt() { | ||||||
|  |     MU_RUN_SUITE(test_bt); | ||||||
|  |     return MU_EXIT_CODE; | ||||||
|  | } | ||||||
| @ -24,6 +24,7 @@ int run_minunit_test_protocol_dict(); | |||||||
| int run_minunit_test_lfrfid_protocols(); | int run_minunit_test_lfrfid_protocols(); | ||||||
| int run_minunit_test_nfc(); | int run_minunit_test_nfc(); | ||||||
| int run_minunit_test_bit_lib(); | int run_minunit_test_bit_lib(); | ||||||
|  | int run_minunit_test_bt(); | ||||||
| 
 | 
 | ||||||
| typedef int (*UnitTestEntry)(); | typedef int (*UnitTestEntry)(); | ||||||
| 
 | 
 | ||||||
| @ -49,6 +50,7 @@ const UnitTest unit_tests[] = { | |||||||
|     {.name = "protocol_dict", .entry = run_minunit_test_protocol_dict}, |     {.name = "protocol_dict", .entry = run_minunit_test_protocol_dict}, | ||||||
|     {.name = "lfrfid", .entry = run_minunit_test_lfrfid_protocols}, |     {.name = "lfrfid", .entry = run_minunit_test_lfrfid_protocols}, | ||||||
|     {.name = "bit_lib", .entry = run_minunit_test_bit_lib}, |     {.name = "bit_lib", .entry = run_minunit_test_bit_lib}, | ||||||
|  |     {.name = "bt", .entry = run_minunit_test_bt}, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| void minunit_print_progress() { | void minunit_print_progress() { | ||||||
|  | |||||||
| @ -367,9 +367,17 @@ int32_t hid_ble_app(void* p) { | |||||||
|     Hid* app = hid_alloc(HidTransportBle); |     Hid* app = hid_alloc(HidTransportBle); | ||||||
|     app = hid_app_alloc_view(app); |     app = hid_app_alloc_view(app); | ||||||
| 
 | 
 | ||||||
|  |     bt_disconnect(app->bt); | ||||||
|  | 
 | ||||||
|  |     // Wait 2nd core to update nvm storage
 | ||||||
|  |     furi_delay_ms(200); | ||||||
|  | 
 | ||||||
|  |     bt_keys_storage_set_storage_path(app->bt, HID_BT_KEYS_STORAGE_PATH); | ||||||
|  | 
 | ||||||
|     if(!bt_set_profile(app->bt, BtProfileHidKeyboard)) { |     if(!bt_set_profile(app->bt, BtProfileHidKeyboard)) { | ||||||
|         FURI_LOG_E(TAG, "Failed to switch profile"); |         FURI_LOG_E(TAG, "Failed to switch to HID profile"); | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|     furi_hal_bt_start_advertising(); |     furi_hal_bt_start_advertising(); | ||||||
|     bt_set_status_changed_callback(app->bt, bt_hid_connection_status_changed_callback, app); |     bt_set_status_changed_callback(app->bt, bt_hid_connection_status_changed_callback, app); | ||||||
| 
 | 
 | ||||||
| @ -378,7 +386,17 @@ int32_t hid_ble_app(void* p) { | |||||||
|     view_dispatcher_run(app->view_dispatcher); |     view_dispatcher_run(app->view_dispatcher); | ||||||
| 
 | 
 | ||||||
|     bt_set_status_changed_callback(app->bt, NULL, NULL); |     bt_set_status_changed_callback(app->bt, NULL, NULL); | ||||||
|     bt_set_profile(app->bt, BtProfileSerial); | 
 | ||||||
|  |     bt_disconnect(app->bt); | ||||||
|  | 
 | ||||||
|  |     // Wait 2nd core to update nvm storage
 | ||||||
|  |     furi_delay_ms(200); | ||||||
|  | 
 | ||||||
|  |     bt_keys_storage_set_default_path(app->bt); | ||||||
|  | 
 | ||||||
|  |     if(!bt_set_profile(app->bt, BtProfileSerial)) { | ||||||
|  |         FURI_LOG_E(TAG, "Failed to switch to Serial profile"); | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     hid_free(app); |     hid_free(app); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -11,6 +11,7 @@ | |||||||
| #include <gui/view.h> | #include <gui/view.h> | ||||||
| #include <gui/view_dispatcher.h> | #include <gui/view_dispatcher.h> | ||||||
| #include <notification/notification.h> | #include <notification/notification.h> | ||||||
|  | #include <storage/storage.h> | ||||||
| 
 | 
 | ||||||
| #include <gui/modules/submenu.h> | #include <gui/modules/submenu.h> | ||||||
| #include <gui/modules/dialog_ex.h> | #include <gui/modules/dialog_ex.h> | ||||||
| @ -22,6 +23,8 @@ | |||||||
| #include "views/hid_mouse_jiggler.h" | #include "views/hid_mouse_jiggler.h" | ||||||
| #include "views/hid_tiktok.h" | #include "views/hid_tiktok.h" | ||||||
| 
 | 
 | ||||||
|  | #define HID_BT_KEYS_STORAGE_PATH EXT_PATH("apps/Tools/.bt_hid.keys") | ||||||
|  | 
 | ||||||
| typedef enum { | typedef enum { | ||||||
|     HidTransportUsb, |     HidTransportUsb, | ||||||
|     HidTransportBle, |     HidTransportBle, | ||||||
|  | |||||||
| @ -117,6 +117,8 @@ Bt* bt_alloc() { | |||||||
|     if(!bt_settings_load(&bt->bt_settings)) { |     if(!bt_settings_load(&bt->bt_settings)) { | ||||||
|         bt_settings_save(&bt->bt_settings); |         bt_settings_save(&bt->bt_settings); | ||||||
|     } |     } | ||||||
|  |     // Keys storage
 | ||||||
|  |     bt->keys_storage = bt_keys_storage_alloc(BT_KEYS_STORAGE_PATH); | ||||||
|     // Alloc queue
 |     // Alloc queue
 | ||||||
|     bt->message_queue = furi_message_queue_alloc(8, sizeof(BtMessage)); |     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) { | static void bt_on_key_storage_change_callback(uint8_t* addr, uint16_t size, void* context) { | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
|     Bt* bt = context; |     Bt* bt = context; | ||||||
|     FURI_LOG_I(TAG, "Changed addr start: %p, size changed: %d", addr, size); |     BtMessage message = { | ||||||
|     BtMessage message = {.type = BtMessageTypeKeysStorageUpdated}; |         .type = BtMessageTypeKeysStorageUpdated, | ||||||
|  |         .data.key_storage_data.start_address = addr, | ||||||
|  |         .data.key_storage_data.size = size}; | ||||||
|     furi_check( |     furi_check( | ||||||
|         furi_message_queue_put(bt->message_queue, &message, FuriWaitForever) == FuriStatusOk); |         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; |             furi_profile = FuriHalBtProfileSerial; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         bt_keys_storage_load(bt->keys_storage); | ||||||
|  | 
 | ||||||
|         if(furi_hal_bt_change_app(furi_profile, bt_on_gap_event_callback, bt)) { |         if(furi_hal_bt_change_app(furi_profile, bt_on_gap_event_callback, bt)) { | ||||||
|             FURI_LOG_I(TAG, "Bt App started"); |             FURI_LOG_I(TAG, "Bt App started"); | ||||||
|             if(bt->bt_settings.enabled) { |             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) { | static void bt_close_connection(Bt* bt) { | ||||||
|     bt_close_rpc_connection(bt); |     bt_close_rpc_connection(bt); | ||||||
|  |     furi_hal_bt_stop_advertising(); | ||||||
|     furi_event_flag_set(bt->api_event, BT_API_UNLOCK_EVENT); |     furi_event_flag_set(bt->api_event, BT_API_UNLOCK_EVENT); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -372,8 +379,8 @@ int32_t bt_srv(void* p) { | |||||||
|         return 0; |         return 0; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // Read keys
 |     // Load keys
 | ||||||
|     if(!bt_keys_storage_load(bt)) { |     if(!bt_keys_storage_load(bt->keys_storage)) { | ||||||
|         FURI_LOG_W(TAG, "Failed to load bonding keys"); |         FURI_LOG_W(TAG, "Failed to load bonding keys"); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -418,13 +425,16 @@ int32_t bt_srv(void* p) { | |||||||
|             // Display PIN code
 |             // Display PIN code
 | ||||||
|             bt_pin_code_show(bt, message.data.pin_code); |             bt_pin_code_show(bt, message.data.pin_code); | ||||||
|         } else if(message.type == BtMessageTypeKeysStorageUpdated) { |         } 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) { |         } else if(message.type == BtMessageTypeSetProfile) { | ||||||
|             bt_change_profile(bt, &message); |             bt_change_profile(bt, &message); | ||||||
|         } else if(message.type == BtMessageTypeDisconnect) { |         } else if(message.type == BtMessageTypeDisconnect) { | ||||||
|             bt_close_connection(bt); |             bt_close_connection(bt); | ||||||
|         } else if(message.type == BtMessageTypeForgetBondedDevices) { |         } else if(message.type == BtMessageTypeForgetBondedDevices) { | ||||||
|             bt_keys_storage_delete(bt); |             bt_keys_storage_delete(bt->keys_storage); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     return 0; |     return 0; | ||||||
|  | |||||||
| @ -56,6 +56,19 @@ void bt_set_status_changed_callback(Bt* bt, BtStatusChangedCallback callback, vo | |||||||
|  */ |  */ | ||||||
| void bt_forget_bonded_devices(Bt* bt); | 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 | #ifdef __cplusplus | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
|  | |||||||
| @ -39,3 +39,18 @@ void bt_forget_bonded_devices(Bt* bt) { | |||||||
|     furi_check( |     furi_check( | ||||||
|         furi_message_queue_put(bt->message_queue, &message, FuriWaitForever) == FuriStatusOk); |         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 <power/power_service/power.h> | ||||||
| #include <rpc/rpc.h> | #include <rpc/rpc.h> | ||||||
| #include <notification/notification.h> | #include <notification/notification.h> | ||||||
|  | #include <storage/storage.h> | ||||||
| 
 | 
 | ||||||
| #include <bt/bt_settings.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) | #define BT_API_UNLOCK_EVENT (1UL << 0) | ||||||
| 
 | 
 | ||||||
| @ -29,10 +35,16 @@ typedef enum { | |||||||
|     BtMessageTypeForgetBondedDevices, |     BtMessageTypeForgetBondedDevices, | ||||||
| } BtMessageType; | } BtMessageType; | ||||||
| 
 | 
 | ||||||
|  | typedef struct { | ||||||
|  |     uint8_t* start_address; | ||||||
|  |     uint16_t size; | ||||||
|  | } BtKeyStorageUpdateData; | ||||||
|  | 
 | ||||||
| typedef union { | typedef union { | ||||||
|     uint32_t pin_code; |     uint32_t pin_code; | ||||||
|     uint8_t battery_level; |     uint8_t battery_level; | ||||||
|     BtProfile profile; |     BtProfile profile; | ||||||
|  |     BtKeyStorageUpdateData key_storage_data; | ||||||
| } BtMessageData; | } BtMessageData; | ||||||
| 
 | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
| @ -46,6 +58,7 @@ struct Bt { | |||||||
|     uint16_t bt_keys_size; |     uint16_t bt_keys_size; | ||||||
|     uint16_t max_packet_size; |     uint16_t max_packet_size; | ||||||
|     BtSettings bt_settings; |     BtSettings bt_settings; | ||||||
|  |     BtKeysStorage* keys_storage; | ||||||
|     BtStatus status; |     BtStatus status; | ||||||
|     BtProfile profile; |     BtProfile profile; | ||||||
|     FuriMessageQueue* message_queue; |     FuriMessageQueue* message_queue; | ||||||
|  | |||||||
| @ -1,49 +1,24 @@ | |||||||
| #include "bt_keys_storage.h" | #include "bt_keys_storage.h" | ||||||
| 
 | 
 | ||||||
| #include <furi.h> | #include <furi.h> | ||||||
|  | #include <furi_hal_bt.h> | ||||||
| #include <lib/toolbox/saved_struct.h> | #include <lib/toolbox/saved_struct.h> | ||||||
| #include <storage/storage.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_VERSION (0) | ||||||
| #define BT_KEYS_STORAGE_MAGIC (0x18) | #define BT_KEYS_STORAGE_MAGIC (0x18) | ||||||
| 
 | 
 | ||||||
| bool bt_keys_storage_load(Bt* bt) { | #define TAG "BtKeyStorage" | ||||||
|     furi_assert(bt); |  | ||||||
|     bool file_loaded = false; |  | ||||||
| 
 | 
 | ||||||
|     furi_hal_bt_get_key_storage_buff(&bt->bt_keys_addr_start, &bt->bt_keys_size); | struct BtKeysStorage { | ||||||
|     furi_hal_bt_nvm_sram_sem_acquire(); |     uint8_t* nvm_sram_buff; | ||||||
|     file_loaded = saved_struct_load( |     uint16_t nvm_sram_buff_size; | ||||||
|         BT_KEYS_STORAGE_PATH, |     FuriString* file_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_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 delete_succeed = false; | ||||||
|     bool bt_is_active = furi_hal_bt_is_active(); |     bool bt_is_active = furi_hal_bt_is_active(); | ||||||
| 
 | 
 | ||||||
| @ -55,3 +30,117 @@ bool bt_keys_storage_delete(Bt* bt) { | |||||||
| 
 | 
 | ||||||
|     return delete_succeed; |     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 | #pragma once | ||||||
| 
 | 
 | ||||||
| #include "bt_i.h" | #include <stdint.h> | ||||||
| #include "bt_keys_filename.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); | ||||||
|  | |||||||
| @ -1,5 +1,5 @@ | |||||||
| entry,status,name,type,params | entry,status,name,type,params | ||||||
| Version,+,11.1,, | Version,+,11.2,, | ||||||
| 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,, | ||||||
| @ -562,6 +562,8 @@ Function,+,ble_glue_wait_for_c2_start,_Bool,int32_t | |||||||
| Function,-,bsearch,void*,"const void*, const void*, size_t, size_t, __compar_fn_t" | Function,-,bsearch,void*,"const void*, const void*, size_t, size_t, __compar_fn_t" | ||||||
| Function,+,bt_disconnect,void,Bt* | Function,+,bt_disconnect,void,Bt* | ||||||
| Function,+,bt_forget_bonded_devices,void,Bt* | Function,+,bt_forget_bonded_devices,void,Bt* | ||||||
|  | Function,+,bt_keys_storage_set_storage_path,void,"Bt*, const char*" | ||||||
|  | Function,+,bt_keys_storage_set_default_path,void,Bt* | ||||||
| Function,+,bt_set_profile,_Bool,"Bt*, BtProfile" | Function,+,bt_set_profile,_Bool,"Bt*, BtProfile" | ||||||
| Function,+,bt_set_status_changed_callback,void,"Bt*, BtStatusChangedCallback, void*" | Function,+,bt_set_status_changed_callback,void,"Bt*, BtStatusChangedCallback, void*" | ||||||
| Function,+,buffered_file_stream_alloc,Stream*,Storage* | Function,+,buffered_file_stream_alloc,Stream*,Storage* | ||||||
| @ -2321,6 +2323,7 @@ Function,+,rpc_system_app_set_data_exchange_callback,void,"RpcAppSystem*, RpcApp | |||||||
| Function,+,rpc_system_app_set_error_code,void,"RpcAppSystem*, uint32_t" | Function,+,rpc_system_app_set_error_code,void,"RpcAppSystem*, uint32_t" | ||||||
| Function,+,rpc_system_app_set_error_text,void,"RpcAppSystem*, const char*" | Function,+,rpc_system_app_set_error_text,void,"RpcAppSystem*, const char*" | ||||||
| Function,-,rpmatch,int,const char* | Function,-,rpmatch,int,const char* | ||||||
|  | Function,+,saved_struct_get_payload_size,_Bool,"const char*, uint8_t, uint8_t, size_t*" | ||||||
| Function,+,saved_struct_load,_Bool,"const char*, void*, size_t, uint8_t, uint8_t" | Function,+,saved_struct_load,_Bool,"const char*, void*, size_t, uint8_t, uint8_t" | ||||||
| Function,+,saved_struct_save,_Bool,"const char*, void*, size_t, uint8_t, uint8_t" | Function,+,saved_struct_save,_Bool,"const char*, void*, size_t, uint8_t, uint8_t" | ||||||
| Function,-,scalbln,double,"double, long int" | Function,-,scalbln,double,"double, long int" | ||||||
|  | |||||||
| 
 | 
| @ -125,3 +125,54 @@ bool saved_struct_load(const char* path, void* data, size_t size, uint8_t magic, | |||||||
| 
 | 
 | ||||||
|     return result; |     return result; | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | bool saved_struct_get_payload_size( | ||||||
|  |     const char* path, | ||||||
|  |     uint8_t magic, | ||||||
|  |     uint8_t version, | ||||||
|  |     size_t* payload_size) { | ||||||
|  |     furi_assert(path); | ||||||
|  |     furi_assert(payload_size); | ||||||
|  | 
 | ||||||
|  |     SavedStructHeader header; | ||||||
|  |     Storage* storage = furi_record_open(RECORD_STORAGE); | ||||||
|  |     File* file = storage_file_alloc(storage); | ||||||
|  | 
 | ||||||
|  |     bool result = false; | ||||||
|  |     do { | ||||||
|  |         if(!storage_file_open(file, path, FSAM_READ, FSOM_OPEN_EXISTING)) { | ||||||
|  |             FURI_LOG_E( | ||||||
|  |                 TAG, "Failed to read \"%s\". Error: %s", path, storage_file_get_error_desc(file)); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         uint16_t bytes_count = storage_file_read(file, &header, sizeof(SavedStructHeader)); | ||||||
|  |         if(bytes_count != sizeof(SavedStructHeader)) { | ||||||
|  |             FURI_LOG_E(TAG, "Failed to read header"); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if((header.magic != magic) || (header.version != version)) { | ||||||
|  |             FURI_LOG_E( | ||||||
|  |                 TAG, | ||||||
|  |                 "Magic(%d != %d) or Version(%d != %d) mismatch of file \"%s\"", | ||||||
|  |                 header.magic, | ||||||
|  |                 magic, | ||||||
|  |                 header.version, | ||||||
|  |                 version, | ||||||
|  |                 path); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         uint64_t file_size = storage_file_size(file); | ||||||
|  |         *payload_size = file_size - sizeof(SavedStructHeader); | ||||||
|  | 
 | ||||||
|  |         result = true; | ||||||
|  |     } while(false); | ||||||
|  | 
 | ||||||
|  |     storage_file_close(file); | ||||||
|  |     storage_file_free(file); | ||||||
|  |     furi_record_close(RECORD_STORAGE); | ||||||
|  | 
 | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | |||||||
| @ -12,6 +12,12 @@ bool saved_struct_load(const char* path, void* data, size_t size, uint8_t magic, | |||||||
| 
 | 
 | ||||||
| bool saved_struct_save(const char* path, void* data, size_t size, uint8_t magic, uint8_t version); | bool saved_struct_save(const char* path, void* data, size_t size, uint8_t magic, uint8_t version); | ||||||
| 
 | 
 | ||||||
|  | bool saved_struct_get_payload_size( | ||||||
|  |     const char* path, | ||||||
|  |     uint8_t magic, | ||||||
|  |     uint8_t version, | ||||||
|  |     size_t* payload_size); | ||||||
|  | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 gornekich
						gornekich