RSS: Implement host ROTPKs

Change-Id: I6daf0954d9d692646b9d4588bb45947123189ceb
Signed-off-by: Raef Coles <raef.coles@arm.com>
diff --git a/platform/CMakeLists.txt b/platform/CMakeLists.txt
index cc6eb9c..381f998 100755
--- a/platform/CMakeLists.txt
+++ b/platform/CMakeLists.txt
@@ -142,7 +142,7 @@
     target_include_directories(platform_crypto_keys
         PRIVATE
             $<$<BOOL:${PLATFORM_DEFAULT_CRYPTO_KEYS}>:${CMAKE_SOURCE_DIR}/interface/include/crypto_keys>
-            $<$<BOOL:${PLATFORM_DEFAULT_CRYPTO_KEYS}>:${CMAKE_BINARY_DIR}/generated/interface/include>
+            ${CMAKE_BINARY_DIR}/generated/interface/include
     )
 
     target_compile_definitions(platform_crypto_keys
diff --git a/platform/ext/target/arm/rss/CMakeLists.txt b/platform/ext/target/arm/rss/CMakeLists.txt
index 733d591..d1347e2 100644
--- a/platform/ext/target/arm/rss/CMakeLists.txt
+++ b/platform/ext/target/arm/rss/CMakeLists.txt
@@ -76,6 +76,10 @@
         $<$<C_COMPILER_ID:GNU>:${CMAKE_CURRENT_SOURCE_DIR}/device/source/gcc/rss_bl1_2.ld>
 )
 
+
+install(FILES       platform_builtin_key_ids.h
+        DESTINATION ${TFM_INSTALL_PATH}/interface/include)
+
 #========================= Platform Secure ====================================#
 
 add_subdirectory(rss_comms)
@@ -122,6 +126,7 @@
         nv_counters.c
         attest_hal.c
         tfm_hal_platform_reset.c
+        provisioning.c
 )
 
 target_sources(tfm_sprt
@@ -148,6 +153,14 @@
         crypto_keys.c
 )
 
