1 /*
2 * Copyright (c) 2018-2022, Arm Limited. All rights reserved.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 *
6 */
7
8 #include "ps_object_table.h"
9
10 #include <stddef.h>
11 #include <string.h>
12
13 #include "cmsis_compiler.h"
14 #include "config_tfm.h"
15 #include "crypto/ps_crypto_interface.h"
16 #include "nv_counters/ps_nv_counters.h"
17 #include "psa/internal_trusted_storage.h"
18 #include "ps_utils.h"
19 #include "tfm_ps_defs.h"
20
21 /* FIXME: Duplicated from flash info */
22 #define PS_FLASH_DEFAULT_VAL 0xFFU
23
24 /*!
25 * \def PS_OBJECT_SYSTEM_VERSION
26 *
27 * \brief Current object system version.
28 */
29 #define PS_OBJECT_SYSTEM_VERSION 0x01
30
31 /*!
32 * \struct ps_obj_table_info_t
33 *
34 * \brief Object table information structure.
35 */
36 struct ps_obj_table_entry_t {
37 #ifdef PS_ENCRYPTION
38 uint8_t tag[PS_TAG_LEN_BYTES]; /*!< MAC value of AEAD object */
39 #else
40 uint32_t version; /*!< File version */
41 #endif
42 psa_storage_uid_t uid; /*!< Object UID */
43 int32_t client_id; /*!< Client ID */
44 };
45
46 /* Specifies number of entries in the table. The number of entries is the
47 * number of assets, defined in asset_defs.h, plus one extra entry to store
48 * a new object when the code processes a change in a file.
49 */
50 #define PS_OBJ_TABLE_ENTRIES (PS_NUM_ASSETS + 1)
51
52 /*!
53 * \struct ps_obj_table_t
54 *
55 * \brief Object table structure.
56 */
57 struct ps_obj_table_t {
58 #ifdef PS_ENCRYPTION
59 union ps_crypto_t crypto; /*!< Crypto metadata. */
60 #endif
61
62 uint8_t version; /*!< PS object system version. */
63
64 #if (!PS_ROLLBACK_PROTECTION)
65 uint8_t swap_count; /*!< Swap counter to distinguish 2 different
66 * object tables.
67 */
68 #endif /* PS_ROLLBACK_PROTECTION */
69
70 struct ps_obj_table_entry_t obj_db[PS_OBJ_TABLE_ENTRIES]; /*!< Table's
71 * entries
72 */
73 };
74
75 #ifdef PS_ENCRYPTION
76 /* Even tho ps_table_key_label is read only it is left as non constant variable
77 * to ensure that it is protected as part of PS partition data.
78 */
79 static uint8_t ps_table_key_label[] = "table_key_label";
80 #endif
81
82 /* Object table indexes */
83 #define PS_OBJ_TABLE_IDX_0 0
84 #define PS_OBJ_TABLE_IDX_1 1
85
86 /* Number of object tables (active and scratch) */
87 #define PS_NUM_OBJ_TABLES 2
88
89 /*!
90 * \def PS_TABLE_FS_ID
91 *
92 * \brief File ID to be used in order to store the object table in the
93 * file system.
94 *
95 * \param[in] idx Table index to convert into a file ID.
96 *
97 * \return Returns file ID
98 *
99 */
100 #define PS_TABLE_FS_ID(idx) (idx + 1)
101
102 /*!
103 * \def PS_OBJECT_FS_ID
104 *
105 * \brief File ID to be used in order to store an object in the
106 * file system.
107 *
108 * \param[in] idx Object table index to convert into a file ID.
109 *
110 * \return Returns file ID
111 */
112 #define PS_OBJECT_FS_ID(idx) ((idx + 1) + \
113 PS_TABLE_FS_ID(PS_OBJ_TABLE_IDX_1))
114
115 /*!
116 * \def PS_OBJECT_FS_ID_TO_IDX
117 *
118 * \brief Gets object index in the table based on the file ID.
119 *
120 * \param[in] fid File ID of an object in the object table
121 *
122 * \return Returns object table index
123 */
124 #define PS_OBJECT_FS_ID_TO_IDX(fid) ((fid - 1) - \
125 PS_TABLE_FS_ID(PS_OBJ_TABLE_IDX_1))
126
127 /*!
128 * \struct ps_obj_table_ctx_t
129 *
130 * \brief Object table context structure.
131 */
132 struct ps_obj_table_ctx_t {
133 struct ps_obj_table_t obj_table; /*!< Object tables */
134 uint8_t active_table; /*!< Active object table */
135 uint8_t scratch_table; /*!< Scratch object table */
136 };
137
138 /* Object table context */
139 static struct ps_obj_table_ctx_t ps_obj_table_ctx;
140
141 /* Object table size */
142 #define PS_OBJ_TABLE_SIZE sizeof(struct ps_obj_table_t)
143
144 /* Object table entry size */
145 #define PS_OBJECTS_TABLE_ENTRY_SIZE sizeof(struct ps_obj_table_entry_t)
146
147 /* Size of the data that is not required to authenticate */
148 #define PS_NON_AUTH_OBJ_TABLE_SIZE sizeof(union ps_crypto_t)
149
150 /* Start position to store the object table data in the FS object */
151 #define PS_OBJECT_TABLE_OBJECT_OFFSET 0
152
153 /* The associated data is the header minus the crypto data */
154 #define PS_CRYPTO_ASSOCIATED_DATA(crypto) ((uint8_t *)crypto + \
155 PS_NON_AUTH_OBJ_TABLE_SIZE)
156
157 #if PS_ROLLBACK_PROTECTION
158 #define PS_OBJ_TABLE_AUTH_DATA_SIZE (PS_OBJ_TABLE_SIZE - \
159 PS_NON_AUTH_OBJ_TABLE_SIZE)
160
161 struct ps_crypto_assoc_data_t {
162 uint8_t obj_table_data[PS_OBJ_TABLE_AUTH_DATA_SIZE];
163 uint32_t nv_counter;
164 };
165
166 #define PS_CRYPTO_ASSOCIATED_DATA_LEN sizeof(struct ps_crypto_assoc_data_t)
167
168 #else
169
170 /* The associated data is the header, minus the the tag data */
171 #define PS_CRYPTO_ASSOCIATED_DATA_LEN (PS_OBJ_TABLE_SIZE - \
172 PS_NON_AUTH_OBJ_TABLE_SIZE)
173 #endif /* PS_ROLLBACK_PROTECTION */
174
175 /* The ps_object_table_init function uses the static memory allocated for
176 * the object data manipulation, in ps_object_table.c (g_ps_object), to load a
177 * temporary object table to be validated at that stage.
178 * To make sure the object table data fits in the static memory allocated for
179 * object manipulation, the following macro checks if the memory allocated is
180 * big enough, at compile time
181 */
182
183 /* Check at compilation time if metadata fits in g_ps_object.data */
184 PS_UTILS_BOUND_CHECK(OBJ_TABLE_NOT_FIT_IN_STATIC_OBJ_DATA_BUF,
185 PS_OBJ_TABLE_SIZE, PS_MAX_ASSET_SIZE);
186
187 enum ps_obj_table_state {
188 PS_OBJ_TABLE_VALID = 0, /*!< Table content is valid */
189 PS_OBJ_TABLE_INVALID, /*!< Table content is invalid */
190 PS_OBJ_TABLE_NVC_1_VALID, /*!< Table content valid with NVC 1 value */
191 PS_OBJ_TABLE_NVC_3_VALID, /*!< Table content valid with NVC 3 value */
192 };
193
194 /* Specifies that PS NV counter value is invalid */
195 #define PS_INVALID_NVC_VALUE 0
196
197 /*!
198 * \struct ps_obj_table_ctx_t
199 *
200 * \brief Object table init context structure.
201 */
202 struct ps_obj_table_init_ctx_t {
203 struct ps_obj_table_t *p_table[PS_NUM_OBJ_TABLES]; /*!< Pointers to
204 * object tables
205 */
206 enum ps_obj_table_state table_state[PS_NUM_OBJ_TABLES]; /*!< Array to
207 * indicate if
208 * the object
209 * table X is
210 * valid
211 */
212 #if PS_ROLLBACK_PROTECTION
213 uint32_t nvc_1; /*!< Non-volatile counter value 1 */
214 uint32_t nvc_3; /*!< Non-volatile counter value 3 */
215 #endif /* PS_ROLLBACK_PROTECTION */
216 };
217
218 /**
219 * \brief Reads object table from persistent memory.
220 *
221 * \param[out] init_ctx Pointer to the init object table context
222 *
223 */
224 __attribute__ ((always_inline))
ps_object_table_fs_read_table(struct ps_obj_table_init_ctx_t * init_ctx)225 __STATIC_INLINE void ps_object_table_fs_read_table(
226 struct ps_obj_table_init_ctx_t *init_ctx)
227 {
228 psa_status_t err;
229 size_t data_length;
230
231 /* Read file with the table 0 data */
232
233 err = psa_its_get(PS_TABLE_FS_ID(PS_OBJ_TABLE_IDX_0),
234 PS_OBJECT_TABLE_OBJECT_OFFSET,
235 PS_OBJ_TABLE_SIZE,
236 (void *)init_ctx->p_table[PS_OBJ_TABLE_IDX_0],
237 &data_length);
238 if (err != PSA_SUCCESS) {
239 init_ctx->table_state[PS_OBJ_TABLE_IDX_0] = PS_OBJ_TABLE_INVALID;
240 }
241
242 /* Read file with the table 1 data */
243 err = psa_its_get(PS_TABLE_FS_ID(PS_OBJ_TABLE_IDX_1),
244 PS_OBJECT_TABLE_OBJECT_OFFSET,
245 PS_OBJ_TABLE_SIZE,
246 (void *)init_ctx->p_table[PS_OBJ_TABLE_IDX_1],
247 &data_length);
248 if (err != PSA_SUCCESS) {
249 init_ctx->table_state[PS_OBJ_TABLE_IDX_1] = PS_OBJ_TABLE_INVALID;
250 }
251 }
252
253 /**
254 * \brief Writes object table in persistent memory.
255 *
256 * \param[in,out] obj_table Pointer to the object table to generate
257 * authentication
258 *
259 * \return Returns error code as specified in \ref psa_status_t
260 */
261 __attribute__ ((always_inline))
ps_object_table_fs_write_table(struct ps_obj_table_t * obj_table)262 __STATIC_INLINE psa_status_t ps_object_table_fs_write_table(
263 struct ps_obj_table_t *obj_table)
264 {
265 psa_status_t err;
266 uint32_t obj_table_id = PS_TABLE_FS_ID(ps_obj_table_ctx.scratch_table);
267 uint8_t swap_table_idxs = ps_obj_table_ctx.scratch_table;
268
269 /* Create file to store object table in the FS */
270 err = psa_its_set(obj_table_id,
271 PS_OBJ_TABLE_SIZE,
272 (const void *)obj_table,
273 PSA_STORAGE_FLAG_NONE);
274
275 if (err != PSA_SUCCESS) {
276 return err;
277 }
278
279 /* Swap active and scratch table values */
280 ps_obj_table_ctx.scratch_table = ps_obj_table_ctx.active_table;
281 ps_obj_table_ctx.active_table = swap_table_idxs;
282
283 return PSA_SUCCESS;
284 }
285
286 #ifdef PS_ENCRYPTION
287 #if PS_ROLLBACK_PROTECTION
288 /**
289 * \brief Aligns all PS non-volatile counters.
290 *
291 * \param[in] nvc_1 Value of PS non-volatile counter 1
292 *
293 * \return Returns error code as specified in \ref psa_status_t
294 */
ps_object_table_align_nv_counters(uint32_t nvc_1)295 static psa_status_t ps_object_table_align_nv_counters(uint32_t nvc_1)
296 {
297 psa_status_t err;
298 uint32_t nvc_x_val = 0;
299
300 /* Align PS NVC 2 with NVC 1 */
301 err = ps_read_nv_counter(TFM_PS_NV_COUNTER_2, &nvc_x_val);
302 if (err != PSA_SUCCESS) {
303 return PSA_ERROR_GENERIC_ERROR;
304 }
305
306 for (; nvc_x_val < nvc_1; nvc_x_val++) {
307 err = ps_increment_nv_counter(TFM_PS_NV_COUNTER_2);
308 if (err != PSA_SUCCESS) {
309 return err;
310 }
311 }
312
313 /* Align PS NVC 3 with NVC 1 */
314 err = ps_read_nv_counter(TFM_PS_NV_COUNTER_3, &nvc_x_val);
315 if (err != PSA_SUCCESS) {
316 return PSA_ERROR_GENERIC_ERROR;
317 }
318
319 for (; nvc_x_val < nvc_1; nvc_x_val++) {
320 err = ps_increment_nv_counter(TFM_PS_NV_COUNTER_3);
321 if (err != PSA_SUCCESS) {
322 return err;
323 }
324 }
325
326 return PSA_SUCCESS;
327 }
328
329 /**
330 * \brief Generates table authentication tag.
331 *
332 * \param[in] nvc_1 Value of PS non-volatile counter 1
333 * \param[in,out] obj_table Pointer to the object table to generate
334 * authentication
335 *
336 * \return Returns error code as specified in \ref psa_status_t
337 */
338 __attribute__ ((always_inline))
ps_object_table_nvc_generate_auth_tag(uint32_t nvc_1,struct ps_obj_table_t * obj_table)339 __STATIC_INLINE psa_status_t ps_object_table_nvc_generate_auth_tag(
340 uint32_t nvc_1,
341 struct ps_obj_table_t *obj_table)
342 {
343 struct ps_crypto_assoc_data_t assoc_data;
344 union ps_crypto_t *crypto = &obj_table->crypto;
345 psa_status_t err;
346
347 /* Get new IV */
348 err = ps_crypto_get_iv(crypto);
349 if (err != PSA_SUCCESS) {
350 return err;
351 }
352
353 assoc_data.nv_counter = nvc_1;
354 (void)memcpy(assoc_data.obj_table_data,
355 PS_CRYPTO_ASSOCIATED_DATA(crypto),
356 PS_OBJ_TABLE_AUTH_DATA_SIZE);
357
358 return ps_crypto_generate_auth_tag(crypto, (const uint8_t *)&assoc_data,
359 PS_CRYPTO_ASSOCIATED_DATA_LEN);
360 }
361
362 /**
363 * \brief Authenticates table of objects.
364 *
365 * \param[in] table_idx Table index in the init context
366 * \param[in,out] init_ctx Pointer to the object table to authenticate
367 *
368 */
ps_object_table_authenticate(uint8_t table_idx,struct ps_obj_table_init_ctx_t * init_ctx)369 static void ps_object_table_authenticate(uint8_t table_idx,
370 struct ps_obj_table_init_ctx_t *init_ctx)
371 {
372 struct ps_crypto_assoc_data_t assoc_data;
373 union ps_crypto_t *crypto = &init_ctx->p_table[table_idx]->crypto;
374 psa_status_t err;
375
376 /* Init associated data with NVC 1 */
377 assoc_data.nv_counter = init_ctx->nvc_1;
378 (void)memcpy(assoc_data.obj_table_data,
379 PS_CRYPTO_ASSOCIATED_DATA(crypto),
380 PS_OBJ_TABLE_AUTH_DATA_SIZE);
381
382 err = ps_crypto_authenticate(crypto, (const uint8_t *)&assoc_data,
383 PS_CRYPTO_ASSOCIATED_DATA_LEN);
384 if (err == PSA_SUCCESS) {
385 init_ctx->table_state[table_idx] = PS_OBJ_TABLE_NVC_1_VALID;
386 return;
387 }
388
389 if (init_ctx->nvc_3 == PS_INVALID_NVC_VALUE) {
390 init_ctx->table_state[table_idx] = PS_OBJ_TABLE_INVALID;
391 return;
392 }
393
394 /* Check with NVC 3 */
395 assoc_data.nv_counter = init_ctx->nvc_3;
396
397 err = ps_crypto_authenticate(crypto, (const uint8_t *)&assoc_data,
398 PS_CRYPTO_ASSOCIATED_DATA_LEN);
399 if (err != PSA_SUCCESS) {
400 init_ctx->table_state[table_idx] = PS_OBJ_TABLE_INVALID;
401 } else {
402 init_ctx->table_state[table_idx] = PS_OBJ_TABLE_NVC_3_VALID;
403 }
404 }
405
406 /**
407 * \brief Authenticates tables of objects.
408 *
409 * \param[in,out] init_ctx Pointer to the object table to authenticate
410 *
411 * \return Returns error code as specified in \ref psa_status_t
412 */
413 __attribute__ ((always_inline))
ps_object_table_nvc_authenticate(struct ps_obj_table_init_ctx_t * init_ctx)414 __STATIC_INLINE psa_status_t ps_object_table_nvc_authenticate(
415 struct ps_obj_table_init_ctx_t *init_ctx)
416 {
417 psa_status_t err;
418 uint32_t nvc_2;
419
420 err = ps_read_nv_counter(TFM_PS_NV_COUNTER_1, &init_ctx->nvc_1);
421 if (err != PSA_SUCCESS) {
422 return err;
423 }
424
425 err = ps_read_nv_counter(TFM_PS_NV_COUNTER_2, &nvc_2);
426 if (err != PSA_SUCCESS) {
427 return err;
428 }
429
430 err = ps_read_nv_counter(TFM_PS_NV_COUNTER_3, &init_ctx->nvc_3);
431 if (err != PSA_SUCCESS) {
432 return err;
433 }
434
435 /* Check if NVC 3 value can be used to validate an object table */
436 if (init_ctx->nvc_3 != nvc_2) {
437 /* If NVC 3 is different from NVC 2, it is possible to load an old PS
438 * area image in the system by manipulating the FS to return a system
439 * error from the file system layer and triggering power fault before
440 * increasing the NVC 3. So, in that case, NVC 3 value cannot be used to
441 * validate an old object table at the init process.
442 */
443 init_ctx->nvc_3 = PS_INVALID_NVC_VALUE;
444 }
445
446 /* Authenticate table 0 if data is valid */
447 if (init_ctx->table_state[PS_OBJ_TABLE_IDX_0] != PS_OBJ_TABLE_INVALID) {
448 ps_object_table_authenticate(PS_OBJ_TABLE_IDX_0, init_ctx);
449 }
450
451 /* Authenticate table 1 if data is valid */
452 if (init_ctx->table_state[PS_OBJ_TABLE_IDX_1] != PS_OBJ_TABLE_INVALID) {
453 ps_object_table_authenticate(PS_OBJ_TABLE_IDX_1, init_ctx);
454 }
455
456 return PSA_SUCCESS;
457 }
458 #else /* PS_ROLLBACK_PROTECTION */
459
460 /**
461 * \brief Generates table authentication
462 *
463 * \param[in,out] obj_table Pointer to the object table to generate
464 * authentication
465 *
466 * \return Returns error code as specified in \ref psa_status_t
467 */
468 __attribute__ ((always_inline))
ps_object_table_generate_auth_tag(struct ps_obj_table_t * obj_table)469 __STATIC_INLINE psa_status_t ps_object_table_generate_auth_tag(
470 struct ps_obj_table_t *obj_table)
471 {
472 union ps_crypto_t *crypto = &obj_table->crypto;
473 psa_status_t err;
474
475 /* Get new IV */
476 err = ps_crypto_get_iv(crypto);
477 if (err != PSA_SUCCESS) {
478 return err;
479 }
480
481 return ps_crypto_generate_auth_tag(crypto,
482 PS_CRYPTO_ASSOCIATED_DATA(crypto),
483 PS_CRYPTO_ASSOCIATED_DATA_LEN);
484 }
485
486 /**
487 * \brief Authenticates tables of objects.
488 *
489 * \param[in,out] init_ctx Pointer to the object table to authenticate
490 *
491 */
492 __attribute__ ((always_inline))
ps_object_table_authenticate_ctx_tables(struct ps_obj_table_init_ctx_t * init_ctx)493 __STATIC_INLINE void ps_object_table_authenticate_ctx_tables(
494 struct ps_obj_table_init_ctx_t *init_ctx)
495 {
496 psa_status_t err;
497 union ps_crypto_t *crypto =
498 &init_ctx->p_table[PS_OBJ_TABLE_IDX_0]->crypto;
499
500 /* Authenticate table 0 if data is valid */
501 if (init_ctx->table_state[PS_OBJ_TABLE_IDX_0] != PS_OBJ_TABLE_INVALID) {
502 err = ps_crypto_authenticate(crypto,
503 PS_CRYPTO_ASSOCIATED_DATA(crypto),
504 PS_CRYPTO_ASSOCIATED_DATA_LEN);
505 if (err != PSA_SUCCESS) {
506 init_ctx->table_state[PS_OBJ_TABLE_IDX_0] = PS_OBJ_TABLE_INVALID;
507 }
508 }
509
510 /* Authenticate table 1 if data is valid */
511 if (init_ctx->table_state[PS_OBJ_TABLE_IDX_1] != PS_OBJ_TABLE_INVALID) {
512 crypto = &init_ctx->p_table[PS_OBJ_TABLE_IDX_1]->crypto;
513
514 err = ps_crypto_authenticate(crypto,
515 PS_CRYPTO_ASSOCIATED_DATA(crypto),
516 PS_CRYPTO_ASSOCIATED_DATA_LEN);
517 if (err != PSA_SUCCESS) {
518 init_ctx->table_state[PS_OBJ_TABLE_IDX_1] = PS_OBJ_TABLE_INVALID;
519 }
520 }
521 }
522 #endif /* PS_ROLLBACK_PROTECTION */
523 #endif /* PS_ENCRYPTION */
524
525 /**
526 * \brief Saves object table in the persistent memory.
527 *
528 * \param[in,out] obj_table Pointer to the object table to save
529 *
530 * \return Returns error code as specified in \ref psa_status_t
531 */
ps_object_table_save_table(struct ps_obj_table_t * obj_table)532 static psa_status_t ps_object_table_save_table(
533 struct ps_obj_table_t *obj_table)
534 {
535 psa_status_t err;
536
537 #if PS_ROLLBACK_PROTECTION
538 uint32_t nvc_1 = 0;
539
540 err = ps_increment_nv_counter(TFM_PS_NV_COUNTER_1);
541 if (err != PSA_SUCCESS) {
542 return err;
543 }
544
545 err = ps_read_nv_counter(TFM_PS_NV_COUNTER_1, &nvc_1);
546 if (err != PSA_SUCCESS) {
547 return err;
548 }
549 #else
550 obj_table->swap_count++;
551
552 if (obj_table->swap_count == PS_FLASH_DEFAULT_VAL) {
553 /* When a flash block is erased, the default value is usually 0xFF
554 * (i.e. all 1s). Since the swap count is updated last (when encryption
555 * is disabled), it is possible that due to a power failure, the swap
556 * count value in metadata header is 0xFFFF..., which mean it will
557 * appear to be most recent block.
558 */
559 obj_table->swap_count = 0;
560 }
561 #endif /* PS_ROLLBACK_PROTECTION */
562
563 #ifdef PS_ENCRYPTION
564 /* Set object table key */
565 err = ps_crypto_setkey(ps_table_key_label, sizeof(ps_table_key_label));
566 if (err != PSA_SUCCESS) {
567 return err;
568 }
569
570 #if PS_ROLLBACK_PROTECTION
571 /* Generate authentication tag from the current table content and PS
572 * NV counter 1.
573 */
574 err = ps_object_table_nvc_generate_auth_tag(nvc_1, obj_table);
575 #else
576 /* Generate authentication tag from the current table content */
577 err = ps_object_table_generate_auth_tag(obj_table);
578 #endif /* PS_ROLLBACK_PROTECTION */
579
580 if (err != PSA_SUCCESS) {
581 (void)ps_crypto_destroykey();
582 return err;
583 }
584
585 err = ps_crypto_destroykey();
586 if (err != PSA_SUCCESS) {
587 return err;
588 }
589 #endif /* PS_ENCRYPTION */
590
591 err = ps_object_table_fs_write_table(obj_table);
592
593 #if PS_ROLLBACK_PROTECTION
594 if (err != PSA_SUCCESS) {
595 return err;
596 }
597
598 /* Align PS NV counters to have the same value */
599 err = ps_object_table_align_nv_counters(nvc_1);
600 #endif /* PS_ROLLBACK_PROTECTION */
601
602 return err;
603 }
604
605 /**
606 * \brief Checks the validity of the table version.
607 *
608 * \param[in,out] init_ctx Pointer to the init object table context
609 *
610 */
611 __attribute__ ((always_inline))
ps_object_table_validate_version(struct ps_obj_table_init_ctx_t * init_ctx)612 __STATIC_INLINE void ps_object_table_validate_version(
613 struct ps_obj_table_init_ctx_t *init_ctx)
614 {
615 /* Looks for exact version number.
616 * FIXME: backward compatibility could be considered in future revisions.
617 */
618 if (PS_OBJECT_SYSTEM_VERSION !=
619 init_ctx->p_table[PS_OBJ_TABLE_IDX_0]->version) {
620 init_ctx->table_state[PS_OBJ_TABLE_IDX_0] = PS_OBJ_TABLE_INVALID;
621 }
622
623 if (PS_OBJECT_SYSTEM_VERSION !=
624 init_ctx->p_table[PS_OBJ_TABLE_IDX_1]->version) {
625 init_ctx->table_state[PS_OBJ_TABLE_IDX_1] = PS_OBJ_TABLE_INVALID;
626 }
627 }
628
629 /**
630 * \brief Sets the active object table based on the swap count and validity of
631 * the object table data.
632 *
633 * \param[in] init_ctx Pointer to the init object table context
634 *
635 * \return Returns error code as specified in \ref psa_status_t
636 */
ps_set_active_object_table(const struct ps_obj_table_init_ctx_t * init_ctx)637 static psa_status_t ps_set_active_object_table(
638 const struct ps_obj_table_init_ctx_t *init_ctx)
639 {
640 #if (!PS_ROLLBACK_PROTECTION)
641 uint8_t table0_swap_count =
642 init_ctx->p_table[PS_OBJ_TABLE_IDX_0]->swap_count;
643 uint8_t table1_swap_count =
644 init_ctx->p_table[PS_OBJ_TABLE_IDX_1]->swap_count;
645 #endif
646
647 /* Check if there is an invalid object table */
648 if ((init_ctx->table_state[PS_OBJ_TABLE_IDX_0] == PS_OBJ_TABLE_INVALID)
649 && (init_ctx->table_state[PS_OBJ_TABLE_IDX_1] ==
650 PS_OBJ_TABLE_INVALID)) {
651 /* Both tables are invalid */
652 return PSA_ERROR_GENERIC_ERROR;
653 } else if (init_ctx->table_state[PS_OBJ_TABLE_IDX_0] ==
654 PS_OBJ_TABLE_INVALID) {
655 /* Table 0 is invalid, the active one is table 1 */
656 ps_obj_table_ctx.active_table = PS_OBJ_TABLE_IDX_1;
657 ps_obj_table_ctx.scratch_table = PS_OBJ_TABLE_IDX_0;
658
659 /* As table 1 is the active object, load the content into the
660 * PS object table context.
661 */
662 (void)memcpy(&ps_obj_table_ctx.obj_table,
663 init_ctx->p_table[PS_OBJ_TABLE_IDX_1],
664 PS_OBJ_TABLE_SIZE);
665
666 return PSA_SUCCESS;
667 } else if (init_ctx->table_state[PS_OBJ_TABLE_IDX_1] ==
668 PS_OBJ_TABLE_INVALID) {
669 /* Table 1 is invalid, the active one is table 0 */
670 ps_obj_table_ctx.active_table = PS_OBJ_TABLE_IDX_0;
671 ps_obj_table_ctx.scratch_table = PS_OBJ_TABLE_IDX_1;
672
673 /* As table 0 is already in the PS object table context, it is not
674 * needed to copy the table in the context.
675 */
676
677 return PSA_SUCCESS;
678 }
679
680 #if PS_ROLLBACK_PROTECTION
681 if (init_ctx->table_state[PS_OBJ_TABLE_IDX_1] ==
682 PS_OBJ_TABLE_NVC_1_VALID) {
683 /* Table 0 is invalid, the active one is table 1 */
684 ps_obj_table_ctx.active_table = PS_OBJ_TABLE_IDX_1;
685 ps_obj_table_ctx.scratch_table = PS_OBJ_TABLE_IDX_0;
686 } else {
687 /* In case both tables are valid or table 0 is valid, table 0 is the
688 * valid on as it is already in the PS object table context.
689 */
690 ps_obj_table_ctx.active_table = PS_OBJ_TABLE_IDX_0;
691 ps_obj_table_ctx.scratch_table = PS_OBJ_TABLE_IDX_1;
692 }
693 #else
694 /* Logic: if the swap count is 0, then it has rolled over. The object table
695 * with a swap count of 0 is the latest one, unless the other block has a
696 * swap count of 1, in which case the roll over occurred in the previous
697 * update. In all other cases, the table with the highest swap count is the
698 * latest one.
699 */
700 if ((table1_swap_count == 0) && (table0_swap_count != 1)) {
701 /* Table 1 swap count has rolled over and table 0 swap count has not,
702 * so table 1 is the latest.
703 */
704 ps_obj_table_ctx.active_table = PS_OBJ_TABLE_IDX_1;
705 ps_obj_table_ctx.scratch_table = PS_OBJ_TABLE_IDX_0;
706
707 } else if ((table0_swap_count == 0) && (table1_swap_count != 1)) {
708 /* Table 0 swap count has rolled over and table 1 swap count has not,
709 * so table 0 is the latest.
710 */
711 ps_obj_table_ctx.active_table = PS_OBJ_TABLE_IDX_0;
712 ps_obj_table_ctx.scratch_table = PS_OBJ_TABLE_IDX_1;
713
714 } else if (table1_swap_count > table0_swap_count) {
715 /* Neither swap count has just rolled over and table 1 has a
716 * higher swap count, so table 1 is the latest.
717 */
718 ps_obj_table_ctx.active_table = PS_OBJ_TABLE_IDX_1;
719 ps_obj_table_ctx.scratch_table = PS_OBJ_TABLE_IDX_0;
720
721 } else {
722 /* Neither swap count has just rolled over and table 0 has a
723 * higher or equal swap count, so table 0 is the latest.
724 */
725 ps_obj_table_ctx.active_table = PS_OBJ_TABLE_IDX_0;
726 ps_obj_table_ctx.scratch_table = PS_OBJ_TABLE_IDX_1;
727 }
728 #endif /* PS_ROLLBACK_PROTECTION */
729
730 /* If active object table is table 1, then copy the content into the
731 * PS object table context.
732 */
733 if (ps_obj_table_ctx.active_table == PS_OBJ_TABLE_IDX_1) {
734 (void)memcpy(&ps_obj_table_ctx.obj_table,
735 init_ctx->p_table[PS_OBJ_TABLE_IDX_1],
736 PS_OBJ_TABLE_SIZE);
737 }
738
739 return PSA_SUCCESS;
740 }
741
742 /**
743 * \brief Gets table's entry index based on the given object UID and client ID.
744 *
745 * \param[in] uid Object UID
746 * \param[in] client_id Client UID
747 * \param[out] idx Pointer to store the entry's index
748 *
749 * \return Returns PSA_SUCCESS and index of the table, if object exists
750 * in the table. Otherwise, it returns PSA_ERROR_DOES_NOT_EXIST.
751 */
ps_get_object_entry_idx(psa_storage_uid_t uid,int32_t client_id,uint32_t * idx)752 static psa_status_t ps_get_object_entry_idx(psa_storage_uid_t uid,
753 int32_t client_id,
754 uint32_t *idx)
755 {
756 uint32_t i;
757 struct ps_obj_table_t *p_table = &ps_obj_table_ctx.obj_table;
758
759 for (i = 0; i < PS_OBJ_TABLE_ENTRIES; i++) {
760 if (p_table->obj_db[i].uid == uid
761 && p_table->obj_db[i].client_id == client_id) {
762 *idx = i;
763 return PSA_SUCCESS;
764 }
765 }
766
767 return PSA_ERROR_DOES_NOT_EXIST;
768 }
769
770 /**
771 * \brief Gets free index in the table
772 *
773 * \param[in] idx_num The number of indices required to be free before one can
774 * be allocated. Primarily used to prevent index
775 * exhaustion.Note that this function will only ever return
776 * 1 index.
777 * \param[out] idx Pointer to store the free index
778 *
779 * \note The table is dimensioned to fit PS_NUM_ASSETS + 1
780 *
781 * \return Returns PSA_SUCCESS and a table index if idx_num free indices are
782 * available. Otherwise, it returns PSA_ERROR_INSUFFICIENT_STORAGE.
783 */
784 __attribute__ ((always_inline))
ps_table_free_idx(uint32_t idx_num,uint32_t * idx)785 __STATIC_INLINE psa_status_t ps_table_free_idx(uint32_t idx_num,
786 uint32_t *idx)
787 {
788 uint32_t i;
789 uint32_t last_free = 0;
790 struct ps_obj_table_t *p_table = &ps_obj_table_ctx.obj_table;
791
792 if (idx_num == 0) {
793 return PSA_ERROR_INVALID_ARGUMENT;
794 }
795
796 for (i = 0; i < PS_OBJ_TABLE_ENTRIES && idx_num > 0; i++) {
797 if (p_table->obj_db[i].uid == TFM_PS_INVALID_UID) {
798 last_free = i;
799 idx_num--;
800 }
801 }
802
803 if (idx_num != 0) {
804 return PSA_ERROR_INSUFFICIENT_STORAGE;
805 } else {
806 *idx = last_free;
807 return PSA_SUCCESS;
808 }
809 }
810
811 /**
812 * \brief Deletes an entry from the table
813 *
814 * \param[in] idx Entry index to delete
815 *
816 */
ps_table_delete_entry(uint32_t idx)817 static void ps_table_delete_entry(uint32_t idx)
818 {
819 /* Initialise object table entry structure */
820 (void)memset(&ps_obj_table_ctx.obj_table.obj_db[idx],
821 PS_DEFAULT_EMPTY_BUFF_VAL, PS_OBJECTS_TABLE_ENTRY_SIZE);
822 }
823
ps_object_table_create(void)824 psa_status_t ps_object_table_create(void)
825 {
826 struct ps_obj_table_t *p_table = &ps_obj_table_ctx.obj_table;
827
828 /* Initialize object structure */
829 (void)memset(&ps_obj_table_ctx, PS_DEFAULT_EMPTY_BUFF_VAL,
830 sizeof(struct ps_obj_table_ctx_t));
831
832 /* Invert the other in the context as ps_object_table_save_table will
833 * use the scratch index to create and store the current table.
834 */
835 ps_obj_table_ctx.active_table = PS_OBJ_TABLE_IDX_1;
836 ps_obj_table_ctx.scratch_table = PS_OBJ_TABLE_IDX_0;
837
838 p_table->version = PS_OBJECT_SYSTEM_VERSION;
839
840 /* Save object table contents */
841 return ps_object_table_save_table(p_table);
842 }
843
ps_object_table_init(uint8_t * obj_data)844 psa_status_t ps_object_table_init(uint8_t *obj_data)
845 {
846 psa_status_t err;
847 struct ps_obj_table_init_ctx_t init_ctx = {
848 .p_table = {&ps_obj_table_ctx.obj_table, NULL},
849 .table_state = {PS_OBJ_TABLE_VALID, PS_OBJ_TABLE_VALID},
850 #if PS_ROLLBACK_PROTECTION
851 .nvc_1 = 0U,
852 .nvc_3 = 0U,
853 #endif /* PS_ROLLBACK_PROTECTION */
854 };
855
856 init_ctx.p_table[PS_OBJ_TABLE_IDX_1] = (struct ps_obj_table_t *)obj_data;
857
858 /* Read table from the file system */
859 ps_object_table_fs_read_table(&init_ctx);
860
861 #ifdef PS_ENCRYPTION
862 err = ps_crypto_init();
863 if (err != PSA_SUCCESS) {
864 return err;
865 }
866
867 /* Set object table key */
868 err = ps_crypto_setkey(ps_table_key_label, sizeof(ps_table_key_label));
869 if (err != PSA_SUCCESS) {
870 return err;
871 }
872
873 #if PS_ROLLBACK_PROTECTION
874 /* Authenticate table */
875 err = ps_object_table_nvc_authenticate(&init_ctx);
876 if (err != PSA_SUCCESS) {
877 (void)ps_crypto_destroykey();
878 return err;
879 }
880 #else
881 ps_object_table_authenticate_ctx_tables(&init_ctx);
882 #endif /* PS_ROLLBACK_PROTECTION */
883
884 err = ps_crypto_destroykey();
885 if (err != PSA_SUCCESS) {
886 return err;
887 }
888 #endif /* PS_ENCRYPTION */
889
890 /* Check tables version */
891 ps_object_table_validate_version(&init_ctx);
892
893 /* Set active tables */
894 err = ps_set_active_object_table(&init_ctx);
895 if (err != PSA_SUCCESS) {
896 return err;
897 }
898
899 /* Remove the old object table file */
900 err = psa_its_remove(PS_TABLE_FS_ID(ps_obj_table_ctx.scratch_table));
901 if (err != PSA_SUCCESS && err != PSA_ERROR_DOES_NOT_EXIST) {
902 return err;
903 }
904
905 #if PS_ROLLBACK_PROTECTION
906 /* Align PS NV counters */
907 err = ps_object_table_align_nv_counters(init_ctx.nvc_1);
908 if (err != PSA_SUCCESS) {
909 return err;
910 }
911 #endif /* PS_ROLLBACK_PROTECTION */
912
913 #ifdef PS_ENCRYPTION
914 ps_crypto_set_iv(&ps_obj_table_ctx.obj_table.crypto);
915 #endif
916
917 return PSA_SUCCESS;
918 }
919
ps_object_table_obj_exist(psa_storage_uid_t uid,int32_t client_id)920 psa_status_t ps_object_table_obj_exist(psa_storage_uid_t uid,
921 int32_t client_id)
922 {
923 uint32_t idx = 0;
924
925 return ps_get_object_entry_idx(uid, client_id, &idx);
926 }
927
ps_object_table_get_free_fid(uint32_t fid_num,uint32_t * p_fid)928 psa_status_t ps_object_table_get_free_fid(uint32_t fid_num,
929 uint32_t *p_fid)
930 {
931 psa_status_t err;
932 uint32_t fid;
933 uint32_t idx;
934
935 err = ps_table_free_idx(fid_num, &idx);
936 if (err != PSA_SUCCESS) {
937 return err;
938 }
939
940 /* There first two file IDs are reserved for the active table
941 * and scratch table files.
942 */
943 fid = PS_OBJECT_FS_ID(idx);
944
945 /* If there is a file in the persistent area with that ID then remove it.
946 * That can happen when the system is rebooted (e.g. power cut, ...) in the
947 * middle of a create, write or delete operation.
948 */
949 err = psa_its_remove(fid);
950 if (err != PSA_SUCCESS && err != PSA_ERROR_DOES_NOT_EXIST) {
951 return err;
952 }
953
954 *p_fid = fid;
955
956 return PSA_SUCCESS;
957 }
958
ps_object_table_set_obj_tbl_info(psa_storage_uid_t uid,int32_t client_id,const struct ps_obj_table_info_t * obj_tbl_info)959 psa_status_t ps_object_table_set_obj_tbl_info(psa_storage_uid_t uid,
960 int32_t client_id,
961 const struct ps_obj_table_info_t *obj_tbl_info)
962 {
963 psa_status_t err;
964 uint32_t idx = 0;
965 uint32_t backup_idx = 0;
966 struct ps_obj_table_entry_t backup_entry = {
967 #ifdef PS_ENCRYPTION
968 .tag = {0U},
969 #else
970 .version = 0U,
971 #endif /* PS_ENCRYPTION */
972 .uid = TFM_PS_INVALID_UID,
973 .client_id = 0,
974 };
975 struct ps_obj_table_t *p_table = &ps_obj_table_ctx.obj_table;
976
977 err = ps_get_object_entry_idx(uid, client_id, &backup_idx);
978 if (err == PSA_SUCCESS) {
979 /* If an entry exists for this UID, it creates a backup copy in case
980 * an error happens while updating the new table in the filesystem.
981 */
982 (void)memcpy(&backup_entry, &p_table->obj_db[backup_idx],
983 PS_OBJECTS_TABLE_ENTRY_SIZE);
984
985 /* Deletes old object information if it exist in the table */
986 ps_table_delete_entry(backup_idx);
987 }
988
989 idx = PS_OBJECT_FS_ID_TO_IDX(obj_tbl_info->fid);
990 p_table->obj_db[idx].uid = uid;
991 p_table->obj_db[idx].client_id = client_id;
992
993 /* Add new object information */
994 #ifdef PS_ENCRYPTION
995 (void)memcpy(p_table->obj_db[idx].tag, obj_tbl_info->tag,
996 PS_TAG_LEN_BYTES);
997 #else
998 p_table->obj_db[idx].version = obj_tbl_info->version;
999 #endif
1000
1001 err = ps_object_table_save_table(p_table);
1002 if (err != PSA_SUCCESS) {
1003 if (backup_entry.uid != TFM_PS_INVALID_UID) {
1004 /* Rollback the change in the table */
1005 (void)memcpy(&p_table->obj_db[backup_idx], &backup_entry,
1006 PS_OBJECTS_TABLE_ENTRY_SIZE);
1007 }
1008
1009 ps_table_delete_entry(idx);
1010 }
1011
1012 return err;
1013 }
1014
ps_object_table_get_obj_tbl_info(psa_storage_uid_t uid,int32_t client_id,struct ps_obj_table_info_t * obj_tbl_info)1015 psa_status_t ps_object_table_get_obj_tbl_info(psa_storage_uid_t uid,
1016 int32_t client_id,
1017 struct ps_obj_table_info_t *obj_tbl_info)
1018 {
1019 psa_status_t err;
1020 uint32_t idx;
1021 struct ps_obj_table_t *p_table = &ps_obj_table_ctx.obj_table;
1022
1023 err = ps_get_object_entry_idx(uid, client_id, &idx);
1024 if (err != PSA_SUCCESS) {
1025 return err;
1026 }
1027
1028 obj_tbl_info->fid = PS_OBJECT_FS_ID(idx);
1029
1030 #ifdef PS_ENCRYPTION
1031 (void)memcpy(obj_tbl_info->tag, p_table->obj_db[idx].tag,
1032 PS_TAG_LEN_BYTES);
1033 #else
1034 obj_tbl_info->version = p_table->obj_db[idx].version;
1035 #endif
1036
1037 return PSA_SUCCESS;
1038 }
1039
ps_object_table_delete_object(psa_storage_uid_t uid,int32_t client_id)1040 psa_status_t ps_object_table_delete_object(psa_storage_uid_t uid,
1041 int32_t client_id)
1042 {
1043 uint32_t backup_idx = 0;
1044 struct ps_obj_table_entry_t backup_entry;
1045 psa_status_t err;
1046 struct ps_obj_table_t *p_table = &ps_obj_table_ctx.obj_table;
1047
1048 /* Create a backup copy in case an error happens while updating the new
1049 * table in the filesystem.
1050 */
1051 err = ps_get_object_entry_idx(uid, client_id, &backup_idx);
1052 if (err != PSA_SUCCESS) {
1053 /* If the object is not present in the table, it returns an error
1054 * to not generate a new file where the table content is the same.
1055 * Otherwise, that could be used by an attacker to get the encryption
1056 * key.
1057 */
1058 return err;
1059 }
1060
1061 (void)memcpy(&backup_entry, &p_table->obj_db[backup_idx],
1062 PS_OBJECTS_TABLE_ENTRY_SIZE);
1063
1064 ps_table_delete_entry(backup_idx);
1065
1066 err = ps_object_table_save_table(p_table);
1067 if (err != PSA_SUCCESS) {
1068 /* Rollback the change in the table */
1069 (void)memcpy(&p_table->obj_db[backup_idx], &backup_entry,
1070 PS_OBJECTS_TABLE_ENTRY_SIZE);
1071 }
1072
1073 return err;
1074 }
1075
ps_object_table_delete_old_table(void)1076 psa_status_t ps_object_table_delete_old_table(void)
1077 {
1078 uint32_t table_id = PS_TABLE_FS_ID(ps_obj_table_ctx.scratch_table);
1079
1080 return psa_its_remove(table_id);
1081 }
1082