// SPDX-License-Identifier: BSD-2-Clause
/*
 * Copyright (c) 2014, STMicroelectronics International N.V.
 * All rights reserved.
 * Copyright (c) 2022, Linaro Limited.
 */

#include <assert.h>
#include <cryp_taf.h>
#include <memtag.h>
#include <tee_internal_api.h>
#include <trace.h>
#include <user_ta_header.h>

#include "handle.h"

#define ASSERT_PARAM_TYPE(pt)                    \
do {                                             \
	if ((pt) != param_type)                  \
		return TEE_ERROR_BAD_PARAMETERS; \
} while (0)

static struct handle_db op_db = HANDLE_DB_INITIALIZER;
static struct handle_db obj_db = HANDLE_DB_INITIALIZER;

static uint32_t op_handle_get(TEE_OperationHandle h)
{
	int rc = handle_get(&op_db, h);

	if (rc < 0) {
		EMSG("Failed to allocate handle");
		TEE_Panic(TEE_ERROR_OUT_OF_MEMORY);
	}

	/* +1 since 0 (TEE_HANDLE_NULL) is invalid */
	return rc + 1;
}

static TEE_OperationHandle op_handle_put(uint32_t val)
{
	void *h = NULL;

	if (val) {
		h = handle_put(&op_db, val - 1);
		if (!h) {
			EMSG("Invalid handle 0x%"PRIx32, val);
			TEE_Panic(TEE_ERROR_BAD_PARAMETERS);
		}
	}

	return h;
}

static TEE_OperationHandle op_handle_lookup(uint32_t val)
{
	void *h = NULL;

	if (val) {
		h = handle_lookup(&op_db, val - 1);
		if (!h) {
			EMSG("Invalid handle 0x%"PRIx32, val);
			TEE_Panic(TEE_ERROR_BAD_PARAMETERS);
		}
	}

	return h;
}

static uint32_t obj_handle_get(TEE_ObjectHandle h)
{
	int rc = handle_get(&obj_db, h);

	if (rc < 0) {
		EMSG("Failed to allocate handle");
		TEE_Panic(TEE_ERROR_OUT_OF_MEMORY);
	}

	/* +1 since 0 (TEE_HANDLE_NULL) is invalid */
	return rc + 1;
}

static TEE_ObjectHandle obj_handle_put(uint32_t val)
{
	void *h = NULL;

	if (val) {
		h = handle_put(&obj_db, val - 1);
		if (!h) {
			EMSG("Invalid handle 0x%"PRIx32, val);
			TEE_Panic(TEE_ERROR_BAD_PARAMETERS);
		}
	}

	return h;
}

static TEE_ObjectHandle obj_handle_lookup(uint32_t val)
{
	void *h = NULL;

	if (val) {
		h = handle_lookup(&obj_db, val - 1);
		if (!h) {
			EMSG("Invalid handle 0x%"PRIx32, val);
			TEE_Panic(TEE_ERROR_BAD_PARAMETERS);
		}
	}

	return h;
}

TEE_Result ta_entry_allocate_operation(uint32_t param_type, TEE_Param params[4])
{
	TEE_Result res = TEE_ERROR_GENERIC;
	TEE_OperationHandle op = TEE_HANDLE_NULL;

	ASSERT_PARAM_TYPE(TEE_PARAM_TYPES
			  (TEE_PARAM_TYPE_VALUE_INOUT,
			   TEE_PARAM_TYPE_VALUE_INPUT, TEE_PARAM_TYPE_NONE,
			   TEE_PARAM_TYPE_NONE));

	res = TEE_AllocateOperation(&op,
				    params[0].value.b, params[1].value.a,
				    params[1].value.b);
	params[0].value.a = op_handle_get(op);
	return res;
}

TEE_Result ta_entry_free_operation(uint32_t param_type, TEE_Param params[4])
{
	TEE_OperationHandle op = op_handle_put(params[0].value.a);

	ASSERT_PARAM_TYPE(TEE_PARAM_TYPES
			  (TEE_PARAM_TYPE_VALUE_INPUT, TEE_PARAM_TYPE_NONE,
			   TEE_PARAM_TYPE_NONE, TEE_PARAM_TYPE_NONE));

	TEE_FreeOperation(op);
	return TEE_SUCCESS;
}