+target_include_directories(platform_crypto_keys
+    PUBLIC
+        .
+        ./native_drivers
+        ./device/include
+        ./device/config
+)
+
 #========================= Platform Non-Secure ================================#
 
 target_sources(platform_ns
@@ -181,6 +194,8 @@
 target_sources(platform_bl2
     PRIVATE
         tfm_hal_platform_reset.c
+        bl2/flash_map_bl2.c
+        bl2/provisioning.c
         bl2/boot_hal_bl2.c
         cmsis_drivers/Driver_Flash.c
         cmsis_drivers/Driver_USART.c
@@ -213,11 +228,6 @@
         native_drivers
 )
 
-target_sources(bl2
-    PRIVATE
-        bl2/flash_map_bl2.c
-)
-
 target_link_libraries(platform_bl2
     PRIVATE
         $<$<AND:$<BOOL:${CONFIG_TFM_BOOT_STORE_MEASUREMENTS}>,$<BOOL:${TFM_PARTITION_MEASURED_BOOT}>>:tfm_boot_status>
diff --git a/platform/ext/target/arm/rss/bl2/provisioning.c b/platform/ext/target/arm/rss/bl2/provisioning.c
new file mode 100644
index 0000000..88cac14
--- /dev/null
+++ b/platform/ext/target/arm/rss/bl2/provisioning.c
@@ -0,0 +1,215 @@
+/*
+ * Copyright (c) 2021-2022, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "tfm_plat_provisioning.h"
+
+#include "cmsis_compiler.h"
+#include "tfm_plat_otp.h"
+#include "tfm_attest_hal.h"
+#include "psa/crypto.h"
+#include "bootutil/bootutil_log.h"
+
+#include <string.h>
+
+#define ASSEMBLY_AND_TEST_PROV_DATA_MAGIC 0xC0DEFEED
+
+__PACKED_STRUCT bl2_assembly_and_test_provisioning_data_t {
+    uint32_t magic;
+    uint8_t bl2_rotpk_0[32];
+    uint8_t bl2_rotpk_1[32];
+    uint8_t bl2_rotpk_2[32];
+    uint8_t bl2_rotpk_3[32];
+
+#ifdef PLATFORM_PSA_ADAC_SECURE_DEBUG
+    uint8_t secure_debug_pk[32];
+#endif /* PLATFORM_PSA_ADAC_SECURE_DEBUG */
+};
+
+#ifdef TFM_DUMMY_PROVISIONING
+static const struct bl2_assembly_and_test_provisioning_data_t bl2_assembly_and_test_prov_data = {
+    ASSEMBLY_AND_TEST_PROV_DATA_MAGIC,
+#if (MCUBOOT_SIGN_RSA_LEN == 2048)
+    /* bl2 rotpk 0 */
+    {
+        0xfc, 0x57, 0x01, 0xdc, 0x61, 0x35, 0xe1, 0x32,
+        0x38, 0x47, 0xbd, 0xc4, 0x0f, 0x04, 0xd2, 0xe5,
+        0xbe, 0xe5, 0x83, 0x3b, 0x23, 0xc2, 0x9f, 0x93,
+        0x59, 0x3d, 0x00, 0x01, 0x8c, 0xfa, 0x99, 0x94,
+    },
+    /* bl2 rotpk 1 */
+    {
+        0xe1, 0x80, 0x15, 0x99, 0x3d, 0x6d, 0x27, 0x60,
+        0xb4, 0x99, 0x27, 0x4b, 0xae, 0xf2, 0x64, 0xb8,
+        0x3a, 0xf2, 0x29, 0xe9, 0xa7, 0x85, 0xf3, 0xd5,
+        0xbf, 0x00, 0xb9, 0xd3, 0x2c, 0x1f, 0x03, 0x96,
+    },
+    /* bl2 rotpk 2 */
+    {
+        0xfc, 0x57, 0x01, 0xdc, 0x61, 0x35, 0xe1, 0x32,
+        0x38, 0x47, 0xbd, 0xc4, 0x0f, 0x04, 0xd2, 0xe5,
+        0xbe, 0xe5, 0x83, 0x3b, 0x23, 0xc2, 0x9f, 0x93,
+        0x59, 0x3d, 0x00, 0x01, 0x8c, 0xfa, 0x99, 0x94,
+    },
+    /* bl2 rotpk 3 */
+    {
+        0xfc, 0x57, 0x01, 0xdc, 0x61, 0x35, 0xe1, 0x32,
+        0x38, 0x47, 0xbd, 0xc4, 0x0f, 0x04, 0xd2, 0xe5,
+        0xbe, 0xe5, 0x83, 0x3b, 0x23, 0xc2, 0x9f, 0x93,
+        0x59, 0x3d, 0x00, 0x01, 0x8c, 0xfa, 0x99, 0x94,
+    },
+#elif (MCUBOOT_SIGN_RSA_LEN == 3072)
+    /* bl2 rotpk 0 */
+    {
+        0xbf, 0xe6, 0xd8, 0x6f, 0x88, 0x26, 0xf4, 0xff,
+        0x97, 0xfb, 0x96, 0xc4, 0xe6, 0xfb, 0xc4, 0x99,
+        0x3e, 0x46, 0x19, 0xfc, 0x56, 0x5d, 0xa2, 0x6a,
+        0xdf, 0x34, 0xc3, 0x29, 0x48, 0x9a, 0xdc, 0x38,
+    },
+    /* bl2 rotpk 1 */
+    {
+        0xb3, 0x60, 0xca, 0xf5, 0xc9, 0x8c, 0x6b, 0x94,
+        0x2a, 0x48, 0x82, 0xfa, 0x9d, 0x48, 0x23, 0xef,
+        0xb1, 0x66, 0xa9, 0xef, 0x6a, 0x6e, 0x4a, 0xa3,
+        0x7c, 0x19, 0x19, 0xed, 0x1f, 0xcc, 0xc0, 0x49,
+    },
+    /* bl2 rotpk 2 */
+    {
+        0xbf, 0xe6, 0xd8, 0x6f, 0x88, 0x26, 0xf4, 0xff,
+        0x97, 0xfb, 0x96, 0xc4, 0xe6, 0xfb, 0xc4, 0x99,
+        0x3e, 0x46, 0x19, 0xfc, 0x56, 0x5d, 0xa2, 0x6a,
+        0xdf, 0x34, 0xc3, 0x29, 0x48, 0x9a, 0xdc, 0x38,
+    },
+    /* bl2 rotpk 3 */
+    {
+        0xbf, 0xe6, 0xd8, 0x6f, 0x88, 0x26, 0xf4, 0xff,
+        0x97, 0xfb, 0x96, 0xc4, 0xe6, 0xfb, 0xc4, 0x99,
+        0x3e, 0x46, 0x19, 0xfc, 0x56, 0x5d, 0xa2, 0x6a,
+        0xdf, 0x34, 0xc3, 0x29, 0x48, 0x9a, 0xdc, 0x38,
+    },
+#else
+#error "No public key available for given signing algorithm."
+#endif /* MCUBOOT_SIGN_RSA_LEN */
+
+#ifdef PLATFORM_PSA_ADAC_SECURE_DEBUG
+    {
+        0xf4, 0x0c, 0x8f, 0xbf, 0x12, 0xdb, 0x78, 0x2a,
+        0xfd, 0xf4, 0x75, 0x96, 0x6a, 0x06, 0x82, 0x36,
+        0xe0, 0x32, 0xab, 0x80, 0xd1, 0xb7, 0xf1, 0xbc,
+        0x9f, 0xe7, 0xd8, 0x7a, 0x88, 0xcb, 0x26, 0xd0,
+    },
+#endif /* PLATFORM_PSA_ADAC_SECURE_DEBUG */
+};
+#else
+static const struct bl2_assembly_and_test_provisioning_data_t bl2_assembly_and_test_prov_data;
+#endif /* TFM_DUMMY_PROVISIONING */
+
+void tfm_plat_provisioning_check_for_dummy_keys(void)
+{
+    uint64_t iak_start;
+
+    tfm_plat_otp_read(PLAT_OTP_ID_IAK, sizeof(iak_start), (uint8_t*)&iak_start);
+
+    if(iak_start == 0xA4906F6DB254B4A9) {
+        BOOT_LOG_WRN("%s%s%s%s",
+                     "\033[1;31m",
+                     "This device was provisioned with dummy keys. ",
+                     "This device is \033[1;1mNOT SECURE",
+                     "\033[0m");
+    }
+
+    memset(&iak_start, 0, sizeof(iak_start));
+}
+
+int tfm_plat_provisioning_is_required(void)
+{
+    enum tfm_plat_err_t err;
+    enum plat_otp_lcs_t lcs;
+
+    err = tfm_plat_otp_read(PLAT_OTP_ID_LCS, sizeof(lcs), (uint8_t*)&lcs);
+    if (err != TFM_PLAT_ERR_SUCCESS) {
+        return err;
+    }
+
+    return lcs == PLAT_OTP_LCS_ASSEMBLY_AND_TEST
+        || lcs == PLAT_OTP_LCS_PSA_ROT_PROVISIONING;
+}
+
+enum tfm_plat_err_t provision_assembly_and_test(void)
+{
+    enum tfm_plat_err_t err;
+
+    err = tfm_plat_otp_write(PLAT_OTP_ID_BL2_ROTPK_0,
+                             sizeof(bl2_assembly_and_test_prov_data.bl2_rotpk_0),
+                             bl2_assembly_and_test_prov_data.bl2_rotpk_0);
+    if (err != TFM_PLAT_ERR_SUCCESS && err != TFM_PLAT_ERR_UNSUPPORTED) {
+        return err;
+    }
+    err = tfm_plat_otp_write(PLAT_OTP_ID_BL2_ROTPK_1,
+                             sizeof(bl2_assembly_and_test_prov_data.bl2_rotpk_1),
+                             bl2_assembly_and_test_prov_data.bl2_rotpk_1);
+    if (err != TFM_PLAT_ERR_SUCCESS && err != TFM_PLAT_ERR_UNSUPPORTED) {
+        return err;
+    }
+    err = tfm_plat_otp_write(PLAT_OTP_ID_BL2_ROTPK_2,
+                             sizeof(bl2_assembly_and_test_prov_data.bl2_rotpk_2),
+                             bl2_assembly_and_test_prov_data.bl2_rotpk_2);
+    if (err != TFM_PLAT_ERR_SUCCESS && err != TFM_PLAT_ERR_UNSUPPORTED) {
+        return err;
+    }
+    err = tfm_plat_otp_write(PLAT_OTP_ID_BL2_ROTPK_3,
+                             sizeof(bl2_assembly_and_test_prov_data.bl2_rotpk_3),
+                             bl2_assembly_and_test_prov_data.bl2_rotpk_3);
+    if (err != TFM_PLAT_ERR_SUCCESS && err != TFM_PLAT_ERR_UNSUPPORTED) {
+        return err;
+    }
+
+#ifdef PLATFORM_PSA_ADAC_SECURE_DEBUG
+    err = tfm_plat_otp_write(PLAT_OTP_ID_SECURE_DEBUG_PK,
+                             sizeof(bl2_assembly_and_test_prov_data.secure_debug_pk),
+                             bl2_assembly_and_test_prov_data.secure_debug_pk);
+    if (err != TFM_PLAT_ERR_SUCCESS && err != TFM_PLAT_ERR_UNSUPPORTED) {
+        return err;
+    }
+#endif /* PLATFORM_PSA_ADAC_SECURE_DEBUG */
+
+    return err;
+}
+
+enum tfm_plat_err_t tfm_plat_provisioning_perform(void)
+{
+    enum tfm_plat_err_t err;
+    enum plat_otp_lcs_t lcs;
+
+    err = tfm_plat_otp_read(PLAT_OTP_ID_LCS, sizeof(lcs), (uint8_t*)&lcs);
+    if (err != TFM_PLAT_ERR_SUCCESS) {
+        return err;
+    }
+
+    BOOT_LOG_INF("Beginning BL2 provisioning");
+
+#ifdef TFM_DUMMY_PROVISIONING
+    BOOT_LOG_WRN("%s%s%s%s",
+                 "\033[1;31m",
+                 "TFM_DUMMY_PROVISIONING is not suitable for production! ",
+                 "This device is \033[1;1mNOT SECURE",
+                 "\033[0m");
+#endif /* TFM_DUMMY_PROVISIONING */
+
+    if (lcs == PLAT_OTP_LCS_ASSEMBLY_AND_TEST) {
+        if (bl2_assembly_and_test_prov_data.magic != ASSEMBLY_AND_TEST_PROV_DATA_MAGIC) {
+            BOOT_LOG_ERR("No valid ASSEMBLY_AND_TEST provisioning data found");
+            return TFM_PLAT_ERR_INVALID_INPUT;
+        }
+
+        err = provision_assembly_and_test();
+        if (err != TFM_PLAT_ERR_SUCCESS) {
+            return err;
+        }
+    }
+
+    return TFM_PLAT_ERR_SUCCESS;
+}
diff --git a/platform/ext/target/arm/rss/config.cmake b/platform/ext/target/arm/rss/config.cmake
index 2a040f2..f5269d2 100644
--- a/platform/ext/target/arm/rss/config.cmake
+++ b/platform/ext/target/arm/rss/config.cmake
@@ -8,6 +8,8 @@
 set(CRYPTO_HW_ACCELERATOR               ON         CACHE BOOL     "Whether to enable the crypto hardware accelerator on supported platforms")
 set(CRYPTO_NV_SEED                      OFF        CACHE BOOL     "Use stored NV seed to provide entropy")
 set(PLATFORM_DEFAULT_OTP                OFF        CACHE BOOL     "Use trusted on-chip flash to implement OTP memory")
+set(PLATFORM_DEFAULT_CRYPTO_KEYS        OFF        CACHE BOOL     "Use default crypto keys implementation.")
+set(PLATFORM_DEFAULT_PROVISIONING       OFF        CACHE BOOL     "Use default provisioning implementation")
 # CFB mode is not supported by CC312
 set(TFM_CRYPTO_TEST_ALG_CFB             OFF        CACHE BOOL     "Test CFB cryptography mode")
 set(PLATFORM_DEFAULT_NV_COUNTERS        OFF        CACHE BOOL     "Use default nv counter implementation.")
diff --git a/platform/ext/target/arm/rss/crypto_keys.c b/platform/ext/target/arm/rss/crypto_keys.c
index ac36768..31b8350 100644
--- a/platform/ext/target/arm/rss/crypto_keys.c
+++ b/platform/ext/target/arm/rss/crypto_keys.c
@@ -19,6 +19,7 @@
 #include "tfm_builtin_key_loader.h"
 #include "kmu_drv.h"
 #include "device_definition.h"
+#include "tfm_plat_otp.h"
 
 #define TFM_NS_PARTITION_ID -1
 
@@ -71,6 +72,33 @@
         }
         break;
 #endif /* TFM_PARTITION_DELEGATED_ATTESTATION */
