Add crypto support for symmetric cipher operations

Adds a sub-provider for symmetric cipher operations.

Signed-off-by: Julian Hall <julian.hall@arm.com>
Change-Id: Ic36195ae6eeb2fb81d06f83755fa400d219febc0
diff --git a/components/service/crypto/provider/extension/cipher/cipher_provider.c b/components/service/crypto/provider/extension/cipher/cipher_provider.c
new file mode 100644
index 0000000..7ed32f5
--- /dev/null
+++ b/components/service/crypto/provider/extension/cipher/cipher_provider.c
@@ -0,0 +1,331 @@
+/*
+ * Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+#include <stdint.h>
+#include <stdlib.h>
+#include <protocols/service/crypto/packed-c/opcodes.h>
+#include <service/crypto/provider/extension/cipher/cipher_provider.h>
+#include <protocols/rpc/common/packed-c/status.h>
+#include <psa/crypto.h>
+
+/* Service request handlers */
+static rpc_status_t cipher_setup_handler(void *context, struct call_req* req);
+static rpc_status_t cipher_generate_iv_handler(void *context, struct call_req* req);
+static rpc_status_t cipher_set_iv_handler(void *context, struct call_req* req);
+static rpc_status_t cipher_update_handler(void *context, struct call_req* req);
+static rpc_status_t cipher_finish_handler(void *context, struct call_req* req);
+static rpc_status_t cipher_abort_handler(void *context, struct call_req* req);
+
+/* Handler mapping table for service */
+static const struct service_handler handler_table[] = {
+	{TS_CRYPTO_OPCODE_CIPHER_ENCRYPT_SETUP,   	cipher_setup_handler},
+	{TS_CRYPTO_OPCODE_CIPHER_DECRYPT_SETUP,   	cipher_setup_handler},
+	{TS_CRYPTO_OPCODE_CIPHER_GENERATE_IV,   	cipher_generate_iv_handler},
+	{TS_CRYPTO_OPCODE_CIPHER_SET_IV,   			cipher_set_iv_handler},
+	{TS_CRYPTO_OPCODE_CIPHER_UPDATE,          	cipher_update_handler},
+	{TS_CRYPTO_OPCODE_CIPHER_FINISH,          	cipher_finish_handler},
+	{TS_CRYPTO_OPCODE_CIPHER_ABORT,          	cipher_abort_handler}
+};
+
+void cipher_provider_init(struct cipher_provider *context)
+{
+	crypto_context_pool_init(&context->context_pool);
+
+	for (size_t encoding = 0; encoding < TS_RPC_ENCODING_LIMIT; ++encoding)
+		context->serializers[encoding] = NULL;
+
+	service_provider_init(&context->base_provider, context,
+		handler_table, sizeof(handler_table)/sizeof(struct service_handler));
+}
+
+void cipher_provider_deinit(struct cipher_provider *context)
+{
+	crypto_context_pool_deinit(&context->context_pool);
+}
+
+void cipher_provider_register_serializer(struct cipher_provider *context,
+	unsigned int encoding, const struct cipher_provider_serializer *serializer)
+{
+	if (encoding < TS_RPC_ENCODING_LIMIT)
+		context->serializers[encoding] = serializer;
+}
+
+static const struct cipher_provider_serializer* get_serializer(void *context,
+	const struct call_req *req)
+{
+	struct cipher_provider *this_instance = (struct cipher_provider*)context;
+	const struct cipher_provider_serializer* serializer = NULL;
+	unsigned int encoding = call_req_get_encoding(req);
+
+	if (encoding < TS_RPC_ENCODING_LIMIT) serializer = this_instance->serializers[encoding];
+
+	return serializer;
+}
+
+static rpc_status_t cipher_setup_handler(void *context, struct call_req* req)
+{
+	rpc_status_t rpc_status = TS_RPC_ERROR_SERIALIZATION_NOT_SUPPORTED;
+	struct call_param_buf *req_buf = call_req_get_req_buf(req);
+	const struct cipher_provider_serializer *serializer = get_serializer(context, req);
+	struct cipher_provider *this_instance = (struct cipher_provider*)context;
+
+	psa_key_id_t key_id;
+	psa_algorithm_t alg;
+
+	if (serializer)
+		rpc_status = serializer->deserialize_cipher_setup_req(req_buf, &key_id, &alg);
+
+	if (rpc_status == TS_RPC_CALL_ACCEPTED) {
+
+		uint32_t op_handle;
+
+		struct crypto_context *crypto_context =
+			crypto_context_pool_alloc(&this_instance->context_pool,
+				CRYPTO_CONTEXT_OP_ID_CIPHER, call_req_get_caller_id(req),
+				&op_handle);
+
+		if (crypto_context) {
+
+			psa_status_t psa_status;
+
+			crypto_context->op.cipher = psa_cipher_operation_init();
+
+			psa_status = (call_req_get_opcode(req) == TS_CRYPTO_OPCODE_CIPHER_ENCRYPT_SETUP) ?
+				psa_cipher_encrypt_setup(&crypto_context->op.cipher, key_id, alg) :
+				psa_cipher_decrypt_setup(&crypto_context->op.cipher, key_id, alg);
+
+			if (psa_status == PSA_SUCCESS) {
+
+				struct call_param_buf *resp_buf = call_req_get_resp_buf(req);
+				rpc_status = serializer->serialize_cipher_setup_resp(resp_buf, op_handle);
+			}
+
+			if ((psa_status != PSA_SUCCESS) || (rpc_status != TS_RPC_CALL_ACCEPTED)) {
+
+				crypto_context_pool_free(&this_instance->context_pool, crypto_context);
+			}
+
+			call_req_set_opstatus(req, psa_status);
+		}
+		else {
+			/* Failed to allocate crypto context for transaction */
+			rpc_status = TS_RPC_ERROR_RESOURCE_FAILURE;
+		}
+	}
+
+	return rpc_status;
+}
+
+static rpc_status_t cipher_generate_iv_handler(void *context, struct call_req* req)
+{
+	rpc_status_t rpc_status = TS_RPC_ERROR_SERIALIZATION_NOT_SUPPORTED;
+	struct call_param_buf *req_buf = call_req_get_req_buf(req);
+	const struct cipher_provider_serializer *serializer = get_serializer(context, req);
+	struct cipher_provider *this_instance = (struct cipher_provider*)context;
+
+	uint32_t op_handle;
+
+	if (serializer)
+		rpc_status = serializer->deserialize_cipher_generate_iv_req(req_buf, &op_handle);
+
+	if (rpc_status == TS_RPC_CALL_ACCEPTED) {
+
+		psa_status_t psa_status = PSA_ERROR_BAD_STATE;
+
+		struct crypto_context *crypto_context =
+			crypto_context_pool_find(&this_instance->context_pool,
+				CRYPTO_CONTEXT_OP_ID_CIPHER, call_req_get_caller_id(req),
+				op_handle);
+
+		if (crypto_context) {
+
+			size_t iv_len;
+			uint8_t iv[PSA_CIPHER_IV_MAX_SIZE];
+
+			psa_status = psa_cipher_generate_iv(&crypto_context->op.cipher, iv, sizeof(iv), &iv_len);
+
+			if (psa_status == PSA_SUCCESS) {
+
+				struct call_param_buf *resp_buf = call_req_get_resp_buf(req);
+				rpc_status = serializer->serialize_cipher_generate_iv_resp(resp_buf, iv, iv_len);
+			}
+		}
+
+		call_req_set_opstatus(req, psa_status);
+	}
+
+	return rpc_status;
+}
+
+static rpc_status_t cipher_set_iv_handler(void *context, struct call_req* req)
+{
+	rpc_status_t rpc_status = TS_RPC_ERROR_SERIALIZATION_NOT_SUPPORTED;
+	struct call_param_buf *req_buf = call_req_get_req_buf(req);
+	const struct cipher_provider_serializer *serializer = get_serializer(context, req);
+	struct cipher_provider *this_instance = (struct cipher_provider*)context;
+
+	uint32_t op_handle;
+	const uint8_t *iv;
+	size_t iv_len;
+
+	if (serializer)
+		rpc_status = serializer->deserialize_cipher_set_iv_req(req_buf, &op_handle,
+			&iv, &iv_len);
+
+	if (rpc_status == TS_RPC_CALL_ACCEPTED) {
+
+		psa_status_t psa_status = PSA_ERROR_BAD_STATE;
+
+		struct crypto_context *crypto_context =
+			crypto_context_pool_find(&this_instance->context_pool,
+				CRYPTO_CONTEXT_OP_ID_CIPHER, call_req_get_caller_id(req),
+				op_handle);
+
+		if (crypto_context) {
+
+			psa_status = psa_cipher_set_iv(&crypto_context->op.cipher, iv, iv_len);
+		}
+
+		call_req_set_opstatus(req, psa_status);
+	}
+
+	return rpc_status;
+}
+
+static rpc_status_t cipher_update_handler(void *context, struct call_req* req)
+{
+	rpc_status_t rpc_status = TS_RPC_ERROR_SERIALIZATION_NOT_SUPPORTED;
+	struct call_param_buf *req_buf = call_req_get_req_buf(req);
+	const struct cipher_provider_serializer *serializer = get_serializer(context, req);
+	struct cipher_provider *this_instance = (struct cipher_provider*)context;
+
+	uint32_t op_handle;
+	const uint8_t *input;
+	size_t input_len;
+
+	if (serializer)
+		rpc_status = serializer->deserialize_cipher_update_req(req_buf, &op_handle,
+			&input, &input_len);
+
+	if (rpc_status == TS_RPC_CALL_ACCEPTED) {
+
+		psa_status_t psa_status = PSA_ERROR_BAD_STATE;
+
+		struct crypto_context *crypto_context =
+			crypto_context_pool_find(&this_instance->context_pool,
+				CRYPTO_CONTEXT_OP_ID_CIPHER, call_req_get_caller_id(req),
+				op_handle);
+
+		if (crypto_context) {
+
+			size_t output_len = 0;
+			size_t output_size = PSA_CIPHER_ENCRYPT_OUTPUT_MAX_SIZE(input_len);
+			uint8_t *output = malloc(output_size);
+
+			if (output) {
+
+				psa_status = psa_cipher_update(&crypto_context->op.cipher,
+					input, input_len,
+					output, output_size, &output_len);
+
+				if (psa_status == PSA_SUCCESS) {
+
+					struct call_param_buf *resp_buf = call_req_get_resp_buf(req);
+					rpc_status = serializer->serialize_cipher_update_resp(resp_buf,
+						output, output_len);
+				}
+
+				free(output);
+			}
+			else {
+
+				psa_status = PSA_ERROR_INSUFFICIENT_MEMORY;
+			}
+		}
+
+		call_req_set_opstatus(req, psa_status);
+	}
+
+	return rpc_status;
+}
+
+static rpc_status_t cipher_finish_handler(void *context, struct call_req* req)
+{
+	rpc_status_t rpc_status = TS_RPC_ERROR_SERIALIZATION_NOT_SUPPORTED;
+	struct call_param_buf *req_buf = call_req_get_req_buf(req);
+	const struct cipher_provider_serializer *serializer = get_serializer(context, req);
+	struct cipher_provider *this_instance = (struct cipher_provider*)context;
+
+	uint32_t op_handle;
+
+	if (serializer)
+		rpc_status = serializer->deserialize_cipher_finish_req(req_buf, &op_handle);
+
+	if (rpc_status == TS_RPC_CALL_ACCEPTED) {
+
+		psa_status_t psa_status = PSA_ERROR_BAD_STATE;
+
+		struct crypto_context *crypto_context =
+			crypto_context_pool_find(&this_instance->context_pool,
+				CRYPTO_CONTEXT_OP_ID_CIPHER, call_req_get_caller_id(req),
+				op_handle);
+
+		if (crypto_context) {
+
+			size_t output_len;
+			uint8_t output[PSA_CIPHER_FINISH_OUTPUT_MAX_SIZE];
+
+			psa_status = psa_cipher_finish(&crypto_context->op.cipher, output, sizeof(output), &output_len);
+
+			if (psa_status == PSA_SUCCESS) {
+
+				struct call_param_buf *resp_buf = call_req_get_resp_buf(req);
+				rpc_status = serializer->serialize_cipher_finish_resp(resp_buf, output, output_len);
+			}
+
+			crypto_context_pool_free(&this_instance->context_pool, crypto_context);
+		}
+
+		call_req_set_opstatus(req, psa_status);
+	}
+
+	return rpc_status;
+}
+
+static rpc_status_t cipher_abort_handler(void *context, struct call_req* req)
+{
+	rpc_status_t rpc_status = TS_RPC_ERROR_SERIALIZATION_NOT_SUPPORTED;
+	struct call_param_buf *req_buf = call_req_get_req_buf(req);
+	const struct cipher_provider_serializer *serializer = get_serializer(context, req);
+	struct cipher_provider *this_instance = (struct cipher_provider*)context;
+
+	uint32_t op_handle;
+
+	if (serializer)
+		rpc_status = serializer->deserialize_cipher_abort_req(req_buf, &op_handle);
+
+	if (rpc_status == TS_RPC_CALL_ACCEPTED) {
+
+		/* Return success if operation is no longer active and
+		 * doesn't need aborting.
+		 */
+		psa_status_t psa_status = PSA_SUCCESS;
+
+		struct crypto_context *crypto_context =
+			crypto_context_pool_find(&this_instance->context_pool,
+				CRYPTO_CONTEXT_OP_ID_CIPHER, call_req_get_caller_id(req),
+				op_handle);
+
+		if (crypto_context) {
+
+			psa_status = psa_cipher_abort(&crypto_context->op.cipher);
+			crypto_context_pool_free(&this_instance->context_pool, crypto_context);
+		}
+
+		call_req_set_opstatus(req, psa_status);
+	}
+
+	return rpc_status;
+}
diff --git a/components/service/crypto/provider/extension/cipher/cipher_provider.h b/components/service/crypto/provider/extension/cipher/cipher_provider.h
new file mode 100644
index 0000000..939c333
--- /dev/null
+++ b/components/service/crypto/provider/extension/cipher/cipher_provider.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef CIPHER_PROVIDER_H
+#define CIPHER_PROVIDER_H
+
+#include <rpc/common/endpoint/rpc_interface.h>
+#include <service/common/provider/service_provider.h>
+#include <service/crypto/provider/extension/cipher/serializer/cipher_provider_serializer.h>
+#include <service/crypto/provider/crypto_context_pool.h>
+#include <protocols/rpc/common/packed-c/encoding.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * A service provider that can be used to add symmetric cipher operations to the base
+ * crypto provider.
+ */
+struct cipher_provider
+{
+	struct service_provider base_provider;
+	struct crypto_context_pool context_pool;
+	const struct cipher_provider_serializer *serializers[TS_RPC_ENCODING_LIMIT];
+};
+
+/*
+ * Initializes an instance of the cipher service provider.
+ */
+void cipher_provider_init(struct cipher_provider *context);
+
+/*
+ * When operation of the provider is no longer required, this function
+ * frees any resource used by the previously initialized provider instance.
+ */
+void cipher_provider_deinit(struct cipher_provider *context);
+
+/*
+ * Register a serializer for supportng a particular parameter encoding.
+ */
+void cipher_provider_register_serializer(struct cipher_provider *context,
+	unsigned int encoding, const struct cipher_provider_serializer *serializer);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* CIPHER_PROVIDER_H */
diff --git a/components/service/crypto/provider/extension/cipher/component.cmake b/components/service/crypto/provider/extension/cipher/component.cmake
new file mode 100644
index 0000000..b412184
--- /dev/null
+++ b/components/service/crypto/provider/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_provider.c"
+	)
diff --git a/components/service/crypto/provider/extension/cipher/serializer/cipher_provider_serializer.h b/components/service/crypto/provider/extension/cipher/serializer/cipher_provider_serializer.h
new file mode 100644
index 0000000..ccc9b96
--- /dev/null
+++ b/components/service/crypto/provider/extension/cipher/serializer/cipher_provider_serializer.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef CIPHER_PROVIDER_SERIALIZER_H
+#define CIPHER_PROVIDER_SERIALIZER_H
+
+#include <stddef.h>
+#include <stdint.h>
+#include <psa/crypto.h>
+#include <rpc/common/endpoint/rpc_interface.h>
+
+/* Provides a common interface for parameter serialization operations
+ * for the cipher service provider.
+ */
+struct cipher_provider_serializer {
+
+	/* Operation: cipher_setup */
+	rpc_status_t (*deserialize_cipher_setup_req)(const struct call_param_buf *req_buf,
+		psa_key_id_t *id,
+		psa_algorithm_t *alg);
+
+	rpc_status_t (*serialize_cipher_setup_resp)(struct call_param_buf *resp_buf,
+		uint32_t op_handle);
+
+	/* Operation: cipher_generate_iv */
+	rpc_status_t (*deserialize_cipher_generate_iv_req)(const struct call_param_buf *req_buf,
+		uint32_t *op_handle);
+
+	rpc_status_t (*serialize_cipher_generate_iv_resp)(struct call_param_buf *resp_buf,
+		const uint8_t *iv, size_t iv_len);
+
+	/* Operation: cipher_set_iv */
+	rpc_status_t (*deserialize_cipher_set_iv_req)(const struct call_param_buf *req_buf,
+		uint32_t *op_handle,
+		const uint8_t **iv, size_t *iv_len);
+
+	/* Operation: cipher_update */
+	rpc_status_t (*deserialize_cipher_update_req)(const struct call_param_buf *req_buf,
+		uint32_t *op_handle,
+		const uint8_t **data, size_t *data_len);
+
+	rpc_status_t (*serialize_cipher_update_resp)(struct call_param_buf *resp_buf,
+		const uint8_t *data, size_t data_len);
+
+	/* Operation: cipher_finish */
+	rpc_status_t (*deserialize_cipher_finish_req)(const struct call_param_buf *req_buf,
+		uint32_t *op_handle);
+
+	rpc_status_t (*serialize_cipher_finish_resp)(struct call_param_buf *resp_buf,
+		const uint8_t *data, size_t data_len);
+
+	/* Operation: cipher_abort */
+	rpc_status_t (*deserialize_cipher_abort_req)(const struct call_param_buf *req_buf,
+		uint32_t *op_handle);
+};
+
+#endif /* CIPHER_PROVIDER_SERIALIZER_H */
diff --git a/components/service/crypto/provider/extension/cipher/serializer/packed-c/component.cmake b/components/service/crypto/provider/extension/cipher/serializer/packed-c/component.cmake
new file mode 100644
index 0000000..1ccd990
--- /dev/null
+++ b/components/service/crypto/provider/extension/cipher/serializer/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}/packedc_cipher_provider_serializer.c"
+	)
diff --git a/components/service/crypto/provider/extension/cipher/serializer/packed-c/packedc_cipher_provider_serializer.c b/components/service/crypto/provider/extension/cipher/serializer/packed-c/packedc_cipher_provider_serializer.c
new file mode 100644
index 0000000..1acb165
--- /dev/null
+++ b/components/service/crypto/provider/extension/cipher/serializer/packed-c/packedc_cipher_provider_serializer.c
@@ -0,0 +1,265 @@
+/*
+ * Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+#include <string.h>
+#include <stdlib.h>
+#include <common/tlv/tlv.h>
+#include <psa/crypto.h>
+#include <protocols/service/crypto/packed-c/cipher.h>
+#include "packedc_cipher_provider_serializer.h"
+
+/* Operation: cipher_setup */
+static rpc_status_t deserialize_cipher_setup_req(const struct call_param_buf *req_buf,
+	psa_key_id_t *id,
+	psa_algorithm_t *alg)
+{
+	rpc_status_t rpc_status = TS_RPC_ERROR_INVALID_REQ_BODY;
+	struct ts_crypto_cipher_setup_in recv_msg;
+	size_t expected_fixed_len = sizeof(struct ts_crypto_cipher_setup_in);
+
+	if (expected_fixed_len <= req_buf->data_len) {
+
+		memcpy(&recv_msg, req_buf->data, expected_fixed_len);
+		*id = recv_msg.key_id;
+		*alg = recv_msg.alg;
+		rpc_status = TS_RPC_CALL_ACCEPTED;
+	}
+
+	return rpc_status;
+}
+
+static rpc_status_t serialize_cipher_setup_resp(struct call_param_buf *resp_buf,
+	uint32_t op_handle)
+{
+	rpc_status_t rpc_status = TS_RPC_ERROR_INTERNAL;
+	struct ts_crypto_cipher_setup_out resp_msg;
+	size_t fixed_len = sizeof(struct ts_crypto_cipher_setup_out);
+
+	resp_msg.op_handle = op_handle;
+
+	if (fixed_len <= resp_buf->size) {
+
+		memcpy(resp_buf->data, &resp_msg, fixed_len);
+		resp_buf->data_len = fixed_len;
+		rpc_status = TS_RPC_CALL_ACCEPTED;
+	}
+
+	return rpc_status;
+}
+
+/* Operation: cipher_generate_iv */
+static rpc_status_t deserialize_cipher_generate_iv_req(const struct call_param_buf *req_buf,
+	uint32_t *op_handle)
+{
+	rpc_status_t rpc_status = TS_RPC_ERROR_INVALID_REQ_BODY;
+	struct ts_crypto_cipher_generate_iv_in recv_msg;
+	size_t expected_fixed_len = sizeof(struct ts_crypto_cipher_generate_iv_in);
+
+	if (expected_fixed_len <= req_buf->data_len) {
+
+		memcpy(&recv_msg, req_buf->data, expected_fixed_len);
+		*op_handle = recv_msg.op_handle;
+		rpc_status = TS_RPC_CALL_ACCEPTED;
+	}
+
+	return rpc_status;
+}
+
+static rpc_status_t serialize_cipher_generate_iv_resp(struct call_param_buf *resp_buf,
+	const uint8_t *iv, size_t iv_len)
+{
+	rpc_status_t rpc_status = TS_RPC_ERROR_INTERNAL;
+	struct tlv_iterator resp_iter;
+
+	struct tlv_record out_record;
+	out_record.tag = TS_CRYPTO_CIPHER_GENERATE_IV_OUT_TAG_IV;
+	out_record.length = iv_len;
+	out_record.value = iv;
+
+	tlv_iterator_begin(&resp_iter, resp_buf->data, resp_buf->size);
+
+	if (tlv_encode(&resp_iter, &out_record)) {
+
+		resp_buf->data_len = tlv_required_space(iv_len);
+		rpc_status = TS_RPC_CALL_ACCEPTED;
+	}
+
+	return rpc_status;
+}
+
+/* Operation: cipher_set_iv */
+static rpc_status_t deserialize_cipher_set_iv_req(const struct call_param_buf *req_buf,
+	uint32_t *op_handle,
+	const uint8_t **iv, size_t *iv_len)
+{
+	rpc_status_t rpc_status = TS_RPC_ERROR_INVALID_REQ_BODY;
+	struct ts_crypto_cipher_set_iv_in recv_msg;
+	size_t expected_fixed_len = sizeof(struct ts_crypto_cipher_set_iv_in);
+
+	if (expected_fixed_len <= req_buf->data_len) {
+
+		struct tlv_const_iterator req_iter;
+		struct tlv_record decoded_record;
+
+		rpc_status = TS_RPC_CALL_ACCEPTED;
+
+		memcpy(&recv_msg, req_buf->data, expected_fixed_len);
+
+		*op_handle = recv_msg.op_handle;
+
+		tlv_const_iterator_begin(&req_iter,
+			(uint8_t*)req_buf->data + expected_fixed_len,
+			req_buf->data_len - expected_fixed_len);
+
+		if (tlv_find_decode(&req_iter, TS_CRYPTO_CIPHER_SET_IV_IN_TAG_IV, &decoded_record)) {
+
+			*iv = decoded_record.value;
+			*iv_len = decoded_record.length;
+		}
+		else {
+			/* Default to a zero length data */
+			*iv_len = 0;
+		}
+	}
+
+	return rpc_status;
+}
+
+/* Operation: cipher_update */
+static rpc_status_t deserialize_cipher_update_req(const struct call_param_buf *req_buf,
+	uint32_t *op_handle,
+	const uint8_t **data, size_t *data_len)
+{
+	rpc_status_t rpc_status = TS_RPC_ERROR_INVALID_REQ_BODY;
+	struct ts_crypto_cipher_update_in recv_msg;
+	size_t expected_fixed_len = sizeof(struct ts_crypto_cipher_update_in);
+
+	if (expected_fixed_len <= req_buf->data_len) {
+
+		struct tlv_const_iterator req_iter;
+		struct tlv_record decoded_record;
+
+		rpc_status = TS_RPC_CALL_ACCEPTED;
+
+		memcpy(&recv_msg, req_buf->data, expected_fixed_len);
+
+		*op_handle = recv_msg.op_handle;
+
+		tlv_const_iterator_begin(&req_iter,
+			(uint8_t*)req_buf->data + expected_fixed_len,
+			req_buf->data_len - expected_fixed_len);
+
+		if (tlv_find_decode(&req_iter, TS_CRYPTO_CIPHER_UPDATE_IN_TAG_DATA, &decoded_record)) {
+
+			*data = decoded_record.value;
+			*data_len = decoded_record.length;
+		}
+		else {
+			/* Default to a zero length data */
+			*data_len = 0;
+		}
+	}
+
+	return rpc_status;
+}
+
+static rpc_status_t serialize_cipher_update_resp(struct call_param_buf *resp_buf,
+		const uint8_t *data, size_t data_len)
+{
+	rpc_status_t rpc_status = TS_RPC_ERROR_INTERNAL;
+	struct tlv_iterator resp_iter;
+
+	struct tlv_record out_record;
+	out_record.tag = TS_CRYPTO_CIPHER_UPDATE_OUT_TAG_DATA;
+	out_record.length = data_len;
+	out_record.value = data;
+
+	tlv_iterator_begin(&resp_iter, resp_buf->data, resp_buf->size);
+
+	if (tlv_encode(&resp_iter, &out_record)) {
+
+		resp_buf->data_len = tlv_required_space(data_len);
+		rpc_status = TS_RPC_CALL_ACCEPTED;
+	}
+
+	return rpc_status;
+}
+
+/* Operation: cipher_finish */
+static rpc_status_t deserialize_cipher_finish_req(const struct call_param_buf *req_buf,
+	uint32_t *op_handle)
+{
+	rpc_status_t rpc_status = TS_RPC_ERROR_INVALID_REQ_BODY;
+	struct ts_crypto_cipher_finish_in recv_msg;
+	size_t expected_fixed_len = sizeof(struct ts_crypto_cipher_finish_in);
+
+	if (expected_fixed_len <= req_buf->data_len) {
+
+		memcpy(&recv_msg, req_buf->data, expected_fixed_len);
+		*op_handle = recv_msg.op_handle;
+		rpc_status = TS_RPC_CALL_ACCEPTED;
+	}
+
+	return rpc_status;
+}
+
+static rpc_status_t serialize_cipher_finish_resp(struct call_param_buf *resp_buf,
+	const uint8_t *data, size_t data_len)
+{
+	rpc_status_t rpc_status = TS_RPC_ERROR_INTERNAL;
+	struct tlv_iterator resp_iter;
+
+	struct tlv_record out_record;
+	out_record.tag = TS_CRYPTO_CIPHER_FINISH_OUT_TAG_DATA;
+	out_record.length = data_len;
+	out_record.value = data;
+
+	tlv_iterator_begin(&resp_iter, resp_buf->data, resp_buf->size);
+
+	if (tlv_encode(&resp_iter, &out_record)) {
+
+		resp_buf->data_len = tlv_required_space(data_len);
+		rpc_status = TS_RPC_CALL_ACCEPTED;
+	}
+
+	return rpc_status;
+}
+
+/* Operation: cipher_abort */
+static rpc_status_t deserialize_cipher_abort_req(const struct call_param_buf *req_buf,
+	uint32_t *op_handle)
+{
+	rpc_status_t rpc_status = TS_RPC_ERROR_INVALID_REQ_BODY;
+	struct ts_crypto_cipher_abort_in recv_msg;
+	size_t expected_fixed_len = sizeof(struct ts_crypto_cipher_abort_in);
+
+	if (expected_fixed_len <= req_buf->data_len) {
+
+		memcpy(&recv_msg, req_buf->data, expected_fixed_len);
+		*op_handle = recv_msg.op_handle;
+		rpc_status = TS_RPC_CALL_ACCEPTED;
+	}
+
+	return rpc_status;
+}
+
+/* Singleton method to provide access to the serializer instance */
+const struct cipher_provider_serializer *packedc_cipher_provider_serializer_instance(void)
+{
+	static const struct cipher_provider_serializer instance = {
+		deserialize_cipher_setup_req,
+		serialize_cipher_setup_resp,
+		deserialize_cipher_generate_iv_req,
+		serialize_cipher_generate_iv_resp,
+		deserialize_cipher_set_iv_req,
+		deserialize_cipher_update_req,
+		serialize_cipher_update_resp,
+		deserialize_cipher_finish_req,
+		serialize_cipher_finish_resp,
+		deserialize_cipher_abort_req
+	};
+
+	return &instance;
+}
diff --git a/components/service/crypto/provider/extension/cipher/serializer/packed-c/packedc_cipher_provider_serializer.h b/components/service/crypto/provider/extension/cipher/serializer/packed-c/packedc_cipher_provider_serializer.h
new file mode 100644
index 0000000..98b8f17
--- /dev/null
+++ b/components/service/crypto/provider/extension/cipher/serializer/packed-c/packedc_cipher_provider_serializer.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef PACKEDC_CIPHER_PROVIDER_SERIALIZER_H
+#define PACKEDC_CIPHER_PROVIDER_SERIALIZER_H
+
+#include <service/crypto/provider/extension/cipher/serializer/cipher_provider_serializer.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Singleton method to provide access to the packed-c serializer
+ * for the cipher service provider.
+ */
+const struct cipher_provider_serializer *packedc_cipher_provider_serializer_instance(void);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* PACKEDC_CIPHER_PROVIDER_SERIALIZER_H */