Add symmetric cipher service level tests

Adds end-to-end tests for cipher operations implemented by
a crypto service provider.

Signed-off-by: Julian Hall <julian.hall@arm.com>
Change-Id: I37c29095bac452ad7ac24fa44ac3df9b9e29457e
diff --git a/components/service/crypto/client/caller/packed-c/crypto_caller_cipher.h b/components/service/crypto/client/caller/packed-c/crypto_caller_cipher.h
index 58679bf..2ac9daa 100644
--- a/components/service/crypto/client/caller/packed-c/crypto_caller_cipher.h
+++ b/components/service/crypto/client/caller/packed-c/crypto_caller_cipher.h
@@ -390,7 +390,7 @@
 	return psa_status;
 }
 
-static inline size_t crypto_caller_cipher_max_update_size(struct service_client *context)
+static inline size_t crypto_caller_cipher_max_update_size(const struct service_client *context)
 {
 	/* Returns the maximum number of bytes that may be
 	 * carried as a parameter of the cipher_update operation
@@ -399,6 +399,9 @@
 	size_t payload_space = context->service_info.max_payload;
 	size_t overhead = sizeof(struct ts_crypto_cipher_update_in) + TLV_HDR_LEN;
 
+	/* Allow for output to be a whole number of blocks */
+	overhead += PSA_BLOCK_CIPHER_BLOCK_MAX_SIZE;
+
 	return (payload_space > overhead) ? payload_space - overhead : 0;
 }
 
diff --git a/components/service/crypto/client/cpp/crypto_client.h b/components/service/crypto/client/cpp/crypto_client.h
index 68d80ee..2b5a0f0 100644
--- a/components/service/crypto/client/cpp/crypto_client.h
+++ b/components/service/crypto/client/cpp/crypto_client.h
@@ -115,6 +115,39 @@
 		uint32_t source_op_handle,
 		uint32_t *target_op_handle) = 0;
 
+	/* Cipher methods */
+	virtual size_t cipher_max_update_size() const = 0;
+
+	virtual psa_status_t cipher_encrypt_setup(
+		uint32_t *op_handle,
+		psa_key_id_t key,
+		psa_algorithm_t alg) = 0;
+
+	virtual psa_status_t cipher_decrypt_setup(
+		uint32_t *op_handle,
+		psa_key_id_t key,
+		psa_algorithm_t alg) = 0;
+
+	virtual psa_status_t cipher_generate_iv(
+		uint32_t op_handle,
+		uint8_t *iv, size_t iv_size, size_t *iv_length) = 0;
+
+	virtual psa_status_t cipher_set_iv(
+		uint32_t op_handle,
+		const uint8_t *iv, size_t iv_length) = 0;
+
+	virtual psa_status_t cipher_update(
+		uint32_t op_handle,
+		const uint8_t *input, size_t input_length,
+		uint8_t *output, size_t output_size, size_t *output_length) = 0;
+
+	virtual psa_status_t cipher_finish(
+		uint32_t op_handle,
+		uint8_t *output, size_t output_size, size_t *output_length) = 0;
+
+	virtual psa_status_t cipher_abort(
+		uint32_t op_handle) = 0;
+
 protected:
 	crypto_client();
 	crypto_client(struct rpc_caller *caller);
diff --git a/components/service/crypto/client/cpp/protocol/packed-c/packedc_crypto_client.cpp b/components/service/crypto/client/cpp/protocol/packed-c/packedc_crypto_client.cpp
index 09ff7af..edcac7b 100644
--- a/components/service/crypto/client/cpp/protocol/packed-c/packedc_crypto_client.cpp
+++ b/components/service/crypto/client/cpp/protocol/packed-c/packedc_crypto_client.cpp
@@ -26,6 +26,7 @@
 
 }
 
+/* Core crypto methods */
 psa_status_t packedc_crypto_client::generate_key(
 	const psa_key_attributes_t *attributes,
 	psa_key_id_t *id)
@@ -137,6 +138,7 @@
 		output, output_size);
 }
 
