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