TEE_Result ta_entry_get_operation_info(uint32_t param_type, TEE_Param params[4])
{
	TEE_OperationHandle op = op_handle_lookup(params[0].value.a);

	ASSERT_PARAM_TYPE(TEE_PARAM_TYPES
			  (TEE_PARAM_TYPE_VALUE_INPUT,
			   TEE_PARAM_TYPE_MEMREF_OUTPUT, TEE_PARAM_TYPE_NONE,
			   TEE_PARAM_TYPE_NONE));

	if (params[1].memref.size < sizeof(TEE_OperationInfo))
		return TEE_ERROR_SHORT_BUFFER;
	params[1].memref.size = sizeof(TEE_OperationInfo);

	TEE_GetOperationInfo(op, params[1].memref.buffer);
	return TEE_SUCCESS;
}

TEE_Result ta_entry_reset_operation(uint32_t param_type, TEE_Param params[4])
{
	TEE_OperationHandle op = op_handle_lookup(params[0].value.a);

	ASSERT_PARAM_TYPE(TEE_PARAM_TYPES
			  (TEE_PARAM_TYPE_VALUE_INPUT, TEE_PARAM_TYPE_NONE,
			   TEE_PARAM_TYPE_NONE, TEE_PARAM_TYPE_NONE));

	TEE_ResetOperation(op);
	return TEE_SUCCESS;
}

TEE_Result ta_entry_set_operation_key(uint32_t param_type, TEE_Param params[4])
{
	TEE_OperationHandle op = op_handle_lookup(params[0].value.a);
	TEE_ObjectHandle key = obj_handle_lookup(params[0].value.b);

	ASSERT_PARAM_TYPE(TEE_PARAM_TYPES
			  (TEE_PARAM_TYPE_VALUE_INPUT, TEE_PARAM_TYPE_NONE,
			   TEE_PARAM_TYPE_NONE, TEE_PARAM_TYPE_NONE));

	return TEE_SetOperationKey(op, key);
}

TEE_Result ta_entry_set_operation_key2(uint32_t param_type, TEE_Param params[4])
{
	TEE_OperationHandle op = op_handle_lookup(params[0].value.a);
	TEE_ObjectHandle key1 = obj_handle_lookup(params[0].value.b);
	TEE_ObjectHandle key2 = obj_handle_lookup(params[1].value.a);

	ASSERT_PARAM_TYPE(TEE_PARAM_TYPES
			  (TEE_PARAM_TYPE_VALUE_INPUT,
			   TEE_PARAM_TYPE_VALUE_INPUT, TEE_PARAM_TYPE_NONE,
			   TEE_PARAM_TYPE_NONE));

	return TEE_SetOperationKey2(op, key1, key2);
}

TEE_Result ta_entry_copy_operation(uint32_t param_type, TEE_Param params[4])
{
	TEE_OperationHandle dst = op_handle_lookup(params[0].value.a);
	TEE_OperationHandle src = op_handle_lookup(params[0].value.b);

	ASSERT_PARAM_TYPE(TEE_PARAM_TYPES
			  (TEE_PARAM_TYPE_VALUE_INPUT, TEE_PARAM_TYPE_NONE,
			   TEE_PARAM_TYPE_NONE, TEE_PARAM_TYPE_NONE));


	TEE_CopyOperation(dst, src);
	return TEE_SUCCESS;
}

TEE_Result ta_entry_digest_update(uint32_t param_type, TEE_Param params[4])
{
	TEE_OperationHandle op = op_handle_lookup(params[0].value.a);

	ASSERT_PARAM_TYPE(TEE_PARAM_TYPES
			  (TEE_PARAM_TYPE_VALUE_INPUT,
			   TEE_PARAM_TYPE_MEMREF_INPUT, TEE_PARAM_TYPE_NONE,
			   TEE_PARAM_TYPE_NONE));

	TEE_DigestUpdate(op, params[1].memref.buffer, params[1].memref.size);
	return TEE_SUCCESS;
}

TEE_Result ta_entry_digest_do_final(uint32_t param_type, TEE_Param params[4])
{
	TEE_OperationHandle op = op_handle_lookup(params[0].value.a);

	ASSERT_PARAM_TYPE(TEE_PARAM_TYPES
			  (TEE_PARAM_TYPE_VALUE_INPUT,
			   TEE_PARAM_TYPE_MEMREF_INPUT,
			   TEE_PARAM_TYPE_MEMREF_OUTPUT, TEE_PARAM_TYPE_NONE));

	return TEE_DigestDoFinal(op,
			params[1].memref.buffer, params[1].memref.size,
			params[2].memref.buffer, &params[2].memref.size);
}

