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