4
4
5
5
#include <bt/bt_service/bt_keys_storage.h>
6
6
#include <storage/storage.h>
7
+ #include <toolbox/saved_struct.h>
7
8
8
9
#define BT_TEST_KEY_STORAGE_FILE_PATH EXT_PATH("unit_tests/bt_test.keys")
10
+ #define BT_TEST_MIGRATION_FILE_PATH EXT_PATH("unit_tests/bt_migration_test.keys")
9
11
#define BT_TEST_NVM_RAM_BUFF_SIZE (507 * 4) // The same as in ble NVM storage
10
12
13
+ // Identity root key
14
+ static const uint8_t gap_legacy_irk [16 ] =
15
+ {0x12 , 0x34 , 0x56 , 0x78 , 0x9a , 0xbc , 0xde , 0xf0 , 0x12 , 0x34 , 0x56 , 0x78 , 0x9a , 0xbc , 0xde , 0xf0 };
16
+ // Encryption root key
17
+ static const uint8_t gap_legacy_erk [16 ] =
18
+ {0xfe , 0xdc , 0xba , 0x09 , 0x87 , 0x65 , 0x43 , 0x21 , 0xfe , 0xdc , 0xba , 0x09 , 0x87 , 0x65 , 0x43 , 0x21 };
19
+
20
+ // Test constants for migration (matching bt_keys_storage.c)
21
+ #define BT_KEYS_STORAGE_MAGIC_TEST (0x18)
22
+ #define BT_KEYS_STORAGE_VERSION_1_TEST (1)
23
+
11
24
typedef struct {
12
25
Storage * storage ;
13
26
BtKeysStorage * bt_keys_storage ;
@@ -88,6 +101,134 @@ static void bt_test_keys_remove_test_file(void) {
88
101
"Can't remove test file" );
89
102
}
90
103
104
+ // Helper function to create a version 0 file manually
105
+ static bool
106
+ bt_test_create_v0_file (const char * file_path , const uint8_t * nvm_data , size_t nvm_size ) {
107
+ // Version 0 files use saved_struct format with magic 0x18, version 0, containing only BLE pairing data
108
+ return saved_struct_save (
109
+ file_path ,
110
+ nvm_data ,
111
+ nvm_size ,
112
+ BT_KEYS_STORAGE_MAGIC_TEST ,
113
+ 0 ); // Version 0
114
+ }
115
+
116
+ // Helper function to verify file format version
117
+ static bool bt_test_verify_file_version (const char * file_path , uint32_t expected_version ) {
118
+ uint8_t magic , version ;
119
+ size_t size ;
120
+
121
+ if (!saved_struct_get_metadata (file_path , & magic , & version , & size )) {
122
+ return false;
123
+ }
124
+
125
+ return (magic == BT_KEYS_STORAGE_MAGIC_TEST && version == expected_version );
126
+ }
127
+
128
+ // Test migration from version 0 to version 1, including root key preservation
129
+ static void bt_test_migration_v0_to_v1 (void ) {
130
+ // Create test NVM data
131
+ const size_t test_nvm_size = 100 ;
132
+ uint8_t test_nvm_data [test_nvm_size ];
133
+ for (size_t i = 0 ; i < test_nvm_size ; i ++ ) {
134
+ test_nvm_data [i ] = (uint8_t )(i & 0xFF );
135
+ }
136
+
137
+ // Create a version 0 file
138
+ mu_assert (
139
+ bt_test_create_v0_file (BT_TEST_MIGRATION_FILE_PATH , test_nvm_data , test_nvm_size ),
140
+ "Failed to create version 0 test file" );
141
+
142
+ // Create BT keys storage and load the v0 file (should trigger migration)
143
+ BtKeysStorage * migration_storage = bt_keys_storage_alloc (BT_TEST_MIGRATION_FILE_PATH );
144
+ uint8_t loaded_buffer [BT_TEST_NVM_RAM_BUFF_SIZE ];
145
+ memset (loaded_buffer , 0 , sizeof (loaded_buffer ));
146
+ bt_keys_storage_set_ram_params (migration_storage , loaded_buffer , sizeof (loaded_buffer ));
147
+
148
+ // Load should succeed and migrate v0 to v1
149
+ mu_assert (bt_keys_storage_load (migration_storage ), "Failed to load and migrate v0 file" );
150
+
151
+ // Verify the file is now version 1
152
+ mu_assert (
153
+ bt_test_verify_file_version (BT_TEST_MIGRATION_FILE_PATH , BT_KEYS_STORAGE_VERSION_1_TEST ),
154
+ "File was not migrated to version 1" );
155
+
156
+ // Verify the NVM data was preserved during migration
157
+ mu_assert (
158
+ memcmp (test_nvm_data , loaded_buffer , test_nvm_size ) == 0 ,
159
+ "NVM data was corrupted during migration" );
160
+
161
+ // Verify that legacy root keys are used after migration
162
+ const GapRootSecurityKeys * migrated_keys = bt_keys_storage_get_root_keys (migration_storage );
163
+ mu_assert (
164
+ memcmp (migrated_keys -> irk , gap_legacy_irk , sizeof (gap_legacy_irk )) == 0 ,
165
+ "IRK not set to legacy after migration" );
166
+ mu_assert (
167
+ memcmp (migrated_keys -> erk , gap_legacy_erk , sizeof (gap_legacy_erk )) == 0 ,
168
+ "ERK not set to legacy after migration" );
169
+
170
+ bt_keys_storage_free (migration_storage );
171
+ storage_simply_remove (bt_test -> storage , BT_TEST_MIGRATION_FILE_PATH );
172
+ }
173
+
174
+ // Test that migration preserves existing pairing data and root keys are not changed on reload
175
+ static void bt_test_migration_preserves_pairings_and_keys (void ) {
176
+ const size_t pairing_data_size = 200 ;
177
+ uint8_t pairing_data [pairing_data_size ];
178
+ for (size_t i = 0 ; i < pairing_data_size ; i ++ ) {
179
+ pairing_data [i ] = (uint8_t )((i * 7 + 42 ) & 0xFF );
180
+ }
181
+ mu_assert (
182
+ bt_test_create_v0_file (BT_TEST_MIGRATION_FILE_PATH , pairing_data , pairing_data_size ),
183
+ "Failed to create v0 file with pairing data" );
184
+
185
+ GapRootSecurityKeys keys_after_first_load ;
186
+ for (int iteration = 0 ; iteration < 2 ; iteration ++ ) {
187
+ BtKeysStorage * storage = bt_keys_storage_alloc (BT_TEST_MIGRATION_FILE_PATH );
188
+ uint8_t buffer [BT_TEST_NVM_RAM_BUFF_SIZE ];
189
+ memset (buffer , 0 , sizeof (buffer ));
190
+ bt_keys_storage_set_ram_params (storage , buffer , sizeof (buffer ));
191
+ mu_assert (bt_keys_storage_load (storage ), "Failed to load on iteration" );
192
+ mu_assert (
193
+ memcmp (pairing_data , buffer , pairing_data_size ) == 0 ,
194
+ "Pairing data corrupted on iteration" );
195
+ const GapRootSecurityKeys * keys = bt_keys_storage_get_root_keys (storage );
196
+ if (iteration == 0 )
197
+ memcpy (& keys_after_first_load , keys , sizeof (GapRootSecurityKeys ));
198
+ else
199
+ mu_assert (
200
+ memcmp (& keys_after_first_load , keys , sizeof (GapRootSecurityKeys )) == 0 ,
201
+ "Root keys changed after reload" );
202
+ bt_keys_storage_free (storage );
203
+ }
204
+ storage_simply_remove (bt_test -> storage , BT_TEST_MIGRATION_FILE_PATH );
205
+ }
206
+
207
+ // Test that delete operation generates new secure keys in v1 and does not match legacy
208
+ static void bt_test_delete_generates_new_keys_and_not_legacy (void ) {
209
+ BtKeysStorage * storage = bt_keys_storage_alloc (BT_TEST_MIGRATION_FILE_PATH );
210
+ uint8_t buffer [BT_TEST_NVM_RAM_BUFF_SIZE ];
211
+ memset (buffer , 0x55 , sizeof (buffer ));
212
+ bt_keys_storage_set_ram_params (storage , buffer , sizeof (buffer ));
213
+ mu_assert (bt_keys_storage_update (storage , buffer , 100 ), "Failed to create initial v1 file" );
214
+ const GapRootSecurityKeys * original_keys = bt_keys_storage_get_root_keys (storage );
215
+ uint8_t original_keys_copy [sizeof (GapRootSecurityKeys )];
216
+ memcpy (original_keys_copy , original_keys , sizeof (original_keys_copy ));
217
+ bt_keys_storage_delete (storage );
218
+ const GapRootSecurityKeys * new_keys = bt_keys_storage_get_root_keys (storage );
219
+ mu_assert (
220
+ memcmp (original_keys_copy , new_keys , sizeof (original_keys_copy )) != 0 ,
221
+ "Root keys were not regenerated after delete" );
222
+ mu_assert (
223
+ memcmp (new_keys -> irk , gap_legacy_irk , sizeof (gap_legacy_irk )) != 0 ,
224
+ "IRK after delete should not match legacy" );
225
+ mu_assert (
226
+ memcmp (new_keys -> erk , gap_legacy_erk , sizeof (gap_legacy_erk )) != 0 ,
227
+ "ERK after delete should not match legacy" );
228
+ bt_keys_storage_free (storage );
229
+ storage_simply_remove (bt_test -> storage , BT_TEST_MIGRATION_FILE_PATH );
230
+ }
231
+
91
232
MU_TEST (bt_test_keys_storage_serial_profile ) {
92
233
furi_check (bt_test );
93
234
@@ -96,10 +237,28 @@ MU_TEST(bt_test_keys_storage_serial_profile) {
96
237
bt_test_keys_remove_test_file ();
97
238
}
98
239
240
+ MU_TEST (bt_test_migration_v0_to_v1_test ) {
241
+ furi_check (bt_test );
242
+ bt_test_migration_v0_to_v1 ();
243
+ }
244
+
245
+ MU_TEST (bt_test_migration_preserves_pairings_and_keys_test ) {
246
+ furi_check (bt_test );
247
+ bt_test_migration_preserves_pairings_and_keys ();
248
+ }
249
+
250
+ MU_TEST (bt_test_delete_generates_new_keys_and_not_legacy_test ) {
251
+ furi_check (bt_test );
252
+ bt_test_delete_generates_new_keys_and_not_legacy ();
253
+ }
254
+
99
255
MU_TEST_SUITE (test_bt ) {
100
256
bt_test_alloc ();
101
257
102
258
MU_RUN_TEST (bt_test_keys_storage_serial_profile );
259
+ MU_RUN_TEST (bt_test_migration_v0_to_v1_test );
260
+ MU_RUN_TEST (bt_test_migration_preserves_pairings_and_keys_test );
261
+ MU_RUN_TEST (bt_test_delete_generates_new_keys_and_not_legacy_test );
103
262
104
263
bt_test_free ();
105
264
}
0 commit comments