TEE_Result ta_entry_digest_extract(uint32_t param_type, TEE_Param params[4])
{
	TEE_OperationHandle op = op_handle_lookup(params[0].value.a);

	ASSERT_PARAM_TYPE(TEE_PARAM_TYPES(TEE_PARAM_TYPE_VALUE_INPUT,
					  TEE_PARAM_TYPE_MEMREF_OUTPUT,
					  TEE_PARAM_TYPE_NONE,
					  TEE_PARAM_TYPE_NONE));

	return TEE_DigestExtract(op, params[1].memref.buffer,
				 &params[1].memref.size);
}

TEE_Result ta_entry_cipher_init(uint32_t param_type, TEE_Param params[4])
{
	TEE_OperationHandle op = op_handle_lookup(params[0].value.a);
	void *buffer = NULL;
	size_t size = 0;

	if (param_type == TEE_PARAM_TYPES(TEE_PARAM_TYPE_VALUE_INPUT,
					  TEE_PARAM_TYPE_NONE,
					  TEE_PARAM_TYPE_NONE,
					  TEE_PARAM_TYPE_NONE)) {
		buffer = NULL;
		size = 0;
	} else if (param_type == TEE_PARAM_TYPES(TEE_PARAM_TYPE_VALUE_INPUT,
						 TEE_PARAM_TYPE_MEMREF_INPUT,
						 TEE_PARAM_TYPE_NONE,
						 TEE_PARAM_TYPE_NONE)) {
		buffer = params[1].memref.buffer;
		size = params[1].memref.size;
	} else
		return TEE_ERROR_BAD_PARAMETERS;

	TEE_CipherInit(op, buffer, size);
	return TEE_SUCCESS;
}

TEE_Result ta_entry_cipher_update(uint32_t param_type, TEE_Param params[4])
{
	TEE_OperationHandle op = op_handle_lookup(params[0].value.a);

	ASSERT_PARAM_TYPE(TEE_PARAM_TYPES
			  (TEE_PARAM_TYPE_VALUE_INPUT,
			   TEE_PARAM_TYPE_MEMREF_INPUT,
			   TEE_PARAM_TYPE_MEMREF_OUTPUT, TEE_PARAM_TYPE_NONE));

	return TEE_CipherUpdate(op,
			params[1].memref.buffer, params[1].memref.size,
			params[2].memref.buffer, &params[2].memref.size);
}

TEE_Result ta_entry_cipher_do_final(uint32_t param_type, TEE_Param params[4])
{
	TEE_OperationHandle op = op_handle_lookup(params[0].value.a);

	ASSERT_PARAM_TYPE(TEE_PARAM_TYPES
			  (TEE_PARAM_TYPE_VALUE_INPUT,
			   TEE_PARAM_TYPE_MEMREF_INPUT,
			   TEE_PARAM_TYPE_MEMREF_OUTPUT, TEE_PARAM_TYPE_NONE));

	return TEE_CipherDoFinal(op,
			 params[1].memref.buffer, params[1].memref.size,
			 params[2].memref.buffer, &params[2].memref.size);
}

TEE_Result ta_entry_mac_init(uint32_t param_type, TEE_Param params[4])
{
	TEE_OperationHandle op = op_handle_lookup(params[0].value.a);
	void *buffer = NULL;
	size_t size = 0;

	if (param_type == TEE_PARAM_TYPES(TEE_PARAM_TYPE_VALUE_INPUT,
					  TEE_PARAM_TYPE_NONE,
					  TEE_PARAM_TYPE_NONE,
					  TEE_PARAM_TYPE_NONE)) {
		buffer = NULL;
		size = 0;
	} else if (param_type == TEE_PARAM_TYPES(TEE_PARAM_TYPE_VALUE_INPUT,
						 TEE_PARAM_TYPE_MEMREF_INPUT,
						 TEE_PARAM_TYPE_NONE,
						 TEE_PARAM_TYPE_NONE)) {
		buffer = params[1].memref.buffer;
		size = params[1].memref.size;
	} else
		return TEE_ERROR_BAD_PARAMETERS;

	TEE_MACInit(op, buffer, size);
	return TEE_SUCCESS;
}