+/* Hash methods */
 size_t packedc_crypto_client::hash_max_update_size() const
 {
 	return crypto_caller_hash_max_update_size(&m_client);
@@ -188,3 +190,68 @@
 	return crypto_caller_hash_clone(&m_client,
 		source_op_handle, target_op_handle);
 }
+
+/* Cipher methods */
+size_t packedc_crypto_client::cipher_max_update_size() const
+{
+	return crypto_caller_cipher_max_update_size(&m_client);
+}
+
+psa_status_t packedc_crypto_client::cipher_encrypt_setup(
+	uint32_t *op_handle,
+	psa_key_id_t key,
+	psa_algorithm_t alg)
+{
+	return crypto_caller_cipher_encrypt_setup(&m_client,
+		op_handle, key, alg);
+}
+
+psa_status_t packedc_crypto_client::cipher_decrypt_setup(
+	uint32_t *op_handle,
+	psa_key_id_t key,
+	psa_algorithm_t alg)
+{
+	return crypto_caller_cipher_decrypt_setup(&m_client,
+		op_handle, key, alg);
+}
+
+psa_status_t packedc_crypto_client::cipher_generate_iv(
+	uint32_t op_handle,
+	uint8_t *iv, size_t iv_size, size_t *iv_length)
+{
+	return crypto_caller_cipher_generate_iv(&m_client,
+		op_handle, iv, iv_size, iv_length);
+}
+
+psa_status_t packedc_crypto_client::cipher_set_iv(
+	uint32_t op_handle,
+	const uint8_t *iv, size_t iv_length)
+{
+	return crypto_caller_cipher_set_iv(&m_client,
+		op_handle, iv, iv_length);
+}
+
+psa_status_t packedc_crypto_client::cipher_update(
+	uint32_t op_handle,
+	const uint8_t *input, size_t input_length,
+	uint8_t *output, size_t output_size, size_t *output_length)
+{
+	return crypto_caller_cipher_update(&m_client,
+		op_handle, input, input_length,
+		output, output_size, output_length);
+}
+
+psa_status_t packedc_crypto_client::cipher_finish(
+	uint32_t op_handle,
+	uint8_t *output, size_t output_size, size_t *output_length)
+{
+	return crypto_caller_cipher_finish(&m_client,
+		op_handle, output, output_size, output_length);
+}
+
+psa_status_t packedc_crypto_client::cipher_abort(
+	uint32_t op_handle)
+{
+	return crypto_caller_cipher_abort(&m_client,
+		op_handle);
+}
diff --git a/components/service/crypto/client/cpp/protocol/packed-c/packedc_crypto_client.h b/components/service/crypto/client/cpp/protocol/packed-c/packedc_crypto_client.h
index bd6a484..9c30372 100644
--- a/components/service/crypto/client/cpp/protocol/packed-c/packedc_crypto_client.h
+++ b/components/service/crypto/client/cpp/protocol/packed-c/packedc_crypto_client.h
@@ -112,6 +112,39 @@
 		uint32_t source_op_handle,
 		uint32_t *target_op_handle);
 
+	/* Cipher methods */
+	size_t cipher_max_update_size() const;
+
+	psa_status_t cipher_encrypt_setup(
+		uint32_t *op_handle,
+		psa_key_id_t key,
+		psa_algorithm_t alg);
+
+	psa_status_t cipher_decrypt_setup(
+		uint32_t *op_handle,
+		psa_key_id_t key,
+		psa_algorithm_t alg);
+
+	psa_status_t cipher_generate_iv(
+		uint32_t op_handle,
+		uint8_t *iv, size_t iv_size, size_t *iv_length);
+
+	psa_status_t cipher_set_iv(
+		uint32_t op_handle,
+		const uint8_t *iv, size_t iv_length);
+
+	psa_status_t cipher_update(
+		uint32_t op_handle,
+		const uint8_t *input, size_t input_length,
+		uint8_t *output, size_t output_size, size_t *output_length);
+
+	psa_status_t cipher_finish(
+		uint32_t op_handle,
+		uint8_t *output, size_t output_size, size_t *output_length);
+
+	psa_status_t cipher_abort(
+		uint32_t op_handle);
+
 };
 
 #endif /* PACKEDC_CRYPTO_CLIENT_H */