+    case TFM_BUILTIN_KEY_ID_HOST_S_ROTPK:
+        switch(owner) {
+        case TFM_NS_PARTITION_ID:
+            *usage = PSA_KEY_USAGE_VERIFY_HASH;
+            break;
+        default:
+            return TFM_PLAT_ERR_NOT_PERMITTED;
+        }
+        break;
+    case TFM_BUILTIN_KEY_ID_HOST_NS_ROTPK:
+        switch(owner) {
+        case TFM_NS_PARTITION_ID:
+            *usage = PSA_KEY_USAGE_VERIFY_HASH;
+            break;
+        default:
+            return TFM_PLAT_ERR_NOT_PERMITTED;
+        }
+        break;
+    case TFM_BUILTIN_KEY_ID_HOST_CCA_ROTPK:
+        switch(owner) {
+        case TFM_NS_PARTITION_ID:
+            *usage = PSA_KEY_USAGE_VERIFY_HASH;
+            break;
+        default:
+            return TFM_PLAT_ERR_NOT_PERMITTED;
+        }
+        break;
     default:
         return TFM_PLAT_ERR_UNSUPPORTED;
     }
@@ -101,6 +129,21 @@
                                                                    TFM_BUILTIN_KEY_LOADER_KEY_LOCATION);
         break;
 #endif /* TFM_PARTITION_DELEGATED_ATTESTATION */
+    case TFM_BUILTIN_KEY_ID_HOST_S_ROTPK:
+        *slot_number = TFM_BUILTIN_KEY_SLOT_HOST_S_ROTPK;
+        *lifetime = PSA_KEY_LIFETIME_FROM_PERSISTENCE_AND_LOCATION(PSA_KEY_LIFETIME_PERSISTENT,
+                                                                   TFM_BUILTIN_KEY_LOADER_KEY_LOCATION);
+        break;
+    case TFM_BUILTIN_KEY_ID_HOST_NS_ROTPK:
+        *slot_number = TFM_BUILTIN_KEY_SLOT_HOST_NS_ROTPK;
+        *lifetime = PSA_KEY_LIFETIME_FROM_PERSISTENCE_AND_LOCATION(PSA_KEY_LIFETIME_PERSISTENT,
+                                                                   TFM_BUILTIN_KEY_LOADER_KEY_LOCATION);
+        break;
+    case TFM_BUILTIN_KEY_ID_HOST_CCA_ROTPK:
+        *slot_number = TFM_BUILTIN_KEY_SLOT_HOST_CCA_ROTPK;
+        *lifetime = PSA_KEY_LIFETIME_FROM_PERSISTENCE_AND_LOCATION(PSA_KEY_LIFETIME_PERSISTENT,
+                                                                   TFM_BUILTIN_KEY_LOADER_KEY_LOCATION);
+        break;
     default:
         return PSA_ERROR_DOES_NOT_EXIST;
     }
@@ -259,6 +302,63 @@
 }
 #endif /* TFM_PARTITION_DELEGATED_ATTESTATION */
 
+static enum tfm_plat_err_t tfm_plat_get_host_s_rotpk(uint8_t *buf, size_t buf_len,
+                                                     size_t *key_len,
+                                                     size_t *key_bits,
+                                                     psa_algorithm_t *algorithm,
+                                                     psa_key_type_t *type)
+{
+    if (buf_len < 96) {
+        return TFM_PLAT_ERR_SYSTEM_ERR;
+    }
+
+    /* P384 public keys are 96 bytes in length */
+    *key_len = 96;
+    *key_bits = 384;
+    *algorithm = PSA_ALG_ECDSA(PSA_ALG_SHA_384);
+    *type = PSA_KEY_TYPE_ECC_PUBLIC_KEY(PSA_ECC_FAMILY_SECP_R1);
+
+    return tfm_plat_otp_read(PLAT_OTP_ID_HOST_ROTPK_S, buf_len, buf);
+}
+
+static enum tfm_plat_err_t tfm_plat_get_host_ns_rotpk(uint8_t *buf, size_t buf_len,
+                                                      size_t *key_len,
+                                                      size_t *key_bits,
+                                                      psa_algorithm_t *algorithm,
+                                                      psa_key_type_t *type)
+{
+    if (buf_len < 96) {
+        return TFM_PLAT_ERR_SYSTEM_ERR;
+    }
+
+    /* P384 public keys are 96 bytes in length */
+    *key_len = 96;
+    *key_bits = 384;
+    *algorithm = PSA_ALG_ECDSA(PSA_ALG_SHA_384);
+    *type = PSA_KEY_TYPE_ECC_PUBLIC_KEY(PSA_ECC_FAMILY_SECP_R1);
+
+    return tfm_plat_otp_read(PLAT_OTP_ID_HOST_ROTPK_NS, buf_len, buf);
+}
+
+static enum tfm_plat_err_t tfm_plat_get_host_cca_rotpk(uint8_t *buf, size_t buf_len,
+                                                       size_t *key_len,
+                                                       size_t *key_bits,
+                                                       psa_algorithm_t *algorithm,
+                                                       psa_key_type_t *type)
+{
+    if (buf_len < 96) {
+        return TFM_PLAT_ERR_SYSTEM_ERR;
+    }
+
+    /* P384 public keys are 96 bytes in length */
+    *key_len = 96;
+    *key_bits = 384;
+    *algorithm = PSA_ALG_ECDSA(PSA_ALG_SHA_384);
+    *type = PSA_KEY_TYPE_ECC_PUBLIC_KEY(PSA_ECC_FAMILY_SECP_R1);
+
+    return tfm_plat_otp_read(PLAT_OTP_ID_HOST_ROTPK_CCA, buf_len, buf);
+}
+
 enum tfm_plat_err_t tfm_plat_load_builtin_keys(void)
 {
     psa_status_t err;
@@ -266,7 +366,7 @@
     psa_key_attributes_t attr = PSA_KEY_ATTRIBUTES_INIT;
     enum tfm_plat_err_t plat_err;
     /* The KMU requires word alignment */
-    uint8_t __ALIGNED(4) buf[48];
+    uint8_t __ALIGNED(4) buf[96];
     size_t key_len;
     size_t key_bits;
     psa_algorithm_t algorithm;
@@ -325,5 +425,56 @@
     }
 #endif /* TFM_PARTITION_DELEGATED_ATTESTATION */
 