TEE_Result ta_entry_mac_update(uint32_t param_type, TEE_Param params[4])
{
	TEE_OperationHandle op = op_handle_lookup(params[0].value.a);

	ASSERT_PARAM_TYPE(TEE_PARAM_TYPES
			  (TEE_PARAM_TYPE_VALUE_INPUT,
			   TEE_PARAM_TYPE_MEMREF_INPUT, TEE_PARAM_TYPE_NONE,
			   TEE_PARAM_TYPE_NONE));

	TEE_MACUpdate(op, params[1].memref.buffer, params[1].memref.size);
	return TEE_SUCCESS;
}

TEE_Result ta_entry_mac_final_compute(uint32_t param_type, TEE_Param params[4])
{
	TEE_OperationHandle op = op_handle_lookup(params[0].value.a);

	ASSERT_PARAM_TYPE(TEE_PARAM_TYPES
			  (TEE_PARAM_TYPE_VALUE_INPUT,
			   TEE_PARAM_TYPE_MEMREF_INPUT,
			   TEE_PARAM_TYPE_MEMREF_OUTPUT, TEE_PARAM_TYPE_NONE));

	return TEE_MACComputeFinal(op,
			   params[1].memref.buffer, params[1].memref.size,
			   params[2].memref.buffer, &params[2].memref.size);
}

TEE_Result ta_entry_mac_final_compare(uint32_t param_type, TEE_Param params[4])
{
	TEE_OperationHandle op = op_handle_lookup(params[0].value.a);

	ASSERT_PARAM_TYPE(TEE_PARAM_TYPES
			  (TEE_PARAM_TYPE_VALUE_INPUT,
			   TEE_PARAM_TYPE_MEMREF_INPUT,
			   TEE_PARAM_TYPE_MEMREF_INPUT, TEE_PARAM_TYPE_NONE));

	return TEE_MACCompareFinal(op,
			   params[1].memref.buffer, params[1].memref.size,
			   params[2].memref.buffer, params[2].memref.size);
}

TEE_Result ta_entry_allocate_transient_object(uint32_t param_type,
					      TEE_Param params[4])
{
	TEE_Result res = TEE_ERROR_GENERIC;
	TEE_ObjectHandle o = TEE_HANDLE_NULL;

	ASSERT_PARAM_TYPE(TEE_PARAM_TYPES
			  (TEE_PARAM_TYPE_VALUE_INPUT,
			   TEE_PARAM_TYPE_VALUE_OUTPUT, TEE_PARAM_TYPE_NONE,
			   TEE_PARAM_TYPE_NONE));

	res = TEE_AllocateTransientObject(params[0].value.a, params[0].value.b,
					  &o);
	if (res == TEE_SUCCESS)
		params[1].value.a = obj_handle_get(o);
	return res;
}

TEE_Result ta_entry_free_transient_object(uint32_t param_type,
					  TEE_Param params[4])
{
	TEE_ObjectHandle o = obj_handle_put(params[0].value.a);

	ASSERT_PARAM_TYPE(TEE_PARAM_TYPES
			  (TEE_PARAM_TYPE_VALUE_INPUT, TEE_PARAM_TYPE_NONE,
			   TEE_PARAM_TYPE_NONE, TEE_PARAM_TYPE_NONE));

	TEE_FreeTransientObject(o);
	return TEE_SUCCESS;
}

TEE_Result ta_entry_reset_transient_object(uint32_t param_type,
					   TEE_Param params[4])
{
	TEE_ObjectHandle o = obj_handle_lookup(params[0].value.a);

	ASSERT_PARAM_TYPE(TEE_PARAM_TYPES
			  (TEE_PARAM_TYPE_VALUE_INPUT, TEE_PARAM_TYPE_NONE,
			   TEE_PARAM_TYPE_NONE, TEE_PARAM_TYPE_NONE));

	TEE_ResetTransientObject(o);
	return TEE_SUCCESS;
}

struct attr_packed {
	uint32_t id;
	uint32_t a;
	uint32_t b;
};

