Add encrypted block store component tests

Change-Id: If0cbd13f7133edd0313bedd02f5b758a0afa606f
Signed-off-by: Gabor Toth <gabor.toth2@arm.com>
diff --git a/components/service/block_storage/block_store/encrypted/test/component.cmake b/components/service/block_storage/block_store/encrypted/test/component.cmake
new file mode 100644
index 0000000..5247b85
--- /dev/null
+++ b/components/service/block_storage/block_store/encrypted/test/component.cmake
@@ -0,0 +1,13 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2024, 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}/encrypted_block_store_tests.cpp"
+	)
diff --git a/components/service/block_storage/block_store/encrypted/test/encrypted_block_store_tests.cpp b/components/service/block_storage/block_store/encrypted/test/encrypted_block_store_tests.cpp
new file mode 100644
index 0000000..e649581
--- /dev/null
+++ b/components/service/block_storage/block_store/encrypted/test/encrypted_block_store_tests.cpp
@@ -0,0 +1,388 @@
+/*
+ * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <cstring>
+#include <service/crypto/client/psa/psa_crypto_client.h>
+#include <service_locator.h>
+
+#include "CppUTest/TestHarness.h"
+#include "service/block_storage/block_store/device/ram/ram_block_store.h"
+#include "service/block_storage/block_store/encrypted/encrypted_block_store.h"
+
+TEST_GROUP(EncryptedBlockStoreTests)
+{
+	void setup()
+	{
+		open_crypto_session();
+
+		uuid_guid_octets_from_canonical(&m_partition_guid,
+						"6152f22b-8128-4c1f-981f-3bd279519907");
+
+		m_back_store = ram_block_store_init(&m_ram_store, &m_partition_guid, NUM_BLOCKS,
+						    BLOCK_SIZE);
+
+		CHECK_TRUE(m_back_store);
+
+		m_block_store = encrypted_block_store_init(&m_encrypted_store, CLIENT_ID,
+							   &m_partition_guid, m_back_store);
+
+		CHECK_TRUE(m_block_store);
+
+		m_status = block_store_open(m_encrypted_store.back_store,
+					    m_encrypted_store.local_client_id, &m_partition_guid,
+					    &m_encrypted_store.back_store_handle);
+
+		LONGS_EQUAL(PSA_SUCCESS, m_status);
+	}
+
+	void teardown()
+	{
+		encrypted_block_store_deinit(&m_encrypted_store);
+		ram_block_store_deinit(&m_ram_store);
+
+		close_crypto_session();
+	}
+
+	void open_crypto_session()
+	{
+		service_locator_init();
+		m_crypto_service_context = service_locator_query("sn:trustedfirmware.org:crypto:0");
+
+		if (m_crypto_service_context) {
+			m_crypto_session = service_context_open(m_crypto_service_context);
+			if (m_crypto_session) {
+				psa_crypto_client_init(m_crypto_session);
+				psa_crypto_init();
+			}
+		}
+	}
+
+	void close_crypto_session()
+	{
+		psa_crypto_client_deinit();
+
+		if (m_crypto_service_context && m_crypto_session) {
+			service_context_close(m_crypto_service_context, m_crypto_session);
+			m_crypto_session = NULL;
+
+			service_context_relinquish(m_crypto_service_context);
+			m_crypto_service_context = NULL;
+		}
+	}
+
+	/* Back store configuration */
+	static const size_t NUM_BLOCKS = 100;
+	static const size_t BLOCK_SIZE = 512;
+	static const uint32_t CLIENT_ID = 27;
+	static const size_t AES_BLOCK_SIZE = 16;
+
+	struct service_context *m_crypto_service_context;
+	struct rpc_caller_session *m_crypto_session;
+
+	psa_status_t m_status = PSA_SUCCESS;
+	struct block_store *m_back_store;
+	struct block_store *m_block_store;
+	struct ram_block_store m_ram_store;
+	struct encrypted_block_store m_encrypted_store;
+	struct uuid_octets m_partition_guid = { 0, 1, 2, 3, 4, 5, 6, 7, 8 };
+};
+
+TEST(EncryptedBlockStoreTests, getPartitionInfo)
+{
+	struct storage_partition_info info;
+	struct uuid_octets partition_guid = { 5 };
+
+	/* Request wrong partition guid */
+	psa_status_t status = block_store_get_partition_info(m_block_store, &partition_guid, &info);
+	LONGS_EQUAL(PSA_ERROR_INVALID_ARGUMENT, status);
+
+	status = block_store_get_partition_info(m_block_store, &m_partition_guid, &info);
+
+	LONGS_EQUAL(PSA_SUCCESS, status);
+	LONGS_EQUAL(NUM_BLOCKS, info.num_blocks);
+	LONGS_EQUAL(BLOCK_SIZE, info.block_size);
+	MEMCMP_EQUAL(m_partition_guid.octets, info.partition_guid.octets,
+		     sizeof(info.partition_guid.octets));
+}
+
+TEST(EncryptedBlockStoreTests, openClose)
+{
+	storage_partition_handle_t handle;
+
+	psa_status_t status =
+		block_store_open(m_block_store, CLIENT_ID, &m_partition_guid, &handle);
+	LONGS_EQUAL(PSA_SUCCESS, status);
+
+	status = block_store_close(m_block_store, CLIENT_ID, handle);
+	LONGS_EQUAL(PSA_SUCCESS, status);
+}
+
+TEST(EncryptedBlockStoreTests, writeReadEraseBlock)
+{
+	storage_partition_handle_t handle;
+	uint8_t write_buffer[BLOCK_SIZE];
+	uint8_t read_buffer[BLOCK_SIZE];
+	size_t data_len = 0;
+	size_t num_written = 0;
+	uint64_t lba = 10;
+
+	psa_status_t status =
+		block_store_open(m_block_store, CLIENT_ID, &m_partition_guid, &handle);
+	LONGS_EQUAL(PSA_SUCCESS, status);
+
+	/* Request write with wrong offset */
+	status = block_store_write(m_block_store, CLIENT_ID, handle, lba, BLOCK_SIZE + 1,
+				   write_buffer, BLOCK_SIZE, &num_written);
+	LONGS_EQUAL(PSA_ERROR_INVALID_ARGUMENT, status);
+
+	/* Expect to be able to write to a block as all blocks are assumed to
+	 * be in the erased state */
+	memset(write_buffer, 0xaa, BLOCK_SIZE);
+	status = block_store_write(m_block_store, CLIENT_ID, handle, lba, 0, write_buffer,
+				   BLOCK_SIZE, &num_written);
+	LONGS_EQUAL(PSA_SUCCESS, status);
+	UNSIGNED_LONGS_EQUAL(BLOCK_SIZE, num_written);
+
+	/* Verify that only the encrypted data can be accessed int the back store */
+	memset(read_buffer, 0, BLOCK_SIZE);
+	status = block_store_read(m_back_store, CLIENT_ID, handle, lba, 0, BLOCK_SIZE, read_buffer,
+				  &data_len);
+	LONGS_EQUAL(PSA_SUCCESS, status);
+	UNSIGNED_LONGS_EQUAL(BLOCK_SIZE, data_len);
+	CHECK(memcmp(write_buffer, read_buffer, BLOCK_SIZE) != 0);
+
+	/* Request read with wrong offset */
+	status = block_store_read(m_block_store, CLIENT_ID, handle, lba, BLOCK_SIZE + 1, BLOCK_SIZE,
+				  read_buffer, &data_len);
+	LONGS_EQUAL(PSA_ERROR_INVALID_ARGUMENT, status);
+
+	/* Read back the original data if read through the encrypted block store */
+	memset(read_buffer, 0, BLOCK_SIZE);
+	status = block_store_read(m_block_store, CLIENT_ID, handle, lba, 0, BLOCK_SIZE, read_buffer,
+				  &data_len);
+	LONGS_EQUAL(PSA_SUCCESS, status);
+	UNSIGNED_LONGS_EQUAL(BLOCK_SIZE, data_len);
+	MEMCMP_EQUAL(write_buffer, read_buffer, BLOCK_SIZE);
+
+	/* Write the same block again without erase. */
+	memset(write_buffer, 0xbb, BLOCK_SIZE);
+	status = block_store_write(m_block_store, CLIENT_ID, handle, lba, 0, write_buffer,
+				   BLOCK_SIZE, &num_written);
+	LONGS_EQUAL(PSA_SUCCESS, status);
+
+	/* Erase the block */
+	status = block_store_erase(m_block_store, CLIENT_ID, handle, lba, 1);
+	LONGS_EQUAL(PSA_SUCCESS, status);
+
+	/* Write to an erased block should work again */
+	status = block_store_write(m_block_store, CLIENT_ID, handle, lba, 0, write_buffer,
+				   BLOCK_SIZE, &num_written);
+	LONGS_EQUAL(PSA_SUCCESS, status);
+	UNSIGNED_LONGS_EQUAL(BLOCK_SIZE, num_written);
+
+	status = block_store_close(m_block_store, CLIENT_ID, handle);
+	LONGS_EQUAL(PSA_SUCCESS, status);
+}
+
+TEST(EncryptedBlockStoreTests, writeBiggerThanBlock)
+{
+	storage_partition_handle_t handle;
+	uint8_t write_buffer[BLOCK_SIZE + BLOCK_SIZE / 2];
+	uint8_t read_buffer[BLOCK_SIZE];
+	size_t data_len = 0;
+	size_t num_written = 0;
+	uint64_t lba = 10;
+
+	psa_status_t status =
+		block_store_open(m_block_store, CLIENT_ID, &m_partition_guid, &handle);
+	LONGS_EQUAL(PSA_SUCCESS, status);
+
+	/* Try writing more than the block size. Expect the operation to succeed but
+	 * that the written data is limited to the block size*/
+	memset(write_buffer, 0xaa, sizeof(write_buffer));
+	status = block_store_write(m_block_store, CLIENT_ID, handle, lba, 0, write_buffer,
+				   sizeof(write_buffer), &num_written);
+	LONGS_EQUAL(PSA_SUCCESS, status);
+	UNSIGNED_LONGS_EQUAL(BLOCK_SIZE, num_written);
+
+	/* Expect to read back the same data upto the end of a block */
+	memset(read_buffer, 0, BLOCK_SIZE);
+	status = block_store_read(m_block_store, CLIENT_ID, handle, lba, 0, BLOCK_SIZE, read_buffer,
+				  &data_len);
+	LONGS_EQUAL(PSA_SUCCESS, status);
+	UNSIGNED_LONGS_EQUAL(BLOCK_SIZE, data_len);
+	MEMCMP_EQUAL(write_buffer, read_buffer, BLOCK_SIZE);
+
+	status = block_store_close(m_block_store, CLIENT_ID, handle);
+	LONGS_EQUAL(PSA_SUCCESS, status);
+}
+
+TEST(EncryptedBlockStoreTests, writeToInvalidBlock)
+{
+	storage_partition_handle_t handle;
+	uint8_t write_buffer[BLOCK_SIZE];
+	size_t num_written = 0;
+	uint64_t lba = NUM_BLOCKS + 7;
+
+	psa_status_t status =
+		block_store_open(m_block_store, CLIENT_ID, &m_partition_guid, &handle);
+	LONGS_EQUAL(PSA_SUCCESS, status);
+
+	/* Write to block beyond the limits of the storage partition should fail */
+	memset(write_buffer, 0xaa, BLOCK_SIZE);
+	status = block_store_write(m_block_store, CLIENT_ID, handle, lba, 0, write_buffer,
+				   BLOCK_SIZE, &num_written);
+	LONGS_EQUAL(PSA_ERROR_INVALID_ARGUMENT, status);
+
+	status = block_store_close(m_block_store, CLIENT_ID, handle);
+	LONGS_EQUAL(PSA_SUCCESS, status);
+}
+
+TEST(EncryptedBlockStoreTests, eraseOperations)
+{
+	storage_partition_handle_t handle;
+
+	psa_status_t status =
+		block_store_open(m_block_store, CLIENT_ID, &m_partition_guid, &handle);
+	LONGS_EQUAL(PSA_SUCCESS, status);
+
+	/* Erase all blocks */
+	status = block_store_erase(m_block_store, CLIENT_ID, handle, 0, NUM_BLOCKS);
+	LONGS_EQUAL(PSA_SUCCESS, status);
+
+	/* Erase all blocks using a length that exceeds the partition limit - should clip */
+	status = block_store_erase(m_block_store, CLIENT_ID, handle, 0, UINT32_MAX);
+	LONGS_EQUAL(PSA_SUCCESS, status);
+
+	/* Begin LBA is outside of partition */
+	status = block_store_erase(m_block_store, CLIENT_ID, handle, NUM_BLOCKS + 1, NUM_BLOCKS);
+	LONGS_EQUAL(PSA_ERROR_INVALID_ARGUMENT, status);
+
+	status = block_store_close(m_block_store, CLIENT_ID, handle);
+	LONGS_EQUAL(PSA_SUCCESS, status);
+}
+
+TEST(EncryptedBlockStoreTests, isBackstoreEncrypted)
+{
+	storage_partition_handle_t handle;
+	uint8_t write_buffer[BLOCK_SIZE];
+	uint8_t read_buffer[BLOCK_SIZE];
+	size_t data_len = 0;
+	size_t num_written = 0;
+	uint64_t lba = 10;
+
+	psa_status_t status =
+		block_store_open(m_block_store, CLIENT_ID, &m_partition_guid, &handle);
+	LONGS_EQUAL(PSA_SUCCESS, status);
+
+	/* Expect to be able to write to a block as all blocks are assumed to
+	 * be in the erased state */
+	memset(write_buffer, 0xaa, BLOCK_SIZE);
+	status = block_store_write(m_block_store, CLIENT_ID, handle, lba, 0, write_buffer,
+				   BLOCK_SIZE, &num_written);
+	LONGS_EQUAL(PSA_SUCCESS, status);
+	UNSIGNED_LONGS_EQUAL(BLOCK_SIZE, num_written);
+
+	/* Expect to read back encrypted data from the back store */
+	memset(read_buffer, 0, BLOCK_SIZE);
+	status = block_store_read(m_back_store, CLIENT_ID, handle, lba, 0, BLOCK_SIZE, read_buffer,
+				  &data_len);
+	LONGS_EQUAL(PSA_SUCCESS, status);
+	UNSIGNED_LONGS_EQUAL(BLOCK_SIZE, data_len);
+	CHECK(memcmp(write_buffer, read_buffer, BLOCK_SIZE) != 0);
+
+	status = block_store_close(m_block_store, CLIENT_ID, handle);
+	LONGS_EQUAL(PSA_SUCCESS, status);
+}
+
+TEST(EncryptedBlockStoreTests, unalignedBlockStoreInit)
+{
+	struct block_store *back_store;
+	struct block_store *block_store;
+
+	struct ram_block_store ram_store;
+	struct encrypted_block_store m_encrypted_store;
+	struct uuid_octets partition_guid = { 0 };
+	back_store =
+		ram_block_store_init(&ram_store, &partition_guid, NUM_BLOCKS, AES_BLOCK_SIZE + 1);
+
+	CHECK_TRUE(back_store);
+
+	block_store = encrypted_block_store_init(&m_encrypted_store, CLIENT_ID, &partition_guid,
+						 back_store);
+
+	CHECK_FALSE(block_store);
+
+	ram_block_store_deinit(&ram_store);
+}
+
+TEST(EncryptedBlockStoreTests, writeUnaligned)
+{
+	storage_partition_handle_t handle;
+	uint8_t write_buffer[AES_BLOCK_SIZE + 1];
+	uint8_t read_buffer[AES_BLOCK_SIZE + 1];
+	size_t data_len = 0;
+	size_t num_written = 0;
+	uint64_t lba = 10;
+
+	psa_status_t status =
+		block_store_open(m_block_store, CLIENT_ID, &m_partition_guid, &handle);
+	LONGS_EQUAL(PSA_SUCCESS, status);
+
+	/* Expect to be able to write to a block as all blocks are assumed to
+	 * be in the erased state */
+	memset(write_buffer, 0xaa, AES_BLOCK_SIZE);
+	status = block_store_write(m_block_store, CLIENT_ID, handle, lba, 0, write_buffer,
+				   AES_BLOCK_SIZE + 1, &num_written);
+	LONGS_EQUAL(PSA_SUCCESS, status);
+	UNSIGNED_LONGS_EQUAL(AES_BLOCK_SIZE + 1, num_written);
+
+	/* Expect to read back encrypted data from the back store */
+	memset(read_buffer, 0, AES_BLOCK_SIZE + 1);
+	status = block_store_read(m_back_store, CLIENT_ID, handle, lba, 0, AES_BLOCK_SIZE + 1,
+				  read_buffer, &data_len);
+	LONGS_EQUAL(PSA_SUCCESS, status);
+	UNSIGNED_LONGS_EQUAL(AES_BLOCK_SIZE + 1, data_len);
+	CHECK(memcmp(write_buffer, read_buffer, AES_BLOCK_SIZE + 1) != 0);
+
+	status = block_store_close(m_block_store, CLIENT_ID, handle);
+	LONGS_EQUAL(PSA_SUCCESS, status);
+}
+
+TEST(EncryptedBlockStoreTests, writeInTwoChunks)
+{
+	storage_partition_handle_t handle;
+	uint8_t write_buffer[BLOCK_SIZE];
+	uint8_t read_buffer[BLOCK_SIZE];
+	size_t data_len = 0;
+	size_t num_written = 0;
+	uint64_t lba = 10;
+
+	psa_status_t status =
+		block_store_open(m_block_store, CLIENT_ID, &m_partition_guid, &handle);
+	LONGS_EQUAL(PSA_SUCCESS, status);
+
+	for (size_t i = 0; i < BLOCK_SIZE; i++)
+		write_buffer[i] = i;
+
+	status = block_store_write(m_block_store, CLIENT_ID, handle, lba, 0, write_buffer,
+				   BLOCK_SIZE / 2, &num_written);
+	LONGS_EQUAL(PSA_SUCCESS, status);
+	UNSIGNED_LONGS_EQUAL(BLOCK_SIZE / 2, num_written);
+
+	status = block_store_write(m_block_store, CLIENT_ID, handle, lba, BLOCK_SIZE / 2,
+				   write_buffer + BLOCK_SIZE / 2, BLOCK_SIZE / 2, &num_written);
+	LONGS_EQUAL(PSA_SUCCESS, status);
+	UNSIGNED_LONGS_EQUAL(BLOCK_SIZE / 2, num_written);
+
+	/* Expect to read back the same data upto the end of a block */
+	memset(read_buffer, 0, BLOCK_SIZE);
+	status = block_store_read(m_block_store, CLIENT_ID, handle, lba, 0, BLOCK_SIZE, read_buffer,
+				  &data_len);
+	LONGS_EQUAL(PSA_SUCCESS, status);
+	UNSIGNED_LONGS_EQUAL(BLOCK_SIZE, data_len);
+	MEMCMP_EQUAL(write_buffer, read_buffer, BLOCK_SIZE);
+}
diff --git a/deployments/component-test/component-test.cmake b/deployments/component-test/component-test.cmake
index c20e2fe..bd2d89e 100644
--- a/deployments/component-test/component-test.cmake
+++ b/deployments/component-test/component-test.cmake
@@ -97,6 +97,8 @@
 		"components/service/block_storage/block_store/client"
 		"components/service/block_storage/block_store/partitioned"
 		"components/service/block_storage/block_store/partitioned/test"
+		"components/service/block_storage/block_store/encrypted"
+		"components/service/block_storage/block_store/encrypted/test"
 		"components/service/block_storage/provider"
 		"components/service/block_storage/provider/serializer/packed-c"
 		"components/service/block_storage/config/ref"