+    /* HOST S ROTPK */
+    plat_err = tfm_plat_get_host_s_rotpk(buf, sizeof(buf), &key_len, &key_bits,
+                                         &algorithm, &type);
+    if (plat_err != TFM_PLAT_ERR_SUCCESS) {
+        return plat_err;
+    }
+    key_id.MBEDTLS_PRIVATE(key_id) = TFM_BUILTIN_KEY_ID_HOST_S_ROTPK;
+    key_id.MBEDTLS_PRIVATE(owner) = 0;
+    psa_set_key_id(&attr, key_id);
+    psa_set_key_bits(&attr, key_bits);
+    psa_set_key_algorithm(&attr, algorithm);
+    psa_set_key_type(&attr, type);
+    err = tfm_builtin_key_loader_load_key(buf, key_len, &attr);
+    if (err != PSA_SUCCESS) {
+        return TFM_PLAT_ERR_SYSTEM_ERR;
+    }
+
+    /* HOST NS ROTPK */
+    plat_err = tfm_plat_get_host_ns_rotpk(buf, sizeof(buf), &key_len, &key_bits,
+                                         &algorithm, &type);
+    if (plat_err != TFM_PLAT_ERR_SUCCESS) {
+        return plat_err;
+    }
+    key_id.MBEDTLS_PRIVATE(key_id) = TFM_BUILTIN_KEY_ID_HOST_NS_ROTPK;
+    key_id.MBEDTLS_PRIVATE(owner) = 0;
+    psa_set_key_id(&attr, key_id);
+    psa_set_key_bits(&attr, key_bits);
+    psa_set_key_algorithm(&attr, algorithm);
+    psa_set_key_type(&attr, type);
+    err = tfm_builtin_key_loader_load_key(buf, key_len, &attr);
+    if (err != PSA_SUCCESS) {
+        return TFM_PLAT_ERR_SYSTEM_ERR;
+    }
+
+    /* HOST CCA ROTPK */
+    plat_err = tfm_plat_get_host_cca_rotpk(buf, sizeof(buf), &key_len, &key_bits,
+                                         &algorithm, &type);
+    if (plat_err != TFM_PLAT_ERR_SUCCESS) {
+        return plat_err;
+    }
+    key_id.MBEDTLS_PRIVATE(key_id) = TFM_BUILTIN_KEY_ID_HOST_CCA_ROTPK;
+    key_id.MBEDTLS_PRIVATE(owner) = 0;
+    psa_set_key_id(&attr, key_id);
+    psa_set_key_bits(&attr, key_bits);
+    psa_set_key_algorithm(&attr, algorithm);
+    psa_set_key_type(&attr, type);
+    err = tfm_builtin_key_loader_load_key(buf, key_len, &attr);
+    if (err != PSA_SUCCESS) {
+        return TFM_PLAT_ERR_SYSTEM_ERR;
+    }
+
     return TFM_PLAT_ERR_SUCCESS;
 }
diff --git a/platform/ext/target/arm/rss/otp_lcm.c b/platform/ext/target/arm/rss/otp_lcm.c
index d85fe5f..fb0f6c4 100644
--- a/platform/ext/target/arm/rss/otp_lcm.c
+++ b/platform/ext/target/arm/rss/otp_lcm.c
@@ -30,6 +30,9 @@
     uint32_t bl2_image_hash_zero_bits;
     uint32_t bl1_rotpk_0_zero_bits;
     uint32_t secure_debug_pk_zero_bits;
+    uint32_t host_rotpk_s_zero_bits;
+    uint32_t host_rotpk_ns_zero_bits;
+    uint32_t host_rotpk_cca_zero_bits;
 
     uint32_t iak_len;
     uint32_t iak_type;
@@ -54,6 +57,11 @@
     uint32_t host_nv_counter[3][128];
 
     uint32_t bl1_rotpk_0[14];
+
+    uint32_t host_rotpk_s[24];
+    uint32_t host_rotpk_ns[24];
+    uint32_t host_rotpk_cca[24];
+
     uint32_t bl1_2_image[BL1_2_CODE_SIZE / sizeof(uint32_t)];
 };
 
@@ -199,29 +207,6 @@
         return err;
     }
 
-    err = verify_zero_bits_count(USER_AREA_OFFSET(iak_len),
-                                 USER_AREA_SIZE(iak_len),
-                                 USER_AREA_OFFSET(iak_len_zero_bits));
-    if (err != TFM_PLAT_ERR_SUCCESS) {
-        return err;
-    }
-
-    err = verify_zero_bits_count(USER_AREA_OFFSET(iak_type),
-                                 USER_AREA_SIZE(iak_type),
-                                 USER_AREA_OFFSET(iak_type_zero_bits));
-    if (err != TFM_PLAT_ERR_SUCCESS) {
-        return err;
-    }
-
-#ifdef ATTEST_INCLUDE_COSE_KEY_ID
-    err = verify_zero_bits_count(USER_AREA_OFFSET(iak_id),
-                                 USER_AREA_SIZE(iak_id),
-                                 USER_AREA_OFFSET(iak_id_zero_bits));
-    if (err != TFM_PLAT_ERR_SUCCESS) {
-        return err;
-    }
-#endif /* ATTEST_INCLUDE_COSE_KEY_ID */
-
     /* The rotpk (used as the ROTPK for the RSS rutime) is special as it's zero
      * count is stored in the cm_config_2 field, but it's not checked so we
      * still need to do it manually
@@ -284,6 +269,27 @@
     }
 #endif /* BL1 */
 
+    err = verify_zero_bits_count(USER_AREA_OFFSET(host_rotpk_s),
+                                 USER_AREA_SIZE(host_rotpk_s),
+                                 USER_AREA_OFFSET(host_rotpk_s_zero_bits));
+    if (err != TFM_PLAT_ERR_SUCCESS) {
+        return err;
+    }
+
+    err = verify_zero_bits_count(USER_AREA_OFFSET(host_rotpk_ns),
+                                 USER_AREA_SIZE(host_rotpk_ns),
+                                 USER_AREA_OFFSET(host_rotpk_ns_zero_bits));
+    if (err != TFM_PLAT_ERR_SUCCESS) {
+        return err;
+    }
+
+    err = verify_zero_bits_count(USER_AREA_OFFSET(host_rotpk_cca),
+                                 USER_AREA_SIZE(host_rotpk_cca),
+                                 USER_AREA_OFFSET(host_rotpk_cca_zero_bits));
+    if (err != TFM_PLAT_ERR_SUCCESS) {
+        return err;
+    }
+
     return TFM_PLAT_ERR_SUCCESS;
 }
 
@@ -407,8 +413,6 @@
         return otp_read(OTP_OFFSET(huk), OTP_SIZE(huk), out_len, out);
     case PLAT_OTP_ID_GUK:
         return otp_read(OTP_OFFSET(guk), OTP_SIZE(guk), out_len, out);