static TEE_Result unpack_attrs(const uint8_t *buf, size_t blen,
			       TEE_Attribute **attrs, uint32_t *attr_count)
{
	TEE_Result res = TEE_SUCCESS;
	TEE_Attribute *a = NULL;
	const struct attr_packed *ap = NULL;
	size_t num_attrs = 0;
	const size_t num_attrs_size = sizeof(uint32_t);

	if (blen == 0)
		goto out;

	if (((uintptr_t)buf & 0x3) != 0 || blen < num_attrs_size)
		return TEE_ERROR_BAD_PARAMETERS;
	num_attrs = *(uint32_t *) (void *)buf;
	if ((blen - num_attrs_size) < (num_attrs * sizeof(*ap)))
		return TEE_ERROR_BAD_PARAMETERS;
	ap = (const struct attr_packed *)(const void *)(buf + num_attrs_size);

	if (num_attrs > 0) {
		size_t n;

		a = TEE_Malloc(num_attrs * sizeof(TEE_Attribute), 0);
		if (!a)
			return TEE_ERROR_OUT_OF_MEMORY;
		for (n = 0; n < num_attrs; n++) {
			uintptr_t p;

			a[n].attributeID = ap[n].id;
			if (ap[n].id & TEE_ATTR_FLAG_VALUE) {
				a[n].content.value.a = ap[n].a;
				a[n].content.value.b = ap[n].b;
				continue;
			}

			a[n].content.ref.length = ap[n].b;
			p = (uintptr_t)ap[n].a;
			if (p) {
				if ((p + a[n].content.ref.length) > blen) {
					res = TEE_ERROR_BAD_PARAMETERS;
					goto out;
				}
				p += (uintptr_t)buf;
			}
			a[n].content.ref.buffer = (void *)p;
		}
	}

	res = TEE_SUCCESS;
out:
	if (res == TEE_SUCCESS) {
		*attrs = a;
		*attr_count = num_attrs;
	} else {
		TEE_Free(a);
	}
	return res;
}

TEE_Result ta_entry_populate_transient_object(uint32_t param_type,
					      TEE_Param params[4])
{
	TEE_Result res = TEE_ERROR_GENERIC;
	TEE_Attribute *attrs = NULL;
	uint32_t attr_count = 0;
	TEE_ObjectHandle o = obj_handle_lookup(params[0].value.a);

	ASSERT_PARAM_TYPE(TEE_PARAM_TYPES
			  (TEE_PARAM_TYPE_VALUE_INPUT,
			   TEE_PARAM_TYPE_MEMREF_INPUT, TEE_PARAM_TYPE_NONE,
			   TEE_PARAM_TYPE_NONE));

	res = unpack_attrs(params[1].memref.buffer, params[1].memref.size,
			   &attrs, &attr_count);
	if (res != TEE_SUCCESS)
		return res;

	res = TEE_PopulateTransientObject(o, attrs, attr_count);
	TEE_Free(attrs);
	return res;
}

TEE_Result ta_entry_copy_object_attributes(uint32_t param_type,
					   TEE_Param params[4])
{
	TEE_ObjectHandle dst = obj_handle_lookup(params[0].value.a);
	TEE_ObjectHandle src = obj_handle_lookup(params[0].value.b);

	ASSERT_PARAM_TYPE(TEE_PARAM_TYPES
			  (TEE_PARAM_TYPE_VALUE_INPUT, TEE_PARAM_TYPE_NONE,
			   TEE_PARAM_TYPE_NONE, TEE_PARAM_TYPE_NONE));

	return TEE_CopyObjectAttributes1(dst, src);
}

TEE_Result ta_entry_generate_key(uint32_t param_type, TEE_Param params[4])
{
	TEE_ObjectHandle o = obj_handle_lookup(params[0].value.a);
	TEE_Result res = TEE_ERROR_GENERIC;
	TEE_Attribute *attrs = NULL;
	uint32_t attr_count = 0;

	ASSERT_PARAM_TYPE(TEE_PARAM_TYPES
			  (TEE_PARAM_TYPE_VALUE_INPUT,
			   TEE_PARAM_TYPE_MEMREF_INPUT, TEE_PARAM_TYPE_NONE,
			   TEE_PARAM_TYPE_NONE));

	res = unpack_attrs(params[1].memref.buffer, params[1].memref.size,
			   &attrs, &attr_count);
	if (res != TEE_SUCCESS)
		return res;

	res = TEE_GenerateKey(o, params[0].value.b, attrs, attr_count);
	TEE_Free(attrs);
	return res;
}