diff --git a/components/service/crypto/client/cpp/protocol/protobuf/protobuf_crypto_client.cpp b/components/service/crypto/client/cpp/protocol/protobuf/protobuf_crypto_client.cpp
index db9c0e9..6c0fe76 100644
--- a/components/service/crypto/client/cpp/protocol/protobuf/protobuf_crypto_client.cpp
+++ b/components/service/crypto/client/cpp/protocol/protobuf/protobuf_crypto_client.cpp
@@ -41,6 +41,19 @@
 
 }
 
+void protobuf_crypto_client::translate_key_attributes(ts_crypto_KeyAttributes &proto_attributes,
+							const psa_key_attributes_t &psa_attributes)
+{
+	proto_attributes.type = psa_get_key_type(&psa_attributes);
+	proto_attributes.key_bits = psa_get_key_bits(&psa_attributes);
+	proto_attributes.lifetime = psa_get_key_lifetime(&psa_attributes);
+	proto_attributes.id = psa_get_key_id(&psa_attributes);
+
+	proto_attributes.has_policy = true;
+	proto_attributes.policy.usage = psa_get_key_usage_flags(&psa_attributes);
+	proto_attributes.policy.alg = psa_get_key_algorithm(&psa_attributes);
+ }
+
 psa_status_t protobuf_crypto_client::generate_key(const psa_key_attributes_t *attributes,
 	psa_key_id_t *id)
 {
@@ -775,15 +788,59 @@
 	return PSA_ERROR_NOT_SUPPORTED;
 }
 
-void protobuf_crypto_client::translate_key_attributes(ts_crypto_KeyAttributes &proto_attributes,
-							const psa_key_attributes_t &psa_attributes)
+/* Cipher methods */
+size_t protobuf_crypto_client::cipher_max_update_size() const
 {
-	proto_attributes.type = psa_get_key_type(&psa_attributes);
-	proto_attributes.key_bits = psa_get_key_bits(&psa_attributes);
-	proto_attributes.lifetime = psa_get_key_lifetime(&psa_attributes);
-	proto_attributes.id = psa_get_key_id(&psa_attributes);
+	return 0;
+}
 
-	proto_attributes.has_policy = true;
-	proto_attributes.policy.usage = psa_get_key_usage_flags(&psa_attributes);
-	proto_attributes.policy.alg = psa_get_key_algorithm(&psa_attributes);
- }
+psa_status_t protobuf_crypto_client::cipher_encrypt_setup(
+	uint32_t *op_handle,
+	psa_key_id_t key,
+	psa_algorithm_t alg)
+{
+	return PSA_ERROR_NOT_SUPPORTED;
+}
+
+psa_status_t protobuf_crypto_client::cipher_decrypt_setup(
+	uint32_t *op_handle,
+	psa_key_id_t key,
+	psa_algorithm_t alg)
+{
+	return PSA_ERROR_NOT_SUPPORTED;
+}
+
+psa_status_t protobuf_crypto_client::cipher_generate_iv(
+	uint32_t op_handle,
+	uint8_t *iv, size_t iv_size, size_t *iv_length)
+{
+	return PSA_ERROR_NOT_SUPPORTED;
+}
+
+psa_status_t protobuf_crypto_client::cipher_set_iv(
+	uint32_t op_handle,
+	const uint8_t *iv, size_t iv_length)
+{
+	return PSA_ERROR_NOT_SUPPORTED;
+}
+
+psa_status_t protobuf_crypto_client::cipher_update(
+	uint32_t op_handle,
+	const uint8_t *input, size_t input_length,
+	uint8_t *output, size_t output_size, size_t *output_length)
+{
+	return PSA_ERROR_NOT_SUPPORTED;
+}
+
+psa_status_t protobuf_crypto_client::cipher_finish(
+	uint32_t op_handle,
+	uint8_t *output, size_t output_size, size_t *output_length)
+{
+	return PSA_ERROR_NOT_SUPPORTED;
+}
+
+psa_status_t protobuf_crypto_client::cipher_abort(
+	uint32_t op_handle)
+{
+	return PSA_ERROR_NOT_SUPPORTED;
+}
diff --git a/components/service/crypto/client/cpp/protocol/protobuf/protobuf_crypto_client.h b/components/service/crypto/client/cpp/protocol/protobuf/protobuf_crypto_client.h
index 0221597..d152f04 100644
--- a/components/service/crypto/client/cpp/protocol/protobuf/protobuf_crypto_client.h
+++ b/components/service/crypto/client/cpp/protocol/protobuf/protobuf_crypto_client.h
@@ -112,6 +112,40 @@
 		uint32_t source_op_handle,
 		uint32_t *target_op_handle);
 