-    case PLAT_OTP_ID_IAK:
-        return 0;
 
     case PLAT_OTP_ID_BOOT_SEED:
         return otp_read(USER_AREA_OFFSET(boot_seed), USER_AREA_SIZE(boot_seed),
@@ -429,16 +433,6 @@
         return otp_read(USER_AREA_OFFSET(profile_definition),
                         USER_AREA_SIZE(profile_definition), out_len, out);
 
-    case PLAT_OTP_ID_IAK_LEN:
-        return otp_read(USER_AREA_OFFSET(iak_len),
-                        USER_AREA_SIZE(iak_len), out_len, out);
-    case PLAT_OTP_ID_IAK_TYPE:
-        return otp_read(USER_AREA_OFFSET(iak_type),
-                        USER_AREA_SIZE(iak_type), out_len, out);
-    case PLAT_OTP_ID_IAK_ID:
-        return otp_read(USER_AREA_OFFSET(iak_id),
-                        USER_AREA_SIZE(iak_id), out_len, out);
-
     case PLAT_OTP_ID_BL2_ROTPK_0:
         return otp_read(OTP_OFFSET(rotpk), OTP_SIZE(rotpk), out_len, out);
     case PLAT_OTP_ID_NV_COUNTER_BL2_0:
@@ -504,6 +498,16 @@
         return otp_read(USER_AREA_OFFSET(secure_debug_pk),
                         USER_AREA_SIZE(secure_debug_pk), out_len, out);
 
+    case PLAT_OTP_ID_HOST_ROTPK_S:
+        return otp_read(USER_AREA_OFFSET(host_rotpk_s),
+                        USER_AREA_SIZE(host_rotpk_s), out_len, out);
+    case PLAT_OTP_ID_HOST_ROTPK_NS:
+        return otp_read(USER_AREA_OFFSET(host_rotpk_ns),
+                        USER_AREA_SIZE(host_rotpk_ns), out_len, out);
+    case PLAT_OTP_ID_HOST_ROTPK_CCA:
+        return otp_read(USER_AREA_OFFSET(host_rotpk_cca),
+                        USER_AREA_SIZE(host_rotpk_cca), out_len, out);
+
     default:
         return TFM_PLAT_ERR_UNSUPPORTED;
     }
@@ -543,8 +547,6 @@
     case PLAT_OTP_ID_GUK:
         return otp_write(OTP_OFFSET(guk), OTP_SIZE(guk), in_len, in,
                          0);
-    case PLAT_OTP_ID_IAK:
-        return 0;
 
     case PLAT_OTP_ID_BOOT_SEED:
         return otp_write(USER_AREA_OFFSET(boot_seed), USER_AREA_SIZE(boot_seed),
@@ -568,19 +570,6 @@
                          USER_AREA_SIZE(profile_definition), in_len,
                          in, USER_AREA_OFFSET(profile_definition_zero_bits));
 
-    case PLAT_OTP_ID_IAK_LEN:
-        return otp_write(USER_AREA_OFFSET(iak_len),
-                         USER_AREA_SIZE(iak_len), in_len,
-                         in, USER_AREA_OFFSET(iak_len_zero_bits));
-    case PLAT_OTP_ID_IAK_TYPE:
-        return otp_write(USER_AREA_OFFSET(iak_type),
-                         USER_AREA_SIZE(iak_type), in_len,
-                         in, USER_AREA_OFFSET(iak_type_zero_bits));
-    case PLAT_OTP_ID_IAK_ID:
-        return otp_write(USER_AREA_OFFSET(iak_id),
-                         USER_AREA_SIZE(iak_id), in_len,
-                         in, USER_AREA_OFFSET(iak_id_zero_bits));
-
     case PLAT_OTP_ID_BL2_ROTPK_0:
         return otp_write(OTP_OFFSET(rotpk), OTP_SIZE(rotpk), in_len, in, 0);
     case PLAT_OTP_ID_NV_COUNTER_BL2_0:
@@ -654,6 +643,19 @@
                          USER_AREA_SIZE(secure_debug_pk), in_len, in,
                          USER_AREA_OFFSET(secure_debug_pk_zero_bits));
 
+    case PLAT_OTP_ID_HOST_ROTPK_S:
+        return otp_write(USER_AREA_OFFSET(host_rotpk_s),
+                         USER_AREA_SIZE(host_rotpk_s), in_len, in,
+                         USER_AREA_OFFSET(host_rotpk_s_zero_bits));
+    case PLAT_OTP_ID_HOST_ROTPK_NS:
+        return otp_write(USER_AREA_OFFSET(host_rotpk_ns),
+                         USER_AREA_SIZE(host_rotpk_ns), in_len, in,
+                         USER_AREA_OFFSET(host_rotpk_ns_zero_bits));
+    case PLAT_OTP_ID_HOST_ROTPK_CCA:
+        return otp_write(USER_AREA_OFFSET(host_rotpk_cca),
+                         USER_AREA_SIZE(host_rotpk_cca), in_len, in,
+                         USER_AREA_OFFSET(host_rotpk_cca_zero_bits));
+
     default:
         return TFM_PLAT_ERR_UNSUPPORTED;
     }
@@ -670,8 +672,6 @@
     case PLAT_OTP_ID_GUK:
         *size = OTP_SIZE(guk);
         break;
-    case PLAT_OTP_ID_IAK:
-        return 0;
 
     case PLAT_OTP_ID_BOOT_SEED:
         *size = USER_AREA_SIZE(boot_seed);
@@ -692,16 +692,6 @@
         *size = USER_AREA_SIZE(profile_definition);
         break;
 
-    case PLAT_OTP_ID_IAK_LEN:
-        *size = USER_AREA_SIZE(iak_len);
-        break;
-    case PLAT_OTP_ID_IAK_TYPE:
-        *size = USER_AREA_SIZE(iak_type);
-        break;
-    case PLAT_OTP_ID_IAK_ID:
-        *size = USER_AREA_SIZE(iak_id);
-        break;
-
     case PLAT_OTP_ID_BL2_ROTPK_0:
         *size = OTP_SIZE(rotpk);
         break;
@@ -768,6 +758,16 @@
         *size = USER_AREA_SIZE(secure_debug_pk);
         break;
 
+    case PLAT_OTP_ID_HOST_ROTPK_S:
+        *size = USER_AREA_SIZE(host_rotpk_s);
+        break;
+    case PLAT_OTP_ID_HOST_ROTPK_NS:
+        *size = USER_AREA_SIZE(host_rotpk_ns);
+        break;
+    case PLAT_OTP_ID_HOST_ROTPK_CCA:
+        *size = USER_AREA_SIZE(host_rotpk_cca);
+        break;
+
     default:
         return TFM_PLAT_ERR_UNSUPPORTED;
     }