TEE_Result ta_entry_asymmetric_encrypt(uint32_t param_type, TEE_Param params[4])
{
	TEE_OperationHandle op = op_handle_lookup(params[0].value.a);
	TEE_Result res = TEE_ERROR_GENERIC;
	TEE_Attribute *attrs = NULL;
	uint32_t attr_count = 0;

	ASSERT_PARAM_TYPE(TEE_PARAM_TYPES
			  (TEE_PARAM_TYPE_VALUE_INPUT,
			   TEE_PARAM_TYPE_MEMREF_INPUT,
			   TEE_PARAM_TYPE_MEMREF_INPUT,
			   TEE_PARAM_TYPE_MEMREF_OUTPUT));

	res = unpack_attrs(params[1].memref.buffer, params[1].memref.size,
			   &attrs, &attr_count);
	if (res != TEE_SUCCESS)
		return res;

	res = TEE_AsymmetricEncrypt(op, attrs, attr_count,
			params[2].memref.buffer, params[2].memref.size,
			params[3].memref.buffer, &params[3].memref.size);
	TEE_Free(attrs);
	return res;
}

TEE_Result ta_entry_asymmetric_decrypt(uint32_t param_type, TEE_Param params[4])
{
	TEE_OperationHandle op = op_handle_lookup(params[0].value.a);
	TEE_Result res = TEE_ERROR_GENERIC;
	TEE_Attribute *attrs = NULL;
	uint32_t attr_count = 0;

	ASSERT_PARAM_TYPE(TEE_PARAM_TYPES
			  (TEE_PARAM_TYPE_VALUE_INPUT,
			   TEE_PARAM_TYPE_MEMREF_INPUT,
			   TEE_PARAM_TYPE_MEMREF_INPUT,
			   TEE_PARAM_TYPE_MEMREF_OUTPUT));

	res = unpack_attrs(params[1].memref.buffer, params[1].memref.size,
			   &attrs, &attr_count);
	if (res != TEE_SUCCESS)
		return res;

	res = TEE_AsymmetricDecrypt(op, attrs, attr_count,
			params[2].memref.buffer, params[2].memref.size,
			params[3].memref.buffer, &params[3].memref.size);
	TEE_Free(attrs);
	return res;
}

TEE_Result ta_entry_asymmetric_sign_digest(uint32_t param_type,
					   TEE_Param params[4])
{
	TEE_OperationHandle op = op_handle_lookup(params[0].value.a);
	TEE_Result res = TEE_ERROR_GENERIC;
	TEE_Attribute *attrs = NULL;
	uint32_t attr_count = 0;

	ASSERT_PARAM_TYPE(TEE_PARAM_TYPES
			  (TEE_PARAM_TYPE_VALUE_INPUT,
			   TEE_PARAM_TYPE_MEMREF_INPUT,
			   TEE_PARAM_TYPE_MEMREF_INPUT,
			   TEE_PARAM_TYPE_MEMREF_OUTPUT));

	res = unpack_attrs(params[1].memref.buffer, params[1].memref.size,
			   &attrs, &attr_count);
	if (res != TEE_SUCCESS)
		return res;

	res = TEE_AsymmetricSignDigest(op, attrs, attr_count,
			params[2].memref.buffer, params[2].memref.size,
			params[3].memref.buffer, &params[3].memref.size);
	TEE_Free(attrs);
	return res;
}

TEE_Result ta_entry_asymmetric_verify_digest(uint32_t param_type,
					     TEE_Param params[4])
{
	TEE_OperationHandle op = op_handle_lookup(params[0].value.a);
	TEE_Result res = TEE_ERROR_GENERIC;
	TEE_Attribute *attrs = NULL;
	uint32_t attr_count = 0;

	ASSERT_PARAM_TYPE(TEE_PARAM_TYPES
			  (TEE_PARAM_TYPE_VALUE_INPUT,
			   TEE_PARAM_TYPE_MEMREF_INPUT,
			   TEE_PARAM_TYPE_MEMREF_INPUT,
			   TEE_PARAM_TYPE_MEMREF_INPUT));

	res = unpack_attrs(params[1].memref.buffer, params[1].memref.size,
			   &attrs, &attr_count);
	if (res != TEE_SUCCESS)
		return res;

	res = TEE_AsymmetricVerifyDigest(op, attrs, attr_count,
			params[2].memref.buffer, params[2].memref.size,
			params[3].memref.buffer, params[3].memref.size);
	TEE_Free(attrs);
	return res;
}

