SST: Add rollback protection in SST service
This patch adds rollback protection in SST service, enables it for all
supported platforms and updates the SST service integration guide.
Change-Id: If4697051824766e1ae5774ede0e9960088bdd46a
Signed-off-by: Marc Moreno <marc.morenoberengue@arm.com>
diff --git a/CommonConfig.cmake b/CommonConfig.cmake
index af7af02..77ba88e 100644
--- a/CommonConfig.cmake
+++ b/CommonConfig.cmake
@@ -193,6 +193,10 @@
set (SST_ENCRYPTION ON)
endif()
+ if (NOT DEFINED SST_ROLLBACK_PROTECTION)
+ set (SST_ROLLBACK_PROTECTION OFF)
+ endif()
+
if (NOT DEFINED SST_CREATE_FLASH_LAYOUT)
set (SST_CREATE_FLASH_LAYOUT OFF)
endif()
diff --git a/docs/user_guides/services/tfm_sst_integration_guide.md b/docs/user_guides/services/tfm_sst_integration_guide.md
index 6f7665a..5eab3be 100644
--- a/docs/user_guides/services/tfm_sst_integration_guide.md
+++ b/docs/user_guides/services/tfm_sst_integration_guide.md
@@ -64,12 +64,6 @@
inherent failures of storage mediums (e.g. bad blocks in a NAND based device)
is not supported by the current design.
-**Rollback Protection** - In the current design, the rollback protection is not
-supported. If the storage medium is not hardware protected against malicious
-writes then it is possible to replace the current contents with older version.
-However, such attack would be limited to a single device since a HUK based
-encryption policy is used.
-
**Key Diversification** - In a more robust design, each asset would be encrypted
through a different key.
@@ -92,6 +86,7 @@
- Flash filesystem interfaces
- Flash interfaces
- Cryptographic interfaces
+ - Non-volatile (NV) counters interfaces
- Assets definitions
The PSA interfaces for SST service are located in `interface/include`
@@ -182,6 +177,18 @@
replace this implementation with calls to another service, crypto library or
hardware crypto unit.
+### Non-volatile (NV) Counters Interface
+
+`nv_counters/sst_nv_counters.h` - Abstracts SST non-volatile counters
+operations. This API detaches the use of NV counters from the TF-M NV counters
+implementation, provided by the platform, and provides a mechanism to compile
+in a different API implementation for test purposes. A SST test suite **may**
+provide its own custom implementation to be able to test different SST service
+use cases.
+
+`nv_counters/sst_nv_counters.c` - Implements the SST NV counters interfaces
+based on TF-M NV counters implementation provided by the platform.
+
### Asset Definition
`asset/sst_asset_defs.(c/h)` - Contain a reference implementation of the
@@ -418,6 +425,9 @@
- `SST_ENABLE_PARTIAL_ASSET_RW`: this flag allows to enable/disable the
partial asset RW manipulation at compile time. The partial asset
manipulation is allowed by default.
+ - `SST_ROLLBACK_PROTECTION`: this flag allows to enable/disable rollback
+ protection in secure storage service. This flag takes effect only if the
+ target has non-volatile counters and `SST_ENCRYPTION` flag is on.
--------------
diff --git a/platform/ext/Mps2AN519.cmake b/platform/ext/Mps2AN519.cmake
index f1475bf..ca68320 100644
--- a/platform/ext/Mps2AN519.cmake
+++ b/platform/ext/Mps2AN519.cmake
@@ -133,6 +133,9 @@
# API ONLY if the target has non-volatile counters.
list(APPEND ALL_SRC_C "${PLATFORM_DIR}/target/mps2/an519/dummy_nv_counters.c")
set(TARGET_NV_COUNTERS_ENABLE ON)
+ # Sets SST_ROLLBACK_PROTECTION flag to compile in the SST services
+ # rollback protection code as the target supports nv counters.
+ set (SST_ROLLBACK_PROTECTION ON)
endif()
if (NOT DEFINED BUILD_CMSIS_DRIVERS)
diff --git a/platform/ext/Mps2AN521.cmake b/platform/ext/Mps2AN521.cmake
index a0c453f..c56faef 100644
--- a/platform/ext/Mps2AN521.cmake
+++ b/platform/ext/Mps2AN521.cmake
@@ -134,6 +134,9 @@
# API ONLY if the target has non-volatile counters.
list(APPEND ALL_SRC_C "${PLATFORM_DIR}/target/mps2/an521/dummy_nv_counters.c")
set(TARGET_NV_COUNTERS_ENABLE ON)
+ # Sets SST_ROLLBACK_PROTECTION flag to compile in the SST services
+ # rollback protection code as the target supports nv counters.
+ set (SST_ROLLBACK_PROTECTION ON)
endif()
if (NOT DEFINED BUILD_CMSIS_DRIVERS)
diff --git a/platform/ext/musca_a.cmake b/platform/ext/musca_a.cmake
index e9c06c5..57fad7e 100755
--- a/platform/ext/musca_a.cmake
+++ b/platform/ext/musca_a.cmake
@@ -137,6 +137,9 @@
# API ONLY if the target has non-volatile counters.
list(APPEND ALL_SRC_C "${PLATFORM_DIR}/target/musca_a/dummy_nv_counters.c")
set(TARGET_NV_COUNTERS_ENABLE ON)
+ # Sets SST_ROLLBACK_PROTECTION flag to compile in the SST services
+ # rollback protection code as the target supports nv counters.
+ set (SST_ROLLBACK_PROTECTION ON)
endif()
if (NOT DEFINED BUILD_CMSIS_DRIVERS)
diff --git a/secure_fw/services/secure_storage/CMakeLists.inc b/secure_fw/services/secure_storage/CMakeLists.inc
index 2231a24..e3d6737 100644
--- a/secure_fw/services/secure_storage/CMakeLists.inc
+++ b/secure_fw/services/secure_storage/CMakeLists.inc
@@ -39,6 +39,10 @@
message(FATAL_ERROR "Incomplete build configuration: SST_ENCRYPTION is undefined. ")
endif()
+ if (NOT DEFINED SST_ROLLBACK_PROTECTION)
+ message(FATAL_ERROR "Incomplete build configuration: SST_ROLLBACK_PROTECTION is undefined.")
+ endif()
+
if (NOT DEFINED SST_CREATE_FLASH_LAYOUT)
message(FATAL_ERROR "Incomplete build configuration: SST_CREATE_FLASH_LAYOUT is undefined. ")
endif()
@@ -70,6 +74,12 @@
)
set_property(SOURCE ${SECURE_STORAGE_C_SRC} APPEND PROPERTY COMPILE_DEFINITIONS SST_ENCRYPTION)
set_property(DIRECTORY ${TEST_DIR} APPEND PROPERTY COMPILE_DEFINITIONS SST_ENCRYPTION)
+
+ if (SST_ROLLBACK_PROTECTION)
+ list (APPEND SECURE_STORAGE_C_SRC
+ "${SECURE_STORAGE_DIR}/nv_counters/sst_nv_counters.c")
+ set_property(SOURCE ${SECURE_STORAGE_C_SRC} APPEND PROPERTY COMPILE_DEFINITIONS SST_ROLLBACK_PROTECTION)
+ endif()
endif()
if (SST_VALIDATE_METADATA_FROM_FLASH)
@@ -91,6 +101,11 @@
#Inform the user about SST service features selected based on the SST service cmake flags
message("The SST service compile configuration is as follows:")
message("- SST_ENCRYPTION: " ${SST_ENCRYPTION})
+ if (SST_ENCRYPTION)
+ message("- SST_ROLLBACK_PROTECTION: " ${SST_ROLLBACK_PROTECTION})
+ else()
+ message("- SST_ROLLBACK_PROTECTION: N/A")
+ endif()
message("- SST_VALIDATE_METADATA_FROM_FLASH: " ${SST_VALIDATE_METADATA_FROM_FLASH})
message("- SST_CREATE_FLASH_LAYOUT: " ${SST_CREATE_FLASH_LAYOUT})
message("- SST_ENABLE_PARTIAL_ASSET_RW: " ${SST_ENABLE_PARTIAL_ASSET_RW})
diff --git a/secure_fw/services/secure_storage/manifest.yaml b/secure_fw/services/secure_storage/manifest.yaml
index 818d62c..3b1708a 100644
--- a/secure_fw/services/secure_storage/manifest.yaml
+++ b/secure_fw/services/secure_storage/manifest.yaml
@@ -85,6 +85,7 @@
"flash_fs/sst_flash_fs.c",
"flash_fs/sst_flash_dblock.c",
"flash_fs/sst_flash_mbock.c",
+ "nv_counters/sst_nv_counters.c",
],
"tfm_linker_pattern": {
"library_list": [
diff --git a/secure_fw/services/secure_storage/nv_counters/sst_nv_counters.c b/secure_fw/services/secure_storage/nv_counters/sst_nv_counters.c
new file mode 100644
index 0000000..aa103e8
--- /dev/null
+++ b/secure_fw/services/secure_storage/nv_counters/sst_nv_counters.c
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2018, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "sst_nv_counters.h"
+
+enum psa_sst_err_t sst_init_nv_counter(void)
+{
+ enum tfm_plat_err_t err;
+
+ err = tfm_plat_init_nv_counter();
+ if (err != TFM_PLAT_ERR_SUCCESS) {
+ return PSA_SST_ERR_SYSTEM_ERROR;
+ }
+
+ return PSA_SST_ERR_SUCCESS;
+}
+
+enum psa_sst_err_t sst_read_nv_counter(enum tfm_nv_counter_t counter_id,
+ uint32_t *val)
+{
+ enum tfm_plat_err_t err;
+
+ err = tfm_plat_read_nv_counter(counter_id, SST_NV_COUNTER_SIZE,
+ (uint8_t *)val);
+ if (err != TFM_PLAT_ERR_SUCCESS) {
+ return PSA_SST_ERR_SYSTEM_ERROR;
+ }
+
+ return PSA_SST_ERR_SUCCESS;
+}
+
+enum psa_sst_err_t sst_increment_nv_counter(enum tfm_nv_counter_t counter_id)
+{
+ enum tfm_plat_err_t err;
+
+ /* NOTE: tfm_plat_increment_nv_counter returns TFM_PLAT_ERR_MAX_VALUE when
+ * the counter reaches its maximum value. The current SST
+ * implementation treats this condition as an error as, from that
+ * moment onwards, the rollback protection can not be achieved based
+ * on the NV counters.
+ */
+ err = tfm_plat_increment_nv_counter(counter_id);
+ if (err != TFM_PLAT_ERR_SUCCESS) {
+ return PSA_SST_ERR_SYSTEM_ERROR;
+ }
+
+ return PSA_SST_ERR_SUCCESS;
+}
+
diff --git a/secure_fw/services/secure_storage/nv_counters/sst_nv_counters.h b/secure_fw/services/secure_storage/nv_counters/sst_nv_counters.h
new file mode 100644
index 0000000..8686d60
--- /dev/null
+++ b/secure_fw/services/secure_storage/nv_counters/sst_nv_counters.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2018, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __SST_NV_COUNTERS_H__
+#define __SST_NV_COUNTERS_H__
+
+/* NOTE: This API abstracts SST NV counters operations. This API detaches the
+ * use of NV counters from the TF-M NV counters implementation, provided
+ * by the platform, and provides a mechanism to compile in a different
+ * API implementation for test purposes. A SST test suite may provide
+ * its own custom implementation to be able to test different SST service
+ * use cases.
+ */
+
+#include <stdint.h>
+#include "psa_sst_api.h"
+#include "platform/include/tfm_plat_nv_counters.h"
+
+#define SST_NV_COUNTER_SIZE 4 /* In bytes */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief Initializes all non-volatile (NV) counters.
+ *
+ * \return PSA_SST_ERR_SUCCESS if the initialization succeeds, otherwise
+ * PSA_SST_ERR_SYSTEM_ERROR
+ */
+enum psa_sst_err_t sst_init_nv_counter(void);
+
+/**
+ * \brief Reads the given non-volatile (NV) counter.
+ *
+ * \param[in] counter_id NV counter ID.
+ * \param[out] val Pointer to store the current NV counter value.
+ *
+ * \return PSA_SST_ERR_SUCCESS if the value is read correctly, otherwise
+ * PSA_SST_ERR_SYSTEM_ERROR
+ */
+enum psa_sst_err_t sst_read_nv_counter(enum tfm_nv_counter_t counter_id,
+ uint32_t *val);
+
+/**
+ * \brief Increments the given non-volatile (NV) counter.
+ *
+ * \param[in] counter_id NV counter ID.
+ *
+ * \return If the counter is incremented correctly, it returns
+ * PSA_SST_ERR_SUCCESS. Otherwise, PSA_SST_ERR_SYSTEM_ERROR.
+ */
+enum psa_sst_err_t sst_increment_nv_counter(enum tfm_nv_counter_t counter_id);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __SST_NV_COUNTERS_H__ */
+
diff --git a/secure_fw/services/secure_storage/sst_object_table.c b/secure_fw/services/secure_storage/sst_object_table.c
index da4669e..f9cb085 100644
--- a/secure_fw/services/secure_storage/sst_object_table.c
+++ b/secure_fw/services/secure_storage/sst_object_table.c
@@ -12,6 +12,7 @@
#include "crypto/sst_crypto_interface.h"
#include "flash/sst_flash.h"
#include "flash_fs/sst_flash_fs.h"
+#include "nv_counters/sst_nv_counters.h"
#include "sst_utils.h"
/*!
@@ -35,7 +36,6 @@
uint32_t uuid; /*!< Object UUID */
};
-
/* Specifies number of entries in the table. The number of entries is the
* number of assets, defined in asset_defs.h, plus one extra entry to store
* a new object when the code processes a change in a file.
@@ -49,16 +49,23 @@
*/
struct __attribute__((__packed__)) sst_obj_table_t {
#ifdef SST_ENCRYPTION
- union sst_crypto_t crypto; /*!< Crypto metadata. */
+ union sst_crypto_t crypto; /*!< Crypto metadata. */
#endif
- uint8_t version; /*!< SST object system version. */
- uint8_t swap_count; /*!< Swap counter to distinguish 2 different
- * object tables.
- */
- uint8_t reserved[2]; /*!< 32 bits alignment. */
- struct sst_obj_table_entry_t obj_db[SST_OBJ_TABLE_ENTRIES]; /*!< Table's
- * entries
- */
+
+ uint8_t version; /*!< SST object system version. */
+
+#ifndef SST_ROLLBACK_PROTECTION
+ uint8_t swap_count; /*!< Swap counter to distinguish 2 different
+ * object tables.
+ */
+ uint8_t reserved[2]; /*!< 32 bits alignment. */
+#else
+ uint8_t reserved[3]; /*!< 32 bits alignment. */
+#endif /* SST_ROLLBACK_PROTECTION */
+
+ struct sst_obj_table_entry_t obj_db[SST_OBJ_TABLE_ENTRIES]; /*!< Table's
+ * entries
+ */
};
/* Object table indexes */
@@ -66,7 +73,7 @@
#define SST_OBJ_TABLE_IDX_1 1
/* Number of object tables (active and scratch) */
-#define SST_NUM_OBJ_TABLES 2
+#define SST_NUM_OBJ_TABLES 2
/*!
* \def SST_TABLE_FS_ID
@@ -141,16 +148,27 @@
#define SST_OBJECT_TABLE_EMPTY_SIZE 0
#define SST_OBJECT_TABLE_EMPTY NULL
-/* Invalid object table data value */
-#define SST_OBJ_TABLE_INVALID 1
-
/* The associated data is the header minus the crypto data */
#define SST_CRYPTO_ASSOCIATED_DATA(crypto) ((uint8_t *)crypto + \
- SST_NON_AUTH_OBJ_TABLE_SIZE)
+ SST_NON_AUTH_OBJ_TABLE_SIZE)
+
+#ifdef SST_ROLLBACK_PROTECTION
+#define SST_OBJ_TABLE_AUTH_DATA_SIZE (SST_OBJ_TABLE_SIZE - \
+ SST_NON_AUTH_OBJ_TABLE_SIZE)
+
+struct sst_crypto_assoc_data_t {
+ uint8_t obj_table_data[SST_OBJ_TABLE_AUTH_DATA_SIZE];
+ uint32_t nv_counter;
+};
+
+#define SST_CRYPTO_ASSOCIATED_DATA_LEN sizeof(struct sst_crypto_assoc_data_t)
+
+#else
/* The associated data is the header, minus the the tag data */
#define SST_CRYPTO_ASSOCIATED_DATA_LEN (SST_OBJ_TABLE_SIZE - \
SST_NON_AUTH_OBJ_TABLE_SIZE)
+#endif /* SST_ROLLBACK_PROTECTION */
/* The sst_object_table_init function uses the static memory allocated for
* the object data manipulation, in sst_object_table.c (g_sst_object), to load a
@@ -164,6 +182,16 @@
SST_UTILS_BOUND_CHECK(OBJ_TABLE_NOT_FIT_IN_STATIC_OBJ_DATA_BUF,
SST_OBJ_TABLE_SIZE, SST_MAX_ASSET_SIZE);
+enum sst_obj_table_state {
+ SST_OBJ_TABLE_VALID = 0, /*!< Table content is valid */
+ SST_OBJ_TABLE_INVALID, /*!< Table content is invalid */
+ SST_OBJ_TABLE_NVC_1_VALID, /*!< Table content valid with NVC 1 value */
+ SST_OBJ_TABLE_NVC_3_VALID, /*!< Table content valid with NVC 3 value */
+};
+
+/* Specifies that SST NV counter value is invalid */
+#define SST_INVALID_NVC_VALUE 0
+
/*!
* \struct sst_obj_table_ctx_t
*
@@ -173,10 +201,16 @@
struct sst_obj_table_t *p_table[SST_NUM_OBJ_TABLES]; /*!< Pointer to
* object tables
*/
- uint8_t invalid_table[SST_NUM_OBJ_TABLES]; /*!< Array to indicate if the
- * object table X has valid
- * data
- */
+ enum sst_obj_table_state table_state[SST_NUM_OBJ_TABLES]; /*!< Array to
+ * indicate if
+ * the object
+ * table X is
+ * valid
+ */
+#ifdef SST_ROLLBACK_PROTECTION
+ uint32_t nvc_1; /*!< Non-volatile counter value 1 */
+ uint32_t nvc_3; /*!< Non-volatile counter value 3 */
+#endif /* SST_ROLLBACK_PROTECTION */
};
/**
@@ -198,7 +232,7 @@
SST_OBJECT_TABLE_OBJECT_OFFSET,
(uint8_t *)init_ctx->p_table[SST_OBJ_TABLE_IDX_0]);
if (err != PSA_SST_ERR_SUCCESS) {
- init_ctx->invalid_table[SST_OBJ_TABLE_IDX_0] = SST_OBJ_TABLE_INVALID;
+ init_ctx->table_state[SST_OBJ_TABLE_IDX_0] = SST_OBJ_TABLE_INVALID;
}
/* Read file with the table 1 data */
@@ -207,7 +241,7 @@
SST_OBJECT_TABLE_OBJECT_OFFSET,
(uint8_t *)init_ctx->p_table[SST_OBJ_TABLE_IDX_1]);
if (err != PSA_SST_ERR_SUCCESS) {
- init_ctx->invalid_table[SST_OBJ_TABLE_IDX_1] = SST_OBJ_TABLE_INVALID;
+ init_ctx->table_state[SST_OBJ_TABLE_IDX_1] = SST_OBJ_TABLE_INVALID;
}
}
@@ -268,6 +302,175 @@
return err;
}
+#ifdef SST_ROLLBACK_PROTECTION
+/**
+ * \brief Aligns all SST non-volatile counters.
+ *
+ * \param[in] nvc_1 Value of SST non-volatile counter 1
+ *
+ * \return Returns error code as specified in \ref psa_sst_err_t
+ */
+static enum psa_sst_err_t sst_object_table_align_nv_counters(uint32_t nvc_1)
+{
+ enum psa_sst_err_t err;
+ uint32_t nvc_x_val = 0;
+
+ /* Align SST NVC 2 with NVC 1 */
+ err = sst_read_nv_counter(TFM_SST_NV_COUNTER_2, &nvc_x_val);
+ if (err != PSA_SST_ERR_SUCCESS) {
+ return PSA_SST_ERR_SYSTEM_ERROR;
+ }
+
+ for (; nvc_x_val < nvc_1; nvc_x_val++) {
+ err = sst_increment_nv_counter(TFM_SST_NV_COUNTER_2);
+ if (err != PSA_SST_ERR_SUCCESS) {
+ return err;
+ }
+ }
+
+ /* Align SST NVC 3 with NVC 1 */
+ err = sst_read_nv_counter(TFM_SST_NV_COUNTER_3, &nvc_x_val);
+ if (err != PSA_SST_ERR_SUCCESS) {
+ return PSA_SST_ERR_SYSTEM_ERROR;
+ }
+
+ for (; nvc_x_val < nvc_1; nvc_x_val++) {
+ err = sst_increment_nv_counter(TFM_SST_NV_COUNTER_3);
+ if (err != PSA_SST_ERR_SUCCESS) {
+ return err;
+ }
+ }
+
+ return PSA_SST_ERR_SUCCESS;
+}
+
+/**
+ * \brief Generates table authentication tag.
+ *
+ * \param[in] nvc_1 Value of SST non-volatile counter 1
+ * \param[in/out] obj_table Pointer to the object table to generate
+ * authentication
+ *
+ * \return Returns error code as specified in \ref psa_sst_err_t
+ */
+__attribute__ ((always_inline))
+__STATIC_INLINE enum psa_sst_err_t sst_object_table_nvc_generate_auth_tag(
+ uint32_t nvc_1,
+ struct sst_obj_table_t *obj_table)
+{
+ struct sst_crypto_assoc_data_t assoc_data;
+ union sst_crypto_t *crypto = &obj_table->crypto;
+
+ /* Get new IV */
+ sst_crypto_get_iv(crypto);
+
+ assoc_data.nv_counter = nvc_1;
+ sst_utils_memcpy(assoc_data.obj_table_data,
+ SST_CRYPTO_ASSOCIATED_DATA(crypto),
+ SST_OBJ_TABLE_AUTH_DATA_SIZE);
+
+ return sst_crypto_generate_auth_tag(crypto, (const uint8_t *)&assoc_data,
+ SST_CRYPTO_ASSOCIATED_DATA_LEN);
+}
+
+/**
+ * \brief Authenticates table of objects.
+ *
+ * \param[in] table_idx Table index in the init context
+ * \param[in/out] init_ctx Pointer to the object table to authenticate
+ *
+ */
+static void sst_object_table_authenticate(uint8_t table_idx,
+ struct sst_obj_table_init_ctx_t *init_ctx)
+{
+ struct sst_crypto_assoc_data_t assoc_data;
+ union sst_crypto_t *crypto = &init_ctx->p_table[table_idx]->crypto;
+ enum psa_sst_err_t err;
+
+ /* Init associated data with NVC 1 */
+ assoc_data.nv_counter = init_ctx->nvc_1;
+ sst_utils_memcpy(assoc_data.obj_table_data,
+ SST_CRYPTO_ASSOCIATED_DATA(crypto),
+ SST_OBJ_TABLE_AUTH_DATA_SIZE);
+
+ err = sst_crypto_authenticate(crypto, (const uint8_t *)&assoc_data,
+ SST_CRYPTO_ASSOCIATED_DATA_LEN);
+ if (err == PSA_SST_ERR_SUCCESS) {
+ init_ctx->table_state[table_idx] = SST_OBJ_TABLE_NVC_1_VALID;
+ return;
+ }
+
+ if (init_ctx->nvc_3 == SST_INVALID_NVC_VALUE) {
+ init_ctx->table_state[table_idx] = SST_OBJ_TABLE_INVALID;
+ return;
+ }
+
+ /* Check with NVC 3 */
+ assoc_data.nv_counter = init_ctx->nvc_3;
+
+ err = sst_crypto_authenticate(crypto, (const uint8_t *)&assoc_data,
+ SST_CRYPTO_ASSOCIATED_DATA_LEN);
+ if (err != PSA_SST_ERR_SUCCESS) {
+ init_ctx->table_state[table_idx] = SST_OBJ_TABLE_INVALID;
+ } else {
+ init_ctx->table_state[table_idx] = SST_OBJ_TABLE_NVC_3_VALID;
+ }
+}
+
+/**
+ * \brief Authenticates tables of objects.
+ *
+ * \param[in/out] init_ctx Pointer to the object table to authenticate
+ *
+ * \return Returns error code as specified in \ref psa_sst_err_t
+ */
+__attribute__ ((always_inline))
+__STATIC_INLINE enum psa_sst_err_t sst_object_table_nvc_authenticate(
+ struct sst_obj_table_init_ctx_t *init_ctx)
+{
+ enum psa_sst_err_t err;
+ uint32_t nvc_2;
+
+ err = sst_read_nv_counter(TFM_SST_NV_COUNTER_1, &init_ctx->nvc_1);
+ if (err != PSA_SST_ERR_SUCCESS) {
+ return err;
+ }
+
+ err = sst_read_nv_counter(TFM_SST_NV_COUNTER_2, &nvc_2);
+ if (err != PSA_SST_ERR_SUCCESS) {
+ return err;
+ }
+
+ err = sst_read_nv_counter(TFM_SST_NV_COUNTER_3, &init_ctx->nvc_3);
+ if (err != PSA_SST_ERR_SUCCESS) {
+ return err;
+ }
+
+ /* Check if NVC 3 value can be used to validate an object table */
+ if (init_ctx->nvc_3 != nvc_2) {
+ /* If NVC 3 is different from NVC 2, it is possible to load an old SST
+ * area image in the system by manipulating the FS to return a system
+ * error from the file system layer and triggering power fault before
+ * increasing the NVC 3. So, in that case, NVC 3 value cannot be used to
+ * validate an old object table at the init process.
+ */
+ init_ctx->nvc_3 = SST_INVALID_NVC_VALUE;
+ }
+
+ /* Authenticate table 0 if data is valid */
+ if (init_ctx->table_state[SST_OBJ_TABLE_IDX_0] != SST_OBJ_TABLE_INVALID) {
+ sst_object_table_authenticate(SST_OBJ_TABLE_IDX_0, init_ctx);
+ }
+
+ /* Authenticate table 1 if data is valid */
+ if (init_ctx->table_state[SST_OBJ_TABLE_IDX_1] != SST_OBJ_TABLE_INVALID) {
+ sst_object_table_authenticate(SST_OBJ_TABLE_IDX_1, init_ctx);
+ }
+
+ return PSA_SST_ERR_SUCCESS;
+}
+#else /* SST_ROLLBACK_PROTECTION */
+
/**
* \brief Generates table authentication
*
@@ -276,7 +479,8 @@
*
* \return Returns error code as specified in \ref psa_sst_err_t
*/
-static enum psa_sst_err_t sst_object_table_generate_auth_tag(
+__attribute__ ((always_inline))
+__STATIC_INLINE enum psa_sst_err_t sst_object_table_generate_auth_tag(
struct sst_obj_table_t *obj_table)
{
union sst_crypto_t *crypto = &obj_table->crypto;
@@ -296,7 +500,7 @@
*
*/
__attribute__ ((always_inline))
-__STATIC_INLINE void sst_object_tables_authenticate(
+__STATIC_INLINE void sst_object_table_authenticate_ctx_tables(
struct sst_obj_table_init_ctx_t *init_ctx)
{
enum psa_sst_err_t err;
@@ -304,30 +508,29 @@
&init_ctx->p_table[SST_OBJ_TABLE_IDX_0]->crypto;
/* Authenticate table 0 if data is valid */
- if (init_ctx->invalid_table[SST_OBJ_TABLE_IDX_0] != SST_OBJ_TABLE_INVALID) {
+ if (init_ctx->table_state[SST_OBJ_TABLE_IDX_0] != SST_OBJ_TABLE_INVALID) {
err = sst_crypto_authenticate(crypto,
SST_CRYPTO_ASSOCIATED_DATA(crypto),
SST_CRYPTO_ASSOCIATED_DATA_LEN);
if (err != PSA_SST_ERR_SUCCESS) {
- init_ctx->invalid_table[SST_OBJ_TABLE_IDX_0] =
- SST_OBJ_TABLE_INVALID;
+ init_ctx->table_state[SST_OBJ_TABLE_IDX_0] = SST_OBJ_TABLE_INVALID;
}
}
/* Authenticate table 1 if data is valid */
- if (init_ctx->invalid_table[SST_OBJ_TABLE_IDX_1] != SST_OBJ_TABLE_INVALID) {
+ if (init_ctx->table_state[SST_OBJ_TABLE_IDX_1] != SST_OBJ_TABLE_INVALID) {
crypto = &init_ctx->p_table[SST_OBJ_TABLE_IDX_1]->crypto;
err = sst_crypto_authenticate(crypto,
SST_CRYPTO_ASSOCIATED_DATA(crypto),
SST_CRYPTO_ASSOCIATED_DATA_LEN);
if (err != PSA_SST_ERR_SUCCESS) {
- init_ctx->invalid_table[SST_OBJ_TABLE_IDX_1] =
- SST_OBJ_TABLE_INVALID;
+ init_ctx->table_state[SST_OBJ_TABLE_IDX_1] = SST_OBJ_TABLE_INVALID;
}
}
}
-#endif
+#endif /* SST_ROLLBACK_PROTECTION */
+#endif /* SST_ENCRYPTION */
/**
* \brief Saves object table in the persistent memory.
@@ -339,10 +542,21 @@
static enum psa_sst_err_t sst_object_table_save_table(
struct sst_obj_table_t *obj_table)
{
-#ifdef SST_ENCRYPTION
enum psa_sst_err_t err;
-#endif
+#ifdef SST_ROLLBACK_PROTECTION
+ uint32_t nvc_1 = 0;
+
+ err = sst_increment_nv_counter(TFM_SST_NV_COUNTER_1);
+ if (err != PSA_SST_ERR_SUCCESS) {
+ return err;
+ }
+
+ err = sst_read_nv_counter(TFM_SST_NV_COUNTER_1, &nvc_1);
+ if (err != PSA_SST_ERR_SUCCESS) {
+ return err;
+ }
+#else
obj_table->swap_count++;
if (obj_table->swap_count == SST_FLASH_DEFAULT_VAL) {
@@ -354,19 +568,42 @@
*/
obj_table->swap_count = 0;
}
+#endif /* SST_ROLLBACK_PROTECTION */
#ifdef SST_ENCRYPTION
/* Set object table key */
- sst_object_table_set_crypto_key();
+ err = sst_object_table_set_crypto_key();
+ if (err != PSA_SST_ERR_SUCCESS) {
+ return err;
+ }
+#ifdef SST_ROLLBACK_PROTECTION
+ /* Generate authentication tag from the current table content and SST
+ * NV counter 1.
+ */
+ err = sst_object_table_nvc_generate_auth_tag(nvc_1, obj_table);
+#else
/* Generate authentication tag from the current table content */
err = sst_object_table_generate_auth_tag(obj_table);
+#endif /* SST_ROLLBACK_PROTECTION */
+
if (err != PSA_SST_ERR_SUCCESS) {
return err;
}
#endif /* SST_ENCRYPTION */
- return sst_object_table_fs_write_table(obj_table);
+ err = sst_object_table_fs_write_table(obj_table);
+
+#ifdef SST_ROLLBACK_PROTECTION
+ if (err != PSA_SST_ERR_SUCCESS) {
+ return err;
+ }
+
+ /* Align SST NV counters to have the same value */
+ err = sst_object_table_align_nv_counters(nvc_1);
+#endif /* SST_ROLLBACK_PROTECTION */
+
+ return err;
}
/**
@@ -375,7 +612,8 @@
* \param[in/out] init_ctx Pointer to the init object table context
*
*/
-static void sst_object_table_validate_version(
+__attribute__ ((always_inline))
+__STATIC_INLINE void sst_object_table_validate_version(
struct sst_obj_table_init_ctx_t *init_ctx)
{
/* Looks for exact version number.
@@ -383,12 +621,12 @@
*/
if (SST_OBJECT_SYSTEM_VERSION !=
init_ctx->p_table[SST_OBJ_TABLE_IDX_0]->version) {
- init_ctx->invalid_table[SST_OBJ_TABLE_IDX_0] = SST_OBJ_TABLE_INVALID;
+ init_ctx->table_state[SST_OBJ_TABLE_IDX_0] = SST_OBJ_TABLE_INVALID;
}
if (SST_OBJECT_SYSTEM_VERSION !=
init_ctx->p_table[SST_OBJ_TABLE_IDX_1]->version) {
- init_ctx->invalid_table[SST_OBJ_TABLE_IDX_1] = SST_OBJ_TABLE_INVALID;
+ init_ctx->table_state[SST_OBJ_TABLE_IDX_1] = SST_OBJ_TABLE_INVALID;
}
}
@@ -396,40 +634,54 @@
* \brief Sets the active object table based on the swap count and validity of
* the object table data.
*
- * \param[in] init_ctx Pointer to the init object table context
+ * \param[in] init_ctx Pointer to the init object table context
*
*/
static enum psa_sst_err_t sst_set_active_object_table(
const struct sst_obj_table_init_ctx_t *init_ctx)
{
-
+#ifndef SST_ROLLBACK_PROTECTION
uint8_t table0_swap_count =
init_ctx->p_table[SST_OBJ_TABLE_IDX_0]->swap_count;
uint8_t table1_swap_count =
init_ctx->p_table[SST_OBJ_TABLE_IDX_1]->swap_count;
+#endif
/* Check if there is an invalid object table */
-
- if ((init_ctx->invalid_table[SST_OBJ_TABLE_IDX_0] == SST_OBJ_TABLE_INVALID)
- && (init_ctx->invalid_table[SST_OBJ_TABLE_IDX_1] ==
+ if ((init_ctx->table_state[SST_OBJ_TABLE_IDX_0] == SST_OBJ_TABLE_INVALID)
+ && (init_ctx->table_state[SST_OBJ_TABLE_IDX_1] ==
SST_OBJ_TABLE_INVALID)) {
/* Both tables are invalid */
return PSA_SST_ERR_SYSTEM_ERROR;
- } else if (init_ctx->invalid_table[SST_OBJ_TABLE_IDX_0] ==
+ } else if (init_ctx->table_state[SST_OBJ_TABLE_IDX_0] ==
SST_OBJ_TABLE_INVALID) {
- /* Table 0 is invalid, the the active one is table 1 */
+ /* Table 0 is invalid, the active one is table 1 */
sst_obj_table_ctx.active_table = SST_OBJ_TABLE_IDX_1;
sst_obj_table_ctx.scratch_table = SST_OBJ_TABLE_IDX_0;
return PSA_SST_ERR_SUCCESS;
} else {
- /* Table 1 is invalid, the the active one is table 0 */
+ /* Table 1 is invalid, the active one is table 0 */
sst_obj_table_ctx.active_table = SST_OBJ_TABLE_IDX_0;
sst_obj_table_ctx.scratch_table = SST_OBJ_TABLE_IDX_1;
return PSA_SST_ERR_SUCCESS;
}
+#ifdef SST_ROLLBACK_PROTECTION
+ if (init_ctx->table_state[SST_OBJ_TABLE_IDX_1] ==
+ SST_OBJ_TABLE_NVC_1_VALID) {
+ /* Table 0 is invalid, the active one is table 1 */
+ sst_obj_table_ctx.active_table = SST_OBJ_TABLE_IDX_1;
+ sst_obj_table_ctx.scratch_table = SST_OBJ_TABLE_IDX_0;
+ } else {
+ /* In case both tables are valid or table 0 is valid, table 0 is the
+ * valid on as it is already in the SST object table context.
+ */
+ sst_obj_table_ctx.active_table = SST_OBJ_TABLE_IDX_0;
+ sst_obj_table_ctx.scratch_table = SST_OBJ_TABLE_IDX_1;
+ }
+#else
/* Logic: if the swap count is 0, then it has rolled over. The object table
* with a swap count of 0 is the latest one, unless the other block has a
* swap count of 1, in which case the roll over occurred in the previous
@@ -464,7 +716,7 @@
sst_obj_table_ctx.active_table = SST_OBJ_TABLE_IDX_0;
sst_obj_table_ctx.scratch_table = SST_OBJ_TABLE_IDX_1;
}
-
+#endif /* SST_ROLLBACK_PROTECTION */
/* If active object table is table 1, then copy the content into the
* SST object table context.
@@ -510,7 +762,8 @@
*
* \return Returns free index in the table
*/
-static uint32_t sst_table_free_idx(void)
+__attribute__ ((always_inline))
+__STATIC_INLINE uint32_t sst_table_free_idx(void)
{
uint32_t idx;
struct sst_obj_table_t *p_table = &sst_obj_table_ctx.obj_table;
@@ -548,6 +801,16 @@
{
struct sst_obj_table_t *p_table = &sst_obj_table_ctx.obj_table;
+#ifdef SST_ROLLBACK_PROTECTION
+ enum psa_sst_err_t err;
+
+ /* Initialize SST NV counters */
+ err = sst_init_nv_counter();
+ if (err != PSA_SST_ERR_SUCCESS) {
+ return err;
+ }
+#endif
+
/* Initialize object structure */
sst_utils_memset(&sst_obj_table_ctx, SST_DEFAULT_EMPTY_BUFF_VAL,
sizeof(struct sst_obj_table_ctx_t));
@@ -555,7 +818,7 @@
/* Invert the other in the context as sst_object_table_save_table will
* use the scratch index to create and store the current table.
*/
- sst_obj_table_ctx.active_table = SST_OBJ_TABLE_IDX_1;
+ sst_obj_table_ctx.active_table = SST_OBJ_TABLE_IDX_1;
sst_obj_table_ctx.scratch_table = SST_OBJ_TABLE_IDX_0;
p_table->version = SST_OBJECT_SYSTEM_VERSION;
@@ -569,7 +832,7 @@
enum psa_sst_err_t err;
struct sst_obj_table_init_ctx_t init_ctx = {
.p_table = {&sst_obj_table_ctx.obj_table, 0},
- .invalid_table = {0, 0}
+ .table_state = {0, 0}
};
init_ctx.p_table[SST_OBJ_TABLE_IDX_1] = (struct sst_obj_table_t *)obj_data;
@@ -579,10 +842,27 @@
#ifdef SST_ENCRYPTION
/* Set object table key */
- sst_object_table_set_crypto_key();
+ err = sst_object_table_set_crypto_key();
+ if (err != PSA_SST_ERR_SUCCESS) {
+ return err;
+ }
+
+#ifdef SST_ROLLBACK_PROTECTION
+ /* Initialize SST NV counters */
+ err = sst_init_nv_counter();
+ if (err != PSA_SST_ERR_SUCCESS) {
+ return err;
+ }
/* Authenticate table */
- sst_object_tables_authenticate(&init_ctx);
+ err = sst_object_table_nvc_authenticate(&init_ctx);
+ if (err != PSA_SST_ERR_SUCCESS) {
+ return err;
+ }
+#else
+ sst_object_table_authenticate_ctx_tables(&init_ctx);
+#endif /* SST_ROLLBACK_PROTECTION */
+
#endif /* SST_ENCRYPTION */
/* Check tables version */
@@ -601,6 +881,18 @@
return err;
}
+#ifdef SST_ROLLBACK_PROTECTION
+ /* Align SST NV counters */
+ err = sst_object_table_align_nv_counters(init_ctx.nvc_1);
+ if (err != PSA_SST_ERR_SUCCESS) {
+ return err;
+ }
+#endif /* SST_ROLLBACK_PROTECTION */
+
+#ifdef SST_ENCRYPTION
+ sst_crypto_set_iv(&sst_obj_table_ctx.obj_table.crypto);
+#endif
+
#ifdef SST_ENCRYPTION
sst_crypto_set_iv(&sst_obj_table_ctx.obj_table.crypto);
#endif
diff --git a/secure_fw/services/secure_storage/sst_object_table.h b/secure_fw/services/secure_storage/sst_object_table.h
index 9c55158..78c48c9 100644
--- a/secure_fw/services/secure_storage/sst_object_table.h
+++ b/secure_fw/services/secure_storage/sst_object_table.h
@@ -70,7 +70,7 @@
/**
* \brief Sets object's internal information in the object table and
- * stores it persitently.
+ * stores it persistently.
*
* \param[in] uuid Object UUID
* \param[in] obj_tbl_info Pointer to the location to store object table