Add secure storage service

Implements secure storage service client and provider. The client is
compatible with PSA Internal Trusted Storage API. There are two provider
versions implemented:
  * mock_store is a dummy storage for testing purposes,
  * secure_flash_store is a RAM based proof-of-concept storage solution
    based on the ITS implementation in Trusted Firmware-M.

Some files in this commit were forked from Trusted Firmware-M, keeping
the original license header intact:
    tag name: TF-Mv1.1 (8c22a260e24bc5778e5ce0dbdf9da3ccec1da880)
    tagged commit: a6b336c1509fd5f5522450e3cec0fcd6c060f9c8

From 'secure_fw/partitions/internal_trusted_storage/' of TF-M,
   to 'components/service/secure_storage/provider/secure_flash_store/':
       flash/its_flash.c -> flash/sfs_flash.c
       flash/its_flash.h -> flash/sfs_flash.h
       flash/its_flash_info_internal.c -> flash/sfs_flash_info.c
       flash/its_flash_ram.c -> flash/sfs_flash_ram.c
       flash/its_flash_ram.h -> flash/sfs_flash_ram.h
       flash_fs/its_flash_fs.c -> flash_fs/sfs_flash_fs.c
       flash_fs/its_flash_fs.h -> flash_fs/sfs_flash_fs.h
       flash_fs/its_flash_fs_check_info.h ->
                                     flash_fs/sfs_flash_fs_check_info.h
       flash_fs/its_flash_fs_dblock.c -> flash_fs/sfs_flash_fs_dblock.c
       flash_fs/its_flash_fs_dblock.h -> flash_fs/sfs_flash_fs_dblock.h
       flash_fs/its_flash_fs_mblock.c -> flash_fs/sfs_flash_fs_mblock.c
       flash_fs/its_flash_fs_mblock.h -> flash_fs/sfs_flash_fs_mblock.h
       its_utils.c -> sfs_utils.c
       its_utils.h -> sfs_utils.h
       tfm_internal_trusted_storage.c -> secure_flash_store.c
       tfm_internal_trusted_storage.h -> secure_flash_store.h