TEE_Result ta_entry_derive_key(uint32_t param_type, TEE_Param params[4])
{
	TEE_OperationHandle op = op_handle_lookup(params[0].value.a);
	TEE_ObjectHandle key = obj_handle_lookup(params[0].value.b);
	TEE_Result res = TEE_ERROR_GENERIC;
	TEE_Attribute *attrs = NULL;
	uint32_t attr_count = 0;

	ASSERT_PARAM_TYPE(TEE_PARAM_TYPES
			  (TEE_PARAM_TYPE_VALUE_INPUT,
			   TEE_PARAM_TYPE_MEMREF_INPUT, TEE_PARAM_TYPE_NONE,
			   TEE_PARAM_TYPE_NONE));

	res = unpack_attrs(params[1].memref.buffer, params[1].memref.size,
			   &attrs, &attr_count);
	if (res != TEE_SUCCESS)
		return res;

	TEE_DeriveKey(op, attrs, attr_count, key);
	TEE_Free(attrs);
	return TEE_SUCCESS;
}

TEE_Result ta_entry_random_number_generate(uint32_t param_type,
					   TEE_Param params[4])
{
	void *buf = NULL;

	ASSERT_PARAM_TYPE(TEE_PARAM_TYPES
			  (TEE_PARAM_TYPE_MEMREF_OUTPUT, TEE_PARAM_TYPE_NONE,
			   TEE_PARAM_TYPE_NONE, TEE_PARAM_TYPE_NONE));

	buf = TEE_Malloc(params[0].memref.size, 0);
	if (!buf)
		return TEE_ERROR_OUT_OF_MEMORY;

	TEE_GenerateRandom(buf, params[0].memref.size);
	TEE_MemMove(params[0].memref.buffer, buf, params[0].memref.size);
	TEE_Free(buf);
	return TEE_SUCCESS;
}

TEE_Result ta_entry_ae_init(uint32_t param_type, TEE_Param params[4])
{
	TEE_OperationHandle op = op_handle_lookup(params[0].value.a);

	ASSERT_PARAM_TYPE(TEE_PARAM_TYPES
			  (TEE_PARAM_TYPE_VALUE_INPUT,
			   TEE_PARAM_TYPE_MEMREF_INPUT,
			   TEE_PARAM_TYPE_VALUE_INPUT, TEE_PARAM_TYPE_NONE));
	return TEE_AEInit(op, params[1].memref.buffer, params[1].memref.size,
			  params[0].value.b * 8, /* tag_len in bits */
			  params[2].value.a, params[2].value.b);
}

TEE_Result ta_entry_ae_update_aad(uint32_t param_type, TEE_Param params[4])
{
	TEE_OperationHandle op = op_handle_lookup(params[0].value.a);

	ASSERT_PARAM_TYPE(TEE_PARAM_TYPES
			  (TEE_PARAM_TYPE_VALUE_INPUT,
			   TEE_PARAM_TYPE_MEMREF_INPUT, TEE_PARAM_TYPE_NONE,
			   TEE_PARAM_TYPE_NONE));

	TEE_AEUpdateAAD(op, params[1].memref.buffer, params[1].memref.size);
	return TEE_SUCCESS;
}

TEE_Result ta_entry_ae_update(uint32_t param_type, TEE_Param params[4])
{
	TEE_OperationHandle op = op_handle_lookup(params[0].value.a);

	ASSERT_PARAM_TYPE(TEE_PARAM_TYPES
			  (TEE_PARAM_TYPE_VALUE_INPUT,
			   TEE_PARAM_TYPE_MEMREF_INPUT,
			   TEE_PARAM_TYPE_MEMREF_OUTPUT, TEE_PARAM_TYPE_NONE));

	TEE_AEUpdate(op, params[1].memref.buffer, params[1].memref.size,
		     params[2].memref.buffer, &params[2].memref.size);
	return TEE_SUCCESS;
}