+	/* Cipher methods */
+	size_t cipher_max_update_size() const;
+
+	psa_status_t cipher_encrypt_setup(
+		uint32_t *op_handle,
+		psa_key_id_t key,
+		psa_algorithm_t alg);
+
+	psa_status_t cipher_decrypt_setup(
+		uint32_t *op_handle,
+		psa_key_id_t key,
+		psa_algorithm_t alg);
+
+	psa_status_t cipher_generate_iv(
+		uint32_t op_handle,
+		uint8_t *iv, size_t iv_size, size_t *iv_length);
+
+	psa_status_t cipher_set_iv(
+		uint32_t op_handle,
+		const uint8_t *iv, size_t iv_length);
+
+	psa_status_t cipher_update(
+		uint32_t op_handle,
+		const uint8_t *input, size_t input_length,
+		uint8_t *output, size_t output_size, size_t *output_length);
+
+	psa_status_t cipher_finish(
+		uint32_t op_handle,
+		uint8_t *output, size_t output_size, size_t *output_length);
+
+	psa_status_t cipher_abort(
+		uint32_t op_handle);
+
+
 private:
 
 	void translate_key_attributes(
diff --git a/components/service/crypto/test/service/crypto_service_scenarios.h b/components/service/crypto/test/service/crypto_service_scenarios.h
index 648baae..4407d06 100644
--- a/components/service/crypto/test/service/crypto_service_scenarios.h
+++ b/components/service/crypto/test/service/crypto_service_scenarios.h
@@ -4,6 +4,9 @@
  * SPDX-License-Identifier: BSD-3-Clause
  */
 
+#ifndef CRYPTO_SERVICE_SCENARIOS_H
+#define CRYPTO_SERVICE_SCENARIOS_H
+
 #include <service/crypto/client/cpp/crypto_client.h>
 
 /*
@@ -14,19 +17,21 @@
 class crypto_service_scenarios
 {
 public:
-    crypto_service_scenarios(crypto_client *crypto_client);
-    ~crypto_service_scenarios();
+	crypto_service_scenarios(crypto_client *crypto_client);
+	~crypto_service_scenarios();
 
-    void generateRandomNumbers();
-    void asymEncryptDecrypt();
-    void asymEncryptDecryptWithSalt();
-    void signAndVerifyHash();
-    void signAndVerifyEat();
-    void exportAndImportKeyPair();
-    void exportPublicKey();
-    void generatePersistentKeys();
-    void generateVolatileKeys();
+	void generateRandomNumbers();
+	void asymEncryptDecrypt();
+	void asymEncryptDecryptWithSalt();
+	void signAndVerifyHash();
+	void signAndVerifyEat();
+	void exportAndImportKeyPair();
+	void exportPublicKey();
+	void generatePersistentKeys();
+	void generateVolatileKeys();
 
 private:
-    crypto_client *m_crypto_client;
+	crypto_client *m_crypto_client;
 };
+
+#endif /* CRYPTO_SERVICE_SCENARIOS_H */
diff --git a/components/service/crypto/test/service/extension/cipher/cipher_service_scenarios.cpp b/components/service/crypto/test/service/extension/cipher/cipher_service_scenarios.cpp
new file mode 100644
index 0000000..687bb53
--- /dev/null
+++ b/components/service/crypto/test/service/extension/cipher/cipher_service_scenarios.cpp
@@ -0,0 +1,179 @@
+/*
+ * Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <cstring>
+#include <cstdint>
+#include <CppUTest/TestHarness.h>
+#include "cipher_service_scenarios.h"
+
+
+cipher_service_scenarios::cipher_service_scenarios(crypto_client *crypto_client) :
+	m_crypto_client(crypto_client),
+	m_ref_plaintext(NULL),
+	m_keyid(0)
+{
+
+}
+
+cipher_service_scenarios::~cipher_service_scenarios()
+{
+	destroy_key();
+
+	delete m_crypto_client;
+	m_crypto_client = NULL;
+
+	delete [] m_ref_plaintext;
+	m_ref_plaintext = NULL;
+}
+
+void cipher_service_scenarios::create_ref_plaintext(size_t size)
+{
+	m_ref_plaintext = new uint8_t[size];
+	memset(m_ref_plaintext, 0x42, size);
+}
+
+psa_status_t cipher_service_scenarios::generate_key()
+{
+	destroy_key();
+
+	psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
+
+	psa_set_key_lifetime(&attributes, PSA_KEY_LIFETIME_VOLATILE);
+	psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT);
+	psa_set_key_algorithm(&attributes, PSA_ALG_CTR);
+	psa_set_key_type(&attributes, PSA_KEY_TYPE_AES);
+	psa_set_key_bits(&attributes, 256);
+
+	psa_status_t status = m_crypto_client->generate_key(&attributes, &m_keyid);
+
+	psa_reset_key_attributes(&attributes);
+
+	return status;
+}
+
+void cipher_service_scenarios::destroy_key()
+{
+	if (m_keyid) {
+
+		m_crypto_client->destroy_key(m_keyid);
+		m_keyid = 0;
+	}
+}
+
+void cipher_service_scenarios::encryptDecryptRoundtrip()
+{
+	size_t max_payload = m_crypto_client->cipher_max_update_size();
+	static const size_t plaintext_size = 9999;
+	psa_status_t status;
+
+	create_ref_plaintext(plaintext_size);
+
+	status = generate_key();
+	CHECK_EQUAL(PSA_SUCCESS, status);
+
+	uint8_t ciphertext[PSA_CIPHER_ENCRYPT_OUTPUT_MAX_SIZE(plaintext_size)];
+
+	uint32_t op_handle = 0;
+
+	/* Encrypt the input plaintext */
+	size_t input_byte_count = 0;
+	size_t output_byte_count = 0;
+
+	status = m_crypto_client->cipher_encrypt_setup(&op_handle, m_keyid, PSA_ALG_CTR);
+	CHECK_EQUAL(PSA_SUCCESS, status);
+
+	size_t iv_len;
+	uint8_t iv[PSA_CIPHER_IV_MAX_SIZE];
+	status = m_crypto_client->cipher_generate_iv(op_handle, iv, sizeof(iv), &iv_len);
+	CHECK_EQUAL(PSA_SUCCESS, status);
+
+	while (input_byte_count < plaintext_size) {
+
+		size_t update_output_len = 0;
+		size_t output_space = sizeof(ciphertext) - output_byte_count;
+		size_t bytes_left = plaintext_size - input_byte_count;
+		size_t update_len = (bytes_left < max_payload) ? bytes_left : max_payload;
+
+		status = m_crypto_client->cipher_update(op_handle,
+			&m_ref_plaintext[input_byte_count], update_len,
+			&ciphertext[output_byte_count], output_space, &update_output_len);
+		CHECK_EQUAL(PSA_SUCCESS, status);
+
+		input_byte_count += update_len;
+		output_byte_count += update_output_len;
+	}
+
+	size_t finish_output_len = 0;
+	size_t finish_output_space = sizeof(ciphertext) - output_byte_count;
+
+	status = m_crypto_client->cipher_finish(op_handle,
+		&ciphertext[output_byte_count], finish_output_space, &finish_output_len);
+	CHECK_EQUAL(PSA_SUCCESS, status);
+
+	output_byte_count += finish_output_len;
+
+	/* Decrypt the ciphertext */
+	uint8_t decrypted_plaintext[plaintext_size];
+	op_handle = 0;
+
+	size_t ciphertext_size = output_byte_count;
+	input_byte_count = 0;
+	output_byte_count = 0;
+
+	status = m_crypto_client->cipher_decrypt_setup(&op_handle, m_keyid, PSA_ALG_CTR);
+	CHECK_EQUAL(PSA_SUCCESS, status);
+
+	status = m_crypto_client->cipher_set_iv(op_handle, iv, iv_len);
+	CHECK_EQUAL(PSA_SUCCESS, status);
+
+	while (input_byte_count < ciphertext_size) {
+
+		size_t update_output_len = 0;
+		size_t output_space = sizeof(decrypted_plaintext) - output_byte_count;
+		size_t bytes_left = ciphertext_size - input_byte_count;
+		size_t update_len = (bytes_left < max_payload) ? bytes_left : max_payload;
+
+		status = m_crypto_client->cipher_update(op_handle,
+			&ciphertext[input_byte_count], update_len,
+			&decrypted_plaintext[output_byte_count], output_space, &update_output_len);
+		CHECK_EQUAL(PSA_SUCCESS, status);
+
+		input_byte_count += update_len;
+		output_byte_count += update_output_len;
+	}
+
+	finish_output_len = 0;
+	finish_output_space = sizeof(decrypted_plaintext) - output_byte_count;
+
+	status = m_crypto_client->cipher_finish(op_handle,
+		&ciphertext[output_byte_count], finish_output_space, &finish_output_len);
+	CHECK_EQUAL(PSA_SUCCESS, status);
+
+	output_byte_count += finish_output_len;
+
+	/* Expect decrypted plaintext to be identical to reference plaintext */
+	UNSIGNED_LONGS_EQUAL(plaintext_size, output_byte_count);
+	MEMCMP_EQUAL(m_ref_plaintext, decrypted_plaintext, plaintext_size);
+}
+
+void cipher_service_scenarios::cipherAbort()
+{
+	/* Aborts a cipher operation prior to the first update */
+	uint32_t op_handle = 0;
+	psa_status_t status = generate_key();
+	CHECK_EQUAL(PSA_SUCCESS, status);
+
+	status = m_crypto_client->cipher_encrypt_setup(&op_handle, m_keyid, PSA_ALG_CTR);
+	CHECK_EQUAL(PSA_SUCCESS, status);
+
+	status = m_crypto_client->cipher_abort(op_handle);
+	CHECK_EQUAL(PSA_SUCCESS, status);
+
+	size_t iv_len;
+	uint8_t iv[PSA_CIPHER_IV_MAX_SIZE];
+	status = m_crypto_client->cipher_generate_iv(op_handle, iv, sizeof(iv), &iv_len);
+	CHECK_EQUAL(PSA_ERROR_BAD_STATE, status);
+}
diff --git a/components/service/crypto/test/service/extension/cipher/cipher_service_scenarios.h b/components/service/crypto/test/service/extension/cipher/cipher_service_scenarios.h
new file mode 100644
index 0000000..7281ddf
--- /dev/null
+++ b/components/service/crypto/test/service/extension/cipher/cipher_service_scenarios.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef CIPHER_SERVICE_SCENARIOS_H
+#define CIPHER_SERVICE_SCENARIOS_H
+
+#include <stddef.h>
+#include <stdint.h>
+#include <service/crypto/client/cpp/crypto_client.h>
+
+/*
+ * Service-level test scenarios for the cipher extension to the crypto service
+ * that may be reused using different concrete crypto_clients to check
+ * end-to-end operation using different protocol serialization schemes.
+ */
+class cipher_service_scenarios
+{
+public:
+	cipher_service_scenarios(crypto_client *crypto_client);
+	~cipher_service_scenarios();
+
+	void encryptDecryptRoundtrip();
+	void cipherAbort();
+
+private:
+
+	psa_status_t generate_key();
+	void destroy_key();
+
+	void create_ref_plaintext(size_t size);
+
+	crypto_client *m_crypto_client;
+	uint8_t *m_ref_plaintext;
+	psa_key_id_t m_keyid;
+};
+
+#endif /* CIPHER_SERVICE_SCENARIOS_H */
diff --git a/components/service/crypto/test/service/extension/cipher/component.cmake b/components/service/crypto/test/service/extension/cipher/component.cmake
new file mode 100644
index 0000000..4a8d166
--- /dev/null
+++ b/components/service/crypto/test/service/extension/cipher/component.cmake
@@ -0,0 +1,13 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2021, 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}/cipher_service_scenarios.cpp"
+	)
diff --git a/components/service/crypto/test/service/extension/cipher/packed-c/cipher_service_packedc_tests.cpp b/components/service/crypto/test/service/extension/cipher/packed-c/cipher_service_packedc_tests.cpp
new file mode 100644
index 0000000..d0e7f38
--- /dev/null
+++ b/components/service/crypto/test/service/extension/cipher/packed-c/cipher_service_packedc_tests.cpp
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <service/crypto/client/cpp/protocol/packed-c/packedc_crypto_client.h>
+#include <service/crypto/test/service/extension/cipher/cipher_service_scenarios.h>
+#include <protocols/rpc/common/packed-c/encoding.h>
+#include <service_locator.h>
+#include <CppUTest/TestHarness.h>
+
+/*
+ * Service-level cipher tests that use the packed-c access protocol serialization
+ */
+TEST_GROUP(CryptoCipherServicePackedcTests)
+{
+	void setup()
+	{
+		struct rpc_caller *caller;
+		int status;
+
+		m_rpc_session_handle = NULL;
+		m_crypto_service_context = NULL;
+		m_scenarios = NULL;
+
+		service_locator_init();
+
+		m_crypto_service_context = service_locator_query("sn:trustedfirmware.org:crypto:0", &status);
+		CHECK_TRUE(m_crypto_service_context);
+
+		m_rpc_session_handle = service_context_open(m_crypto_service_context, TS_RPC_ENCODING_PACKED_C, &caller);
+		CHECK_TRUE(m_rpc_session_handle);
+
+		m_scenarios = new cipher_service_scenarios(new packedc_crypto_client(caller));
+	}
+
+	void teardown()
+	{
+		delete m_scenarios;
+		m_scenarios = NULL;
+
+		service_context_close(m_crypto_service_context, m_rpc_session_handle);
+		m_rpc_session_handle = NULL;
+
+		service_context_relinquish(m_crypto_service_context);
+		m_crypto_service_context = NULL;
+	}
+
+	rpc_session_handle m_rpc_session_handle;
+	struct service_context *m_crypto_service_context;
+	cipher_service_scenarios *m_scenarios;
+};
+
+TEST(CryptoCipherServicePackedcTests, encryptDecryptRoundtrip)
+{
+	m_scenarios->encryptDecryptRoundtrip();
+}
+
+TEST(CryptoCipherServicePackedcTests, cipherAbort)
+{
+	m_scenarios->cipherAbort();
+}
diff --git a/components/service/crypto/test/service/extension/cipher/packed-c/component.cmake b/components/service/crypto/test/service/extension/cipher/packed-c/component.cmake
new file mode 100644
index 0000000..7efd130
--- /dev/null
+++ b/components/service/crypto/test/service/extension/cipher/packed-c/component.cmake
@@ -0,0 +1,13 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2021, 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}/cipher_service_packedc_tests.cpp"
+	)
diff --git a/components/service/crypto/test/service/extension/hash/hash_service_scenarios.cpp b/components/service/crypto/test/service/extension/hash/hash_service_scenarios.cpp
index f76d59e..b02cfd1 100644
--- a/components/service/crypto/test/service/extension/hash/hash_service_scenarios.cpp
+++ b/components/service/crypto/test/service/extension/hash/hash_service_scenarios.cpp
@@ -65,7 +65,7 @@
 {
 	/* Calculates and verifies hash over a large reference input */
 	size_t max_payload = m_crypto_client->hash_max_update_size();
-	size_t input_size = 9999;
+	static const size_t input_size = 9999;
 	size_t byte_count = 0;
 
 	create_ref_input(input_size);
@@ -108,7 +108,7 @@
 {
 	/* Aborts a hash operation after the first update */
 	size_t max_payload = m_crypto_client->hash_max_update_size();
-	size_t input_size = 15999;
+	static const size_t input_size = 15999;
 
 	create_ref_input(input_size);
 
diff --git a/components/service/crypto/test/service/extension/hash/hash_service_scenarios.h b/components/service/crypto/test/service/extension/hash/hash_service_scenarios.h
index 84bd7d7..7912a55 100644
--- a/components/service/crypto/test/service/extension/hash/hash_service_scenarios.h
+++ b/components/service/crypto/test/service/extension/hash/hash_service_scenarios.h
@@ -4,6 +4,9 @@
  * SPDX-License-Identifier: BSD-3-Clause
  */
 
+#ifndef HASH_SERVICE_SCENARIOS_H
+#define HASH_SERVICE_SCENARIOS_H
+
 #include <stddef.h>
 #include <stdint.h>
 #include <service/crypto/client/cpp/crypto_client.h>
@@ -16,17 +19,19 @@
 class hash_service_scenarios
 {
 public:
-    hash_service_scenarios(crypto_client *crypto_client);
-    ~hash_service_scenarios();
+	hash_service_scenarios(crypto_client *crypto_client);
+	~hash_service_scenarios();
 
-    void calculateHash();
-    void hashAndVerify();
-    void hashAbort();
+	void calculateHash();
+	void hashAndVerify();
+	void hashAbort();
 
 private:
 
-    void create_ref_input(size_t size);
+	void create_ref_input(size_t size);
 
-    crypto_client *m_crypto_client;
-    uint8_t *m_ref_input;
+	crypto_client *m_crypto_client;
+	uint8_t *m_ref_input;
 };
+
+#endif /* HASH_SERVICE_SCENARIOS_H */
diff --git a/components/service/crypto/test/service/extension/hash/hash_test_vectors.h b/components/service/crypto/test/service/extension/hash/hash_test_vectors.h
index 71c396a..8728050 100644
--- a/components/service/crypto/test/service/extension/hash/hash_test_vectors.h
+++ b/components/service/crypto/test/service/extension/hash/hash_test_vectors.h
@@ -4,6 +4,9 @@
  * SPDX-License-Identifier: BSD-3-Clause
  */
 
+#ifndef HASH_TEST_VECTORS_H
+#define HASH_TEST_VECTORS_H
+
 #include <cstdint>
 #include <vector>
 
@@ -19,3 +22,5 @@
 	static void sha256_1(std::vector<uint8_t> &hash);
 
 };
+
+#endif /* HASH_TEST_VECTORS_H */
diff --git a/deployments/component-test/component-test.cmake b/deployments/component-test/component-test.cmake
index 681957f..ca894c9 100644
--- a/deployments/component-test/component-test.cmake
+++ b/deployments/component-test/component-test.cmake
@@ -92,6 +92,8 @@
 		"components/service/crypto/test/service/packed-c"
 		"components/service/crypto/test/service/extension/hash"
 		"components/service/crypto/test/service/extension/hash/packed-c"
+		"components/service/crypto/test/service/extension/cipher"
+		"components/service/crypto/test/service/extension/cipher/packed-c"
 		"components/service/crypto/test/protocol"
 		"components/service/secure_storage/include"
 		"components/service/secure_storage/frontend/psa/its"
diff --git a/deployments/ts-service-test/ts-service-test.cmake b/deployments/ts-service-test/ts-service-test.cmake
index 92348ec..07a34ec 100644
--- a/deployments/ts-service-test/ts-service-test.cmake
+++ b/deployments/ts-service-test/ts-service-test.cmake
@@ -38,6 +38,8 @@
 		"components/service/crypto/test/service/packed-c"
 		"components/service/crypto/test/service/extension/hash"
 		"components/service/crypto/test/service/extension/hash/packed-c"
+		"components/service/crypto/test/service/extension/cipher"
+		"components/service/crypto/test/service/extension/cipher/packed-c"
 		"components/service/crypto/client/psa"
 		"components/service/crypto/client/cpp"
 		"components/service/crypto/client/cpp/protocol/protobuf"