Change-Id: If9eb3bb7c8a58364a8da9a0b463015b2bbc160c4
Signed-off-by: Balint Dobszay <balint.dobszay@arm.com>
diff --git a/components/service/common/psa/error.h b/components/service/common/psa/error.h
new file mode 100644
index 0000000..db8ce90
--- /dev/null
+++ b/components/service/common/psa/error.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2020, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef PSA_ERROR_H
+#define PSA_ERROR_H
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+  * Standard PSA error codes for the SPM and RoT Services
+ * As defined in PSA Firmware Framework v1.0
+ */
+
+typedef int32_t psa_status_t;
+
+#define PSA_SUCCESS			            ((psa_status_t)0)
+#define PSA_ERROR_PROGRAMMER_ERROR	    ((psa_status_t)-129)
+#define PSA_ERROR_CONNECTION_REFUSED	((psa_status_t)-130)
+#define PSA_ERROR_CONNECTION_BUSY	    ((psa_status_t)-131)
+#define PSA_ERROR_GENERIC_ERROR		    ((psa_status_t)-132)
+#define PSA_ERROR_NOT_PERMITTED		    ((psa_status_t)-133)
+#define PSA_ERROR_NOT_SUPPORTED		    ((psa_status_t)-134)
+#define PSA_ERROR_INVALID_ARGUMENT	    ((psa_status_t)-135)
+#define PSA_ERROR_INVALID_HANDLE	    ((psa_status_t)-136)
+#define PSA_ERROR_BAD_STATE		        ((psa_status_t)-137)
+#define PSA_ERROR_BUFFER_TOO_SMALL	    ((psa_status_t)-138)
+#define PSA_ERROR_ALREADY_EXISTS	    ((psa_status_t)-139)
+#define PSA_ERROR_DOES_NOT_EXIST	    ((psa_status_t)-140)
+#define PSA_ERROR_INSUFFICIENT_MEMORY	((psa_status_t)-141)
+#define PSA_ERROR_INSUFFICIENT_STORAGE	((psa_status_t)-142)
+#define PSA_ERROR_INSUFFICIENT_DATA	    ((psa_status_t)-143)
+#define PSA_ERROR_SERVICE_FAILURE	    ((psa_status_t)-144)
+#define PSA_ERROR_COMMUNICATION_FAILURE	((psa_status_t)-145)
+#define PSA_ERROR_STORAGE_FAILURE	    ((psa_status_t)-146)
+#define PSA_STATUS_HARDWARE_FAILURE	    ((psa_status_t)-147)
+#define PSA_ERROR_INVALID_SIGNATURE	    ((psa_status_t)-149)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* PSA_ERROR_H */
diff --git a/components/service/secure_storage/client/psa/component.cmake b/components/service/secure_storage/client/psa/component.cmake
new file mode 100644
index 0000000..ae2518b
--- /dev/null
+++ b/components/service/secure_storage/client/psa/component.cmake
@@ -0,0 +1,19 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2020, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+if (NOT DEFINED TGT)
+	message(FATAL_ERROR "mandatory parameter TGT is not defined.")
+endif()
+
+target_sources(${TGT} PRIVATE
+	"${CMAKE_CURRENT_LIST_DIR}/its/its_client.c"
+	)
+
+
+target_include_directories(${TGT}
+	 PRIVATE
+		"${CMAKE_CURRENT_LIST_DIR}/.."
+	)
diff --git a/components/service/secure_storage/client/psa/internal_trusted_storage.h b/components/service/secure_storage/client/psa/internal_trusted_storage.h
new file mode 100644
index 0000000..da69054
--- /dev/null
+++ b/components/service/secure_storage/client/psa/internal_trusted_storage.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2020, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef PSA_INTERNAL_TRUSTED_STORAGE_H
+#define PSA_INTERNAL_TRUSTED_STORAGE_H
+
+#include <psa/error.h>
+#include <psa/storage_common.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * The major version number of the PSA ITS API. It will be incremented on
+ * significant updates that may include breaking changes.
+ */
+#define PSA_ITS_API_VERSION_MAJOR 1
+
+/**
+ * The minor version number of the PSA ITS API. It will be incremented in
+ * small updates that are unlikely to include breaking changes.
+ */
+#define PSA_ITS_API_VERSION_MINOR 0
+
+/**
+ * @brief      Create a new, or modify an existing, uid /value pair.
+ *
+ * @param[in]  uid           The identifier for the data
+ * @param[in]  data_length   The size in bytes of the data in p_data
+ * @param[in]  p_data        A buffer containing the data
+ * @param[in]  create_flags  The flags that the data will be stored with
+ *
+ * @return     A status indicating the success/failure of the operation
+ */
+psa_status_t psa_its_set(psa_storage_uid_t uid,
+			 size_t data_length,
+			 const void *p_data,
+			 psa_storage_create_flags_t create_flags);
+
+/**
+ * @brief      Retrieve data associated with a provided UID.
+ *
+ * @param[in]  uid            The identifier for the data
+ * @param[in]  data_offset    The starting offset of the data requested
+ * @param[in]  data_size      The amount of data requested
+ * @param      p_data         On success, the buffer where the data will be
+ *                            placed
+ * @param      p_data_length  On success, this will contain size of the data
+ *                            placed in p_data
+ *
+ * @return     A status indicating the success/failure of the operation
+ */
+psa_status_t psa_its_get(psa_storage_uid_t uid,
+			 size_t data_offset,
+			 size_t data_size,
+			 void *p_data,
+			 size_t *p_data_length);
+
+/**
+ * @brief      Retrieve the metadata about the provided uid.
+ *
+ * @param[in]  uid     The identifier for the data
+ * @param      p_info  A pointer to the psa_storage_info_t struct that will
+ *                     be populated with the metadata
+ *
+ * @return     A status indicating the success/failure of the operation
+ */
+psa_status_t psa_its_get_info(psa_storage_uid_t uid,
+			      struct psa_storage_info_t *p_info);
+
+/**
+ * @brief      Remove the provided key and its associated data from the storage
+ *
+ * @param[in]  uid   The identifier for the data
+ *
+ * @return     A status indicating the success/failure of the operation
+ */
+psa_status_t psa_its_remove(psa_storage_uid_t uid);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* PSA_INTERNAL_TRUSTED_STORAGE_H */
diff --git a/components/service/secure_storage/client/psa/its/its_client.c b/components/service/secure_storage/client/psa/its/its_client.c
new file mode 100644
index 0000000..54f3efb
--- /dev/null
+++ b/components/service/secure_storage/client/psa/its/its_client.c
@@ -0,0 +1,215 @@
+/*
+ * Copyright (c) 2020, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include "its_client.h"
+#include <psa/internal_trusted_storage.h>
+#include <protocols/service/secure_storage/packed-c/secure_storage_proto.h>
+#include <protocols/rpc/common/packed-c/status.h>
+#include <assert.h>
+#include <string.h>
+
+/* Variables */
+static struct rpc_caller *rpc_caller;
+
+psa_status_t psa_its_client_init(struct rpc_caller *caller)
+{
+	rpc_caller = caller;
+
+	return PSA_SUCCESS;
+}
+
+psa_status_t psa_its_set(psa_storage_uid_t uid,
+			 size_t data_length,
+			 const void *p_data,
+			 psa_storage_create_flags_t create_flags)
+{
+	uint8_t *request;
+	uint8_t *response;
+	size_t request_length = 0;
+	size_t response_length = 0;
+	struct secure_storage_request_set *request_desc;
+	rpc_call_handle handle;
+	rpc_status_t rpc_status = TS_RPC_CALL_ACCEPTED;
+	psa_status_t psa_status = PSA_SUCCESS;
+
+	/* Validating input parameters */
+	if (p_data == NULL)
+		return PSA_ERROR_INVALID_ARGUMENT;
+
+	request_length = sizeof(*request_desc) + data_length;
+	if (request_length < data_length) {
+		/* size_t overflow */
+		return PSA_ERROR_INVALID_ARGUMENT;
+	}
+
+	handle = rpc_caller_begin(rpc_caller, &request, request_length);
+
+	if (handle) {
+		/* Populating request descriptor */
+		request_desc = (struct secure_storage_request_set *)request;
+		request_desc->uid = uid;
+		request_desc->data_length = data_length;
+		request_desc->create_flags = create_flags;
+		memcpy(&request_desc->p_data, p_data, data_length);
+
+		rpc_status = rpc_caller_invoke(rpc_caller, handle, TS_SECURE_STORAGE_OPCODE_SET,
+						(uint32_t *)&psa_status, &response,
+						&response_length);
+
+		if (rpc_status != TS_RPC_CALL_ACCEPTED) {
+			/* RPC failure */
+			psa_status = PSA_ERROR_GENERIC_ERROR;
+		}
+
+		rpc_caller_end(rpc_caller, handle);
+	}
+	else {
+		psa_status = PSA_ERROR_GENERIC_ERROR;
+	}
+
+	return psa_status;
+}
+
+psa_status_t psa_its_get(psa_storage_uid_t uid,
+			 size_t data_offset,
+			 size_t data_size,
+			 void *p_data,
+			 size_t *p_data_length)
+{
+	uint8_t *request;
+	uint8_t *response;
+	size_t response_length = 0;
+	struct secure_storage_request_get *request_desc;
+	rpc_call_handle handle;
+	rpc_status_t rpc_status = TS_RPC_CALL_ACCEPTED;
+	psa_status_t psa_status = PSA_SUCCESS;
+
+	/* Validating input parameters */
+	if (p_data == NULL)
+		return PSA_ERROR_INVALID_ARGUMENT;
+
+	handle = rpc_caller_begin(rpc_caller, &request, sizeof(*request_desc));
+
+	if (handle) {
+		/* Populating request descriptor */
+		request_desc = (struct secure_storage_request_get *)request;
+		request_desc->uid = uid;
+		request_desc->data_offset = data_offset;
+		request_desc->data_size = data_size;
+
+		rpc_status = rpc_caller_invoke(rpc_caller, handle, TS_SECURE_STORAGE_OPCODE_GET,
+						(uint32_t *)&psa_status, &response,
+						&response_length);
+
+		if (rpc_status != TS_RPC_CALL_ACCEPTED ) {
+			/* RPC failure */
+			psa_status = PSA_ERROR_GENERIC_ERROR;
+		}
+
+		/* Filling output parameters */
+		if (psa_status == PSA_SUCCESS) {
+			*p_data_length = (response_length <= data_size) ? response_length : data_size;
+			memcpy(p_data, response, *p_data_length);
+		}
+
+		rpc_caller_end(rpc_caller, handle);
+	}
+	else {
+		psa_status = PSA_ERROR_GENERIC_ERROR;
+	}
+
+	return psa_status;
+}
+
+psa_status_t psa_its_get_info(psa_storage_uid_t uid,
+			      struct psa_storage_info_t *p_info)
+{
+	uint8_t *request;
+	uint8_t *response;
+	size_t response_length = 0;
+	struct secure_storage_request_get_info *request_desc;
+	struct secure_storage_response_get_info *response_desc;
+	rpc_call_handle handle;
+	rpc_status_t rpc_status;
+	psa_status_t psa_status = PSA_ERROR_GENERIC_ERROR;
+
+	/* Validating input parameters */
+	if (p_info == NULL)
+		return PSA_ERROR_INVALID_ARGUMENT;
+
+	handle = rpc_caller_begin(rpc_caller, &request, sizeof(*request_desc));
+
+	if (handle) {
+		/* Populating request descriptor */
+		request_desc = (struct secure_storage_request_get_info *)request;
+		request_desc->uid = uid;
+
+		rpc_status = rpc_caller_invoke(rpc_caller, handle,
+						TS_SECURE_STORAGE_OPCODE_GET_INFO,
+						(uint32_t *)&psa_status, &response,
+						&response_length);
+
+		if (rpc_status != TS_RPC_CALL_ACCEPTED) {
+			/* RPC failure */
+			psa_status = PSA_ERROR_GENERIC_ERROR;
+		} else if (response_length && response_length != sizeof(*response_desc)) {
+			psa_status = PSA_ERROR_GENERIC_ERROR;
+		}
+
+		if (psa_status == PSA_SUCCESS) {
+			response_desc = (struct secure_storage_response_get_info *)response;
+			p_info->capacity = response_desc->capacity;
+			p_info->size = response_desc->size;
+			p_info->flags = response_desc->flags;
+		} else {
+			p_info->capacity = 0;
+			p_info->size = 0;
+			p_info->flags = PSA_STORAGE_FLAG_NONE;
+		}
+
+		rpc_caller_end(rpc_caller, handle);
+	}
+	else {
+		psa_status = PSA_ERROR_GENERIC_ERROR;
+	}
+
+	return psa_status;
+}
+
+psa_status_t psa_its_remove(psa_storage_uid_t uid)
+{
+	uint8_t *request;
+	uint8_t *response;
+	size_t response_length = 0;
+	struct secure_storage_request_remove *request_desc;
+	rpc_call_handle handle;
+	rpc_status_t rpc_status = TS_RPC_CALL_ACCEPTED;
+	psa_status_t psa_status = PSA_SUCCESS;
+
+	handle = rpc_caller_begin(rpc_caller, &request, sizeof(*request_desc));
+
+	if (handle) {
+		/* Populating request descriptor */
+		request_desc = (struct secure_storage_request_remove *)request;
+		request_desc->uid = uid;
+
+		rpc_status = rpc_caller_invoke(rpc_caller, handle, TS_SECURE_STORAGE_OPCODE_REMOVE,
+						(uint32_t *)&psa_status, &response,
+						&response_length);
+
+		if (rpc_status != TS_RPC_CALL_ACCEPTED) {
+			/* RPC failure */
+			psa_status = PSA_ERROR_GENERIC_ERROR;
+		}
+
+		rpc_caller_end(rpc_caller, handle);
+	}
+	else {
+		psa_status = PSA_ERROR_GENERIC_ERROR;
+	}
+
+	return psa_status;
+}
diff --git a/components/service/secure_storage/client/psa/its/its_client.h b/components/service/secure_storage/client/psa/its/its_client.h
new file mode 100644
index 0000000..b8b7209
--- /dev/null
+++ b/components/service/secure_storage/client/psa/its/its_client.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef PSA_ITS_CLIENT_H
+#define PSA_ITS_CLIENT_H
+
+#include <psa/error.h>
+#include <rpc_caller.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief      Assignes a concrete rpc caller to the ITS library and initialises
+ *             the library state.
+ *
+ * @param[in]  rpc_caller RPC caller instance
+ *
+ * @return     A status indicating the success/failure of the operation
+ */
+psa_status_t psa_its_client_init(struct rpc_caller *caller);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* PSA_ITS_CLIENT_H */
diff --git a/components/service/secure_storage/client/psa/storage_common.h b/components/service/secure_storage/client/psa/storage_common.h
new file mode 100644
index 0000000..4f6ba2a
--- /dev/null
+++ b/components/service/secure_storage/client/psa/storage_common.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2020, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef PSA_STORAGE_COMMON_H
+#define PSA_STORAGE_COMMON_H
+
+#include <psa/error.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Types */
+typedef uint64_t psa_storage_uid_t;
+typedef uint32_t psa_storage_create_flags_t;
+
+struct psa_storage_info_t {
+	size_t capacity;
+	size_t size;
+	psa_storage_create_flags_t flags;
+};
+
+/* Storage flags */
+#define PSA_STORAGE_FLAG_NONE			(0u)
+#define PSA_STORAGE_FLAG_WRITE_ONCE		(1u << 0)
+#define PSA_STORAGE_FLAG_NO_CONFIDENTIALITY	(1u << 1)
+#define PSA_STORAGE_FLAG_NO_REPLAY_PROTECTION	(1u << 2)
+#define PSA_STORAGE_SUPPORT_SET_EXTENDED	(1u << 0)
+
+/* Status codes */
+#define PSA_ERROR_INVALID_SIGNATURE		((psa_status_t)-149)
+#define PSA_ERROR_DATA_CORRUPT			((psa_status_t)-152)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* PSA_STORAGE_COMMON_H */
diff --git a/components/service/secure_storage/provider/mock_store/component.cmake b/components/service/secure_storage/provider/mock_store/component.cmake
new file mode 100644
index 0000000..7e05763
--- /dev/null
+++ b/components/service/secure_storage/provider/mock_store/component.cmake
@@ -0,0 +1,14 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2020, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+if (NOT DEFINED TGT)
+	message(FATAL_ERROR "mandatory parameter TGT is not defined.")
+endif()
+
+target_sources(${TGT} PRIVATE
+	"${CMAKE_CURRENT_LIST_DIR}/mock_store_provider.c"
+	)
+
diff --git a/components/service/secure_storage/provider/mock_store/mock_store_provider.c b/components/service/secure_storage/provider/mock_store/mock_store_provider.c
new file mode 100644
index 0000000..9f1ce2e
--- /dev/null
+++ b/components/service/secure_storage/provider/mock_store/mock_store_provider.c
@@ -0,0 +1,257 @@
+/*
+ * Copyright (c) 2020, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include "mock_store_provider.h"
+#include <protocols/service/secure_storage/packed-c/secure_storage_proto.h>
+#include <protocols/rpc/common/packed-c/status.h>
+#include <protocols/service/psa/packed-c/status.h>
+#include <stdlib.h>
+#include <string.h>
+
+static struct mock_store_slot *find_slot(struct mock_store_provider *context, uint32_t id);
+static struct mock_store_slot *find_empty_slot(struct mock_store_provider *context);
+static void free_slot(struct mock_store_slot *slot);
+static rpc_status_t set_handler(void *context, struct call_req* req);
+static rpc_status_t get_handler(void *context, struct call_req* req);
+static rpc_status_t get_info_handler(void *context, struct call_req* req);
+static rpc_status_t remove_handler(void *context, struct call_req* req);
+
+/* Handler mapping table for service */
+static const struct service_handler handler_table[] = {
+	{TS_SECURE_STORAGE_OPCODE_SET,      set_handler},
+	{TS_SECURE_STORAGE_OPCODE_GET,	    get_handler},
+	{TS_SECURE_STORAGE_OPCODE_GET_INFO,	get_info_handler},
+	{TS_SECURE_STORAGE_OPCODE_REMOVE,	remove_handler}
+};
+
+struct call_ep *mock_store_provider_init(struct mock_store_provider *context)
+{
+    for (int i = 0; i < MOCK_STORE_NUM_SLOTS; ++i) {
+
+        context->slots[i].len = 0;
+        context->slots[i].flags = 0;
+        context->slots[i].id = (uint32_t)(-1);
+        context->slots[i].item = NULL;
+    }
+
+    service_provider_init(&context->base_provider, context,
+                    handler_table, sizeof(handler_table)/sizeof(struct service_handler));
+
+    return service_provider_get_call_ep(&context->base_provider);
+}
+
+void mock_store_provider_deinit(struct mock_store_provider *context)
+{
+    mock_store_reset(context);
+}
+
+void mock_store_reset(struct mock_store_provider *context)
+{
+    for (int i = 0; i < MOCK_STORE_NUM_SLOTS; ++i)
+        free_slot(&context->slots[i]);
+}
+
+bool mock_store_exists(const struct mock_store_provider *context, uint32_t id)
+{
+    bool exists = false;
+
+    for (int i = 0; !exists && i < MOCK_STORE_NUM_SLOTS; ++i) {
+        exists = context->slots[i].item && (context->slots[i].id == id);
+    }
+
+    return exists;
+}
+
+size_t mock_store_num_items(const struct mock_store_provider *context)
+{
+    size_t count = 0;
+
+    for (int i = 0; i < MOCK_STORE_NUM_SLOTS; ++i) {
+        if (context->slots[i].item) ++count;
+    }
+
+    return count;
+}
+
+static struct mock_store_slot *find_slot(struct mock_store_provider *context, uint32_t id)
+{
+    struct mock_store_slot *slot = NULL;
+
+    for (int i = 0; i < MOCK_STORE_NUM_SLOTS; ++i) {
+        if (context->slots[i].item && (context->slots[i].id == id)) {
+            slot = &context->slots[i];
+            break;
+        }
+    }
+
+    return slot;
+}
+
+static struct mock_store_slot *find_empty_slot(struct mock_store_provider *context)
+{
+    struct mock_store_slot *slot = NULL;
+
+    for (int i = 0; i < MOCK_STORE_NUM_SLOTS; ++i) {
+        if (!context->slots[i].item) {
+            slot = &context->slots[i];
+            break;
+        }
+    }
+
+    return slot;
+}
+
+static void free_slot(struct mock_store_slot *slot)
+{
+    if (slot->item) {
+        free(slot->item);
+        slot->len = 0;
+        slot->flags = 0;
+        slot->id = (uint32_t)(-1);
+        slot->item = NULL;
+    }
+}
+
+static rpc_status_t set_handler(void *context, struct call_req *req)
+{
+    psa_status_t psa_status = PSA_ERROR_INSUFFICIENT_MEMORY;
+    struct mock_store_provider *this_context = (struct mock_store_provider*)context;
+    struct mock_store_slot *slot;
+  	struct secure_storage_request_set *request_desc;
+
+	/* Checking if the descriptor fits into the request buffer */
+	if (req->req_buf.data_len < sizeof(struct secure_storage_request_set))
+		return TS_RPC_ERROR_INVALID_REQ_BODY;
+
+	request_desc = (struct secure_storage_request_set *)(req->req_buf.data);
+
+	/* Checking for overflow */
+	if (sizeof(struct secure_storage_request_set) + request_desc->data_length < request_desc->data_length)
+		return TS_RPC_ERROR_INVALID_REQ_BODY;
+
+	/* Checking if descriptor and data fits into the request buffer */
+	if (req->req_buf.data_len < sizeof(struct secure_storage_request_set) + request_desc->data_length)
+		return TS_RPC_ERROR_INVALID_REQ_BODY;
+
+    /* Replace existing or add new item */
+    slot = find_slot(this_context, request_desc->uid);
+    if (slot) free_slot(slot);
+    else slot = find_empty_slot(this_context);
+
+    if (slot) {
+        slot->id = request_desc->uid;
+        slot->flags = request_desc->create_flags;
+        slot->len = request_desc->data_length;
+        slot->item = malloc(slot->len);
+        if (slot->item) {
+            memcpy(slot->item, request_desc->p_data, slot->len);
+            psa_status = PSA_SUCCESS;
+        }
+    }
+
+	call_req_set_opstatus(req, psa_status);
+
+	return TS_RPC_CALL_ACCEPTED;
+}
+
+static rpc_status_t get_handler(void *context, struct call_req *req)
+{
+	struct mock_store_provider *this_context = (struct mock_store_provider*)context;
+    struct secure_storage_request_get *request_desc;
+	psa_status_t psa_status = PSA_ERROR_DOES_NOT_EXIST;
+    struct mock_store_slot *slot;
+
+	/* Checking if the descriptor fits into the request buffer */
+	if (req->req_buf.data_len < sizeof(struct secure_storage_request_get))
+		return TS_RPC_ERROR_INVALID_REQ_BODY;
+
+	request_desc = (struct secure_storage_request_get *)(req->req_buf.data);
+
+	/* Check if the requested data would fit into the response buffer. */
+	if (req->resp_buf.size < request_desc->data_size)
+		return TS_RPC_ERROR_INVALID_RESP_BODY;
+
+    /* Find the item */
+    slot = find_slot(this_context, request_desc->uid);
+
+    if (slot && (slot->len <= req->resp_buf.size)) {
+        memcpy(req->resp_buf.data, slot->item, slot->len);
+        req->resp_buf.data_len = slot->len;
+        psa_status = PSA_SUCCESS;
+    }
+
+	call_req_set_opstatus(req, psa_status);
+
+	return TS_RPC_CALL_ACCEPTED;
+}
+
+static rpc_status_t get_info_handler(void *context, struct call_req *req)
+{
+ 	struct mock_store_provider *this_context = (struct mock_store_provider*)context;
+    struct secure_storage_request_get_info *request_desc;
+	struct secure_storage_response_get_info *response_desc;
+	psa_status_t psa_status;
+    struct mock_store_slot *slot;
+
+	/* Checking if the descriptor fits into the request buffer */
+	if (req->req_buf.data_len < sizeof(struct secure_storage_request_get_info))
+		return TS_RPC_ERROR_INVALID_REQ_BODY;
+
+	request_desc = (struct secure_storage_request_get_info *)(req->req_buf.data);
+
+	/* Checking if the response structure would fit the response buffer */
+	if (req->resp_buf.size < sizeof(struct secure_storage_response_get_info))
+		return TS_RPC_ERROR_INVALID_RESP_BODY;
+
+    response_desc = (struct secure_storage_response_get_info *)(req->resp_buf.data);
+    req->resp_buf.data_len = sizeof(struct secure_storage_response_get_info);
+
+    /* Find itemto get info about */
+    slot = find_slot(this_context, request_desc->uid);
+
+    if (slot) {
+        response_desc->capacity = slot->len;
+        response_desc->size = slot->len;
+        response_desc->flags = slot->flags;
+        psa_status = PSA_SUCCESS;
+    }
+    else {
+        response_desc->capacity = 0;
+        response_desc->size = 0;
+        response_desc->flags = 0;
+        psa_status = PSA_ERROR_DOES_NOT_EXIST;
+    }
+
+    call_req_set_opstatus(req, psa_status);
+
+	return TS_RPC_CALL_ACCEPTED;
+}
+
+static rpc_status_t remove_handler(void *context, struct call_req *req)
+{
+	struct mock_store_provider *this_context = (struct mock_store_provider*)context;
+    struct secure_storage_request_remove *request_desc;
+	psa_status_t psa_status = PSA_ERROR_DOES_NOT_EXIST;
+    struct mock_store_slot *slot;
+
+	/* Checking if the descriptor fits into the request buffer */
+	if (req->req_buf.data_len < sizeof(struct secure_storage_request_remove))
+		return TS_RPC_ERROR_INVALID_REQ_BODY;
+
+	request_desc = (struct secure_storage_request_remove *)(req->req_buf.data);
+
+    /* Find and remove the item */
+    slot = find_slot(this_context, request_desc->uid);
+
+    if (slot) {
+        free_slot(slot);
+        psa_status = PSA_SUCCESS;
+    }
+
+    call_req_set_opstatus(req, psa_status);
+
+	return TS_RPC_CALL_ACCEPTED;
+}
\ No newline at end of file
diff --git a/components/service/secure_storage/provider/mock_store/mock_store_provider.h b/components/service/secure_storage/provider/mock_store/mock_store_provider.h
new file mode 100644
index 0000000..ecb457a
--- /dev/null
+++ b/components/service/secure_storage/provider/mock_store/mock_store_provider.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef MOCK_STORE_PROVIDER_H
+#define MOCK_STORE_PROVIDER_H
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <service/common/provider/service_provider.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define MOCK_STORE_NUM_SLOTS        (100)
+
+struct mock_store_slot
+{
+    uint64_t id;
+    uint32_t flags;
+    size_t len;
+    uint8_t *item;
+};
+
+struct mock_store_provider
+{
+    struct service_provider base_provider;
+    struct mock_store_slot slots[MOCK_STORE_NUM_SLOTS];
+};
+
+struct call_ep *mock_store_provider_init(struct mock_store_provider *context);
+void mock_store_provider_deinit(struct mock_store_provider *context);
+
+/* Test support methods */
+void mock_store_reset(struct mock_store_provider *context);
+bool mock_store_exists(const struct mock_store_provider *context, uint32_t id);
+size_t mock_store_num_items(const struct mock_store_provider *context);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* MOCK_STORE_PROVIDER_H */
diff --git a/components/service/secure_storage/provider/secure_flash_store/component.cmake b/components/service/secure_storage/provider/secure_flash_store/component.cmake
new file mode 100644
index 0000000..2e31c20
--- /dev/null
+++ b/components/service/secure_storage/provider/secure_flash_store/component.cmake
@@ -0,0 +1,16 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2020, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+if (NOT DEFINED TGT)
+	message(FATAL_ERROR "mandatory parameter TGT is not defined.")
+endif()
+
+target_sources(${TGT} PRIVATE
+	"${CMAKE_CURRENT_LIST_DIR}/secure_flash_store.c"
+	"${CMAKE_CURRENT_LIST_DIR}/sfs_provider.c"
+	"${CMAKE_CURRENT_LIST_DIR}/sfs_utils.c"
+	)
+
diff --git a/components/service/secure_storage/provider/secure_flash_store/flash/component.cmake b/components/service/secure_storage/provider/secure_flash_store/flash/component.cmake
new file mode 100644
index 0000000..a2f34e7
--- /dev/null
+++ b/components/service/secure_storage/provider/secure_flash_store/flash/component.cmake
@@ -0,0 +1,16 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2020, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+if (NOT DEFINED TGT)
+	message(FATAL_ERROR "mandatory parameter TGT is not defined.")
+endif()
+
+target_sources(${TGT} PRIVATE
+	"${CMAKE_CURRENT_LIST_DIR}/sfs_flash_info.c"
+	"${CMAKE_CURRENT_LIST_DIR}/sfs_flash_ram.c"
+	"${CMAKE_CURRENT_LIST_DIR}/sfs_flash.c"
+	)
+
diff --git a/components/service/secure_storage/provider/secure_flash_store/flash/sfs_flash.c b/components/service/secure_storage/provider/secure_flash_store/flash/sfs_flash.c
new file mode 100644
index 0000000..fce7968
--- /dev/null
+++ b/components/service/secure_storage/provider/secure_flash_store/flash/sfs_flash.c
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2018-2020, Arm Limited. All rights reserved.
+ * Copyright (c) 2020 Cypress Semiconductor Corporation. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "sfs_flash.h"
+
+#ifndef SFS_MAX_BLOCK_DATA_COPY
+#define SFS_MAX_BLOCK_DATA_COPY 256
+#endif
+
+extern const struct sfs_flash_info_t sfs_flash_info_internal;
+
+const struct sfs_flash_info_t *sfs_flash_get_info(void)
+{
+    return &sfs_flash_info_internal;
+}
+
+psa_status_t sfs_flash_block_to_block_move(const struct sfs_flash_info_t *info,
+                                           uint32_t dst_block,
+                                           size_t dst_offset,
+                                           uint32_t src_block,
+                                           size_t src_offset,
+                                           size_t size)
+{
+    psa_status_t status;
+    size_t bytes_to_move;
+    uint8_t dst_block_data_copy[SFS_MAX_BLOCK_DATA_COPY];
+
+    while (size > 0) {
+        /* Calculates the number of bytes to move */
+        bytes_to_move = SFS_UTILS_MIN(size, SFS_MAX_BLOCK_DATA_COPY);
+
+        /* Reads data from source block and store it in the in-memory copy of
+         * destination content.
+         */
+        status = info->read(info, src_block, dst_block_data_copy, src_offset,
+                            bytes_to_move);
+        if (status != PSA_SUCCESS) {
+            return status;
+        }
+
+        /* Writes in flash the in-memory block content after modification */
+        status = info->write(info, dst_block, dst_block_data_copy, dst_offset,
+                             bytes_to_move);
+        if (status != PSA_SUCCESS) {
+            return status;
+        }
+
+        /* Updates pointers to the source and destination flash regions */
+        dst_offset += bytes_to_move;
+        src_offset += bytes_to_move;
+
+        /* Decrement remaining size to move */
+        size -= bytes_to_move;
+    }
+
+    return PSA_SUCCESS;
+}
diff --git a/components/service/secure_storage/provider/secure_flash_store/flash/sfs_flash.h b/components/service/secure_storage/provider/secure_flash_store/flash/sfs_flash.h
new file mode 100644
index 0000000..18361f2
--- /dev/null
+++ b/components/service/secure_storage/provider/secure_flash_store/flash/sfs_flash.h
@@ -0,0 +1,182 @@
+/*
+ * Copyright (c) 2017-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __SFS_FLASH_H__
+#define __SFS_FLASH_H__
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <protocols/service/psa/packed-c/status.h>
+#include "../sfs_utils.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Adjust to match the size of the flash device's physical program unit */
+#define SFS_FLASH_PROGRAM_UNIT (0x1)
+
+/* Invalid block index */
+#define SFS_BLOCK_INVALID_ID 0xFFFFFFFFU
+
+/* FIXME: Duplicated from flash info */
+#if (SFS_FLASH_PROGRAM_UNIT <= 16)
+#define SFS_FLASH_ALIGNMENT SFS_FLASH_PROGRAM_UNIT
+#else
+#define SFS_FLASH_ALIGNMENT 1
+#endif
+
+/**
+ * \brief Provides a compile-time constant for the maximum program unit required
+ *        by any flash device that can be accessed through this interface.
+ */
+#define SFS_FLASH_MAX_ALIGNMENT SFS_UTILS_MAX(SFS_FLASH_ALIGNMENT, \
+                                              1)
+
+/**
+ * \struct sfs_flash_info_t
+ *
+ * \brief Structure containing the required information about a flash device to
+ *        be used by the SFS Flash FS.
+ */
+struct sfs_flash_info_t {
+    /**
+     * \brief Initialize the Flash Interface.
+     *
+     * \param[in] info  Flash device information
+     *
+     * \return Returns PSA_SUCCESS if the function is executed correctly.
+     *         Otherwise, it returns PSA_ERROR_STORAGE_FAILURE.
+     */
+    psa_status_t (*init)(const struct sfs_flash_info_t *info);
+
+    /**
+     * \brief Reads block data from the position specified by block ID and
+     *        offset.
+     *
+     * \param[in]  info      Flash device information
+     * \param[in]  block_id  Block ID
+     * \param[out] buff      Buffer pointer to store the data read
+     * \param[in]  offset    Offset position from the init of the block
+     * \param[in]  size      Number of bytes to read
+     *
+     * \note This function assumes all input values are valid. That is, the
+     *       address range, based on block_id, offset and size, is a valid range
+     *       in flash.
+     *
+     * \return Returns PSA_SUCCESS if the function is executed correctly.
+     *         Otherwise, it returns PSA_ERROR_STORAGE_FAILURE.
+     */
+    psa_status_t (*read)(const struct sfs_flash_info_t *info, uint32_t block_id,
+                         uint8_t *buff, size_t offset, size_t size);
+
+    /**
+     * \brief Writes block data to the position specified by block ID and
+     *        offset.
+     *
+     * \param[in] info      Flash device information
+     * \param[in] block_id  Block ID
+     * \param[in] buff      Buffer pointer to the write data
+     * \param[in] offset    Offset position from the init of the block
+     * \param[in] size      Number of bytes to write
+     *
+     * \note This function assumes all input values are valid. That is, the
+     *       address range, based on block_id, offset and size, is a valid range
+     *       in flash.
+     *
+     * \return Returns PSA_SUCCESS if the function is executed correctly.
+     *         Otherwise, it returns PSA_ERROR_STORAGE_FAILURE.
+     */
+    psa_status_t (*write)(const struct sfs_flash_info_t *info,
+                          uint32_t block_id, const uint8_t *buff, size_t offset,
+                          size_t size);
+
+    /**
+     * \brief Flushes modifications to a block to flash. Must be called after a
+     *        sequence of calls to write() (including via
+     *        sfs_flash_block_to_block_move()) for one block ID, before any call
+     *        to the same functions for a different block ID.
+     *
+     * \param[in] info  Flash device information
+     *
+     * \note It is permitted for write() to commit block updates immediately, in
+     *       which case this function is a no-op.
+     *
+     * \return Returns PSA_SUCCESS if the function is executed correctly.
+     *         Otherwise, it returns PSA_ERROR_STORAGE_FAILURE.
+     */
+    psa_status_t (*flush)(const struct sfs_flash_info_t *info);
+
+    /**
+     * \brief Erases block ID data.
+     *
+     * \param[in] info      Flash device information
+     * \param[in] block_id  Block ID
+     *
+     * \note This function assumes the input value is valid.
+     *
+     * \return Returns PSA_SUCCESS if the function is executed correctly.
+     *         Otherwise, it returns PSA_ERROR_STORAGE_FAILURE.
+     */
+    psa_status_t (*erase)(const struct sfs_flash_info_t *info,
+                          uint32_t block_id);
+
+    void *flash_dev;          /**< Pointer to the flash device */
+    uint32_t flash_area_addr; /**< Start address of the flash area */
+    uint16_t sector_size;     /**< Size of the flash device's physical erase
+                               *   unit
+                               */
+    uint16_t block_size;      /**< Size of a logical erase unit presented by the
+                               *   flash interface, a multiple of sector_size.
+                               */
+    uint16_t num_blocks;      /**< Number of logical erase blocks */
+    uint16_t program_unit;    /**< Minimum size of a program operation */
+    uint16_t max_file_size;   /**< Maximum file size */
+    uint16_t max_num_files;   /**< Maximum number of files */
+    uint8_t erase_val;        /**< Value of a byte after erase (usually 0xFF) */
+};
+
+/**
+ * \brief Gets the flash info structure for the provided flash device.
+ *
+ * \return Pointer to the flash info struct.
+ */
+const struct sfs_flash_info_t *sfs_flash_get_info(void);
+
+/**
+ * \brief Moves data from source block ID to destination block ID.
+ *
+ * \param[in] info        Flash device information
+ * \param[in] dst_block   Destination block ID
+ * \param[in] dst_offset  Destination offset position from the init of the
+ *                        destination block
+ * \param[in] src_block   Source block ID
+ * \param[in] src_offset  Source offset position from the init of the source
+ *                        block
+ * \param[in] size        Number of bytes to moves
+ *
+ * \note This function assumes all input values are valid. That is, the address
+ *       range, based on blockid, offset and size, is a valid range in flash.
+ *       It also assumes that the destination block is already erased and ready
+ *       to be written.
+ *
+ * \return Returns PSA_SUCCESS if the function is executed correctly. Otherwise,
+ *         it returns PSA_ERROR_STORAGE_FAILURE.
+ */
+psa_status_t sfs_flash_block_to_block_move(const struct sfs_flash_info_t *info,
+                                           uint32_t dst_block,
+                                           size_t dst_offset,
+                                           uint32_t src_block,
+                                           size_t src_offset,
+                                           size_t size);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __SFS_FLASH_H__ */
diff --git a/components/service/secure_storage/provider/secure_flash_store/flash/sfs_flash_info.c b/components/service/secure_storage/provider/secure_flash_store/flash/sfs_flash_info.c
new file mode 100644
index 0000000..7dfe803
--- /dev/null
+++ b/components/service/secure_storage/provider/secure_flash_store/flash/sfs_flash_info.c
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2020, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include "sfs_flash.h"
+#include "sfs_flash_ram.h"
+#include "../sfs_utils.h"
+
+#define SFS_FLASH_AREA_ADDR (0x0)
+
+/* Adjust to a size that will allow all assets to fit */
+#define SFS_FLASH_AREA_SIZE (0x4000)
+
+/* Adjust to match the size of the flash device's physical erase unit */
+#define SFS_SECTOR_SIZE (0x1000)
+
+/* Adjust so that the maximum required asset size will fit in one block */
+#define SFS_SECTORS_PER_BLOCK (0x1)
+
+/* Adjust to match the size of the flash device's physical program unit */
+#define SFS_FLASH_PROGRAM_UNIT (0x1)
+
+/* The maximum asset size to be stored in the SFS area */
+#define SFS_MAX_ASSET_SIZE (4096)
+
+/* The maximum number of assets to be stored in the SFS area */
+#define SFS_NUM_ASSETS (10)
+
+/* Calculate the block layout */
+#define FLASH_INFO_BLOCK_SIZE (SFS_SECTOR_SIZE * SFS_SECTORS_PER_BLOCK)
+#define FLASH_INFO_NUM_BLOCKS (SFS_FLASH_AREA_SIZE / FLASH_INFO_BLOCK_SIZE)
+
+/* Maximum file size */
+#define FLASH_INFO_MAX_FILE_SIZE SFS_UTILS_ALIGN(SFS_MAX_ASSET_SIZE, \
+                                                 SFS_FLASH_ALIGNMENT)
+
+/* Maximum number of files */
+#define FLASH_INFO_MAX_NUM_FILES SFS_NUM_ASSETS
+
+/* Default value of each byte in the flash when erased */
+#define FLASH_INFO_ERASE_VAL 0xFFU
+
+/* Allocate a static buffer to emulate storage in RAM */
+static uint8_t sfs_block_data[FLASH_INFO_BLOCK_SIZE * FLASH_INFO_NUM_BLOCKS];
+#define FLASH_INFO_DEV sfs_block_data
+
+const struct sfs_flash_info_t sfs_flash_info_internal = {
+    .init = sfs_flash_ram_init,
+    .read = sfs_flash_ram_read,
+    .write = sfs_flash_ram_write,
+    .flush = sfs_flash_ram_flush,
+    .erase = sfs_flash_ram_erase,
+    .flash_dev = (void *)FLASH_INFO_DEV,
+    .flash_area_addr = SFS_FLASH_AREA_ADDR,
+    .sector_size = SFS_SECTOR_SIZE,
+    .block_size = FLASH_INFO_BLOCK_SIZE,
+    .num_blocks = FLASH_INFO_NUM_BLOCKS,
+    .program_unit = SFS_FLASH_ALIGNMENT,
+    .max_file_size = FLASH_INFO_MAX_FILE_SIZE,
+    .max_num_files = FLASH_INFO_MAX_NUM_FILES,
+    .erase_val = FLASH_INFO_ERASE_VAL,
+};
+
+/* Checks at compile time that the flash device configuration is valid */
+#include "../flash_fs/sfs_flash_fs_check_info.h"
diff --git a/components/service/secure_storage/provider/secure_flash_store/flash/sfs_flash_ram.c b/components/service/secure_storage/provider/secure_flash_store/flash/sfs_flash_ram.c
new file mode 100644
index 0000000..e4af6e6
--- /dev/null
+++ b/components/service/secure_storage/provider/secure_flash_store/flash/sfs_flash_ram.c
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "sfs_flash_ram.h"
+#include <string.h>
+
+/**
+ * \brief Gets physical address of the given block ID.
+ *
+ * \param[in] info      Flash device information
+ * \param[in] block_id  Block ID
+ * \param[in] offset    Offset position from the init of the block
+ *
+ * \returns Returns physical address for the given block ID.
+ */
+static uint32_t get_phys_address(const struct sfs_flash_info_t *info,
+                                 uint32_t block_id, size_t offset)
+{
+    return (block_id * info->block_size) + offset;
+}
+
+psa_status_t sfs_flash_ram_init(const struct sfs_flash_info_t *info)
+{
+    /* Nothing needs to be done in case of flash emulated in RAM */
+    (void)info;
+    return PSA_SUCCESS;
+}
+
+psa_status_t sfs_flash_ram_read(const struct sfs_flash_info_t *info,
+                                uint32_t block_id, uint8_t *buff, size_t offset,
+                                size_t size)
+{
+    uint32_t idx = get_phys_address(info, block_id, offset);
+
+    (void)memcpy(buff, (uint8_t *)info->flash_dev + idx, size);
+
+    return PSA_SUCCESS;
+}
+
+psa_status_t sfs_flash_ram_write(const struct sfs_flash_info_t *info,
+                                 uint32_t block_id, const uint8_t *buff,
+                                 size_t offset, size_t size)
+{
+    uint32_t idx = get_phys_address(info, block_id, offset);
+
+    (void)memcpy((uint8_t *)info->flash_dev + idx, buff, size);
+
+    return PSA_SUCCESS;
+}
+
+psa_status_t sfs_flash_ram_flush(const struct sfs_flash_info_t *info)
+{
+    /* Nothing needs to be done for flash emulated in RAM, as writes are
+     * commited immediately.
+     */
+    (void)info;
+    return PSA_SUCCESS;
+}
+
+psa_status_t sfs_flash_ram_erase(const struct sfs_flash_info_t *info,
+                                 uint32_t block_id)
+{
+    uint32_t idx = get_phys_address(info, block_id, 0);
+
+    (void)memset((uint8_t *)info->flash_dev + idx, info->erase_val,
+                     info->block_size);
+
+    return PSA_SUCCESS;
+}
diff --git a/components/service/secure_storage/provider/secure_flash_store/flash/sfs_flash_ram.h b/components/service/secure_storage/provider/secure_flash_store/flash/sfs_flash_ram.h
new file mode 100644
index 0000000..eecc5e5
--- /dev/null
+++ b/components/service/secure_storage/provider/secure_flash_store/flash/sfs_flash_ram.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+/**
+ * \file sfs_flash_ram.h
+ *
+ * \brief Implementations of the flash interface functions for an emulated flash
+ *        device using RAM. See sfs_flash.h for full documentation of functions.
+ */
+
+#include "sfs_flash.h"
+
+/**
+ * \brief Initialize the Flash Interface.
+ */
+psa_status_t sfs_flash_ram_init(const struct sfs_flash_info_t *info);
+
+/**
+ * \brief Reads block data from the position specified by block ID and offset.
+ */
+psa_status_t sfs_flash_ram_read(const struct sfs_flash_info_t *info,
+                                uint32_t block_id, uint8_t *buff, size_t offset,
+                                size_t size);
+
+/**
+ * \brief Writes block data to the position specified by block ID and offset.
+ */
+psa_status_t sfs_flash_ram_write(const struct sfs_flash_info_t *info,
+                                 uint32_t block_id, const uint8_t *buff,
+                                 size_t offset, size_t size);
+
+/**
+ * \brief Flushes modifications to a block to flash.
+ */
+psa_status_t sfs_flash_ram_flush(const struct sfs_flash_info_t *info);
+
+/**
+ * \brief Erases block ID data.
+ */
+psa_status_t sfs_flash_ram_erase(const struct sfs_flash_info_t *info,
+                                 uint32_t block_id);
diff --git a/components/service/secure_storage/provider/secure_flash_store/flash_fs/component.cmake b/components/service/secure_storage/provider/secure_flash_store/flash_fs/component.cmake
new file mode 100644
index 0000000..a482703
--- /dev/null
+++ b/components/service/secure_storage/provider/secure_flash_store/flash_fs/component.cmake
@@ -0,0 +1,16 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2020, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+if (NOT DEFINED TGT)
+	message(FATAL_ERROR "mandatory parameter TGT is not defined.")
+endif()
+
+target_sources(${TGT} PRIVATE
+	"${CMAKE_CURRENT_LIST_DIR}/sfs_flash_fs_dblock.c"
+	"${CMAKE_CURRENT_LIST_DIR}/sfs_flash_fs_mblock.c"
+	"${CMAKE_CURRENT_LIST_DIR}/sfs_flash_fs.c"
+	)
+
diff --git a/components/service/secure_storage/provider/secure_flash_store/flash_fs/sfs_flash_fs.c b/components/service/secure_storage/provider/secure_flash_store/flash_fs/sfs_flash_fs.c
new file mode 100644
index 0000000..4747e99
--- /dev/null
+++ b/components/service/secure_storage/provider/secure_flash_store/flash_fs/sfs_flash_fs.c
@@ -0,0 +1,468 @@
+/*
+ * Copyright (c) 2018-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "sfs_flash_fs.h"
+
+#include "sfs_flash_fs_dblock.h"
+#include "../sfs_utils.h"
+#include <string.h>
+
+#define SFS_FLASH_FS_INIT_FILE 0
+
+static psa_status_t sfs_flash_fs_file_write_aligned_data(
+                                      struct sfs_flash_fs_ctx_t *fs_ctx,
+                                      const struct sfs_block_meta_t *block_meta,
+                                      const struct sfs_file_meta_t *file_meta,
+                                      size_t offset,
+                                      size_t size,
+                                      const uint8_t *data)
+{
+#if (SFS_FLASH_MAX_ALIGNMENT != 1)
+    /* Check that the offset is aligned with the flash program unit */
+    if (!SFS_UTILS_IS_ALIGNED(offset, fs_ctx->flash_info->program_unit)) {
+        return PSA_ERROR_INVALID_ARGUMENT;
+    }
+
+    /* Set the size to be aligned with the flash program unit */
+    size = SFS_UTILS_ALIGN(size, fs_ctx->flash_info->program_unit);
+#endif
+
+    /* It is not permitted to create gaps in the file */
+    if (offset > file_meta->cur_size) {
+        return PSA_ERROR_INVALID_ARGUMENT;
+    }
+
+    /* Check that the new data is contained within the file's max size */
+    if (sfs_utils_check_contained_in(file_meta->max_size, offset, size)
+        != PSA_SUCCESS) {
+        return PSA_ERROR_INVALID_ARGUMENT;
+    }
+
+    return sfs_flash_fs_dblock_write_file(fs_ctx, block_meta, file_meta, offset,
+                                          size, data);
+}
+
+psa_status_t sfs_flash_fs_prepare(struct sfs_flash_fs_ctx_t *fs_ctx,
+                                  const struct sfs_flash_info_t *flash_info)
+{
+    /* Associate the flash device info with the context */
+    fs_ctx->flash_info = flash_info;
+
+    /* Initialize metadata block with the valid/active metablock */
+    return sfs_flash_fs_mblock_init(fs_ctx);
+}
+
+psa_status_t sfs_flash_fs_wipe_all(struct sfs_flash_fs_ctx_t *fs_ctx)
+{
+    /* Clean and initialize the metadata block */
+    return sfs_flash_fs_mblock_reset_metablock(fs_ctx);
+}
+
+psa_status_t sfs_flash_fs_file_exist(struct sfs_flash_fs_ctx_t *fs_ctx,
+                                     const uint8_t *fid)
+{
+    int32_t err;
+    uint32_t idx;
+
+    err = sfs_flash_fs_mblock_get_file_idx(fs_ctx, fid, &idx);
+    if (err != PSA_SUCCESS) {
+        return PSA_ERROR_DOES_NOT_EXIST;
+    }
+
+    return PSA_SUCCESS;
+}
+
+psa_status_t sfs_flash_fs_file_create(struct sfs_flash_fs_ctx_t *fs_ctx,
+                                      const uint8_t *fid,
+                                      size_t max_size,
+                                      size_t data_size,
+                                      uint32_t flags,
+                                      const uint8_t *data)
+{
+    struct sfs_block_meta_t block_meta;
+    uint32_t cur_phys_block;
+    int32_t err;
+    uint32_t idx;
+    struct sfs_file_meta_t file_meta;
+
+#if (SFS_FLASH_MAX_ALIGNMENT != 1)
+    /* Set the max_size to be aligned with the flash program unit */
+    max_size = SFS_UTILS_ALIGN(max_size, fs_ctx->flash_info->program_unit);
+#endif
+
+    /* Check that the file's maximum size is valid */
+    if (max_size > fs_ctx->flash_info->max_file_size) {
+        return PSA_ERROR_INVALID_ARGUMENT;
+    }
+
+    /* Check if file already exists */
+    err = sfs_flash_fs_mblock_get_file_idx(fs_ctx, fid, &idx);
+    if (err == PSA_SUCCESS) {
+        /* If it exists return an error as needs to be removed first */
+        return PSA_ERROR_INVALID_ARGUMENT;
+    }
+
+    /* Try to reserve an file based on the input parameters */
+    err = sfs_flash_fs_mblock_reserve_file(fs_ctx, fid, max_size, flags, &idx,
+                                           &file_meta, &block_meta);
+    if (err != PSA_SUCCESS) {
+        return err;
+    }
+
+    /* Check if data needs to be stored in the new file */
+    if (data_size != 0) {
+        /* Write the content into scratch data block */
+        err = sfs_flash_fs_file_write_aligned_data(fs_ctx, &block_meta,
+                                                   &file_meta,
+                                                   SFS_FLASH_FS_INIT_FILE,
+                                                   data_size,
+                                                   data);
+        if (err != PSA_SUCCESS) {
+            return PSA_ERROR_GENERIC_ERROR;
+        }
+
+        /* Add current size to the file metadata */
+        file_meta.cur_size = data_size;
+
+        cur_phys_block = block_meta.phy_id;
+
+        /* Cur scratch block become the active datablock */
+        block_meta.phy_id =
+            sfs_flash_fs_mblock_cur_data_scratch_id(fs_ctx, file_meta.lblock);
+
+        /* Swap the scratch data block */
+        sfs_flash_fs_mblock_set_data_scratch(fs_ctx, cur_phys_block,
+                                             file_meta.lblock);
+    }
+
+    /* Update metadata block information */
+    err = sfs_flash_fs_mblock_update_scratch_block_meta(fs_ctx,
+                                                        file_meta.lblock,
+                                                        &block_meta);
+    if (err != PSA_SUCCESS) {
+        return PSA_ERROR_GENERIC_ERROR;
+    }
+
+    /* Add file metadata in the metadata block */
+    err = sfs_flash_fs_mblock_update_scratch_file_meta(fs_ctx, idx, &file_meta);
+    if (err != PSA_SUCCESS) {
+        return PSA_ERROR_GENERIC_ERROR;
+    }
+
+    /* Copy rest of the file metadata entries */
+    err = sfs_flash_fs_mblock_cp_remaining_file_meta(fs_ctx, idx);
+    if (err != PSA_SUCCESS) {
+        return PSA_ERROR_GENERIC_ERROR;
+    }
+
+    /* The file data in the logical block 0 is stored in same physical block
+     * where the metadata is stored. A change in the metadata requires a
+     * swap of physical blocks. So, the file data stored in the current
+     * metadata block needs to be copied in the scratch block, if the data
+     * of the file processed is not located in the logical block 0. When an
+     * file data is located in the logical block 0, that copy has been done
+     * while processing the file data.
+     */
+    if ((file_meta.lblock != SFS_LOGICAL_DBLOCK0) || (data_size == 0)) {
+        err = sfs_flash_fs_mblock_migrate_lb0_data_to_scratch(fs_ctx);
+        if (err != PSA_SUCCESS) {
+            return PSA_ERROR_GENERIC_ERROR;
+        }
+    }
+
+    /* Write metadata header, swap metadata blocks and erase scratch blocks */
+    return sfs_flash_fs_mblock_meta_update_finalize(fs_ctx);
+}
+
+psa_status_t sfs_flash_fs_file_get_info(struct sfs_flash_fs_ctx_t *fs_ctx,
+                                        const uint8_t *fid,
+                                        struct sfs_file_info_t *info)
+{
+    int32_t err;
+    uint32_t idx;
+    struct sfs_file_meta_t tmp_metadata;
+
+    /* Get the meta data index */
+    err = sfs_flash_fs_mblock_get_file_idx(fs_ctx, fid, &idx);
+    if (err != PSA_SUCCESS) {
+        return PSA_ERROR_DOES_NOT_EXIST;
+    }
+
+    /* Read file metadata */
+    err = sfs_flash_fs_mblock_read_file_meta(fs_ctx, idx, &tmp_metadata);
+    if (err != PSA_SUCCESS) {
+        return err;
+    }
+
+    /* Check if index is still referring to same file */
+    if (memcmp(fid, tmp_metadata.id, SFS_FILE_ID_SIZE)) {
+        return PSA_ERROR_DOES_NOT_EXIST;
+    }
+
+    info->size_max = tmp_metadata.max_size;
+    info->size_current = tmp_metadata.cur_size;
+    info->flags = tmp_metadata.flags;
+
+    return PSA_SUCCESS;
+}
+
+psa_status_t sfs_flash_fs_file_write(struct sfs_flash_fs_ctx_t *fs_ctx,
+                                     const uint8_t *fid,
+                                     size_t size,
+                                     size_t offset,
+                                     const uint8_t *data)
+{
+    struct sfs_block_meta_t block_meta;
+    uint32_t cur_phys_block;
+    int32_t err;
+    uint32_t idx;
+    struct sfs_file_meta_t file_meta;
+
+    /* Get the file index */
+    err = sfs_flash_fs_mblock_get_file_idx(fs_ctx, fid, &idx);
+    if (err != PSA_SUCCESS) {
+        return PSA_ERROR_DOES_NOT_EXIST;
+    }
+
+    /* Read file metadata */
+    err = sfs_flash_fs_mblock_read_file_meta(fs_ctx, idx, &file_meta);
+    if (err != PSA_SUCCESS) {
+        return PSA_ERROR_DOES_NOT_EXIST;
+    }
+
+    /* Read block metadata */
+    err = sfs_flash_fs_mblock_read_block_metadata(fs_ctx, file_meta.lblock,
+                                                  &block_meta);
+    if (err != PSA_SUCCESS) {
+        return PSA_ERROR_GENERIC_ERROR;
+    }
+
+    /* Write the content into scratch data block */
+    err = sfs_flash_fs_file_write_aligned_data(fs_ctx, &block_meta, &file_meta,
+                                               offset, size, data);
+    if (err != PSA_SUCCESS) {
+        return PSA_ERROR_GENERIC_ERROR;
+    }
+
+    /* Update the file's current size if required */
+    if (offset + size > file_meta.cur_size) {
+        /* Update the file metadata */
+        file_meta.cur_size = offset + size;
+    }
+
+    cur_phys_block = block_meta.phy_id;
+
+    /* Cur scratch block become the active datablock */
+    block_meta.phy_id =
+        sfs_flash_fs_mblock_cur_data_scratch_id(fs_ctx, file_meta.lblock);
+
+    /* Swap the scratch data block */
+    sfs_flash_fs_mblock_set_data_scratch(fs_ctx, cur_phys_block,
+                                         file_meta.lblock);
+
+    /* Update block metadata in scratch metadata block */
+    err = sfs_flash_fs_mblock_update_scratch_block_meta(fs_ctx,
+                                                        file_meta.lblock,
+                                                        &block_meta);
+    if (err != PSA_SUCCESS) {
+        return PSA_ERROR_GENERIC_ERROR;
+    }
+
+    /* Update file metadata to reflect new attributes */
+    err = sfs_flash_fs_mblock_update_scratch_file_meta(fs_ctx, idx, &file_meta);
+    if (err != PSA_SUCCESS) {
+        return PSA_ERROR_GENERIC_ERROR;
+    }
+
+    /* Copy rest of the file metadata entries */
+    err = sfs_flash_fs_mblock_cp_remaining_file_meta(fs_ctx, idx);
+    if (err != PSA_SUCCESS) {
+        return PSA_ERROR_GENERIC_ERROR;
+    }
+
+    /* The file data in the logical block 0 is stored in same physical block
+     * where the metadata is stored. A change in the metadata requires a
+     * swap of physical blocks. So, the file data stored in the current
+     * metadata block needs to be copied in the scratch block, if the data
+     * of the file processed is not located in the logical block 0. When an
+     * file data is located in the logical block 0, that copy has been done
+     * while processing the file data.
+     */
+    if (file_meta.lblock != SFS_LOGICAL_DBLOCK0) {
+        err = sfs_flash_fs_mblock_migrate_lb0_data_to_scratch(fs_ctx);
+        if (err != PSA_SUCCESS) {
+            return PSA_ERROR_GENERIC_ERROR;
+        }
+    }
+
+    /* Update the metablock header, swap scratch and active blocks,
+     * erase scratch blocks.
+     */
+    return sfs_flash_fs_mblock_meta_update_finalize(fs_ctx);
+}
+
+psa_status_t sfs_flash_fs_file_delete(struct sfs_flash_fs_ctx_t *fs_ctx,
+                                      const uint8_t *fid)
+{
+    size_t del_file_data_idx;
+    uint32_t del_file_lblock;
+    uint32_t del_file_idx;
+    size_t del_file_max_size;
+    psa_status_t err;
+    size_t src_offset = fs_ctx->flash_info->block_size;
+    size_t nbr_bytes_to_move = 0;
+    uint32_t idx;
+    struct sfs_file_meta_t file_meta;
+
+    /* Get the file index */
+    err = sfs_flash_fs_mblock_get_file_idx(fs_ctx, fid, &del_file_idx);
+    if (err != PSA_SUCCESS) {
+        return PSA_ERROR_DOES_NOT_EXIST;
+    }
+
+    err = sfs_flash_fs_mblock_read_file_meta(fs_ctx, del_file_idx, &file_meta);
+    if (err != PSA_SUCCESS) {
+        return err;
+    }
+
+    if (sfs_utils_validate_fid(file_meta.id) != PSA_SUCCESS) {
+        return PSA_ERROR_DOES_NOT_EXIST;
+    }
+
+    /* Save logical block, data_index and max_size to be used later on */
+    del_file_lblock = file_meta.lblock;
+    del_file_data_idx = file_meta.data_idx;
+    del_file_max_size = file_meta.max_size;
+
+    /* Remove file metadata */
+    file_meta = (struct sfs_file_meta_t){0};
+
+    /* Update file metadata in to the scratch block */
+    err = sfs_flash_fs_mblock_update_scratch_file_meta(fs_ctx, del_file_idx,
+                                                       &file_meta);
+    if (err != PSA_SUCCESS) {
+        return err;
+    }
+
+    /* Read all file metadata */
+    for (idx = 0; idx < fs_ctx->flash_info->max_num_files; idx++) {
+        if (idx == del_file_idx) {
+            /* Skip deleted file */
+            continue;
+        }
+
+        /* Read file meta for the given file index */
+        err = sfs_flash_fs_mblock_read_file_meta(fs_ctx, idx, &file_meta);
+        if (err != PSA_SUCCESS) {
+            return err;
+        }
+
+        /* Check if the file is located in the same logical block and has a
+         * valid FID.
+         */
+        if ((file_meta.lblock == del_file_lblock) &&
+            (sfs_utils_validate_fid(file_meta.id) == PSA_SUCCESS)) {
+            /* If a file is located after the data to delete, this
+             * needs to be moved.
+             */
+            if (file_meta.data_idx > del_file_data_idx) {
+                /* Check if this is the position after the deleted
+                 * data. This will be the first file data to move.
+                 */
+                if (src_offset > file_meta.data_idx) {
+                    src_offset = file_meta.data_idx;
+                }
+
+                /* Set the new file data index location in the
+                 * data block.
+                 */
+                file_meta.data_idx -= del_file_max_size;
+
+                /* Increase number of bytes to move */
+                nbr_bytes_to_move += file_meta.max_size;
+            }
+        }
+        /* Update file metadata in to the scratch block */
+        err = sfs_flash_fs_mblock_update_scratch_file_meta(fs_ctx, idx,
+                                                           &file_meta);
+        if (err != PSA_SUCCESS) {
+            return err;
+        }
+    }
+
+    /* Compact data block */
+    err = sfs_flash_fs_dblock_compact_block(fs_ctx, del_file_lblock,
+                                            del_file_max_size,
+                                            src_offset, del_file_data_idx,
+                                            nbr_bytes_to_move);
+    if (err != PSA_SUCCESS) {
+        return err;
+    }
+
+    /* The file data in the logical block 0 is stored in same physical block
+     * where the metadata is stored. A change in the metadata requires a
+     * swap of physical blocks. So, the file data stored in the current
+     * metadata block needs to be copied in the scratch block, if the data
+     * of the file processed is not located in the logical block 0. When an
+     * file data is located in the logical block 0, that copy has been done
+     * while processing the file data.
+     */
+    if (del_file_lblock != SFS_LOGICAL_DBLOCK0) {
+        err = sfs_flash_fs_mblock_migrate_lb0_data_to_scratch(fs_ctx);
+        if (err != PSA_SUCCESS) {
+            return PSA_ERROR_GENERIC_ERROR;
+        }
+    }
+
+    /* Update the metablock header, swap scratch and active blocks,
+     * erase scratch blocks.
+     */
+    return sfs_flash_fs_mblock_meta_update_finalize(fs_ctx);
+}
+
+psa_status_t sfs_flash_fs_file_read(struct sfs_flash_fs_ctx_t *fs_ctx,
+                                    const uint8_t *fid,
+                                    size_t size,
+                                    size_t offset,
+                                    uint8_t *data)
+{
+    psa_status_t err;
+    uint32_t idx;
+    struct sfs_file_meta_t tmp_metadata;
+
+    /* Get the file index */
+    err = sfs_flash_fs_mblock_get_file_idx(fs_ctx, fid, &idx);
+    if (err != PSA_SUCCESS) {
+        return PSA_ERROR_DOES_NOT_EXIST;
+    }
+
+    /* Read file metadata */
+    err = sfs_flash_fs_mblock_read_file_meta(fs_ctx, idx, &tmp_metadata);
+    if (err != PSA_SUCCESS) {
+        return PSA_ERROR_GENERIC_ERROR;
+    }
+
+    /* Check if index is still referring to same file */
+    if (memcmp(fid, tmp_metadata.id, SFS_FILE_ID_SIZE)) {
+        return PSA_ERROR_DOES_NOT_EXIST;
+    }
+
+    /* Boundary check the incoming request */
+    err = sfs_utils_check_contained_in(tmp_metadata.cur_size, offset, size);
+    if (err != PSA_SUCCESS) {
+        return err;
+    }
+
+    /* Read the file from flash */
+    err = sfs_flash_fs_dblock_read_file(fs_ctx, &tmp_metadata, offset, size,
+                                        data);
+    if (err != PSA_SUCCESS) {
+        return PSA_ERROR_GENERIC_ERROR;
+    }
+
+    return PSA_SUCCESS;
+}
diff --git a/components/service/secure_storage/provider/secure_flash_store/flash_fs/sfs_flash_fs.h b/components/service/secure_storage/provider/secure_flash_store/flash_fs/sfs_flash_fs.h
new file mode 100644
index 0000000..704c793
--- /dev/null
+++ b/components/service/secure_storage/provider/secure_flash_store/flash_fs/sfs_flash_fs.h
@@ -0,0 +1,175 @@
+/*
+ * Copyright (c) 2018-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+/**
+ * \file  sfs_flash_fs.h
+ *
+ * \brief The purpose of this abstraction is to have the ability to plug-in
+ *        other filesystems or filesystem proxies (supplicant).
+ */
+
+#ifndef __SFS_FLASH_FS_H__
+#define __SFS_FLASH_FS_H__
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "sfs_flash_fs_mblock.h"
+#include <protocols/service/psa/packed-c/status.h>
+#include "../flash/sfs_flash.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief SFS flash filesytem context type, used to maintain state across FS
+ *        operations.
+ *
+ * \details The user should allocate a variable of this type, initialised to
+ *          zero, before calling sfs_flash_fs_prepare, and then pass it to each
+ *          subsequent FS operation. The contents are internal to the filesytem.
+ */
+typedef struct sfs_flash_fs_ctx_t sfs_flash_fs_ctx_t;
+
+/*!
+ * \struct sfs_file_info_t
+ *
+ * \brief Structure to store the file information.
+ */
+struct sfs_file_info_t {
+    size_t size_current; /*!< The current size of the flash file data */
+    size_t size_max;     /*!< The maximum size of the flash file data in
+                          * bytes.
+                          */
+    uint32_t flags;      /*!< Flags set when the file was created */
+};
+
+/**
+ * \brief Prepares the filesystem to accept operations on the files.
+ *
+ * \param[in,out] fs_ctx      Filesystem context to prepare. Must have been
+ *                            initialised by the caller.
+ * \param[in]     flash_info  Struct containing information about the flash
+ *                            device to associate with the context.
+ *
+ * \return Returns error code as specified in \ref psa_status_t
+ */
+psa_status_t sfs_flash_fs_prepare(sfs_flash_fs_ctx_t *fs_ctx,
+                                  const struct sfs_flash_info_t *flash_info);
+
+/**
+ * \brief Wipes all files from the filesystem.
+ *
+ * \param[in,out] fs_ctx  Filesystem context to wipe. Must be prepared again
+ *                        before further use.
+ *
+ * \return Returns error code as specified in \ref psa_status_t
+ */
+psa_status_t sfs_flash_fs_wipe_all(sfs_flash_fs_ctx_t *fs_ctx);
+
+/**
+ * \brief Checks if a file exists in the filesystem.
+ *
+ * \param[in,out] fs_ctx  Filesystem context
+ * \param[in]     fid     File ID
+ *
+ * \return Returns PSA_SUCCESS if the file exists. If file does not exist, it
+ *         returns PSA_ERROR_DOES_NOT_EXIST. Otherwise, it returns error code as
+ *         specified in \ref psa_status_t.
+ */
+psa_status_t sfs_flash_fs_file_exist(sfs_flash_fs_ctx_t *fs_ctx,
+                                     const uint8_t *fid);
+
+/**
+ * \brief Creates a file in the filesystem.
+ *
+ * \param[in,out] fs_ctx     Filesystem context
+ * \param[in]     fid        File ID
+ * \param[in]     max_size   Size of the file to be created
+ * \param[in]     data_size  Size of the incoming buffer. This parameter is set
+ *                           to 0 when the file is empty after the creation.
+ * \param[in]     flags      Flags of the file
+ * \param[in]     data       Pointer to buffer containing the initial data.
+ *                           This parameter is set to NULL when the file is
+ *                           empty after the creation.
+ *
+ * \return Returns PSA_SUCCESS if the file has been created correctly. If the
+ *         fid is in use, it returns PSA_ERROR_INVALID_ARGUMENT. Otherwise, it
+ *         returns error code as specified in \ref psa_status_t.
+ */
+psa_status_t sfs_flash_fs_file_create(sfs_flash_fs_ctx_t *fs_ctx,
+                                      const uint8_t *fid,
+                                      size_t max_size,
+                                      size_t data_size,
+                                      uint32_t flags,
+                                      const uint8_t *data);
+
+/**
+ * \brief Gets the file information referenced by the file ID.
+ *
+ * \param[in,out] fs_ctx  Filesystem context
+ * \param[in]     fid     File ID
+ * \param[out]    info    Pointer to the information structure to store the
+ *                        file information values \ref sfs_file_info_t
+ *
+ * \return Returns error code specified in \ref psa_status_t
+ */
+psa_status_t sfs_flash_fs_file_get_info(sfs_flash_fs_ctx_t *fs_ctx,
+                                        const uint8_t *fid,
+                                        struct sfs_file_info_t *info);
+
+/**
+ * \brief Writes data to an existing file.
+ *
+ * \param[in,out] fs_ctx  Filesystem context
+ * \param[in]     fid     File ID
+ * \param[in]     size    Size of the incoming buffer
+ * \param[in]     offset  Offset in the file
+ * \param[in]     data    Pointer to buffer containing data to be written
+ *
+ * \return Returns error code as specified in \ref psa_status_t
+ */
+psa_status_t sfs_flash_fs_file_write(sfs_flash_fs_ctx_t *fs_ctx,
+                                     const uint8_t *fid,
+                                     size_t size,
+                                     size_t offset,
+                                     const uint8_t *data);
+
+/**
+ * \brief Reads data from an existing file.
+ *
+ * \param[in,out] fs_ctx  Filesystem context
+ * \param[in]     fid     File ID
+ * \param[in]     size    Size to be read
+ * \param[in]     offset  Offset in the file
+ * \param[out]    data    Pointer to buffer to store the data
+ *
+ * \return Returns error code as specified in \ref psa_status_t
+ */
+psa_status_t sfs_flash_fs_file_read(sfs_flash_fs_ctx_t *fs_ctx,
+                                    const uint8_t *fid,
+                                    size_t size,
+                                    size_t offset,
+                                    uint8_t *data);
+
+/**
+ * \brief Deletes file referenced by the file ID.
+ *
+ * \param[in,out] fs_ctx  Filesystem context
+ * \param[in]     fid     File ID
+ *
+ * \return Returns error code as specified in \ref psa_status_t
+ */
+psa_status_t sfs_flash_fs_file_delete(sfs_flash_fs_ctx_t *fs_ctx,
+                                      const uint8_t *fid);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __SFS_FLASH_FS_H__ */
diff --git a/components/service/secure_storage/provider/secure_flash_store/flash_fs/sfs_flash_fs_check_info.h b/components/service/secure_storage/provider/secure_flash_store/flash_fs/sfs_flash_fs_check_info.h
new file mode 100644
index 0000000..2ca2f10
--- /dev/null
+++ b/components/service/secure_storage/provider/secure_flash_store/flash_fs/sfs_flash_fs_check_info.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+/**
+ * \file sfs_flash_fs_check_info.h
+ *
+ * \brief Checks at compile time that the flash device configuration is valid.
+ *        Should be included after defining the flash info parameters for a
+ *        flash device.
+ */
+
+#ifndef __SFS_FLASH_FS_CHECK_INFO_H__
+#define __SFS_FLASH_FS_CHECK_INFO_H__
+
+#include "sfs_flash_fs_mblock.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define SFS_BLOCK_META_HEADER_SIZE  sizeof(struct sfs_metadata_block_header_t)
+#define SFS_BLOCK_METADATA_SIZE     sizeof(struct sfs_block_meta_t)
+#define SFS_FILE_METADATA_SIZE      sizeof(struct sfs_file_meta_t)
+
+#if ((FLASH_INFO_NUM_BLOCKS < 2) || (FLASH_INFO_NUM_BLOCKS == 3))
+  /* The minimum number of blocks is 2. In this case, metadata and data are
+   * stored in the same physical block, and the other block is required for
+   * power failure safe operation.
+   * If at least 1 data block is available, 1 data scratch block is required for
+   * power failure safe operation. So, in this case, the minimum number of
+   * blocks is 4 (2 metadata block + 2 data blocks).
+   */
+  #error "Total number of blocks should be 2 or bigger than 3"
+#endif
+
+/* The numbers in the defines are physical block indexes, starting from 0,
+ * except for SFS_NUM_DEDICATED_DBLOCKS.
+ */
+#if (FLASH_INFO_NUM_BLOCKS == 2)
+  /* Metadata and data are stored in the same physical block, and the other
+   * block is required for power failure safe operation.
+   */
+
+  /* Initial position of scratch block is the scratch metadata block */
+  #define SFS_INIT_SCRATCH_DBLOCK 1
+
+  /* Metadata and data are stored in the same block */
+  #define SFS_INIT_DBLOCK_START 0
+
+  /* There are no dedicated data blocks when only two blocks are available */
+  #define SFS_NUM_DEDICATED_DBLOCKS 0
+
+#else
+
+  /* Initial position of scratch block is immediately after metadata blocks */
+  #define SFS_INIT_SCRATCH_DBLOCK 2
+
+  /* One metadata block and two scratch blocks are reserved. One scratch block
+   * for metadata operations and the other for files data operations.
+   */
+  #define SFS_INIT_DBLOCK_START 3
+
+  /* Number of blocks dedicated just for data is the number of blocks available
+   * beyond the initial datablock start index.
+   */
+  #define SFS_NUM_DEDICATED_DBLOCKS (FLASH_INFO_NUM_BLOCKS - \
+                                     SFS_INIT_DBLOCK_START)
+#endif /* FLASH_INFO_NUM_BLOCKS == 2 */
+
+/* Total number of datablocks is the number of dedicated datablocks plus
+ * logical datablock 0 stored in the metadata block.
+ */
+#define SFS_NUM_ACTIVE_DBLOCKS (SFS_NUM_DEDICATED_DBLOCKS + 1)
+
+#define SFS_ALL_METADATA_SIZE \
+    (SFS_BLOCK_META_HEADER_SIZE \
+     + (SFS_NUM_ACTIVE_DBLOCKS * SFS_BLOCK_METADATA_SIZE) \
+     + (FLASH_INFO_MAX_NUM_FILES * SFS_FILE_METADATA_SIZE))
+
+/* It is not required that all files fit in SFS flash area at the same time.
+ * So, it is possible that a create action fails because flash is full.
+ * However, the larger file must have enough space in the SFS flash area to be
+ * created, at least, when the SFS flash area is empty.
+ */
+/* Checks at compile time if the largest file fits in the data area */
+SFS_UTILS_BOUND_CHECK(LARGEST_SFS_FILE_NOT_FIT_IN_DATA_BLOCK,
+                      FLASH_INFO_MAX_FILE_SIZE, FLASH_INFO_BLOCK_SIZE);
+
+#if (FLASH_INFO_NUM_BLOCKS == 2)
+SFS_UTILS_BOUND_CHECK(SFS_FILE_NOT_FIT_IN_DATA_AREA, FLASH_INFO_MAX_FILE_SIZE,
+                      (FLASH_INFO_BLOCK_SIZE - SFS_ALL_METADATA_SIZE));
+#endif
+
+/* Checks at compile time if the metadata fits in a flash block */
+SFS_UTILS_BOUND_CHECK(SFS_METADATA_NOT_FIT_IN_METADATA_BLOCK,
+                      SFS_ALL_METADATA_SIZE, FLASH_INFO_BLOCK_SIZE);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __SFS_FLASH_FS_CHECK_INFO_H__ */
diff --git a/components/service/secure_storage/provider/secure_flash_store/flash_fs/sfs_flash_fs_dblock.c b/components/service/secure_storage/provider/secure_flash_store/flash_fs/sfs_flash_fs_dblock.c
new file mode 100644
index 0000000..36dc33a
--- /dev/null
+++ b/components/service/secure_storage/provider/secure_flash_store/flash_fs/sfs_flash_fs_dblock.c
@@ -0,0 +1,194 @@
+/*
+ * Copyright (c) 2018-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "sfs_flash_fs_dblock.h"
+
+#include "../flash/sfs_flash.h"
+
+/**
+ * \brief Converts logical data block number to physical number.
+ *
+ * \param[in,out] fs_ctx  Filesystem context
+ * \param[in]     lblock  Logical block number
+ *
+ * \return Return physical block number.
+ */
+static uint32_t sfs_dblock_lo_to_phy(struct sfs_flash_fs_ctx_t *fs_ctx,
+                                     uint32_t lblock)
+{
+    struct sfs_block_meta_t block_meta;
+    int32_t err;
+
+    err = sfs_flash_fs_mblock_read_block_metadata(fs_ctx, lblock, &block_meta);
+    if (err != PSA_SUCCESS) {
+        return SFS_BLOCK_INVALID_ID;
+    }
+
+    return block_meta.phy_id;
+}
+
+psa_status_t sfs_flash_fs_dblock_compact_block(
+                                              struct sfs_flash_fs_ctx_t *fs_ctx,
+                                              uint32_t lblock,
+                                              size_t free_size,
+                                              size_t src_offset,
+                                              size_t dst_offset,
+                                              size_t size)
+{
+    struct sfs_block_meta_t block_meta;
+    psa_status_t err;
+    uint32_t scratch_id = 0;
+
+    /* Read current block meta */
+    err = sfs_flash_fs_mblock_read_block_metadata(fs_ctx, lblock, &block_meta);
+    if (err != PSA_SUCCESS) {
+        return err;
+    }
+
+    /* Release data from block meta */
+    block_meta.free_size += free_size;
+
+    /* Save scratch data block physical IDs */
+    scratch_id = sfs_flash_fs_mblock_cur_data_scratch_id(fs_ctx, lblock);
+
+    /* Check if there are bytes to be compacted */
+    if (size > 0) {
+        /* Move data from source offset in current data block to scratch block
+         * destination offset.
+         */
+        err = sfs_flash_block_to_block_move(fs_ctx->flash_info, scratch_id,
+                                            dst_offset, block_meta.phy_id,
+                                            src_offset, size);
+        if (err != PSA_SUCCESS) {
+            return PSA_ERROR_GENERIC_ERROR;
+        }
+    }
+
+    if (dst_offset > block_meta.data_start) {
+        /* Copy data from the beginning of data block until
+         * the position where the data will be reallocated later
+         */
+        err = sfs_flash_block_to_block_move(fs_ctx->flash_info, scratch_id,
+                                            block_meta.data_start,
+                                            block_meta.phy_id,
+                                            block_meta.data_start,
+                                            (dst_offset-block_meta.data_start));
+        if (err != PSA_SUCCESS) {
+            return PSA_ERROR_GENERIC_ERROR;
+        }
+    }
+
+    /* Swap the scratch and current data blocks. Must swap even with nothing
+     * to compact so that deleted file is left in scratch and erased as part
+     * of finalization.
+     */
+    sfs_flash_fs_mblock_set_data_scratch(fs_ctx, block_meta.phy_id, lblock);
+
+    /* Set scratch block ID as the one which contains the new data block */
+    block_meta.phy_id = scratch_id;
+
+    /* Update block metadata in scratch metadata block */
+    err = sfs_flash_fs_mblock_update_scratch_block_meta(fs_ctx, lblock,
+                                                        &block_meta);
+    if (err != PSA_SUCCESS) {
+        /* Swap back the data block as there was an issue in the process */
+        sfs_flash_fs_mblock_set_data_scratch(fs_ctx, scratch_id, lblock);
+        return err;
+    }
+
+    /* Commit data block modifications to flash, unless the data is in logical
+     * data block 0, in which case it will be flushed at the end of the metadata
+     * block update.
+     */
+    if (lblock != SFS_LOGICAL_DBLOCK0) {
+        err = fs_ctx->flash_info->flush(fs_ctx->flash_info);
+    }
+
+    return err;
+}
+
+psa_status_t sfs_flash_fs_dblock_read_file(
+                                        struct sfs_flash_fs_ctx_t *fs_ctx,
+                                        const struct sfs_file_meta_t *file_meta,
+                                        size_t offset,
+                                        size_t size,
+                                        uint8_t *buf)
+{
+    uint32_t phys_block;
+    size_t pos;
+
+    phys_block = sfs_dblock_lo_to_phy(fs_ctx, file_meta->lblock);
+    if (phys_block == SFS_BLOCK_INVALID_ID) {
+        return PSA_ERROR_GENERIC_ERROR;
+    }
+
+    pos = (file_meta->data_idx + offset);
+
+    return fs_ctx->flash_info->read(fs_ctx->flash_info, phys_block, buf, pos,
+                                    size);
+}
+
+psa_status_t sfs_flash_fs_dblock_write_file(
+                                      struct sfs_flash_fs_ctx_t *fs_ctx,
+                                      const struct sfs_block_meta_t *block_meta,
+                                      const struct sfs_file_meta_t *file_meta,
+                                      size_t offset,
+                                      size_t size,
+                                      const uint8_t *data)
+{
+    psa_status_t err;
+    uint32_t scratch_id;
+    size_t pos;
+    size_t num_bytes;
+
+    scratch_id = sfs_flash_fs_mblock_cur_data_scratch_id(fs_ctx,
+                                                         file_meta->lblock);
+
+    /* Calculate the position of the new file data in the block */
+    pos = file_meta->data_idx + offset;
+
+    /* Move data up to the new file data position */
+    err = sfs_flash_block_to_block_move(fs_ctx->flash_info,
+                                        scratch_id,
+                                        block_meta->data_start,
+                                        block_meta->phy_id,
+                                        block_meta->data_start,
+                                        pos - block_meta->data_start);
+    if (err != PSA_SUCCESS) {
+        return err;
+    }
+
+    /* Write the new file data */
+    err = fs_ctx->flash_info->write(fs_ctx->flash_info, scratch_id, data, pos,
+                                    size);
+    if (err != PSA_SUCCESS) {
+        return err;
+    }
+
+    /* Calculate the position of the end of the file */
+    pos = file_meta->data_idx + file_meta->max_size;
+
+    /* Calculate the size of the data in the block after the end of the file */
+    num_bytes = (fs_ctx->flash_info->block_size - block_meta->free_size) - pos;
+
+    /* Move data between the end of the file and the end of the block data */
+    err = sfs_flash_block_to_block_move(fs_ctx->flash_info, scratch_id, pos,
+                                        block_meta->phy_id, pos, num_bytes);
+    if (err != PSA_SUCCESS) {
+        return err;
+    }
+
+    /* Commit data block modifications to flash, unless the data is in logical
+     * data block 0, in which case it will be flushed at the end of the metadata
+     * block update.
+     */
+    if (file_meta->lblock != SFS_LOGICAL_DBLOCK0) {
+        err = fs_ctx->flash_info->flush(fs_ctx->flash_info);
+    }
+
+    return err;
+}
diff --git a/components/service/secure_storage/provider/secure_flash_store/flash_fs/sfs_flash_fs_dblock.h b/components/service/secure_storage/provider/secure_flash_store/flash_fs/sfs_flash_fs_dblock.h
new file mode 100644
index 0000000..0fc9d50
--- /dev/null
+++ b/components/service/secure_storage/provider/secure_flash_store/flash_fs/sfs_flash_fs_dblock.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2018-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __SFS_FLASH_FS_DBLOCK_H__
+#define __SFS_FLASH_FS_DBLOCK_H__
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <protocols/service/psa/packed-c/status.h>
+#include "sfs_flash_fs_mblock.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief Compacts block data for the given logical block.
+ *
+ * \param[in,out] fs_ctx      Filesystem context
+ * \param[in]     lblock      Logical data block to compact
+ * \param[in]     free_size   Available data size to compact
+ * \param[in]     src_offset  Offset in the current data block which points to
+ *                            the data position to reallocate
+ * \param[in]     dst_offset  Offset in the scratch block which points to the
+ *                            data position to store the data to be reallocated
+ * \param[in]     size        Number of bytes to be reallocated
+ *
+ * \return Returns error code as specified in \ref psa_status_t
+ */
+psa_status_t sfs_flash_fs_dblock_compact_block(
+                                              struct sfs_flash_fs_ctx_t *fs_ctx,
+                                              uint32_t lblock,
+                                              size_t free_size,
+                                              size_t src_offset,
+                                              size_t dst_offset,
+                                              size_t size);
+
+/**
+ * \brief Reads the file content.
+ *
+ * \param[in,out] fs_ctx     Filesystem context
+ * \param[in]     file_meta  File metadata
+ * \param[in]     offset     Offset in the file
+ * \param[in]     size       Size to be read
+ * \param[out]    buf        Buffer pointer to store the data
+ *
+ * \return Returns error code as specified in \ref psa_status_t
+ */
+psa_status_t sfs_flash_fs_dblock_read_file(
+                                        struct sfs_flash_fs_ctx_t *fs_ctx,
+                                        const struct sfs_file_meta_t *file_meta,
+                                        size_t offset,
+                                        size_t size,
+                                        uint8_t *buf);
+
+/**
+ * \brief Writes scratch data block content with requested data and the rest of
+ *        the data from the given logical block.
+ *
+ * \param[in,out] fs_ctx      Filesystem context
+ * \param[in]     block_meta  Block metadata
+ * \param[in]     file_meta   File metadata
+ * \param[in]     offset      Offset in the scratch data block where to start
+ *                            the copy of the incoming data
+ * \param[in]     size        Size of the incoming data
+ * \param[in]     data        Pointer to data buffer to copy in the scratch data
+ *                            block
+ *
+ * \return Returns error code as specified in \ref psa_status_t
+ */
+psa_status_t sfs_flash_fs_dblock_write_file(
+                                      struct sfs_flash_fs_ctx_t *fs_ctx,
+                                      const struct sfs_block_meta_t *block_meta,
+                                      const struct sfs_file_meta_t *file_meta,
+                                      size_t offset,
+                                      size_t size,
+                                      const uint8_t *data);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __SFS_FLASH_FS_DBLOCK_H__ */
diff --git a/components/service/secure_storage/provider/secure_flash_store/flash_fs/sfs_flash_fs_mblock.c b/components/service/secure_storage/provider/secure_flash_store/flash_fs/sfs_flash_fs_mblock.c
new file mode 100644
index 0000000..cb435cf
--- /dev/null
+++ b/components/service/secure_storage/provider/secure_flash_store/flash_fs/sfs_flash_fs_mblock.c
@@ -0,0 +1,1074 @@
+/*
+ * Copyright (c) 2018-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "sfs_flash_fs_mblock.h"
+#include <string.h>
+
+/* Physical ID of the two metadata blocks */
+/* NOTE: the earmarked area may not always start at block number 0.
+ *       However, the flash interface can always add the required offset.
+ */
+#define SFS_METADATA_BLOCK0  0
+#define SFS_METADATA_BLOCK1  1
+
+/*!
+ * \def SFS_OTHER_META_BLOCK
+ *
+ * \brief Macro to get the the swap metadata block.
+ */
+#define SFS_OTHER_META_BLOCK(metablock) \
+(((metablock) == SFS_METADATA_BLOCK0) ? \
+(SFS_METADATA_BLOCK1) : (SFS_METADATA_BLOCK0))
+
+#define SFS_BLOCK_META_HEADER_SIZE  sizeof(struct sfs_metadata_block_header_t)
+#define SFS_BLOCK_METADATA_SIZE     sizeof(struct sfs_block_meta_t)
+#define SFS_FILE_METADATA_SIZE      sizeof(struct sfs_file_meta_t)
+
+/* FIXME: Precompute these for each context */
+/**
+ * \brief Gets the physical block ID of the initial position of the scratch
+ *        data block, for the current context.
+ *
+ * \param[in,out] fs_ctx  Filesystem context
+ *
+ * \return The physical block ID
+ */
+__attribute__((always_inline))
+static inline uint32_t sfs_init_scratch_dblock(
+                                              struct sfs_flash_fs_ctx_t *fs_ctx)
+{
+    /* When there are two blocks, the initial position of the scratch data block
+     * is the scratch metadata block. Otherwise, the initial position of scratch
+     * data block is immediately after the metadata blocks.
+     */
+    return fs_ctx->flash_info->num_blocks == 2 ? 1 : 2;
+}
+
+/**
+ * \brief Gets the physical block ID of the start position of the data blocks,
+ *        for the current context.
+ *
+ * \param[in,out] fs_ctx  Filesystem context
+ *
+ * \return The physical block ID
+ */
+__attribute__((always_inline))
+static inline uint32_t sfs_init_dblock_start(struct sfs_flash_fs_ctx_t *fs_ctx)
+{
+    /* Metadata and data are always stored in the same block with two blocks.
+     * Otherwise, one metadata block and two scratch blocks are reserved. One
+     * scratch block for metadata operations and the other for data operations.
+     */
+    return fs_ctx->flash_info->num_blocks == 2 ? 0 : 3;
+}
+
+/**
+ * \brief Gets the number of blocks that are dedicated wholely for data, for the
+ *        current context.
+ *
+ * \param[in,out] fs_ctx  Filesystem context
+ *
+ * \return The number of dedicated datablocks
+ */
+static uint32_t sfs_num_dedicated_dblocks(struct sfs_flash_fs_ctx_t *fs_ctx)
+{
+    /* There are no dedicated data blocks when only two blocks are available.
+     * Otherwise, the number of blocks dedicated just for data is the number of
+     * blocks available beyond the initial datablock start index.
+     */
+     return fs_ctx->flash_info->num_blocks == 2 ? 0 :
+                 fs_ctx->flash_info->num_blocks - sfs_init_dblock_start(fs_ctx);
+}
+
+/**
+ * \brief Gets the number of active data blocks, for the current context.
+ *
+ * \param[in,out] fs_ctx  Filesystem context
+ *
+ * \return The number of active datablocks
+ */
+__attribute__((always_inline))
+static inline uint32_t sfs_num_active_dblocks(struct sfs_flash_fs_ctx_t *fs_ctx)
+{
+    /* Total number of data blocks is the number of dedicated data blocks plus
+     * logical data block 0 stored in the metadata block.
+     */
+    return sfs_num_dedicated_dblocks(fs_ctx) + 1;
+}
+
+/**
+ * \brief Gets offset of a logical block's metadata in metadata block.
+ *
+ * \param[in] lblock  Logical block number
+ *
+ * \return Return offset value in metadata block
+ */
+static size_t sfs_mblock_block_meta_offset(uint32_t lblock)
+{
+    return SFS_BLOCK_META_HEADER_SIZE + (lblock * SFS_BLOCK_METADATA_SIZE);
+}
+
+/**
+ * \brief Gets offset of an file metadata in metadata block.
+ *
+ * \param[in,out] fs_ctx  Filesystem context
+ * \param[in]     idx     File metadata entry index
+ *
+ * \return Return offset value in metadata block
+ */
+static size_t sfs_mblock_file_meta_offset(struct sfs_flash_fs_ctx_t *fs_ctx,
+                                          uint32_t idx)
+{
+    return SFS_BLOCK_META_HEADER_SIZE
+           + (sfs_num_active_dblocks(fs_ctx) * SFS_BLOCK_METADATA_SIZE)
+           + (idx * SFS_FILE_METADATA_SIZE);
+}
+
+/**
+ * \brief Swaps metablocks. Scratch becomes active and active becomes scratch.
+ *
+ * \param[in,out] fs_ctx  Filesystem context
+ */
+static void sfs_mblock_swap_metablocks(struct sfs_flash_fs_ctx_t *fs_ctx)
+{
+    uint32_t tmp_block;
+
+    tmp_block = fs_ctx->scratch_metablock;
+    fs_ctx->scratch_metablock = fs_ctx->active_metablock;
+    fs_ctx->active_metablock = tmp_block;
+}
+
+/**
+ * \brief Finds the potential most recent valid metablock.
+ *
+ * \param[in] h_meta0  Header metadata of meta block 0
+ * \param[in] h_meta1  Header metadata of meta block 1
+ *
+ * \return most recent metablock
+ */
+static uint8_t sfs_mblock_latest_meta_block(
+                              const struct sfs_metadata_block_header_t *h_meta0,
+                              const struct sfs_metadata_block_header_t *h_meta1)
+{
+    uint8_t cur_meta;
+    uint8_t meta0_swap_count = h_meta0->active_swap_count;
+    uint8_t meta1_swap_count = h_meta1->active_swap_count;
+
+    /* Logic: if the swap count is 0, then it has rolled over. The metadata
+     * block 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 update. In all other cases, the block with the highest swap
+     * count is the latest one.
+     */
+    if ((meta1_swap_count == 0) && (meta0_swap_count != 1)) {
+        /* Metadata block 1 swap count has rolled over and metadata block 0
+         * swap count has not, so block 1 is the latest.
+         */
+        cur_meta = SFS_METADATA_BLOCK1;
+
+    } else if ((meta0_swap_count == 0) && (meta1_swap_count != 1)) {
+        /* Metadata block 0 swap count has rolled over and metadata block 1
+         * swap count has not, so block 0 is the latest.
+         */
+        cur_meta = SFS_METADATA_BLOCK0;
+
+    } else if (meta1_swap_count > meta0_swap_count) {
+        /* Neither swap count has just rolled over and metadata block 1 has a
+         * higher swap count, so block 1 is the latest.
+         */
+        cur_meta = SFS_METADATA_BLOCK1;
+
+    } else {
+        /* Neither swap count has just rolled over and metadata block 0 has a
+         * higher or equal swap count, so block 0 is the latest.
+         */
+        cur_meta = SFS_METADATA_BLOCK0;
+    }
+
+    return cur_meta;
+}
+
+#ifdef SFS_VALIDATE_METADATA_FROM_FLASH
+/**
+ * \brief Validates file metadata in order to guarantee that a corruption or
+ *        malicious change in stored metadata doesn't result in an invalid
+ *        access.
+ *
+ * \param[in,out] fs_ctx     Filesystem context
+ * \param[in]     file_meta  Pointer to file meta structure
+ *
+ * \return Returns error code as specified in \ref psa_status_t
+ */
+__attribute__((always_inline))
+static inline psa_status_t sfs_mblock_validate_file_meta(
+                                        struct sfs_flash_fs_ctx_t *fs_ctx,
+                                        const struct sfs_file_meta_t *file_meta)
+{
+    int32_t err;
+
+    /* Logical block ID can not be bigger or equal than number of
+     * active blocks.
+     */
+    if (file_meta->lblock >= sfs_num_active_dblocks(fs_ctx)) {
+        return PSA_ERROR_DATA_CORRUPT;
+    }
+
+    /* meta->id can be 0 if the file is not in use. If it is in
+     * use, check the metadata.
+     */
+    if (sfs_utils_validate_fid(file_meta->id) == PSA_SUCCESS) {
+        /* validate files values if file is in use */
+        if (file_meta->max_size > fs_ctx->flash_info->max_file_size) {
+            return PSA_ERROR_DATA_CORRUPT;
+        }
+
+        /* The current file data size must be smaller or equal than
+         * file data max size.
+         */
+        if (file_meta->cur_size > file_meta->max_size) {
+            return PSA_ERROR_DATA_CORRUPT;
+        }
+
+        if (file_meta->lblock == SFS_LOGICAL_DBLOCK0) {
+            /* In block 0, data index must be located after the metadata */
+            if (file_meta->data_idx < sfs_mblock_file_meta_offset(fs_ctx,
+                                           fs_ctx->flash_info->max_num_files)) {
+                return PSA_ERROR_DATA_CORRUPT;
+            }
+        }
+
+        /* Boundary check the incoming request */
+        err = sfs_utils_check_contained_in(fs_ctx->flash_info->block_size,
+                                           file_meta->data_idx,
+                                           file_meta->max_size);
+        if (err != PSA_SUCCESS) {
+            return PSA_ERROR_DATA_CORRUPT;
+        }
+    }
+
+    return PSA_SUCCESS;
+}
+
+/**
+ * \brief Validates block metadata in order to guarantee that a corruption or
+ *        malicious change in stored metadata doesn't result in an invalid
+ *        access.
+ *
+ * \param[in,out] fs_ctx      Filesystem context
+ * \param[in]     block_meta  Pointer to block meta structure
+ *
+ * \return Returns error code as specified in \ref psa_status_t
+ */
+__attribute__((always_inline))
+static inline psa_status_t sfs_mblock_validate_block_meta(
+                                      struct sfs_flash_fs_ctx_t *fs_ctx,
+                                      const struct sfs_block_meta_t *block_meta)
+{
+    psa_status_t err;
+    /* Data block's data start at position 0 */
+    size_t valid_data_start_value = 0;
+
+    if (block_meta->phy_id >= fs_ctx->flash_info->num_blocks) {
+        return PSA_ERROR_DATA_CORRUPT;
+    }
+
+    /* Boundary check: block data start + free size can not be bigger
+     * than max block size.
+     */
+    err = sfs_utils_check_contained_in(fs_ctx->flash_info->block_size,
+                                       block_meta->data_start,
+                                       block_meta->free_size);
+    if (err != PSA_SUCCESS) {
+        return PSA_ERROR_DATA_CORRUPT;
+    }
+
+    if (block_meta->phy_id == SFS_METADATA_BLOCK0 ||
+        block_meta->phy_id == SFS_METADATA_BLOCK1) {
+
+        /* For metadata + data block, data index must start after the
+         * metadata area.
+         */
+        valid_data_start_value = sfs_mblock_file_meta_offset(fs_ctx,
+                                             fs_ctx->flash_info->max_num_files);
+    }
+
+    if (block_meta->data_start != valid_data_start_value) {
+        return PSA_ERROR_DATA_CORRUPT;
+    }
+
+    return PSA_SUCCESS;
+}
+#endif /* SFS_VALIDATE_METADATA_FROM_FLASH */
+
+/**
+ * \brief Gets a free file metadata table entry.
+ *
+ * \param[in,out] fs_ctx  Filesystem context
+ *
+ * \return Return index of a free file meta entry
+ */
+static uint32_t sfs_get_free_file_index(struct sfs_flash_fs_ctx_t *fs_ctx)
+{
+    int32_t err;
+    uint32_t i;
+    struct sfs_file_meta_t tmp_metadata;
+
+    for (i = 0; i < fs_ctx->flash_info->max_num_files; i++) {
+        err = sfs_flash_fs_mblock_read_file_meta(fs_ctx, i, &tmp_metadata);
+        if (err != PSA_SUCCESS) {
+            return SFS_METADATA_INVALID_INDEX;
+        }
+
+        /* Check if this entry is free by checking if ID values is an
+         * invalid ID.
+         */
+        if (sfs_utils_validate_fid(tmp_metadata.id) != PSA_SUCCESS) {
+            /* Found */
+            return i;
+        }
+    }
+
+    return SFS_METADATA_INVALID_INDEX;
+}
+
+/**
+ * \brief Erases data and meta scratch blocks.
+ *
+ * \param[in,out] fs_ctx  Filesystem context
+ *
+ * \return Returns error code as specified in \ref psa_status_t
+ */
+static psa_status_t sfs_mblock_erase_scratch_blocks(
+                                              struct sfs_flash_fs_ctx_t *fs_ctx)
+{
+    psa_status_t err;
+    uint32_t scratch_datablock;
+
+    /* For the atomicity of the data update process
+     * and power-failure-safe operation, it is necessary that
+     * metadata scratch block is erased before data block.
+     */
+    err = fs_ctx->flash_info->erase(fs_ctx->flash_info,
+                                    fs_ctx->scratch_metablock);
+    if (err != PSA_SUCCESS) {
+        return err;
+    }
+
+    /* If the number of blocks is bigger than 2, the code needs to erase the
+     * scratch block used to process any change in the data block which contains
+     * only data. Otherwise, if the number of blocks is equal to 2, it means
+     * that all data is stored in the metadata block.
+     */
+    if (fs_ctx->flash_info->num_blocks > 2) {
+        scratch_datablock =
+            sfs_flash_fs_mblock_cur_data_scratch_id(fs_ctx,
+                                                    (SFS_LOGICAL_DBLOCK0 + 1));
+        err = fs_ctx->flash_info->erase(fs_ctx->flash_info, scratch_datablock);
+    }
+
+    return err;
+}
+
+/**
+ * \brief Updates scratch block meta.
+ *
+ * \param[in,out] fs_ctx      Filesystem context
+ * \param[in]     lblock      Logical block number
+ * \param[in]     block_meta  Pointer to the block metadata data to write in the
+ *                            scratch block
+ *
+ * \return Returns error code as specified in \ref psa_status_t
+ */
+static psa_status_t sfs_mblock_update_scratch_block_meta(
+                                      struct sfs_flash_fs_ctx_t *fs_ctx,
+                                      uint32_t lblock,
+                                      const struct sfs_block_meta_t *block_meta)
+{
+    size_t pos;
+
+    /* Calculate the position */
+    pos = sfs_mblock_block_meta_offset(lblock);
+    return fs_ctx->flash_info->write(fs_ctx->flash_info,
+                                     fs_ctx->scratch_metablock,
+                                     (const uint8_t *)block_meta, pos,
+                                     SFS_BLOCK_METADATA_SIZE);
+}
+
+/**
+ * \brief Copies rest of the block metadata.
+ *
+ * \param[in,out] fs_ctx  Filesystem context
+ * \param[in]     lblock  Logical block number to skip
+ *
+ * \return Returns error code as specified in \ref psa_status_t
+ */
+static psa_status_t sfs_mblock_copy_remaining_block_meta(
+                                              struct sfs_flash_fs_ctx_t *fs_ctx,
+                                              uint32_t lblock)
+{
+    struct sfs_block_meta_t block_meta;
+    psa_status_t err;
+    uint32_t meta_block;
+    size_t pos;
+    uint32_t scratch_block;
+    size_t size;
+
+    scratch_block = fs_ctx->scratch_metablock;
+    meta_block = fs_ctx->active_metablock;
+
+    if (lblock != SFS_LOGICAL_DBLOCK0) {
+        /* The file data in the logical block 0 is stored in same physical
+         * block where the metadata is stored. A change in the metadata requires
+         * a swap of physical blocks. So, the physical block ID of logical block
+         * 0 needs to be updated to reflect this change, if the file processed
+         * is not located in logical block 0. If it is located in block 0,
+         * the physical block ID has been updated while processing the file
+         * data.
+         */
+        err = sfs_flash_fs_mblock_read_block_metadata(fs_ctx,
+                                                      SFS_LOGICAL_DBLOCK0,
+                                                      &block_meta);
+        if (err != PSA_SUCCESS) {
+            return PSA_ERROR_GENERIC_ERROR;
+        }
+
+        /* Update physical ID for logical block 0 to match with the
+         * metadata block physical ID.
+         */
+        block_meta.phy_id = scratch_block;
+        err = sfs_mblock_update_scratch_block_meta(fs_ctx, SFS_LOGICAL_DBLOCK0,
+                                                   &block_meta);
+        if (err != PSA_SUCCESS) {
+            return PSA_ERROR_GENERIC_ERROR;
+        }
+
+        /* Copy the rest of metadata blocks between logical block 0 and
+         * the logical block provided in the function.
+         */
+        if (lblock > 1) {
+            pos = sfs_mblock_block_meta_offset(SFS_LOGICAL_DBLOCK0 + 1);
+
+            size = sfs_mblock_block_meta_offset(lblock) - pos;
+
+            /* Copy rest of the block data from previous block */
+            /* Data before updated content */
+            err = sfs_flash_block_to_block_move(fs_ctx->flash_info,
+                                                scratch_block, pos, meta_block,
+                                                pos, size);
+            if (err != PSA_SUCCESS) {
+                return err;
+            }
+        }
+    }
+
+    /* Move meta blocks data after updated content */
+    pos = sfs_mblock_block_meta_offset(lblock+1);
+
+    size = sfs_mblock_file_meta_offset(fs_ctx, 0) - pos;
+
+    return sfs_flash_block_to_block_move(fs_ctx->flash_info, scratch_block, pos,
+                                         meta_block, pos, size);
+}
+
+/**
+ * \brief Checks the validity of the metadata block's swap count.
+ *
+ * \param[in,out] fs_ctx      Filesystem context
+ * \param[in]     swap_count  Swap count to validate
+ *
+ * \return Returns error code as specified in \ref psa_status_t
+ */
+__attribute__((always_inline))
+static inline psa_status_t sfs_mblock_validate_swap_count(
+                                              struct sfs_flash_fs_ctx_t *fs_ctx,
+                                              uint8_t swap_count)
+{
+    /* When a flash block is erased, the default value
+     * is usually 0xFF (i.e. all 1s). Since the swap count
+     * is updated last (when encryption is disabled), it is
+     * possible that due to a power failure, the swap count
+     * value in metadata header is 0xFFFF..., which mean
+     * it will appear to be most recent block. Which isn't
+     * a problem in itself, as the rest of the metadata is fully
+     * valid (as it would have been written before swap count).
+     * However, this also means that previous update process
+     * wasn't complete. So, if the value is 0xFF..., revert
+     * back to previous metablock instead.
+     */
+    return (swap_count == fs_ctx->flash_info->erase_val)
+           ? PSA_ERROR_GENERIC_ERROR
+           : PSA_SUCCESS;
+}
+
+/**
+ * \brief Checks the validity of FS version.
+ *
+ * \param[in] fs_version  File system version.
+ *
+ * \return Returns error code as specified in \ref psa_status_t
+ */
+__attribute__((always_inline))
+static inline psa_status_t sfs_mblock_validate_fs_version(uint8_t fs_version)
+{
+    /* Looks for exact version number.
+     * FIXME: backward compatibility could be considered in future revisions.
+     */
+    return (fs_version != SFS_SUPPORTED_VERSION) ? PSA_ERROR_GENERIC_ERROR
+                                                 : PSA_SUCCESS;
+}
+
+/**
+ * \brief Validates header metadata in order to guarantee that a corruption or
+ *        malicious change in stored metadata doesn't result in an invalid
+ *        access and the header version is correct.
+ *
+ * \param[in,out] fs_ctx  Filesystem context
+ * \param[in]     h_meta  Pointer to metadata block header
+ *
+ * \return Returns error code as specified in \ref psa_status_t
+ */
+static psa_status_t sfs_mblock_validate_header_meta(
+                               struct sfs_flash_fs_ctx_t *fs_ctx,
+                               const struct sfs_metadata_block_header_t *h_meta)
+{
+    psa_status_t err;
+
+    err = sfs_mblock_validate_fs_version(h_meta->fs_version);
+    if (err == PSA_SUCCESS) {
+        err = sfs_mblock_validate_swap_count(fs_ctx, h_meta->active_swap_count);
+    }
+
+    return err;
+}
+
+/**
+ * \brief Writes the scratch metadata's header.
+ *
+ * \param[in,out] fs_ctx  Filesystem context
+ *
+ * \return Returns error code as specified in \ref psa_status_t
+ */
+static psa_status_t sfs_mblock_write_scratch_meta_header(
+                                              struct sfs_flash_fs_ctx_t *fs_ctx)
+{
+    psa_status_t err;
+
+    /* Increment the swap count */
+    fs_ctx->meta_block_header.active_swap_count += 1;
+
+    err = sfs_mblock_validate_swap_count(fs_ctx,
+                                   fs_ctx->meta_block_header.active_swap_count);
+    if (err != PSA_SUCCESS) {
+        /* Reset the swap count to 0 */
+        fs_ctx->meta_block_header.active_swap_count = 0;
+    }
+
+    /* Write the metadata block header */
+    return fs_ctx->flash_info->write(fs_ctx->flash_info,
+                                     fs_ctx->scratch_metablock,
+                                     (uint8_t *)(&fs_ctx->meta_block_header),
+                                     0, SFS_BLOCK_META_HEADER_SIZE);
+}
+
+/**
+ * \brief Reads the active metadata block header into sfs_system_ctx.
+ *
+ * \param[in,out] fs_ctx  Filesystem context
+ *
+ * \return Returns error code as specified in \ref psa_status_t
+ */
+static psa_status_t sfs_mblock_read_meta_header(
+                                              struct sfs_flash_fs_ctx_t *fs_ctx)
+{
+    psa_status_t err;
+
+    err = fs_ctx->flash_info->read(fs_ctx->flash_info, fs_ctx->active_metablock,
+                                   (uint8_t *)&fs_ctx->meta_block_header, 0,
+                                   SFS_BLOCK_META_HEADER_SIZE);
+    if (err != PSA_SUCCESS) {
+        return err;
+    }
+
+    return sfs_mblock_validate_header_meta(fs_ctx, &fs_ctx->meta_block_header);
+}
+
+/**
+ * \brief Reserves space for an file.
+ *
+ * \param[in,out] fs_ctx      Filesystem context
+ * \param[in]     fid         File ID
+ * \param[in]     size        Size of the file for which space is reserve
+ * \param[in]     flags       Flags set when the file is created
+ * \param[out]    file_meta   File metadata entry
+ * \param[out]    block_meta  Block metadata entry
+ *
+ * \return Returns error code as specified in \ref psa_status_t
+ */
+static psa_status_t sfs_mblock_reserve_file(struct sfs_flash_fs_ctx_t *fs_ctx,
+                                            const uint8_t *fid, size_t size,
+                                            uint32_t flags,
+                                            struct sfs_file_meta_t *file_meta,
+                                            struct sfs_block_meta_t *block_meta)
+{
+    psa_status_t err;
+    uint32_t i;
+
+    for (i = 0; i < sfs_num_active_dblocks(fs_ctx); i++) {
+        err = sfs_flash_fs_mblock_read_block_metadata(fs_ctx, i, block_meta);
+        if (err != PSA_SUCCESS) {
+            return PSA_ERROR_GENERIC_ERROR;
+        }
+
+        if (block_meta->free_size >= size) {
+            /* Set file metadata */
+            file_meta->lblock = i;
+            file_meta->data_idx = fs_ctx->flash_info->block_size
+                                  - block_meta->free_size;
+            file_meta->max_size = size;
+            memcpy(file_meta->id, fid, SFS_FILE_ID_SIZE);
+            file_meta->cur_size = 0;
+            file_meta->flags = flags;
+
+            /* Update block metadata */
+            block_meta->free_size -= size;
+            return PSA_SUCCESS;
+        }
+    }
+
+    /* No block has large enough space to fit the requested file */
+    return PSA_ERROR_INSUFFICIENT_STORAGE;
+}
+
+/**
+ * \brief Validates and find the valid-active metablock
+ *
+ * \param[in,out] fs_ctx  Filesystem context
+ *
+ * \return Returns value as specified in \ref psa_status_t
+ */
+static psa_status_t sfs_init_get_active_metablock(
+                                              struct sfs_flash_fs_ctx_t *fs_ctx)
+{
+    uint32_t cur_meta_block = SFS_BLOCK_INVALID_ID;
+    psa_status_t err;
+    struct sfs_metadata_block_header_t h_meta0;
+    struct sfs_metadata_block_header_t h_meta1;
+    uint8_t num_valid_meta_blocks = 0;
+
+    /* First two blocks are reserved for metadata */
+
+    /* Read the header of both the metdata blocks. If the read succeeds, then
+     * attempt to validate the metadata header, otherwise assume that the block
+     * update was incomplete
+     */
+    err = fs_ctx->flash_info->read(fs_ctx->flash_info, SFS_METADATA_BLOCK0,
+                                   (uint8_t *)&h_meta0, 0,
+                                   SFS_BLOCK_META_HEADER_SIZE);
+    if (err == PSA_SUCCESS) {
+        if (sfs_mblock_validate_header_meta(fs_ctx, &h_meta0) == PSA_SUCCESS) {
+            num_valid_meta_blocks++;
+            cur_meta_block = SFS_METADATA_BLOCK0;
+        }
+    }
+
+    err = fs_ctx->flash_info->read(fs_ctx->flash_info, SFS_METADATA_BLOCK1,
+                                   (uint8_t *)&h_meta1, 0,
+                                   SFS_BLOCK_META_HEADER_SIZE);
+    if (err == PSA_SUCCESS) {
+        if (sfs_mblock_validate_header_meta(fs_ctx, &h_meta1) == PSA_SUCCESS) {
+            num_valid_meta_blocks++;
+            cur_meta_block = SFS_METADATA_BLOCK1;
+        }
+    }
+
+    /* If there are more than 1 potential metablocks, the previous
+     * update operation was interrupted by power failure. In which case,
+     * need to find out which one is potentially latest metablock.
+     */
+    if (num_valid_meta_blocks > 1) {
+        cur_meta_block = sfs_mblock_latest_meta_block(&h_meta0, &h_meta1);
+    } else if (num_valid_meta_blocks == 0) {
+        return PSA_ERROR_GENERIC_ERROR;
+    }
+
+    fs_ctx->active_metablock = cur_meta_block;
+    fs_ctx->scratch_metablock = SFS_OTHER_META_BLOCK(cur_meta_block);
+
+    return PSA_SUCCESS;
+}
+
+psa_status_t sfs_flash_fs_mblock_cp_remaining_file_meta(
+                                              struct sfs_flash_fs_ctx_t *fs_ctx,
+                                              uint32_t idx)
+{
+    psa_status_t err;
+    size_t end;
+    uint32_t meta_block;
+    size_t pos;
+    uint32_t scratch_block;
+
+    scratch_block = fs_ctx->scratch_metablock;
+    meta_block = fs_ctx->active_metablock;
+    /* Calculate the position */
+    pos = sfs_mblock_file_meta_offset(fs_ctx, 0);
+    /* Copy rest of the block data from previous block */
+    /* Data before updated content */
+    err = sfs_flash_block_to_block_move(fs_ctx->flash_info, scratch_block, pos,
+                                        meta_block, pos,
+                                        (idx * SFS_FILE_METADATA_SIZE));
+    if (err != PSA_SUCCESS) {
+        return err;
+    }
+
+    /* Data after updated content */
+    pos = sfs_mblock_file_meta_offset(fs_ctx, idx + 1);
+
+    /* Get end of file meta position which is the position after the last
+     * byte of file meta.
+     */
+    end = sfs_mblock_file_meta_offset(fs_ctx,
+                                      fs_ctx->flash_info->max_num_files);
+    if (end > pos) {
+        err = sfs_flash_block_to_block_move(fs_ctx->flash_info, scratch_block,
+                                            pos, meta_block, pos, (end - pos));
+    }
+
+    return err;
+}
+
+uint32_t sfs_flash_fs_mblock_cur_data_scratch_id(
+                                              struct sfs_flash_fs_ctx_t *fs_ctx,
+                                              uint32_t lblock)
+{
+    if (lblock == SFS_LOGICAL_DBLOCK0) {
+        /* Scratch logical data block 0 physical IDs */
+        return fs_ctx->scratch_metablock;
+    }
+
+    return fs_ctx->meta_block_header.scratch_dblock;
+}
+
+psa_status_t sfs_flash_fs_mblock_get_file_idx(struct sfs_flash_fs_ctx_t *fs_ctx,
+                                              const uint8_t *fid,
+                                              uint32_t *idx)
+{
+    psa_status_t err;
+    uint32_t i;
+    struct sfs_file_meta_t tmp_metadata;
+
+    for (i = 0; i < fs_ctx->flash_info->max_num_files; i++) {
+        err = sfs_flash_fs_mblock_read_file_meta(fs_ctx, i, &tmp_metadata);
+        if (err != PSA_SUCCESS) {
+            return PSA_ERROR_GENERIC_ERROR;
+        }
+
+        /* ID with value 0x00 means end of file meta section */
+        if (!memcmp(tmp_metadata.id, fid, SFS_FILE_ID_SIZE)) {
+            /* Found */
+            *idx = i;
+            return PSA_SUCCESS;
+        }
+    }
+
+    return PSA_ERROR_DOES_NOT_EXIST;
+}
+
+psa_status_t sfs_flash_fs_mblock_init(struct sfs_flash_fs_ctx_t *fs_ctx)
+{
+    psa_status_t err;
+
+    /* Initialize Flash Interface */
+    err = fs_ctx->flash_info->init(fs_ctx->flash_info);
+    if (err != PSA_SUCCESS) {
+        return err;
+    }
+
+    err = sfs_init_get_active_metablock(fs_ctx);
+    if (err != PSA_SUCCESS) {
+        return PSA_ERROR_GENERIC_ERROR;
+    }
+
+    err = sfs_mblock_read_meta_header(fs_ctx);
+    if (err != PSA_SUCCESS) {
+        return PSA_ERROR_GENERIC_ERROR;
+    }
+
+    /* Erase the other scratch metadata block */
+    return sfs_mblock_erase_scratch_blocks(fs_ctx);
+}
+
+psa_status_t sfs_flash_fs_mblock_meta_update_finalize(
+                                              struct sfs_flash_fs_ctx_t *fs_ctx)
+{
+    psa_status_t err;
+
+    /* Write the metadata block header to flash */
+    err = sfs_mblock_write_scratch_meta_header(fs_ctx);
+    if (err != PSA_SUCCESS) {
+        return err;
+    }
+
+    /* Commit metadata block modifications to flash */
+    err = fs_ctx->flash_info->flush(fs_ctx->flash_info);
+    if (err != PSA_SUCCESS) {
+        return err;
+    }
+
+    /* Update the running context */
+    sfs_mblock_swap_metablocks(fs_ctx);
+
+    /* Erase meta block and current scratch block */
+    return sfs_mblock_erase_scratch_blocks(fs_ctx);
+}
+
+psa_status_t sfs_flash_fs_mblock_migrate_lb0_data_to_scratch(
+                                              struct sfs_flash_fs_ctx_t *fs_ctx)
+{
+    struct sfs_block_meta_t block_meta;
+    size_t data_size;
+    psa_status_t err;
+
+    err = sfs_flash_fs_mblock_read_block_metadata(fs_ctx, SFS_LOGICAL_DBLOCK0,
+                                                  &block_meta);
+    if (err != PSA_SUCCESS) {
+        return err;
+    }
+
+    /* Calculate data size stored in the B0 block */
+    data_size = (fs_ctx->flash_info->block_size - block_meta.data_start)
+                - block_meta.free_size;
+
+    return sfs_flash_block_to_block_move(fs_ctx->flash_info,
+                                         fs_ctx->scratch_metablock,
+                                         block_meta.data_start,
+                                         fs_ctx->active_metablock,
+                                         block_meta.data_start,
+                                         data_size);
+}
+
+psa_status_t sfs_flash_fs_mblock_read_file_meta(
+                                              struct sfs_flash_fs_ctx_t *fs_ctx,
+                                              uint32_t idx,
+                                              struct sfs_file_meta_t *file_meta)
+{
+    psa_status_t err;
+    size_t offset;
+
+    offset = sfs_mblock_file_meta_offset(fs_ctx, idx);
+    err = fs_ctx->flash_info->read(fs_ctx->flash_info, fs_ctx->active_metablock,
+                                   (uint8_t *)file_meta, offset,
+                                   SFS_FILE_METADATA_SIZE);
+
+#ifdef SFS_VALIDATE_METADATA_FROM_FLASH
+    if (err == PSA_SUCCESS) {
+        err = sfs_mblock_validate_file_meta(fs_ctx, file_meta);
+    }
+#endif
+
+    return err;
+}
+
+psa_status_t sfs_flash_fs_mblock_read_block_metadata(
+                                            struct sfs_flash_fs_ctx_t *fs_ctx,
+                                            uint32_t lblock,
+                                            struct sfs_block_meta_t *block_meta)
+{
+    psa_status_t err;
+    size_t pos;
+
+    pos = sfs_mblock_block_meta_offset(lblock);
+    err = fs_ctx->flash_info->read(fs_ctx->flash_info, fs_ctx->active_metablock,
+                                   (uint8_t *)block_meta, pos,
+                                   SFS_BLOCK_METADATA_SIZE);
+
+#ifdef SFS_VALIDATE_METADATA_FROM_FLASH
+    if (err == PSA_SUCCESS) {
+        err = sfs_mblock_validate_block_meta(fs_ctx, block_meta);
+    }
+#endif
+
+    return err;
+}
+
+psa_status_t sfs_flash_fs_mblock_reserve_file(
+                                            struct sfs_flash_fs_ctx_t *fs_ctx,
+                                            const uint8_t *fid,
+                                            size_t size,
+                                            uint32_t flags,
+                                            uint32_t *idx,
+                                            struct sfs_file_meta_t *file_meta,
+                                            struct sfs_block_meta_t *block_meta)
+{
+    psa_status_t err;
+
+    err = sfs_mblock_reserve_file(fs_ctx, fid, size, flags, file_meta,
+                                  block_meta);
+
+    *idx = sfs_get_free_file_index(fs_ctx);
+    if ((err != PSA_SUCCESS) ||
+        (*idx == SFS_METADATA_INVALID_INDEX)) {
+        return PSA_ERROR_INSUFFICIENT_STORAGE;
+    }
+
+    return PSA_SUCCESS;
+}
+
+psa_status_t sfs_flash_fs_mblock_reset_metablock(
+                                              struct sfs_flash_fs_ctx_t *fs_ctx)
+{
+    struct sfs_block_meta_t block_meta;
+    psa_status_t err;
+    uint32_t i;
+    uint32_t metablock_to_erase_first = SFS_METADATA_BLOCK0;
+    struct sfs_file_meta_t file_metadata;
+
+    /* Erase both metadata blocks. If at least one metadata block is valid,
+     * ensure that the active metadata block is erased last to prevent rollback
+     * in the case of a power failure between the two erases.
+     */
+    if (sfs_init_get_active_metablock(fs_ctx) == PSA_SUCCESS) {
+        metablock_to_erase_first = fs_ctx->scratch_metablock;
+    }
+
+    err = fs_ctx->flash_info->erase(fs_ctx->flash_info,
+                                    metablock_to_erase_first);
+    if (err != PSA_SUCCESS) {
+        return err;
+    }
+
+    err = fs_ctx->flash_info->erase(fs_ctx->flash_info,
+                                SFS_OTHER_META_BLOCK(metablock_to_erase_first));
+    if (err != PSA_SUCCESS) {
+        return err;
+    }
+
+    fs_ctx->meta_block_header.active_swap_count = 0;
+    fs_ctx->meta_block_header.scratch_dblock = sfs_init_scratch_dblock(fs_ctx);
+    fs_ctx->meta_block_header.fs_version = SFS_SUPPORTED_VERSION;
+    fs_ctx->scratch_metablock = SFS_METADATA_BLOCK1;
+    fs_ctx->active_metablock = SFS_METADATA_BLOCK0;
+
+    /* Fill the block metadata for logical datablock 0, which has the physical
+     * id of the active metadata block. For this datablock, the space available
+     * for data is from the end of the metadata to the end of the block.
+     */
+    block_meta.data_start =
+        sfs_mblock_file_meta_offset(fs_ctx, fs_ctx->flash_info->max_num_files);
+    block_meta.free_size = fs_ctx->flash_info->block_size
+                           - block_meta.data_start;
+    block_meta.phy_id = SFS_METADATA_BLOCK0;
+    err = sfs_mblock_update_scratch_block_meta(fs_ctx, SFS_LOGICAL_DBLOCK0,
+                                               &block_meta);
+    if (err != PSA_SUCCESS) {
+        return err;
+    }
+
+    /* Fill the block metadata for the dedicated datablocks, which have logical
+     * ids beginning from 1 and physical ids initially beginning from
+     * SFS_INIT_DBLOCK_START. For these datablocks, the space available for
+     * data is the entire block.
+     */
+    block_meta.data_start = 0;
+    block_meta.free_size = fs_ctx->flash_info->block_size;
+    for (i = 0; i < sfs_num_dedicated_dblocks(fs_ctx); i++) {
+        /* If a flash error is detected, the code erases the rest
+         * of the blocks anyway to remove all data stored in them.
+         */
+        err |= fs_ctx->flash_info->erase(fs_ctx->flash_info,
+                                         i + sfs_init_dblock_start(fs_ctx));
+    }
+
+    /* If an error is detected while erasing the flash, then return a
+     * system error to abort core wipe process.
+     */
+    if (err != PSA_SUCCESS) {
+        return PSA_ERROR_STORAGE_FAILURE;
+    }
+
+    for (i = 0; i < sfs_num_dedicated_dblocks(fs_ctx); i++) {
+        block_meta.phy_id = i + sfs_init_dblock_start(fs_ctx);
+        err = sfs_mblock_update_scratch_block_meta(fs_ctx, i + 1, &block_meta);
+        if (err != PSA_SUCCESS) {
+            return PSA_ERROR_GENERIC_ERROR;
+        }
+    }
+
+    /* Initialize file metadata table */
+    (void)memset(&file_metadata, SFS_DEFAULT_EMPTY_BUFF_VAL,
+                     SFS_FILE_METADATA_SIZE);
+    for (i = 0; i < fs_ctx->flash_info->max_num_files; i++) {
+        /* In the beginning phys id is same as logical id */
+        /* Update file metadata to reflect new attributes */
+        err = sfs_flash_fs_mblock_update_scratch_file_meta(fs_ctx, i,
+                                                           &file_metadata);
+        if (err != PSA_SUCCESS) {
+            return PSA_ERROR_GENERIC_ERROR;
+        }
+    }
+
+    err = sfs_mblock_write_scratch_meta_header(fs_ctx);
+    if (err != PSA_SUCCESS) {
+        return PSA_ERROR_GENERIC_ERROR;
+    }
+
+    /* Commit metadata block modifications to flash */
+    err = fs_ctx->flash_info->flush(fs_ctx->flash_info);
+    if (err != PSA_SUCCESS) {
+        return err;
+    }
+
+    /* Swap active and scratch metablocks */
+    sfs_mblock_swap_metablocks(fs_ctx);
+
+    return PSA_SUCCESS;
+}
+
+void sfs_flash_fs_mblock_set_data_scratch(struct sfs_flash_fs_ctx_t *fs_ctx,
+                                          uint32_t phy_id, uint32_t lblock)
+{
+    if (lblock != SFS_LOGICAL_DBLOCK0) {
+        fs_ctx->meta_block_header.scratch_dblock = phy_id;
+    }
+}
+
+psa_status_t sfs_flash_fs_mblock_update_scratch_block_meta(
+                                            struct sfs_flash_fs_ctx_t *fs_ctx,
+                                            uint32_t lblock,
+                                            struct sfs_block_meta_t *block_meta)
+{
+    psa_status_t err;
+
+    /* If the file is the logical block 0, then update the physical ID to the
+     * current scratch metadata block so that it is correct after the metadata
+     * blocks are swapped.
+     */
+    if (lblock == SFS_LOGICAL_DBLOCK0) {
+        block_meta->phy_id = fs_ctx->scratch_metablock;
+    }
+
+    err = sfs_mblock_update_scratch_block_meta(fs_ctx, lblock, block_meta);
+    if (err != PSA_SUCCESS) {
+        return PSA_ERROR_GENERIC_ERROR;
+    }
+
+    return sfs_mblock_copy_remaining_block_meta(fs_ctx, lblock);
+}
+
+psa_status_t sfs_flash_fs_mblock_update_scratch_file_meta(
+                                        struct sfs_flash_fs_ctx_t *fs_ctx,
+                                        uint32_t idx,
+                                        const struct sfs_file_meta_t *file_meta)
+{
+    size_t pos;
+
+    /* Calculate the position */
+    pos = sfs_mblock_file_meta_offset(fs_ctx, idx);
+    return fs_ctx->flash_info->write(fs_ctx->flash_info,
+                                     fs_ctx->scratch_metablock,
+                                     (const uint8_t *)file_meta, pos,
+                                     SFS_FILE_METADATA_SIZE);
+}
diff --git a/components/service/secure_storage/provider/secure_flash_store/flash_fs/sfs_flash_fs_mblock.h b/components/service/secure_storage/provider/secure_flash_store/flash_fs/sfs_flash_fs_mblock.h
new file mode 100644
index 0000000..d13a5b0
--- /dev/null
+++ b/components/service/secure_storage/provider/secure_flash_store/flash_fs/sfs_flash_fs_mblock.h
@@ -0,0 +1,295 @@
+/*
+ * Copyright (c) 2018-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __SFS_FLASH_FS_MBLOCK_H__
+#define __SFS_FLASH_FS_MBLOCK_H__
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "../flash/sfs_flash.h"
+#include "../sfs_utils.h"
+#include <protocols/service/psa/packed-c/status.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*!
+ * \def SFS_SUPPORTED_VERSION
+ *
+ * \brief Defines the supported version.
+ */
+#define SFS_SUPPORTED_VERSION  0x01
+
+/*!
+ * \def SFS_METADATA_INVALID_INDEX
+ *
+ * \brief Defines the invalid index value when the metadata table is full
+ */
+#define SFS_METADATA_INVALID_INDEX 0xFFFF
+
+/*!
+ * \def SFS_LOGICAL_DBLOCK0
+ *
+ * \brief Defines logical data block 0 ID
+ */
+#define SFS_LOGICAL_DBLOCK0  0
+
+/*!
+ * \struct sfs_metadata_block_header_t
+ *
+ * \brief Structure to store the metadata block header.
+ *
+ * \note The active_swap_count must be the last member to allow it to be
+ *       programmed last.
+ *
+ * \note This structure is programmed to flash, so it must be aligned to the
+ *       maximum required flash program unit.
+ */
+struct __attribute__((__aligned__(SFS_FLASH_MAX_ALIGNMENT)))
+sfs_metadata_block_header_t {
+    uint32_t scratch_dblock;    /*!< Physical block ID of the data
+                                 *   section's scratch block
+                                 */
+    uint8_t fs_version;         /*!< SFS system version */
+    uint8_t active_swap_count;  /*!< Physical block ID of the data */
+};
+
+/*!
+ * \struct sfs_block_meta_t
+ *
+ * \brief Structure to store information about each physical flash memory block.
+ *
+ * \note This structure is programmed to flash, so it must be aligned to the
+ *       maximum required flash program unit.
+ */
+struct __attribute__((__aligned__(SFS_FLASH_MAX_ALIGNMENT)))
+sfs_block_meta_t {
+    uint32_t phy_id;    /*!< Physical ID of this logical block */
+    size_t data_start;  /*!< Offset from the beginning of the block to the
+                         *   location where the data starts
+                         */
+    size_t free_size;   /*!< Number of bytes free at end of block (set during
+                         *   block compaction for gap reuse)
+                         */
+};
+
+/*!
+ * \struct sfs_file_meta_t
+ *
+ * \brief Structure to store file metadata.
+ *
+ * \note This structure is programmed to flash, so it must be aligned to the
+ *       maximum required flash program unit.
+ */
+struct __attribute__((__aligned__(SFS_FLASH_MAX_ALIGNMENT)))
+sfs_file_meta_t {
+    uint32_t lblock;               /*!< Logical datablock where file is
+                                    *   stored
+                                    */
+    size_t data_idx;               /*!< Offset in the logical data block */
+    size_t cur_size;               /*!< Size in storage system for this #
+                                    *   fragment
+                                    */
+    size_t max_size;               /*!< Maximum size of this file */
+    uint32_t flags;                /*!< Flags set when the file was created */
+    uint8_t id[SFS_FILE_ID_SIZE];  /*!< ID of this file */
+};
+
+/**
+ * \struct sfs_flash_fs_ctx_t
+ *
+ * \brief Structure to store the SFS flash file system context.
+ */
+struct sfs_flash_fs_ctx_t {
+    const struct sfs_flash_info_t *flash_info; /**< Info for the flash device */
+    struct sfs_metadata_block_header_t meta_block_header; /**< Metadata block
+                                                           *   header
+                                                           */
+    uint32_t active_metablock;  /**< Active metadata block */
+    uint32_t scratch_metablock; /**< Scratch metadata block */
+};
+
+/**
+ * \brief Initializes metadata block with the valid/active metablock.
+ *
+ * \param[in,out] fs_ctx  Filesystem context
+ *
+ * \return Returns value as specified in \ref psa_status_t
+ */
+psa_status_t sfs_flash_fs_mblock_init(struct sfs_flash_fs_ctx_t *fs_ctx);
+
+/**
+ * \brief Copies rest of the file metadata, except for the one pointed by
+ *        index.
+ *
+ * \param[in,out] fs_ctx  Filesystem context
+ * \param[in]     idx     File metadata entry index to skip
+ *
+ * \return Returns error code as specified in \ref psa_status_t
+ */
+psa_status_t sfs_flash_fs_mblock_cp_remaining_file_meta(
+                                              struct sfs_flash_fs_ctx_t *fs_ctx,
+                                              uint32_t idx);
+
+/**
+ * \brief Gets current scratch datablock physical ID.
+ *
+ * \param[in,out] fs_ctx  Filesystem context
+ * \param[in]     lblock  Logical block number
+ *
+ * \return current scratch data block
+ */
+uint32_t sfs_flash_fs_mblock_cur_data_scratch_id(
+                                              struct sfs_flash_fs_ctx_t *fs_ctx,
+                                              uint32_t lblock);
+
+/**
+ * \brief Gets file metadata entry index.
+ *
+ * \param[in,out] fs_ctx  Filesystem context
+ * \param[in]     fid     ID of the file
+ * \param[out]    idx     Index of the file metadata in the file system
+ *
+ * \return Returns error code as specified in \ref psa_status_t
+ */
+psa_status_t sfs_flash_fs_mblock_get_file_idx(struct sfs_flash_fs_ctx_t *fs_ctx,
+                                              const uint8_t *fid,
+                                              uint32_t *idx);
+
+/**
+ * \brief Finalizes an update operation.
+ *        Last step when a create/write/delete is performed.
+ *
+ * \param[in,out] fs_ctx  Filesystem context
+ *
+ * \return Returns offset value in metadata block
+ */
+int32_t sfs_flash_fs_mblock_meta_update_finalize(
+                                             struct sfs_flash_fs_ctx_t *fs_ctx);
+
+/**
+ * \brief Writes the files data area of logical block 0 into the scratch
+ *        block.
+ *
+ * \note The files data in the logical block 0 is stored in same physical
+ *       block where the metadata is stored. A change in the metadata requires a
+ *       swap of physical blocks. So, the files data stored in the current
+ *       medadata block needs to be copied in the scratch block, unless
+ *       the data of the file processed is located in the logical block 0.
+ *
+ * \param[in,out] fs_ctx  Filesystem context
+ *
+ * \return Returns error code as specified in \ref psa_status_t
+ */
+psa_status_t sfs_flash_fs_mblock_migrate_lb0_data_to_scratch(
+                                             struct sfs_flash_fs_ctx_t *fs_ctx);
+
+/**
+ * \brief Reads specified file metadata.
+ *
+ * \param[in,out] fs_ctx     Filesystem context
+ * \param[in]     idx        File metadata entry index
+ * \param[out]    file_meta  Pointer to file meta structure
+ *
+ * \return Returns error code as specified in \ref psa_status_t
+ */
+psa_status_t sfs_flash_fs_mblock_read_file_meta(
+                                             struct sfs_flash_fs_ctx_t *fs_ctx,
+                                             uint32_t idx,
+                                             struct sfs_file_meta_t *file_meta);
+
+/**
+ * \brief Reads specified logical block metadata.
+ *
+ * \param[in,out] fs_ctx      Filesystem context
+ * \param[in]     lblock      Logical block number
+ * \param[out]    block_meta  Pointer to block meta structure
+ *
+ * \return Returns error code as specified in \ref psa_status_t
+ */
+psa_status_t sfs_flash_fs_mblock_read_block_metadata(
+                                           struct sfs_flash_fs_ctx_t *fs_ctx,
+                                           uint32_t lblock,
+                                           struct sfs_block_meta_t *block_meta);
+
+/**
+ * \brief Reserves space for a file.
+ *
+ * \param[in,out] fs_ctx         Filesystem context
+ * \param[in]     fid            File ID
+ * \param[in]     size           Size of the file for which space is reserve
+ * \param[in]     flags          Flags set when the file was created
+ * \param[out]    file_meta_idx  File metadata entry index
+ * \param[out]    file_meta      File metadata entry
+ * \param[out]    block_meta     Block metadata entry
+ *
+ * \return Returns error code as specified in \ref psa_status_t
+ */
+psa_status_t sfs_flash_fs_mblock_reserve_file(
+                                           struct sfs_flash_fs_ctx_t *fs_ctx,
+                                           const uint8_t *fid,
+                                           size_t size,
+                                           uint32_t flags,
+                                           uint32_t *file_meta_idx,
+                                           struct sfs_file_meta_t *file_meta,
+                                           struct sfs_block_meta_t *block_meta);
+
+/**
+ * \brief Resets metablock by cleaning and initializing the metadatablock.
+ *
+ * \param[in,out] fs_ctx  Filesystem context
+ *
+ * \return Returns value as specified in \ref psa_status_t
+ */
+psa_status_t sfs_flash_fs_mblock_reset_metablock(
+                                             struct sfs_flash_fs_ctx_t *fs_ctx);
+
+/**
+ * \brief Sets current data scratch block
+ *
+ * \param[in,out] fs_ctx  Filesystem context
+ * \param[in]     phy_id  Physical ID of scratch data block
+ * \param[in]     lblock  Logical block number
+ */
+void sfs_flash_fs_mblock_set_data_scratch(struct sfs_flash_fs_ctx_t *fs_ctx,
+                                          uint32_t phy_id, uint32_t lblock);
+
+/**
+ * \brief Puts logical block's metadata in scratch metadata block
+ *
+ * \param[in,out] fs_ctx      Filesystem context
+ * \param[in]     lblock      Logical block number
+ * \param[in]     block_meta  Pointer to block's metadata
+ *
+ * \return Returns error code as specified in \ref psa_status_t
+ */
+psa_status_t sfs_flash_fs_mblock_update_scratch_block_meta(
+                                           struct sfs_flash_fs_ctx_t *fs_ctx,
+                                           uint32_t lblock,
+                                           struct sfs_block_meta_t *block_meta);
+
+/**
+ * \brief Writes a file metadata entry into scratch metadata block.
+ *
+ * \param[in,out] fs_ctx     Filesystem context
+ * \param[in]     idx        File's index in the metadata table
+ * \param[in]     file_meta  Metadata pointer
+ *
+ * \return Returns error code as specified in \ref psa_status_t
+ */
+psa_status_t sfs_flash_fs_mblock_update_scratch_file_meta(
+                                       struct sfs_flash_fs_ctx_t *fs_ctx,
+                                       uint32_t idx,
+                                       const struct sfs_file_meta_t *file_meta);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __SFS_FLASH_FS_MBLOCK_H__ */
diff --git a/components/service/secure_storage/provider/secure_flash_store/secure_flash_store.c b/components/service/secure_storage/provider/secure_flash_store/secure_flash_store.c
new file mode 100644
index 0000000..a9f85bd
--- /dev/null
+++ b/components/service/secure_storage/provider/secure_flash_store/secure_flash_store.c
@@ -0,0 +1,302 @@
+/*
+ * Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "flash/sfs_flash.h"
+#include "flash_fs/sfs_flash_fs.h"
+#include "sfs_utils.h"
+#include "secure_flash_store.h"
+#include <string.h>
+
+#define SFS_MAX_ASSET_SIZE (4096) /* TODO: comes from flash layout */
+#define SFS_CREATE_FLASH_LAYOUT /* TODO: move this to a proper place */
+
+#ifndef SFS_BUF_SIZE
+/* By default, set the SFS buffer size to the max asset size so that all
+ * requests can be handled in one iteration.
+ */
+#define SFS_BUF_SIZE SFS_MAX_ASSET_SIZE
+#endif
+
+#define SFS_INVALID_UID 0 /* TODO: are there any invalid UID-s? */
+
+/* Buffer to store asset data from the caller.
+ * Note: size must be aligned to the max flash program unit to meet the
+ * alignment requirement of the filesystem.
+ */
+static uint8_t asset_data[SFS_UTILS_ALIGN(SFS_BUF_SIZE,
+                                          SFS_FLASH_MAX_ALIGNMENT)];
+
+static uint8_t g_fid[SFS_FILE_ID_SIZE];
+static struct sfs_file_info_t g_file_info;
+
+static sfs_flash_fs_ctx_t fs_ctx_sfs;
+
+/**
+ * \brief Maps a pair of client id and uid to a file id.
+ *
+ * \param[in]  client_id  Identifier of the asset's owner (client)
+ * \param[in]  uid        Identifier for the data
+ * \param[out] fid        Identifier of the file
+ */
+static void sfs_get_fid(uint32_t client_id,
+                            uint64_t uid,
+                            uint8_t *fid)
+{
+    memcpy(fid, (const void *)&client_id, sizeof(client_id));
+    memcpy(fid + sizeof(client_id), (const void *)&uid, sizeof(uid));
+}
+
+psa_status_t sfs_init(void)
+{
+    psa_status_t status;
+
+    /* Initialise the SFS context */
+    status = sfs_flash_fs_prepare(&fs_ctx_sfs,
+                                  sfs_flash_get_info());
+#ifdef SFS_CREATE_FLASH_LAYOUT
+    /* If SFS_CREATE_FLASH_LAYOUT is set, it indicates that it is required to
+     * create a SFS flash layout. SFS service will generate an empty and valid
+     * SFS flash layout to store assets. It will erase all data located in the
+     * assigned SFS memory area before generating the SFS layout.
+     * This flag is required to be set if the SFS memory area is located in
+     * non-persistent memory.
+     * This flag can be set if the SFS memory area is located in persistent
+     * memory without a previous valid SFS flash layout in it. That is the case
+     * when it is the first time in the device life that the SFS service is
+     * executed.
+     */
+     if (status != PSA_SUCCESS) {
+        /* Remove all data in the SFS memory area and create a valid SFS flash
+         * layout in that area.
+         */
+        status = sfs_flash_fs_wipe_all(&fs_ctx_sfs);
+        if (status != PSA_SUCCESS) {
+            return status;
+        }
+
+        /* Attempt to initialise again */
+        status = sfs_flash_fs_prepare(&fs_ctx_sfs,
+                                     sfs_flash_get_info());
+    }
+#endif /* SFS_CREATE_FLASH_LAYOUT */
+
+
+    return status;
+}
+
+psa_status_t sfs_set(uint32_t client_id,
+                         uint64_t uid,
+                         size_t data_length,
+                         const void *p_data,
+                         uint32_t create_flags)
+{
+    psa_status_t status;
+    size_t write_size;
+    size_t offset;
+    const uint8_t *data = p_data;
+
+    data = (const uint8_t *)p_data;
+
+    /* Check that the UID is valid */
+    if (uid == SFS_INVALID_UID) {
+        return PSA_ERROR_INVALID_ARGUMENT;
+    }
+
+    /* Check that the create_flags does not contain any unsupported flags */
+    if (create_flags & ~(TS_SECURE_STORAGE_FLAG_WRITE_ONCE |
+                         TS_SECURE_STORAGE_FLAG_NO_CONFIDENTIALITY |
+                         TS_SECURE_STORAGE_FLAG_NO_REPLAY_PROTECTION)) {
+        return PSA_ERROR_NOT_SUPPORTED;
+    }
+
+    /* Set file id */
+    sfs_get_fid(client_id, uid, g_fid);
+
+    /* Read file info */
+    status = sfs_flash_fs_file_get_info(&fs_ctx_sfs, g_fid, &g_file_info);
+    if (status == PSA_SUCCESS) {
+        /* If the object exists and has the write once flag set, then it
+         * cannot be modified. Otherwise it needs to be removed.
+         */
+        if (g_file_info.flags & TS_SECURE_STORAGE_FLAG_WRITE_ONCE) {
+            return PSA_ERROR_NOT_PERMITTED;
+        } else {
+            status = sfs_flash_fs_file_delete(&fs_ctx_sfs, g_fid);
+            if (status != PSA_SUCCESS) {
+                return status;
+            }
+        }
+    } else if (status != PSA_ERROR_DOES_NOT_EXIST) {
+        /* If the file does not exist, then do nothing.
+         * If other error occurred, return it
+         */
+        return status;
+    }
+
+    /* Write as much of the data as will fit in the asset_data buffer */
+    write_size = SFS_UTILS_MIN(data_length, sizeof(asset_data));
+
+    /* Read asset data from the caller */
+    memcpy(asset_data, data, write_size);
+    data += write_size;
+
+    /* Create the file in the file system */
+    status = sfs_flash_fs_file_create(&fs_ctx_sfs, g_fid, data_length,
+                                      write_size, (uint32_t)create_flags,
+                                      asset_data);
+    if (status != PSA_SUCCESS) {
+        return status;
+    }
+
+    offset = write_size;
+    data_length -= write_size;
+
+    /* Iteratively read data from the caller and write it to the filesystem, in
+     * chunks no larger than the size of the asset_data buffer.
+     */
+    while (data_length > 0) {
+        write_size = SFS_UTILS_MIN(data_length, sizeof(asset_data));
+
+        /* Read asset data from the caller */
+        memcpy(asset_data, data, write_size);
+        data += write_size;
+
+        /* Write to the file in the file system */
+        status = sfs_flash_fs_file_write(&fs_ctx_sfs, g_fid,
+                                         write_size, offset, asset_data);
+        if (status != PSA_SUCCESS) {
+            /* Delete the file to avoid leaving partial data */
+            (void)sfs_flash_fs_file_delete(&fs_ctx_sfs, g_fid);
+            return status;
+        }
+
+        offset += write_size;
+        data_length -= write_size;
+    }
+
+    return PSA_SUCCESS;
+}
+
+psa_status_t sfs_get(uint32_t client_id,
+                         uint64_t uid,
+                         size_t data_offset,
+                         size_t data_size,
+                         void *p_data,
+                         size_t *p_data_length)
+{
+    psa_status_t status;
+    size_t read_size;
+      uint8_t *data = p_data;
+
+    data = (uint8_t *)p_data;
+
+    /* Check that the UID is valid */
+    if (uid == SFS_INVALID_UID) {
+        return PSA_ERROR_INVALID_ARGUMENT;
+    }
+
+    /* Set file id */
+    sfs_get_fid(client_id, uid, g_fid);
+
+    /* Read file info */
+    status = sfs_flash_fs_file_get_info(&fs_ctx_sfs, g_fid, &g_file_info);
+    if (status != PSA_SUCCESS) {
+        return status;
+    }
+
+    /* Boundary check the incoming request */
+    if (data_offset > g_file_info.size_current) {
+        return PSA_ERROR_INVALID_ARGUMENT;
+    }
+
+    /* Copy the object data only from within the file boundary */
+    data_size = SFS_UTILS_MIN(data_size,
+                              g_file_info.size_current - data_offset);
+
+    /* Update the size of the output data */
+    *p_data_length = data_size;
+
+    /* Iteratively read data from the filesystem and write it to the caller, in
+     * chunks no larger than the size of the asset_data buffer.
+     */
+    do {
+        /* Read as much of the data as will fit in the asset_data buffer */
+        read_size = SFS_UTILS_MIN(data_size, sizeof(asset_data));
+
+        /* Read file data from the filesystem */
+        status = sfs_flash_fs_file_read(&fs_ctx_sfs, g_fid, read_size,
+                                        data_offset, asset_data);
+        if (status != PSA_SUCCESS) {
+            *p_data_length = 0;
+            return status;
+        }
+
+        /* Write asset data to the caller */
+        memcpy(data, asset_data, read_size);
+        data += read_size;
+
+        data_offset += read_size;
+        data_size -= read_size;
+    } while (data_size > 0);
+
+    return PSA_SUCCESS;
+}
+
+psa_status_t sfs_get_info(uint32_t client_id, uint64_t uid,
+                              struct secure_storage_response_get_info *p_info)
+{
+    psa_status_t status;
+
+    /* Check that the UID is valid */
+    if (uid == SFS_INVALID_UID) {
+        return PSA_ERROR_INVALID_ARGUMENT;
+    }
+
+    /* Set file id */
+    sfs_get_fid(client_id, uid, g_fid);
+
+    /* Read file info */
+    status = sfs_flash_fs_file_get_info(&fs_ctx_sfs, g_fid, &g_file_info);
+    if (status != PSA_SUCCESS) {
+        return status;
+    }
+
+    /* Copy file info to the PSA info struct */
+    p_info->capacity = g_file_info.size_current;
+    p_info->size = g_file_info.size_current;
+    p_info->flags = g_file_info.flags;
+
+    return PSA_SUCCESS;
+}
+
+psa_status_t sfs_remove(uint32_t client_id, uint64_t uid)
+{
+    psa_status_t status;
+
+    /* Check that the UID is valid */
+    if (uid == SFS_INVALID_UID) {
+        return PSA_ERROR_INVALID_ARGUMENT;
+    }
+
+    /* Set file id */
+    sfs_get_fid(client_id, uid, g_fid);
+
+    status = sfs_flash_fs_file_get_info(&fs_ctx_sfs, g_fid, &g_file_info);
+    if (status != PSA_SUCCESS) {
+        return status;
+    }
+
+    /* If the object exists and has the write once flag set, then it
+     * cannot be deleted.
+     */
+    if (g_file_info.flags & TS_SECURE_STORAGE_FLAG_WRITE_ONCE) {
+        return PSA_ERROR_NOT_PERMITTED;
+    }
+
+    /* Delete old file from the persistent area */
+    return sfs_flash_fs_file_delete(&fs_ctx_sfs, g_fid);
+}
diff --git a/components/service/secure_storage/provider/secure_flash_store/secure_flash_store.h b/components/service/secure_storage/provider/secure_flash_store/secure_flash_store.h
new file mode 100644
index 0000000..41b7aa8
--- /dev/null
+++ b/components/service/secure_storage/provider/secure_flash_store/secure_flash_store.h
@@ -0,0 +1,171 @@
+/*
+ * Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __SECURE_FLASH_STORE_H__
+#define __SECURE_FLASH_STORE_H__
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <protocols/service/psa/packed-c/status.h>
+#include <protocols/service/secure_storage/packed-c/secure_storage_proto.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief Initializes the internal trusted storage system.
+ *
+ * \return A status indicating the success/failure of the operation
+ *
+ * \retval PSA_SUCCESS                The operation completed successfully
+ * \retval PSA_ERROR_STORAGE_FAILURE  The operation failed because the storage
+ *                                    system initialization has failed (fatal
+ *                                    error)
+ * \retval PSA_ERROR_GENERIC_ERROR    The operation failed because of an
+ *                                    unspecified internal failure
+ */
+psa_status_t sfs_init(void);
+
+/**
+ * \brief Create a new, or modify an existing, uid/value pair
+ *
+ * Stores data in the internal storage.
+ *
+ * \param[in] client_id     Identifier of the asset's owner (client)
+ * \param[in] uid           The identifier for the data
+ * \param[in] data_length   The size in bytes of the data in `p_data`
+ * \param[in] create_flags  The flags that the data will be stored with
+ *
+ * \return A status indicating the success/failure of the operation
+ *
+ * \retval PSA_SUCCESS                     The operation completed successfully
+ * \retval PSA_ERROR_NOT_PERMITTED         The operation failed because the
+ *                                         provided `uid` value was already
+ *                                         created with
+ *                                         TS_SECURE_STORAGE_FLAG_WRITE_ONCE
+ * \retval PSA_ERROR_NOT_SUPPORTED         The operation failed because one or
+ *                                         more of the flags provided in
+ *                                         `create_flags` is not supported or is
+ *                                         not valid
+ * \retval PSA_ERROR_INSUFFICIENT_STORAGE  The operation failed because there
+ *                                         was insufficient space on the
+ *                                         storage medium
+ * \retval PSA_ERROR_STORAGE_FAILURE       The operation failed because the
+ *                                         physical storage has failed (Fatal
+ *                                         error)
+ * \retval PSA_ERROR_INVALID_ARGUMENT      The operation failed because one
+ *                                         of the provided pointers (`p_data`)
+ *                                         is invalid, for example is `NULL` or
+ *                                         references memory the caller cannot
+ *                                         access
+ */
+psa_status_t sfs_set(uint32_t client_id,
+                         uint64_t uid,
+                         size_t data_length,
+                         const void *p_data,
+                         uint32_t create_flags);
+
+/**
+ * \brief Retrieve data associated with a provided UID
+ *
+ * Retrieves up to `data_size` bytes of the data associated with `uid`, starting
+ * at `data_offset` bytes from the beginning of the data. Upon successful
+ * completion, the data will be placed in the `p_data` buffer, which must be at
+ * least `data_size` bytes in size. The length of the data returned will be in
+ * `p_data_length`. If `data_size` is 0, the contents of `p_data_length` will
+ * be set to zero.
+ *
+ * \param[in]  client_id      Identifier of the asset's owner (client)
+ * \param[in]  uid            The uid value
+ * \param[in]  data_offset    The starting offset of the data requested
+ * \param[in]  data_size      The amount of data requested
+ * \param[out] p_data_length  On success, this will contain size of the data
+ *                            placed in `p_data`.
+ *
+ * \return A status indicating the success/failure of the operation
+ *
+ * \retval PSA_SUCCESS                 The operation completed successfully
+ * \retval PSA_ERROR_DOES_NOT_EXIST    The operation failed because the
+ *                                     provided `uid` value was not found in
+ *                                     the storage
+ * \retval PSA_ERROR_STORAGE_FAILURE   The operation failed because the
+ *                                     physical storage has failed (Fatal
+ *                                     error)
+ * \retval PSA_ERROR_INVALID_ARGUMENT  The operation failed because one of the
+ *                                     provided arguments (`p_data`,
+ *                                     `p_data_length`) is invalid, for example
+ *                                     is `NULL` or references memory the
+ *                                     caller cannot access. In addition, this
+ *                                     can also happen if `data_offset` is
+ *                                     larger than the size of the data
+ *                                     associated with `uid`.
+ */
+psa_status_t sfs_get(uint32_t client_id,
+                         uint64_t uid,
+                         size_t data_offset,
+                         size_t data_size,
+                         void *p_data,
+                         size_t *p_data_length);
+
+/**
+ * \brief Retrieve the metadata about the provided uid
+ *
+ * Retrieves the metadata stored for a given `uid` as a `secure_storage_response_get_info`
+ * structure.
+ *
+ * \param[in]  client_id  Identifier of the asset's owner (client)
+ * \param[in]  uid        The `uid` value
+ * \param[out] p_info     A pointer to the `secure_storage_response_get_info` struct that will
+ *                        be populated with the metadata
+ *
+ * \return A status indicating the success/failure of the operation
+ *
+ * \retval PSA_SUCCESS                 The operation completed successfully
+ * \retval PSA_ERROR_DOES_NOT_EXIST    The operation failed because the provided
+ *                                     uid value was not found in the storage
+ * \retval PSA_ERROR_STORAGE_FAILURE   The operation failed because the physical
+ *                                     storage has failed (Fatal error)
+ * \retval PSA_ERROR_INVALID_ARGUMENT  The operation failed because one of the
+ *                                     provided pointers(`p_info`)
+ *                                     is invalid, for example is `NULL` or
+ *                                     references memory the caller cannot
+ *                                     access
+ */
+psa_status_t sfs_get_info(uint32_t client_id, uint64_t uid,
+                              struct secure_storage_response_get_info *p_info);
+
+/**
+ * \brief Remove the provided uid and sfs associated data from the storage
+ *
+ * Deletes the data from internal storage.
+ *
+ * \param[in] client_id  Identifier of the asset's owner (client)
+ * \param[in] uid        The `uid` value
+ *
+ * \return A status indicating the success/failure of the operation
+ *
+ * \retval PSA_SUCCESS                 The operation completed successfully
+ * \retval PSA_ERROR_INVALID_ARGUMENT  The operation failed because one or more
+ *                                     of the given arguments were invalid (null
+ *                                     pointer, wrong flags and so on)
+ * \retval PSA_ERROR_DOES_NOT_EXIST    The operation failed because the provided
+ *                                     uid value was not found in the storage
+ * \retval PSA_ERROR_NOT_PERMITTED     The operation failed because the provided
+ *                                     uid value was created with
+ *                                     TS_SECURE_STORAGE_FLAG_WRITE_ONCE
+ * \retval PSA_ERROR_STORAGE_FAILURE   The operation failed because the physical
+ *                                     storage has failed (Fatal error)
+ */
+psa_status_t sfs_remove(uint32_t client_id, uint64_t uid);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __SECURE_FLASH_STORE_H__ */
diff --git a/components/service/secure_storage/provider/secure_flash_store/sfs_provider.c b/components/service/secure_storage/provider/secure_flash_store/sfs_provider.c
new file mode 100644
index 0000000..5c801ed
--- /dev/null
+++ b/components/service/secure_storage/provider/secure_flash_store/sfs_provider.c
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 2020, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include "sfs_provider.h"
+#include "secure_flash_store.h"
+#include <protocols/service/secure_storage/packed-c/secure_storage_proto.h>
+#include <protocols/service/psa/packed-c/status.h>
+#include <protocols/rpc/common/packed-c/status.h>
+#include <components/rpc/common/endpoint/call_ep.h>
+
+#include <stdio.h>
+
+/* Handler mapping table for service */
+static const struct service_handler handler_table[] = {
+	{TS_SECURE_STORAGE_OPCODE_SET,	sfs_set_handler},
+	{TS_SECURE_STORAGE_OPCODE_GET,	sfs_get_handler},
+	{TS_SECURE_STORAGE_OPCODE_GET_INFO,	sfs_get_info_handler},
+	{TS_SECURE_STORAGE_OPCODE_REMOVE,	sfs_remove_handler}
+};
+
+struct call_ep *sfs_provider_init(struct sfs_provider *context)
+{
+	struct call_ep *call_ep = NULL;
+
+	if (context == NULL)
+		goto out;
+
+	if (sfs_init() != PSA_SUCCESS)
+		goto out;
+
+	service_provider_init(&context->base_provider, context, handler_table,
+			      sizeof(handler_table) / sizeof(handler_table[0]));
+
+	call_ep = service_provider_get_call_ep(&context->base_provider);
+
+out:
+	return call_ep;
+}
+
+rpc_status_t sfs_set_handler(void *context, struct call_req *req)
+{
+	struct secure_storage_request_set *request_desc;
+	psa_status_t psa_status;
+
+	/* Checking if the descriptor fits into the request buffer */
+	if (req->req_buf.data_len < sizeof(struct secure_storage_request_set))
+		return TS_RPC_ERROR_INVALID_REQ_BODY;
+
+	request_desc = (struct secure_storage_request_set *)(req->req_buf.data);
+
+	/* Checking for overflow */
+	if (sizeof(struct secure_storage_request_set) + request_desc->data_length < request_desc->data_length)
+		return TS_RPC_ERROR_INVALID_REQ_BODY;
+
+	/* Checking if descriptor and data fits into the request buffer */
+	if (req->req_buf.data_len < sizeof(struct secure_storage_request_set) + request_desc->data_length)
+		return TS_RPC_ERROR_INVALID_REQ_BODY;
+
+	psa_status = sfs_set(req->caller_id, request_desc->uid,
+				 request_desc->data_length,
+				 request_desc->p_data,
+				 request_desc->create_flags);
+	call_req_set_opstatus(req, psa_status);
+
+	return TS_RPC_CALL_ACCEPTED;
+}
+
+rpc_status_t sfs_get_handler(void *context, struct call_req *req)
+{
+	struct secure_storage_request_get *request_desc;
+	psa_status_t psa_status;
+
+	/* Checking if the descriptor fits into the request buffer */
+	if (req->req_buf.data_len < sizeof(struct secure_storage_request_get))
+		return TS_RPC_ERROR_INVALID_REQ_BODY;
+
+	request_desc = (struct secure_storage_request_get *)(req->req_buf.data);
+
+	/* Check if the requested data would fit into the response buffer. */
+	if (req->resp_buf.size < request_desc->data_size)
+		return TS_RPC_ERROR_INVALID_RESP_BODY;
+
+	psa_status = sfs_get(req->caller_id, request_desc->uid,
+				 request_desc->data_offset,
+				 request_desc->data_size,
+				 req->resp_buf.data, &req->resp_buf.data_len);
+	call_req_set_opstatus(req, psa_status);
+
+	return TS_RPC_CALL_ACCEPTED;
+}
+
+rpc_status_t sfs_get_info_handler(void *context, struct call_req *req)
+{
+	struct secure_storage_request_get_info *request_desc;
+	struct secure_storage_response_get_info *response_desc;
+	struct secure_storage_response_get_info storage_info; //TODO: unnecessary?
+	psa_status_t psa_status;
+
+	/* Checking if the descriptor fits into the request buffer */
+	if (req->req_buf.data_len < sizeof(struct secure_storage_request_get_info))
+		return TS_RPC_ERROR_INVALID_REQ_BODY;
+
+	request_desc = (struct secure_storage_request_get_info *)(req->req_buf.data);
+
+	/* Checking if the response structure would fit the response buffer */
+	if (req->resp_buf.size < sizeof(struct secure_storage_response_get_info))
+		return TS_RPC_ERROR_INVALID_RESP_BODY;
+
+	response_desc = (struct secure_storage_response_get_info *)(req->resp_buf.data);
+
+	psa_status = sfs_get_info(req->caller_id, request_desc->uid, &storage_info);
+	call_req_set_opstatus(req, psa_status);
+
+	if (psa_status != PSA_SUCCESS) {
+		req->resp_buf.data_len = 0;
+	}
+	else {
+		response_desc->capacity = storage_info.capacity;
+		response_desc->size = storage_info.size;
+		response_desc->flags = storage_info.flags;
+
+		req->resp_buf.data_len = sizeof(struct secure_storage_response_get_info);
+	}
+
+	return TS_RPC_CALL_ACCEPTED;
+}
+
+rpc_status_t sfs_remove_handler(void *context, struct call_req *req)
+{
+	struct secure_storage_request_remove *request_desc;
+	psa_status_t psa_status;
+
+	/* Checking if the descriptor fits into the request buffer */
+	if (req->req_buf.data_len < sizeof(struct secure_storage_request_remove))
+		return TS_RPC_ERROR_INVALID_REQ_BODY;
+
+	request_desc = (struct secure_storage_request_remove *)(req->req_buf.data);
+
+	psa_status = sfs_remove(req->caller_id, request_desc->uid);
+	call_req_set_opstatus(req, psa_status);
+
+	return TS_RPC_CALL_ACCEPTED;
+}
diff --git a/components/service/secure_storage/provider/secure_flash_store/sfs_provider.h b/components/service/secure_storage/provider/secure_flash_store/sfs_provider.h
new file mode 100644
index 0000000..82887de
--- /dev/null
+++ b/components/service/secure_storage/provider/secure_flash_store/sfs_provider.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2020, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef SFS_HANDLERS_H
+#define SFS_HANDLERS_H
+
+#include <components/service/common/provider/service_provider.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct sfs_provider {
+	struct service_provider base_provider;
+};
+
+struct call_ep *sfs_provider_init(struct sfs_provider *context);
+rpc_status_t sfs_set_handler(void *context, struct call_req *req);
+rpc_status_t sfs_get_handler(void *context, struct call_req *req);
+rpc_status_t sfs_get_info_handler(void *context, struct call_req *req);
+rpc_status_t sfs_remove_handler(void *context, struct call_req *req);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SFS_HANDLERS_H */
diff --git a/components/service/secure_storage/provider/secure_flash_store/sfs_utils.c b/components/service/secure_storage/provider/secure_flash_store/sfs_utils.c
new file mode 100644
index 0000000..3d16272
--- /dev/null
+++ b/components/service/secure_storage/provider/secure_flash_store/sfs_utils.c
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2017-2019, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "sfs_utils.h"
+
+psa_status_t sfs_utils_check_contained_in(size_t superset_size,
+                                          size_t subset_offset,
+                                          size_t subset_size)
+{
+    /* Check that subset_offset is valid */
+    if (subset_offset > superset_size) {
+        return PSA_ERROR_INVALID_ARGUMENT;
+    }
+
+    /* Check that subset_offset + subset_size fits in superset_size.
+     * The previous check passed, so we know that subset_offset <= superset_size
+     * and so the right hand side of the inequality cannot underflow.
+     */
+    if (subset_size > (superset_size - subset_offset)) {
+        return PSA_ERROR_INVALID_ARGUMENT;
+    }
+
+    return PSA_SUCCESS;
+}
+
+psa_status_t sfs_utils_validate_fid(const uint8_t *fid)
+{
+    uint32_t fid_size = SFS_FILE_ID_SIZE;
+
+    /* A file ID is valid if it is non-zero */
+    while (fid_size--) {
+        if (*fid++) {
+            return PSA_SUCCESS;
+        }
+    }
+
+    return PSA_ERROR_DOES_NOT_EXIST;
+}
diff --git a/components/service/secure_storage/provider/secure_flash_store/sfs_utils.h b/components/service/secure_storage/provider/secure_flash_store/sfs_utils.h
new file mode 100644
index 0000000..4a06d4b
--- /dev/null
+++ b/components/service/secure_storage/provider/secure_flash_store/sfs_utils.h
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2017-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __SFS_UTILS_H__
+#define __SFS_UTILS_H__
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <protocols/service/psa/packed-c/status.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define SFS_FILE_ID_SIZE 12
+#define SFS_DEFAULT_EMPTY_BUFF_VAL 0
+
+/**
+ * \brief Macro to check, at compilation time, if data fits in data buffer
+ *
+ * \param[in] err_msg        Error message which will be displayed in first
+ *                           instance if the error is triggered
+ * \param[in] data_size      Data size to check if it fits
+ * \param[in] data_buf_size  Size of the data buffer
+ *
+ * \return  Triggers a compilation error if data_size is bigger than
+ *          data_buf_size. The compilation error should be
+ *          "... error: 'err_msg' declared as an array with a negative size"
+ */
+#define SFS_UTILS_BOUND_CHECK(err_msg, data_size, data_buf_size) \
+typedef char err_msg[(data_size <= data_buf_size)*2 - 1]
+
+/**
+ * \brief Evaluates to the minimum of the two parameters.
+ */
+#define SFS_UTILS_MIN(x, y) (((x) < (y)) ? (x) : (y))
+
+/**
+ * \brief Evaluates to the maximum of the two parameters.
+ */
+#define SFS_UTILS_MAX(x, y) (((x) > (y)) ? (x) : (y))
+
+/**
+ * \brief Aligns a value up to the provided alignment.
+ *
+ * \param[in] x  Value to be aligned
+ * \param[in] a  Alignment (must be a power of two)
+ *
+ * \return The least value not less than \p x that is aligned to \p a.
+ */
+#define SFS_UTILS_ALIGN(x, a) (((x) + ((a) - 1)) & ~((a) - 1))
+
+/**
+ * \brief Checks that a value is aligned to the provided alignment.
+ *
+ * \param[in] x  Value to check for alignment
+ * \param[in] a  Alignment (must be a power of two)
+ *
+ * \return 1 if \p x is aligned to \p a, 0 otherwise.
+ */
+#define SFS_UTILS_IS_ALIGNED(x, a) (((x) & ((a) - 1)) == 0)
+
+/**
+ * \brief Checks if a subset region is fully contained within a superset region.
+ *
+ * \param[in] superset_size  Size of superset region
+ * \param[in] subset_offset  Offset of start of subset region from start of
+ *                           superset region
+ * \param[in] subset_size    Size of subset region
+ *
+ * \return Returns error code as specified in \ref psa_status_t
+ *
+ * \retval PSA_SUCCESS           The subset is contained within the
+ *                                     superset
+ * \retval PSA_ERROR_INVALID_ARGUMENT Otherwise
+ */
+psa_status_t sfs_utils_check_contained_in(size_t superset_size,
+                                          size_t subset_offset,
+                                          size_t subset_size);
+
+/**
+ * \brief Validates file ID
+ *
+ * \param[in] fid  File ID
+ *
+ * \return Returns error code as specified in \ref psa_status_t
+ */
+psa_status_t sfs_utils_validate_fid(const uint8_t *fid);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __SFS_UTILS_H__ */
diff --git a/components/service/secure_storage/test/component.cmake b/components/service/secure_storage/test/component.cmake
new file mode 100644
index 0000000..8ea41cf
--- /dev/null
+++ b/components/service/secure_storage/test/component.cmake
@@ -0,0 +1,14 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2020, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+if (NOT DEFINED TGT)
+	message(FATAL_ERROR "mandatory parameter TGT is not defined.")
+endif()
+
+target_sources(${TGT} PRIVATE
+	"${CMAKE_CURRENT_LIST_DIR}/its_tests.cpp"
+	)
+
diff --git a/components/service/secure_storage/test/its_tests.cpp b/components/service/secure_storage/test/its_tests.cpp
new file mode 100644
index 0000000..fbd432d
--- /dev/null
+++ b/components/service/secure_storage/test/its_tests.cpp
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2020, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <cstring>
+#include <cstdint>
+#include <CppUTest/TestHarness.h>
+#include <rpc/direct/direct_caller.h>
+#include <service/secure_storage/client/psa/its/its_client.h>
+#include <service/secure_storage/provider/secure_flash_store/sfs_provider.h>
+#include <psa/internal_trusted_storage.h>
+#include <psa/error.h>
+
+TEST_GROUP(InternalTrustedStorageTests)
+{
+    void setup()
+    {
+        struct call_ep *storage_ep = sfs_provider_init(&m_storage_provider);
+        struct rpc_caller *storage_caller = direct_caller_init_default(&m_storage_caller, storage_ep);
+        psa_its_client_init(storage_caller);
+    }
+
+    void teardown()
+    {
+        direct_caller_deinit(&m_storage_caller);
+    }
+
+    struct sfs_provider m_storage_provider;
+    struct direct_caller m_storage_caller;
+};
+
+TEST(InternalTrustedStorageTests, storeNewItem)
+{
+    psa_status_t status;
+    psa_storage_uid_t uid = 10;
+    struct psa_storage_info_t storage_info;
+    static const size_t ITEM_SIZE = 68;
+    uint8_t item[ITEM_SIZE];
+    uint8_t read_item[ITEM_SIZE];
+
+    memset(item, 0x55, sizeof(item));
+
+    /* Probe to check item does not exist */
+    status = psa_its_get_info(uid, &storage_info);
+    CHECK_EQUAL(PSA_ERROR_DOES_NOT_EXIST, status);
+
+    /* Store the item */
+    status = psa_its_set(uid, sizeof(item), item, PSA_STORAGE_FLAG_NONE);
+    CHECK_EQUAL(PSA_SUCCESS, status);
+
+    /* Probe to check item now exists */
+    status = psa_its_get_info(uid, &storage_info);
+    CHECK_EQUAL(PSA_SUCCESS, status);
+    CHECK_EQUAL(sizeof(item), storage_info.size);
+
+    /* Get the item */
+    size_t read_len = 0;
+    status = psa_its_get(uid, 0, sizeof(read_item), read_item, &read_len);
+    CHECK_EQUAL(PSA_SUCCESS, status);
+    CHECK_EQUAL(sizeof(item), read_len);
+    CHECK(memcmp(item, read_item, sizeof(item)) == 0);
+
+    /* Remove the item */
+    status = psa_its_remove(uid);
+    CHECK_EQUAL(PSA_SUCCESS, status);
+
+    /* Expect it to have gone */
+    status = psa_its_get_info(uid, &storage_info);
+    CHECK_EQUAL(PSA_ERROR_DOES_NOT_EXIST, status);
+}
+
+TEST(InternalTrustedStorageTests, storageLimitTest)
+{
+    psa_status_t status;
+    psa_storage_uid_t uid = 10;
+    struct psa_storage_info_t storage_info;
+    static const size_t MAX_ITEM_SIZE = 10000;
+    uint8_t item[MAX_ITEM_SIZE];
+    uint8_t read_item[MAX_ITEM_SIZE];
+
+    memset(item, 0x55, sizeof(item));
+
+    /* Probe to check item does not exist */
+    status = psa_its_get_info(uid, &storage_info);
+    CHECK_EQUAL(PSA_ERROR_DOES_NOT_EXIST, status);
+
+    /* Attempt to store a reasonably big item */
+    status = psa_its_set(uid, 5000, item, PSA_STORAGE_FLAG_NONE);
+    CHECK(PSA_SUCCESS != status);
+
+    /* Attempt to store a stupidly big item */
+    status = psa_its_set(uid, static_cast<size_t>(-1), item, PSA_STORAGE_FLAG_NONE);
+    CHECK(PSA_SUCCESS != status);
+
+    /* Attempt to store a zero length item */
+    status = psa_its_set(uid, 0, item, PSA_STORAGE_FLAG_NONE);
+    CHECK_EQUAL(PSA_SUCCESS, status);
+
+    /* Remove the item */
+    status = psa_its_remove(uid);
+    CHECK_EQUAL(PSA_SUCCESS, status);
+}
\ No newline at end of file
diff --git a/protocols/service/psa/packed-c/status.h b/protocols/service/psa/packed-c/status.h
new file mode 100644
index 0000000..a959088
--- /dev/null
+++ b/protocols/service/psa/packed-c/status.h
@@ -0,0 +1,12 @@
+/*
+ * Copyright (c) 2020, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef PSA_STATUS_H
+#define PSA_STATUS_H
+
+#include <components/service/common/psa/error.h>
+
+#endif /* PSA_STATUS_H */
diff --git a/protocols/service/secure_storage/packed-c/component.cmake b/protocols/service/secure_storage/packed-c/component.cmake
new file mode 100644
index 0000000..b620b46
--- /dev/null
+++ b/protocols/service/secure_storage/packed-c/component.cmake
@@ -0,0 +1,16 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2020, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+if (NOT DEFINED TGT)
+	message(FATAL_ERROR "mandatory parameter TGT is not defined.")
+endif()
+
+
+
+target_include_directories(${TGT}
+	 PRIVATE
+		"${CMAKE_CURRENT_LIST_DIR}"
+	)
diff --git a/protocols/service/secure_storage/packed-c/secure_storage_proto.h b/protocols/service/secure_storage/packed-c/secure_storage_proto.h
new file mode 100644
index 0000000..1a9b8c8
--- /dev/null
+++ b/protocols/service/secure_storage/packed-c/secure_storage_proto.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2020, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef SECURE_STORAGE_PROTO_H
+#define SECURE_STORAGE_PROTO_H
+
+#include <stdint.h>
+
+struct __attribute__ ((__packed__)) secure_storage_request_set {
+	uint64_t uid;
+	uint64_t data_length;
+	uint32_t create_flags;
+	uint8_t p_data[];
+};
+
+struct __attribute__ ((__packed__)) secure_storage_request_get {
+	uint64_t uid;
+	uint64_t data_offset;
+	uint64_t data_size;
+};
+
+struct __attribute__ ((__packed__)) secure_storage_request_get_info {
+	uint64_t uid;
+};
+
+struct __attribute__ ((__packed__)) secure_storage_response_get_info {
+	uint64_t capacity;
+	uint64_t size;
+	uint32_t flags;
+};
+
+struct __attribute__ ((__packed__)) secure_storage_request_remove {
+	uint64_t uid;
+};
+
+#define TS_SECURE_STORAGE_OPCODE_SET			(0u)
+#define TS_SECURE_STORAGE_OPCODE_GET			(1u)
+#define TS_SECURE_STORAGE_OPCODE_GET_INFO		(2u)
+#define TS_SECURE_STORAGE_OPCODE_REMOVE			(3u)
+
+#define TS_SECURE_STORAGE_FLAG_NONE			(0u)
+#define TS_SECURE_STORAGE_FLAG_WRITE_ONCE		(1u << 0)
+#define TS_SECURE_STORAGE_FLAG_NO_CONFIDENTIALITY	(1u << 1)
+#define TS_SECURE_STORAGE_FLAG_NO_REPLAY_PROTECTION	(1u << 2)
+#define TS_SECURE_STORAGE_SUPPORT_SET_EXTENDED		(1u << 0)
+
+#endif /* SECURE_STORAGE_PROTO_H */