TEE_Result ta_entry_ae_encrypt_final(uint32_t param_type, TEE_Param params[4])
{
	TEE_OperationHandle op = op_handle_lookup(params[0].value.a);
	TEE_Result res = TEE_ERROR_OUT_OF_MEMORY;
	void *b2 = NULL;
	void *b3 = NULL;

	ASSERT_PARAM_TYPE(TEE_PARAM_TYPES
			  (TEE_PARAM_TYPE_VALUE_INPUT,
			   TEE_PARAM_TYPE_MEMREF_INPUT,
			   TEE_PARAM_TYPE_MEMREF_OUTPUT,
			   TEE_PARAM_TYPE_MEMREF_OUTPUT));

	b2 = TEE_Malloc(params[2].memref.size, 0);
	b3 = TEE_Malloc(params[3].memref.size, 0);
	if (!b2 || !b3)
		goto out;

	res = TEE_AEEncryptFinal(op, params[1].memref.buffer,
				 params[1].memref.size, b2,
				 &params[2].memref.size, b3,
				 &params[3].memref.size);
	if (!res) {
		TEE_MemMove(params[2].memref.buffer, b2, params[2].memref.size);
		TEE_MemMove(params[3].memref.buffer, b3, params[3].memref.size);
	}
out:
	TEE_Free(b2);
	TEE_Free(b3);
	return res;
}

TEE_Result ta_entry_ae_decrypt_final(uint32_t param_type, TEE_Param params[4])
{
	TEE_OperationHandle op = op_handle_lookup(params[0].value.a);
	TEE_Result res = TEE_ERROR_OUT_OF_MEMORY;
	void *b2 = NULL;
	void *b3 = NULL;

	ASSERT_PARAM_TYPE(TEE_PARAM_TYPES
			  (TEE_PARAM_TYPE_VALUE_INPUT,
			   TEE_PARAM_TYPE_MEMREF_INPUT,
			   TEE_PARAM_TYPE_MEMREF_OUTPUT,
			   TEE_PARAM_TYPE_MEMREF_INPUT));

	b2 = TEE_Malloc(params[2].memref.size, 0);
	b3 = TEE_Malloc(params[3].memref.size, 0);
	if (!b2 || !b3)
		goto out;

	TEE_MemMove(b3, params[3].memref.buffer, params[3].memref.size);
	res = TEE_AEDecryptFinal(op, params[1].memref.buffer,
				 params[1].memref.size, b2,
				 &params[2].memref.size, b3,
				 params[3].memref.size);
	if (!res)
		TEE_MemMove(params[2].memref.buffer, b2, params[2].memref.size);
out:
	TEE_Free(b2);
	TEE_Free(b3);

	return res;
}

TEE_Result ta_entry_get_object_buffer_attribute(uint32_t param_type,
						TEE_Param params[4])
{
	TEE_ObjectHandle o = obj_handle_lookup(params[0].value.a);

	ASSERT_PARAM_TYPE(TEE_PARAM_TYPES
			  (TEE_PARAM_TYPE_VALUE_INPUT,
			   TEE_PARAM_TYPE_MEMREF_OUTPUT, TEE_PARAM_TYPE_NONE,
			   TEE_PARAM_TYPE_NONE));

	return TEE_GetObjectBufferAttribute(o, params[0].value.b,
			params[1].memref.buffer, &params[1].memref.size);
}

TEE_Result ta_entry_get_object_value_attribute(uint32_t param_type,
					       TEE_Param params[4])
{
	TEE_ObjectHandle o = obj_handle_lookup(params[0].value.a);

	ASSERT_PARAM_TYPE(TEE_PARAM_TYPES
			  (TEE_PARAM_TYPE_VALUE_INPUT,
			   TEE_PARAM_TYPE_VALUE_OUTPUT, TEE_PARAM_TYPE_NONE,
			   TEE_PARAM_TYPE_NONE));

	return TEE_GetObjectValueAttribute(o, params[0].value.b,
				   &params[1].value.a, &params[1].value.b);
}

TEE_Result ta_entry_is_algo_supported(uint32_t param_type,
				      TEE_Param params[TEE_NUM_PARAMS])
{
	ASSERT_PARAM_TYPE(TEE_PARAM_TYPES
			  (TEE_PARAM_TYPE_VALUE_INPUT,
			   TEE_PARAM_TYPE_VALUE_OUTPUT, TEE_PARAM_TYPE_NONE,
			   TEE_PARAM_TYPE_NONE));

	params[1].value.a = TEE_IsAlgorithmSupported(params[0].value.a,
						     params[0].value.b);

	return TEE_SUCCESS;
}
