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