diff --git a/platform/ext/target/arm/rss/platform_builtin_key_ids.h b/platform/ext/target/arm/rss/platform_builtin_key_ids.h
new file mode 100644
index 0000000..b45d275
--- /dev/null
+++ b/platform/ext/target/arm/rss/platform_builtin_key_ids.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2022, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __PLATFORM_BUILTIN_KEY_IDS_H__
+#define __PLATFORM_BUILTIN_KEY_IDS_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief The PSA driver location for TF-M builtin keys. Arbitrary within the
+ * ranges documented at
+ * https://armmbed.github.io/mbed-crypto/html/api/keys/lifetimes.html#c.psa_key_location_t
+ */
+#define TFM_BUILTIN_KEY_LOADER_KEY_LOCATION ((psa_key_location_t)0x800001)
+
+/**
+ * \brief The persistent key identifiers for TF-M builtin keys.
+ *
+ * The value of TFM_BUILTIN_KEY_ID_MIN (and therefore of the whole range) is
+ * completely arbitrary except for being inside the PSA builtin keys range.
+ *
+ */
+enum tfm_key_id_builtin_t {
+    TFM_BUILTIN_KEY_ID_MIN = 0x7fff815Bu,
+    TFM_BUILTIN_KEY_ID_HUK,
+    TFM_BUILTIN_KEY_ID_IAK,
+#ifdef TFM_PARTITION_DELEGATED_ATTESTATION
+    TFM_BUILTIN_KEY_ID_DAK_SEED,
+#endif /* TFM_PARTITION_DELEGATED_ATTESTATION */
+    TFM_BUILTIN_KEY_ID_PLAT_SPECIFIC_MIN = 0x7FFF816Bu,
+    TFM_BUILTIN_KEY_ID_HOST_S_ROTPK,
+    TFM_BUILTIN_KEY_ID_HOST_NS_ROTPK,
+    TFM_BUILTIN_KEY_ID_HOST_CCA_ROTPK,
+    TFM_BUILTIN_KEY_ID_MAX = 0x7FFF817Bu,
+};
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* __PLATFORM_BUILTIN_KEY_IDS_H__ */
diff --git a/platform/ext/target/arm/rss/platform_builtin_key_loader_ids.h b/platform/ext/target/arm/rss/platform_builtin_key_loader_ids.h
new file mode 100644
index 0000000..c2b66f0
--- /dev/null
+++ b/platform/ext/target/arm/rss/platform_builtin_key_loader_ids.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2022, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef TFM_BUILTIN_KEY_LOADER_IDS_H
+#define TFM_BUILTIN_KEY_LOADER_IDS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define TFM_BUILTIN_MAX_KEY_LEN 96
+
+enum psa_drv_slot_number_t {
+    TFM_BUILTIN_KEY_SLOT_HUK = 0,
+    TFM_BUILTIN_KEY_SLOT_IAK,
+#ifdef TFM_PARTITION_DELEGATED_ATTESTATION
+    TFM_BUILTIN_KEY_SLOT_DAK_SEED,
+#endif /* TFM_PARTITION_DELEGATED_ATTESTATION */
+    TFM_BUILTIN_KEY_SLOT_HOST_S_ROTPK,
+    TFM_BUILTIN_KEY_SLOT_HOST_NS_ROTPK,
+    TFM_BUILTIN_KEY_SLOT_HOST_CCA_ROTPK,
+    TFM_BUILTIN_KEY_SLOT_MAX,
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* TFM_BUILTIN_KEY_LOADER_IDS_H */
diff --git a/platform/ext/target/arm/rss/platform_otp_ids.h b/platform/ext/target/arm/rss/platform_otp_ids.h
new file mode 100644
index 0000000..015276a
--- /dev/null
+++ b/platform/ext/target/arm/rss/platform_otp_ids.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2022, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __PLATFORM_OTP_IDS_H__
+#define __PLATFORM_OTP_IDS_H__
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum tfm_otp_element_id_t {
+    PLAT_OTP_ID_HUK = 0,
+    PLAT_OTP_ID_GUK,
+    PLAT_OTP_ID_IAK,
+    PLAT_OTP_ID_IAK_LEN,
+    PLAT_OTP_ID_IAK_TYPE,
+    PLAT_OTP_ID_IAK_ID,
+
+    PLAT_OTP_ID_BOOT_SEED,
+    PLAT_OTP_ID_LCS,
+    PLAT_OTP_ID_IMPLEMENTATION_ID,
+    PLAT_OTP_ID_CERT_REF,
+    PLAT_OTP_ID_VERIFICATION_SERVICE_URL,
+    PLAT_OTP_ID_PROFILE_DEFINITION,
+
+    PLAT_OTP_ID_BL2_ROTPK_0,
+    PLAT_OTP_ID_BL2_ROTPK_1,
+    PLAT_OTP_ID_BL2_ROTPK_2,
+    PLAT_OTP_ID_BL2_ROTPK_3,
+
+    PLAT_OTP_ID_NV_COUNTER_BL2_0,
+    PLAT_OTP_ID_NV_COUNTER_BL2_1,
+    PLAT_OTP_ID_NV_COUNTER_BL2_2,
+    PLAT_OTP_ID_NV_COUNTER_BL2_3,
+
+    PLAT_OTP_ID_NV_COUNTER_NS_0,
+    PLAT_OTP_ID_NV_COUNTER_NS_1,
+    PLAT_OTP_ID_NV_COUNTER_NS_2,
+
+    PLAT_OTP_ID_KEY_BL2_ENCRYPTION,
+    PLAT_OTP_ID_BL1_2_IMAGE,
+    PLAT_OTP_ID_BL1_2_IMAGE_HASH,
+    PLAT_OTP_ID_BL2_IMAGE_HASH,
+    PLAT_OTP_ID_BL1_ROTPK_0,
+
+    PLAT_OTP_ID_NV_COUNTER_BL1_0,
+
+    PLAT_OTP_ID_ENTROPY_SEED,
+
+    PLAT_OTP_ID_SECURE_DEBUG_PK,
+
+    PLAT_OTP_ID_HOST_ROTPK_S,
+    PLAT_OTP_ID_HOST_ROTPK_NS,
+    PLAT_OTP_ID_HOST_ROTPK_CCA,
+
+    PLAT_OTP_ID_MAX = UINT32_MAX,
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __PLATFORM_OTP_IDS_H__ */
diff --git a/platform/ext/target/arm/rss/provisioning.c b/platform/ext/target/arm/rss/provisioning.c
new file mode 100644
index 0000000..6e2cb4f
--- /dev/null
+++ b/platform/ext/target/arm/rss/provisioning.c
@@ -0,0 +1,304 @@
+/*
+ * Copyright (c) 2021-2022, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "tfm_plat_provisioning.h"
+
+#include "cmsis_compiler.h"
+#include "tfm_plat_otp.h"
+#include "tfm_attest_hal.h"
+#include "psa/crypto.h"
+#include "tfm_spm_log.h"
+
+#include <string.h>
+
+#define ASSEMBLY_AND_TEST_PROV_DATA_MAGIC 0xC0DEFEED
+#define PSA_ROT_PROV_DATA_MAGIC           0xBEEFFEED
+
+__PACKED_STRUCT tfm_assembly_and_test_provisioning_data_t {
+    uint32_t magic;
+    uint8_t huk[32];
+};
+
+__PACKED_STRUCT tfm_psa_rot_provisioning_data_t {
+    uint32_t magic;
+    uint8_t host_rotpk_s[96];
+    uint8_t host_rotpk_ns[96];
+    uint8_t host_rotpk_cca[96];
+
+    uint8_t boot_seed[32];
+    uint8_t implementation_id[32];
+    uint8_t cert_ref[32];
+    uint8_t verification_service_url[32];
+    uint8_t profile_definition[32];
+};
+
+#ifdef TFM_DUMMY_PROVISIONING
+static const struct tfm_assembly_and_test_provisioning_data_t assembly_and_test_prov_data = {
+    ASSEMBLY_AND_TEST_PROV_DATA_MAGIC,
+    /* HUK */
+    {
+        0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+        0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+        0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+        0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    },
+};
+
+static const struct tfm_psa_rot_provisioning_data_t psa_rot_prov_data = {
+    PSA_ROT_PROV_DATA_MAGIC,
+    /* HOST_ROTPK_S */
+    {
+        0x09, 0x20, 0x59, 0xde, 0xc5, 0x1b, 0xe2, 0x96,
+        0xfe, 0x4b, 0xa0, 0x16, 0x20, 0xac, 0xd7, 0xce,
+        0xe2, 0x1e, 0xd5, 0xbf, 0x74, 0x4f, 0xe4, 0x47,
+        0xab, 0x1f, 0xe4, 0xcb, 0x91, 0x52, 0x94, 0xb2,
+        0xf2, 0xff, 0xaf, 0x3a, 0x47, 0x26, 0x0e, 0x13,
+        0x4f, 0x8f, 0x2c, 0x1b, 0x5e, 0xde, 0xe8, 0x9e,
+        0xdd, 0x2e, 0x1c, 0xf1, 0x0d, 0x3c, 0xc1, 0xee,
+        0x32, 0x92, 0x9d, 0x05, 0xca, 0x57, 0x0d, 0x0e,
+        0xbc, 0xd1, 0x72, 0x32, 0xf4, 0x1f, 0x1c, 0xe4,
+        0x48, 0xd8, 0x79, 0x87, 0xfc, 0x3b, 0x2f, 0xf4,
+        0x79, 0xe2, 0xf1, 0x03, 0x1f, 0xf3, 0x4d, 0xbc,
+        0x76, 0x8a, 0x81, 0x19, 0x4a, 0x95, 0x4d, 0xac
+    },
+    /* HOST_ROTPK_NS */
+    {
+        0x09, 0x20, 0x59, 0xde, 0xc5, 0x1b, 0xe2, 0x96,
+        0xfe, 0x4b, 0xa0, 0x16, 0x20, 0xac, 0xd7, 0xce,
+        0xe2, 0x1e, 0xd5, 0xbf, 0x74, 0x4f, 0xe4, 0x47,
+        0xab, 0x1f, 0xe4, 0xcb, 0x91, 0x52, 0x94, 0xb2,
+        0xf2, 0xff, 0xaf, 0x3a, 0x47, 0x26, 0x0e, 0x13,
+        0x4f, 0x8f, 0x2c, 0x1b, 0x5e, 0xde, 0xe8, 0x9e,
+        0xdd, 0x2e, 0x1c, 0xf1, 0x0d, 0x3c, 0xc1, 0xee,
+        0x32, 0x92, 0x9d, 0x05, 0xca, 0x57, 0x0d, 0x0e,
+        0xbc, 0xd1, 0x72, 0x32, 0xf4, 0x1f, 0x1c, 0xe4,
+        0x48, 0xd8, 0x79, 0x87, 0xfc, 0x3b, 0x2f, 0xf4,
+        0x79, 0xe2, 0xf1, 0x03, 0x1f, 0xf3, 0x4d, 0xbc,
+        0x76, 0x8a, 0x81, 0x19, 0x4a, 0x95, 0x4d, 0xac
+    },
+    /* HOST_ROTPK_CCA */
+    {
+        0x09, 0x20, 0x59, 0xde, 0xc5, 0x1b, 0xe2, 0x96,
+        0xfe, 0x4b, 0xa0, 0x16, 0x20, 0xac, 0xd7, 0xce,
+        0xe2, 0x1e, 0xd5, 0xbf, 0x74, 0x4f, 0xe4, 0x47,
+        0xab, 0x1f, 0xe4, 0xcb, 0x91, 0x52, 0x94, 0xb2,
+        0xf2, 0xff, 0xaf, 0x3a, 0x47, 0x26, 0x0e, 0x13,
+        0x4f, 0x8f, 0x2c, 0x1b, 0x5e, 0xde, 0xe8, 0x9e,
+        0xdd, 0x2e, 0x1c, 0xf1, 0x0d, 0x3c, 0xc1, 0xee,
+        0x32, 0x92, 0x9d, 0x05, 0xca, 0x57, 0x0d, 0x0e,
+        0xbc, 0xd1, 0x72, 0x32, 0xf4, 0x1f, 0x1c, 0xe4,
+        0x48, 0xd8, 0x79, 0x87, 0xfc, 0x3b, 0x2f, 0xf4,
+        0x79, 0xe2, 0xf1, 0x03, 0x1f, 0xf3, 0x4d, 0xbc,
+        0x76, 0x8a, 0x81, 0x19, 0x4a, 0x95, 0x4d, 0xac
+    },
+    /* boot seed */
+    {
+        0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7,
+        0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF,
+        0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7,
+        0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF,
+    },
+    /* implementation id */
+    {
+        0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+        0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB,
+        0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC,
+        0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD,
+    },
+    /* certification reference */
+    "0604565272829-10010",
+    /* verification_service_url */
+    "www.trustedfirmware.org",
+    /* attestation_profile_definition */
+#if defined(ATTEST_TOKEN_PROFILE_PSA_IOT_1)
+    "PSA_IOT_PROFILE_1",
+#elif defined(ATTEST_TOKEN_PROFILE_PSA_2_0_0)
+    "http://arm.com/psa/2.0.0",
+#elif defined(ATTEST_TOKEN_PROFILE_ARM_CCA)
+    "http://arm.com/CCA-SSD/1.0.0",
+#else
+#ifdef TFM_PARTITION_INITIAL_ATTESTATION
+#error "Attestation token profile is incorrect"
+#else
+    "UNDEFINED",
+#endif /* TFM_PARTITION_INITIAL_ATTESTATION */
+#endif
+};
+#else
+static struct tfm_assembly_and_test_provisioning_data_t assembly_and_test_prov_data;
+static struct tfm_psa_rot_provisioning_data_t psa_rot_prov_data;
+#endif /* TFM_DUMMY_PROVISIONING */
+
+void tfm_plat_provisioning_check_for_dummy_keys(void)
+{
+    uint64_t host_rotpk_s_start;
+
+    tfm_plat_otp_read(PLAT_OTP_ID_HOST_ROTPK_S, sizeof(host_rotpk_s_start),
+                      (uint8_t*)&host_rotpk_s_start);
+
+    if(host_rotpk_s_start == 0xA4906F6DB254B4A9) {
+        SPMLOG_ERRMSG("[WRN]\033[1;31m ");
+        SPMLOG_ERRMSG("This device was provisioned with dummy keys. ");
+        SPMLOG_ERRMSG("This device is \033[1;1mNOT SECURE");
+        SPMLOG_ERRMSG("\033[0m\r\n");
+    }
+
+    memset(&host_rotpk_s_start, 0, sizeof(host_rotpk_s_start));
+}
+
+int tfm_plat_provisioning_is_required(void)
+{
+    enum tfm_plat_err_t err;
+    enum plat_otp_lcs_t lcs;
+
+    err = tfm_plat_otp_read(PLAT_OTP_ID_LCS, sizeof(lcs), (uint8_t*)&lcs);
+    if (err != TFM_PLAT_ERR_SUCCESS) {
+        return err;
+    }
+
+    return lcs == PLAT_OTP_LCS_ASSEMBLY_AND_TEST
+        || lcs == PLAT_OTP_LCS_PSA_ROT_PROVISIONING;
+}
+
+enum tfm_plat_err_t provision_assembly_and_test(void)
+{
+    enum tfm_plat_err_t err;
+    uint32_t new_lcs;
+
+    err = tfm_plat_otp_write(PLAT_OTP_ID_HUK, sizeof(assembly_and_test_prov_data.huk),
+                             assembly_and_test_prov_data.huk);
+    if (err != TFM_PLAT_ERR_SUCCESS) {
+        return err;
+    }
+
+    new_lcs = PLAT_OTP_LCS_PSA_ROT_PROVISIONING;
+    err = tfm_plat_otp_write(PLAT_OTP_ID_LCS, sizeof(new_lcs),
+                             (uint8_t*)&new_lcs);
+    if (err != TFM_PLAT_ERR_SUCCESS) {
+        return err;
+    }
+
+    return err;
+}
+
+enum tfm_plat_err_t provision_psa_rot(void)
+{
+    enum tfm_plat_err_t err;
+    uint32_t new_lcs;
+
+    err = tfm_plat_otp_write(PLAT_OTP_ID_HOST_ROTPK_S,
+                             sizeof(psa_rot_prov_data.host_rotpk_s),
+                             psa_rot_prov_data.host_rotpk_s);
+    if (err != TFM_PLAT_ERR_SUCCESS) {
+        return err;
+    }
+
+    err = tfm_plat_otp_write(PLAT_OTP_ID_HOST_ROTPK_NS,
+                             sizeof(psa_rot_prov_data.host_rotpk_ns),
+                             psa_rot_prov_data.host_rotpk_ns);
+    if (err != TFM_PLAT_ERR_SUCCESS) {
+        return err;
+    }
+
+    err = tfm_plat_otp_write(PLAT_OTP_ID_HOST_ROTPK_CCA,
+                             sizeof(psa_rot_prov_data.host_rotpk_cca),
+                             psa_rot_prov_data.host_rotpk_cca);
+    if (err != TFM_PLAT_ERR_SUCCESS) {
+        return err;
+    }
+
+    err = tfm_plat_otp_write(PLAT_OTP_ID_BOOT_SEED,
+                             sizeof(psa_rot_prov_data.boot_seed),
+                             psa_rot_prov_data.boot_seed);
+    if (err != TFM_PLAT_ERR_SUCCESS) {
+        return err;
+    }
+    err = tfm_plat_otp_write(PLAT_OTP_ID_IMPLEMENTATION_ID,
+                             sizeof(psa_rot_prov_data.implementation_id),
+                             psa_rot_prov_data.implementation_id);
+    if (err != TFM_PLAT_ERR_SUCCESS) {
+        return err;
+    }
+    err = tfm_plat_otp_write(PLAT_OTP_ID_CERT_REF,
+                             sizeof(psa_rot_prov_data.cert_ref),
+                             psa_rot_prov_data.cert_ref);
+    if (err != TFM_PLAT_ERR_SUCCESS) {
+        return err;
+    }
+    err = tfm_plat_otp_write(PLAT_OTP_ID_VERIFICATION_SERVICE_URL,
+                             sizeof(psa_rot_prov_data.verification_service_url),
+                             psa_rot_prov_data.verification_service_url);
+    if (err != TFM_PLAT_ERR_SUCCESS) {
+        return err;
+    }
+    err = tfm_plat_otp_write(PLAT_OTP_ID_PROFILE_DEFINITION,
+                             sizeof(psa_rot_prov_data.profile_definition),
+                             psa_rot_prov_data.profile_definition);
+    if (err != TFM_PLAT_ERR_SUCCESS) {
+        return err;
+    }
+
+    new_lcs = PLAT_OTP_LCS_SECURED;
+    err = tfm_plat_otp_write(PLAT_OTP_ID_LCS,
+                             sizeof(new_lcs),
+                             (uint8_t*)&new_lcs);
+    if (err != TFM_PLAT_ERR_SUCCESS) {
+        return err;
+    }
+
+    return err;
+}
+
+enum tfm_plat_err_t tfm_plat_provisioning_perform(void)
+{
+    enum tfm_plat_err_t err;
+    enum plat_otp_lcs_t lcs;
+
+    err = tfm_plat_otp_read(PLAT_OTP_ID_LCS, sizeof(lcs), (uint8_t*)&lcs);
+    if (err != TFM_PLAT_ERR_SUCCESS) {
+        return err;
+    }
+
+    SPMLOG_INFMSG("[INF] Beginning TF-M provisioning\r\n");
+
+#ifdef TFM_DUMMY_PROVISIONING
+    SPMLOG_ERRMSG("[WRN]\033[1;31m ");
+    SPMLOG_ERRMSG("TFM_DUMMY_PROVISIONING is not suitable for production! ");
+    SPMLOG_ERRMSG("This device is \033[1;1mNOT SECURE");
+    SPMLOG_ERRMSG("\033[0m\r\n");
+#endif /* TFM_DUMMY_PROVISIONING */
+
+    if (lcs == PLAT_OTP_LCS_ASSEMBLY_AND_TEST) {
+        if (assembly_and_test_prov_data.magic != ASSEMBLY_AND_TEST_PROV_DATA_MAGIC) {
+            SPMLOG_ERRMSG("No valid ASSEMBLY_AND_TEST provisioning data found\r\n");
+            return TFM_PLAT_ERR_INVALID_INPUT;
+        }
+
+        err = provision_assembly_and_test();
+        if (err != TFM_PLAT_ERR_SUCCESS) {
+            return err;
+        }
+    }
+
+    err = tfm_plat_otp_read(PLAT_OTP_ID_LCS, sizeof(lcs), (uint8_t*)&lcs);
+    if (err != TFM_PLAT_ERR_SUCCESS) {
+        return err;
+    }
+    if (lcs == PLAT_OTP_LCS_PSA_ROT_PROVISIONING) {
+        if (psa_rot_prov_data.magic != PSA_ROT_PROV_DATA_MAGIC) {
+            SPMLOG_ERRMSG("No valid PSA_ROT provisioning data found\r\n");
+            return TFM_PLAT_ERR_INVALID_INPUT;
+        }
+
+        err = provision_psa_rot();
+        if (err != TFM_PLAT_ERR_SUCCESS) {
+            return err;
+        }
+    }
+
+    return TFM_PLAT_ERR_SUCCESS;
+}
diff --git a/platform/ext/target/arm/rss/rss_comms/rss_comms_permissions_hal.c b/platform/ext/target/arm/rss/rss_comms/rss_comms_permissions_hal.c
index a75f010..47369fc 100644
--- a/platform/ext/target/arm/rss/rss_comms/rss_comms_permissions_hal.c
+++ b/platform/ext/target/arm/rss/rss_comms/rss_comms_permissions_hal.c
@@ -91,7 +91,7 @@
                                                     size_t in_len,
                                                     int32_t type)
 {
-    uint32_t function_id;
+    uint32_t function_id = 0;
 
     switch(handle) {
 #ifdef TFM_PARTITION_DELEGATED_ATTESTATION
@@ -115,7 +115,7 @@
 #endif /* TFM_PARTITION_MEASURED_BOOT */
 #ifdef TFM_PARTITION_CRYPTO
     case TFM_CRYPTO_HANDLE:
-        if (in_len > 1) {
+        if (in_len >= 1) {
             function_id = ((struct tfm_crypto_pack_iovec *)in_vec[0].base)->function_id;
             switch(function_id) {
             case (TFM_CRYPTO_EXPORT_PUBLIC_KEY_SID):
diff --git a/platform/ext/target/arm/rss/tfm_builtin_key_ids.h b/platform/ext/target/arm/rss/tfm_builtin_key_ids.h
new file mode 100644
index 0000000..b45d275
--- /dev/null
+++ b/platform/ext/target/arm/rss/tfm_builtin_key_ids.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2022, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __PLATFORM_BUILTIN_KEY_IDS_H__
+#define __PLATFORM_BUILTIN_KEY_IDS_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief The PSA driver location for TF-M builtin keys. Arbitrary within the
+ * ranges documented at
+ * https://armmbed.github.io/mbed-crypto/html/api/keys/lifetimes.html#c.psa_key_location_t
+ */
+#define TFM_BUILTIN_KEY_LOADER_KEY_LOCATION ((psa_key_location_t)0x800001)
+
+/**
+ * \brief The persistent key identifiers for TF-M builtin keys.
+ *
+ * The value of TFM_BUILTIN_KEY_ID_MIN (and therefore of the whole range) is
+ * completely arbitrary except for being inside the PSA builtin keys range.
+ *
+ */
+enum tfm_key_id_builtin_t {
+    TFM_BUILTIN_KEY_ID_MIN = 0x7fff815Bu,
+    TFM_BUILTIN_KEY_ID_HUK,
+    TFM_BUILTIN_KEY_ID_IAK,
+#ifdef TFM_PARTITION_DELEGATED_ATTESTATION
+    TFM_BUILTIN_KEY_ID_DAK_SEED,
+#endif /* TFM_PARTITION_DELEGATED_ATTESTATION */
+    TFM_BUILTIN_KEY_ID_PLAT_SPECIFIC_MIN = 0x7FFF816Bu,
+    TFM_BUILTIN_KEY_ID_HOST_S_ROTPK,
+    TFM_BUILTIN_KEY_ID_HOST_NS_ROTPK,
+    TFM_BUILTIN_KEY_ID_HOST_CCA_ROTPK,
+    TFM_BUILTIN_KEY_ID_MAX = 0x7FFF817Bu,
+};
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* __PLATFORM_BUILTIN_KEY_IDS_H__ */