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

#include <assert.h>
#include <bitstring.h>
#include <compiler.h>
#include <config.h>
#include <crypto/crypto.h>
#include <kernel/tee_ta_manager.h>
#include <kernel/user_access.h>
#include <mm/vm.h>
#include <stdlib_ext.h>
#include <string_ext.h>
#include <string.h>
#include <sys/queue.h>
#include <tee_api_defines_extensions.h>
#include <tee_api_types.h>
#include <tee/tee_cryp_utl.h>
#include <tee/tee_obj.h>
#include <tee/tee_svc_cryp.h>
#include <tee/tee_svc.h>
#include <trace.h>
#include <utee_defines.h>
#include <util.h>
#if defined(CFG_CRYPTO_HKDF)
#include <tee/tee_cryp_hkdf.h>
#endif
#if defined(CFG_CRYPTO_CONCAT_KDF)
#include <tee/tee_cryp_concat_kdf.h>
#endif
#if defined(CFG_CRYPTO_PBKDF2)
#include <tee/tee_cryp_pbkdf2.h>
#endif

enum cryp_state {
	CRYP_STATE_INITIALIZED = 0,
	CRYP_STATE_UNINITIALIZED
};

typedef void (*tee_cryp_ctx_finalize_func_t) (void *ctx);
struct tee_cryp_state {
	TAILQ_ENTRY(tee_cryp_state) link;
	uint32_t algo;
	uint32_t mode;
	vaddr_t key1;
	vaddr_t key2;
	void *ctx;
	tee_cryp_ctx_finalize_func_t ctx_finalize;
	enum cryp_state state;
};

struct tee_cryp_obj_secret {
	uint32_t key_size;
	uint32_t alloc_size;

	/*
	 * Pseudo code visualize layout of structure
	 * Next follows data, such as:
	 *	uint8_t data[alloc_size]
	 * key_size must never exceed alloc_size
	 */
};

#define TEE_TYPE_ATTR_OPTIONAL		BIT(0)
#define TEE_TYPE_ATTR_REQUIRED		BIT(1)
#define TEE_TYPE_ATTR_OPTIONAL_GROUP	BIT(2)
#define TEE_TYPE_ATTR_SIZE_INDICATOR	BIT(3)
#define TEE_TYPE_ATTR_GEN_KEY_OPT	BIT(4)
#define TEE_TYPE_ATTR_GEN_KEY_REQ	BIT(5)
#define TEE_TYPE_ATTR_BIGNUM_MAXBITS	BIT(6)

    /* Handle storing of generic secret keys of varying lengths */
#define ATTR_OPS_INDEX_SECRET     0
    /* Convert to/from big-endian byte array and provider-specific bignum */
#define ATTR_OPS_INDEX_BIGNUM     1
    /* Convert to/from value attribute depending on direction */
#define ATTR_OPS_INDEX_VALUE      2

struct tee_cryp_obj_type_attrs {
	uint32_t attr_id;
	uint16_t flags;
	uint16_t ops_index;
	uint16_t raw_offs;
	uint16_t raw_size;
};

#define RAW_DATA(_x, _y)	\
	.raw_offs = offsetof(_x, _y), .raw_size = MEMBER_SIZE(_x, _y)

static const struct tee_cryp_obj_type_attrs
	tee_cryp_obj_secret_value_attrs[] = {
	{
	.attr_id = TEE_ATTR_SECRET_VALUE,
	.flags = TEE_TYPE_ATTR_REQUIRED | TEE_TYPE_ATTR_SIZE_INDICATOR,
	.ops_index = ATTR_OPS_INDEX_SECRET,
	.raw_offs = 0,
	.raw_size = 0
	},
};

static const struct tee_cryp_obj_type_attrs tee_cryp_obj_rsa_pub_key_attrs[] = {
	{
	.attr_id = TEE_ATTR_RSA_MODULUS,
	.flags = TEE_TYPE_ATTR_REQUIRED | TEE_TYPE_ATTR_SIZE_INDICATOR,
	.ops_index = ATTR_OPS_INDEX_BIGNUM,
	RAW_DATA(struct rsa_public_key, n)
	},

	{
	.attr_id = TEE_ATTR_RSA_PUBLIC_EXPONENT,
	.flags = TEE_TYPE_ATTR_REQUIRED,
	.ops_index = ATTR_OPS_INDEX_BIGNUM,
	RAW_DATA(struct rsa_public_key, e)
	},
};

static const struct tee_cryp_obj_type_attrs tee_cryp_obj_rsa_keypair_attrs[] = {
	{
	.attr_id = TEE_ATTR_RSA_MODULUS,
	.flags = TEE_TYPE_ATTR_REQUIRED | TEE_TYPE_ATTR_SIZE_INDICATOR,
	.ops_index = ATTR_OPS_INDEX_BIGNUM,
	RAW_DATA(struct rsa_keypair, n)
	},

	{
	.attr_id = TEE_ATTR_RSA_PUBLIC_EXPONENT,
	.flags = TEE_TYPE_ATTR_REQUIRED | TEE_TYPE_ATTR_GEN_KEY_OPT,
	.ops_index = ATTR_OPS_INDEX_BIGNUM,
	RAW_DATA(struct rsa_keypair, e)
	},

	{
	.attr_id = TEE_ATTR_RSA_PRIVATE_EXPONENT,
	.flags = TEE_TYPE_ATTR_REQUIRED,
	.ops_index = ATTR_OPS_INDEX_BIGNUM,
	RAW_DATA(struct rsa_keypair, d)
	},

	{
	.attr_id = TEE_ATTR_RSA_PRIME1,
	.flags = TEE_TYPE_ATTR_OPTIONAL_GROUP,
	.ops_index = ATTR_OPS_INDEX_BIGNUM,
	RAW_DATA(struct rsa_keypair, p)
	},

	{
	.attr_id = TEE_ATTR_RSA_PRIME2,
	.flags = TEE_TYPE_ATTR_OPTIONAL_GROUP,
	.ops_index = ATTR_OPS_INDEX_BIGNUM,
	RAW_DATA(struct rsa_keypair, q)
	},

	{
	.attr_id = TEE_ATTR_RSA_EXPONENT1,
	.flags = TEE_TYPE_ATTR_OPTIONAL_GROUP,
	.ops_index = ATTR_OPS_INDEX_BIGNUM,
	RAW_DATA(struct rsa_keypair, dp)
	},

	{
	.attr_id = TEE_ATTR_RSA_EXPONENT2,
	.flags = TEE_TYPE_ATTR_OPTIONAL_GROUP,
	.ops_index = ATTR_OPS_INDEX_BIGNUM,
	RAW_DATA(struct rsa_keypair, dq)
	},

	{
	.attr_id = TEE_ATTR_RSA_COEFFICIENT,
	.flags = TEE_TYPE_ATTR_OPTIONAL_GROUP,
	.ops_index = ATTR_OPS_INDEX_BIGNUM,
	RAW_DATA(struct rsa_keypair, qp)
	},
};

static const struct tee_cryp_obj_type_attrs tee_cryp_obj_dsa_pub_key_attrs[] = {
	{
	.attr_id = TEE_ATTR_DSA_PRIME,
	.flags = TEE_TYPE_ATTR_REQUIRED | TEE_TYPE_ATTR_BIGNUM_MAXBITS,
	.ops_index = ATTR_OPS_INDEX_BIGNUM,
	RAW_DATA(struct dsa_public_key, p)
	},

	{
	.attr_id = TEE_ATTR_DSA_SUBPRIME,
	.flags = TEE_TYPE_ATTR_REQUIRED | TEE_TYPE_ATTR_SIZE_INDICATOR,
	.ops_index = ATTR_OPS_INDEX_BIGNUM,
	RAW_DATA(struct dsa_public_key, q)
	},

	{
	.attr_id = TEE_ATTR_DSA_BASE,
	.flags = TEE_TYPE_ATTR_REQUIRED | TEE_TYPE_ATTR_BIGNUM_MAXBITS,
	.ops_index = ATTR_OPS_INDEX_BIGNUM,
	RAW_DATA(struct dsa_public_key, g)
	},

	{
	.attr_id = TEE_ATTR_DSA_PUBLIC_VALUE,
	.flags = TEE_TYPE_ATTR_REQUIRED | TEE_TYPE_ATTR_BIGNUM_MAXBITS,
	.ops_index = ATTR_OPS_INDEX_BIGNUM,
	RAW_DATA(struct dsa_public_key, y)
	},
};

static const struct tee_cryp_obj_type_attrs tee_cryp_obj_dsa_keypair_attrs[] = {
	{
	.attr_id = TEE_ATTR_DSA_PRIME,
	.flags = TEE_TYPE_ATTR_REQUIRED | TEE_TYPE_ATTR_GEN_KEY_REQ |
		 TEE_TYPE_ATTR_BIGNUM_MAXBITS,
	.ops_index = ATTR_OPS_INDEX_BIGNUM,
	RAW_DATA(struct dsa_keypair, p)
	},

	{
	.attr_id = TEE_ATTR_DSA_SUBPRIME,
	.flags = TEE_TYPE_ATTR_REQUIRED | TEE_TYPE_ATTR_SIZE_INDICATOR |
		 TEE_TYPE_ATTR_GEN_KEY_REQ,
	.ops_index = ATTR_OPS_INDEX_BIGNUM,
	RAW_DATA(struct dsa_keypair, q)
	},

	{
	.attr_id = TEE_ATTR_DSA_BASE,
	.flags = TEE_TYPE_ATTR_REQUIRED | TEE_TYPE_ATTR_GEN_KEY_REQ |
		 TEE_TYPE_ATTR_BIGNUM_MAXBITS,
	.ops_index = ATTR_OPS_INDEX_BIGNUM,
	RAW_DATA(struct dsa_keypair, g)
	},

	{
	.attr_id = TEE_ATTR_DSA_PRIVATE_VALUE,
	.flags = TEE_TYPE_ATTR_REQUIRED | TEE_TYPE_ATTR_BIGNUM_MAXBITS,
	.ops_index = ATTR_OPS_INDEX_BIGNUM,
	RAW_DATA(struct dsa_keypair, x)
	},

	{
	.attr_id = TEE_ATTR_DSA_PUBLIC_VALUE,
	.flags = TEE_TYPE_ATTR_REQUIRED | TEE_TYPE_ATTR_BIGNUM_MAXBITS,
	.ops_index = ATTR_OPS_INDEX_BIGNUM,
	RAW_DATA(struct dsa_keypair, y)
	},
};

static const struct tee_cryp_obj_type_attrs tee_cryp_obj_dh_keypair_attrs[] = {
	{
	.attr_id = TEE_ATTR_DH_PRIME,
	.flags = TEE_TYPE_ATTR_REQUIRED | TEE_TYPE_ATTR_SIZE_INDICATOR |
		 TEE_TYPE_ATTR_GEN_KEY_REQ,
	.ops_index = ATTR_OPS_INDEX_BIGNUM,
	RAW_DATA(struct dh_keypair, p)
	},

	{
	.attr_id = TEE_ATTR_DH_BASE,
	.flags = TEE_TYPE_ATTR_REQUIRED | TEE_TYPE_ATTR_GEN_KEY_REQ,
	.ops_index = ATTR_OPS_INDEX_BIGNUM,
	RAW_DATA(struct dh_keypair, g)
	},

	{
	.attr_id = TEE_ATTR_DH_PUBLIC_VALUE,
	.flags = TEE_TYPE_ATTR_REQUIRED,
	.ops_index = ATTR_OPS_INDEX_BIGNUM,
	RAW_DATA(struct dh_keypair, y)
	},

	{
	.attr_id = TEE_ATTR_DH_PRIVATE_VALUE,
	.flags = TEE_TYPE_ATTR_REQUIRED,
	.ops_index = ATTR_OPS_INDEX_BIGNUM,
	RAW_DATA(struct dh_keypair, x)
	},

	{
	.attr_id = TEE_ATTR_DH_SUBPRIME,
	.flags = TEE_TYPE_ATTR_OPTIONAL_GROUP |	 TEE_TYPE_ATTR_GEN_KEY_OPT,
	.ops_index = ATTR_OPS_INDEX_BIGNUM,
	RAW_DATA(struct dh_keypair, q)
	},

	{
	.attr_id = TEE_ATTR_DH_X_BITS,
	.flags = TEE_TYPE_ATTR_GEN_KEY_OPT,
	.ops_index = ATTR_OPS_INDEX_VALUE,
	RAW_DATA(struct dh_keypair, xbits)
	},
};

#if defined(CFG_CRYPTO_HKDF)
static const struct tee_cryp_obj_type_attrs
	tee_cryp_obj_hkdf_ikm_attrs[] = {
	{
	.attr_id = TEE_ATTR_HKDF_IKM,
	.flags = TEE_TYPE_ATTR_REQUIRED | TEE_TYPE_ATTR_SIZE_INDICATOR,
	.ops_index = ATTR_OPS_INDEX_SECRET,
	.raw_offs = 0,
	.raw_size = 0
	},
};
#endif

#if defined(CFG_CRYPTO_CONCAT_KDF)
static const struct tee_cryp_obj_type_attrs
	tee_cryp_obj_concat_kdf_z_attrs[] = {
	{
	.attr_id = TEE_ATTR_CONCAT_KDF_Z,
	.flags = TEE_TYPE_ATTR_REQUIRED | TEE_TYPE_ATTR_SIZE_INDICATOR,
	.ops_index = ATTR_OPS_INDEX_SECRET,
	.raw_offs = 0,
	.raw_size = 0
	},
};
#endif

#if defined(CFG_CRYPTO_PBKDF2)
static const struct tee_cryp_obj_type_attrs
	tee_cryp_obj_pbkdf2_passwd_attrs[] = {
	{
	.attr_id = TEE_ATTR_PBKDF2_PASSWORD,
	.flags = TEE_TYPE_ATTR_REQUIRED | TEE_TYPE_ATTR_SIZE_INDICATOR,
	.ops_index = ATTR_OPS_INDEX_SECRET,
	.raw_offs = 0,
	.raw_size = 0
	},
};
#endif

static const struct tee_cryp_obj_type_attrs tee_cryp_obj_ecc_pub_key_attrs[] = {
	{
	.attr_id = TEE_ATTR_ECC_PUBLIC_VALUE_X,
	.flags = TEE_TYPE_ATTR_REQUIRED,
	.ops_index = ATTR_OPS_INDEX_BIGNUM,
	RAW_DATA(struct ecc_public_key, x)
	},

	{
	.attr_id = TEE_ATTR_ECC_PUBLIC_VALUE_Y,
	.flags = TEE_TYPE_ATTR_REQUIRED,
	.ops_index = ATTR_OPS_INDEX_BIGNUM,
	RAW_DATA(struct ecc_public_key, y)
	},

	{
	.attr_id = TEE_ATTR_ECC_CURVE,
	.flags = TEE_TYPE_ATTR_REQUIRED | TEE_TYPE_ATTR_SIZE_INDICATOR,
	.ops_index = ATTR_OPS_INDEX_VALUE,
	RAW_DATA(struct ecc_public_key, curve)
	},
};

static const struct tee_cryp_obj_type_attrs tee_cryp_obj_ecc_keypair_attrs[] = {
	{
	.attr_id = TEE_ATTR_ECC_PRIVATE_VALUE,
	.flags = TEE_TYPE_ATTR_REQUIRED,
	.ops_index = ATTR_OPS_INDEX_BIGNUM,
	RAW_DATA(struct ecc_keypair, d)
	},

	{
	.attr_id = TEE_ATTR_ECC_PUBLIC_VALUE_X,
	.flags = TEE_TYPE_ATTR_REQUIRED,
	.ops_index = ATTR_OPS_INDEX_BIGNUM,
	RAW_DATA(struct ecc_keypair, x)
	},

	{
	.attr_id = TEE_ATTR_ECC_PUBLIC_VALUE_Y,
	.flags = TEE_TYPE_ATTR_REQUIRED,
	.ops_index = ATTR_OPS_INDEX_BIGNUM,
	RAW_DATA(struct ecc_keypair, y)
	},

	{
	.attr_id = TEE_ATTR_ECC_CURVE,
	.flags = TEE_TYPE_ATTR_REQUIRED | TEE_TYPE_ATTR_SIZE_INDICATOR |
		 TEE_TYPE_ATTR_GEN_KEY_REQ,
	.ops_index = ATTR_OPS_INDEX_VALUE,
	RAW_DATA(struct ecc_keypair, curve)
	},
};

struct tee_cryp_obj_type_props {
	TEE_ObjectType obj_type;
	uint16_t min_size;	/* may not be smaller than this */
	uint16_t max_size;	/* may not be larger than this */
	uint16_t alloc_size;	/* this many bytes are allocated to hold data */
	uint8_t quanta;		/* may only be an multiple of this */

	uint8_t num_type_attrs;
	const struct tee_cryp_obj_type_attrs *type_attrs;
};

#define PROP(obj_type, quanta, min_size, max_size, alloc_size, type_attrs) \
		{ (obj_type), (min_size), (max_size), (alloc_size), (quanta), \
		  ARRAY_SIZE(type_attrs), (type_attrs) }

static const struct tee_cryp_obj_type_props tee_cryp_obj_props[] = {
	PROP(TEE_TYPE_AES, 64, 128, 256,	/* valid sizes 128, 192, 256 */
		256 / 8 + sizeof(struct tee_cryp_obj_secret),
		tee_cryp_obj_secret_value_attrs),
	PROP(TEE_TYPE_DES, 64, 64, 64,
	     /* Valid size 64 with parity */
	     64 / 8 + sizeof(struct tee_cryp_obj_secret),
	     tee_cryp_obj_secret_value_attrs),
	PROP(TEE_TYPE_DES3, 64, 128, 192,
	     /* Valid sizes 128, 192 with parity */
	     192 / 8 + sizeof(struct tee_cryp_obj_secret),
	     tee_cryp_obj_secret_value_attrs),
	PROP(TEE_TYPE_SM4, 128, 128, 128,
		128 / 8 + sizeof(struct tee_cryp_obj_secret),
		tee_cryp_obj_secret_value_attrs),
	PROP(TEE_TYPE_HMAC_MD5, 8, 64, 512,
		512 / 8 + sizeof(struct tee_cryp_obj_secret),
		tee_cryp_obj_secret_value_attrs),
	PROP(TEE_TYPE_HMAC_SHA1, 8, 80, 512,
		512 / 8 + sizeof(struct tee_cryp_obj_secret),
		tee_cryp_obj_secret_value_attrs),
	PROP(TEE_TYPE_HMAC_SHA224, 8, 112, 512,
		512 / 8 + sizeof(struct tee_cryp_obj_secret),
		tee_cryp_obj_secret_value_attrs),
	PROP(TEE_TYPE_HMAC_SHA256, 8, 192, 1024,
		1024 / 8 + sizeof(struct tee_cryp_obj_secret),
		tee_cryp_obj_secret_value_attrs),
	PROP(TEE_TYPE_HMAC_SHA384, 8, 256, 1024,
		1024 / 8 + sizeof(struct tee_cryp_obj_secret),
		tee_cryp_obj_secret_value_attrs),
	PROP(TEE_TYPE_HMAC_SHA512, 8, 256, 1024,
		1024 / 8 + sizeof(struct tee_cryp_obj_secret),
		tee_cryp_obj_secret_value_attrs),
	PROP(TEE_TYPE_HMAC_SM3, 8, 80, 1024,
		512 / 8 + sizeof(struct tee_cryp_obj_secret),
		tee_cryp_obj_secret_value_attrs),
	PROP(TEE_TYPE_GENERIC_SECRET, 8, 0, 4096,
		4096 / 8 + sizeof(struct tee_cryp_obj_secret),
		tee_cryp_obj_secret_value_attrs),
#if defined(CFG_CRYPTO_HKDF)
	PROP(TEE_TYPE_HKDF_IKM, 8, 0, 4096,
		4096 / 8 + sizeof(struct tee_cryp_obj_secret),
		tee_cryp_obj_hkdf_ikm_attrs),
#endif
#if defined(CFG_CRYPTO_CONCAT_KDF)
	PROP(TEE_TYPE_CONCAT_KDF_Z, 8, 0, 4096,
		4096 / 8 + sizeof(struct tee_cryp_obj_secret),
		tee_cryp_obj_concat_kdf_z_attrs),
#endif
#if defined(CFG_CRYPTO_PBKDF2)
	PROP(TEE_TYPE_PBKDF2_PASSWORD, 8, 0, 4096,
		4096 / 8 + sizeof(struct tee_cryp_obj_secret),
		tee_cryp_obj_pbkdf2_passwd_attrs),
#endif
	PROP(TEE_TYPE_RSA_PUBLIC_KEY, 1, 256, CFG_CORE_BIGNUM_MAX_BITS,
		sizeof(struct rsa_public_key),
		tee_cryp_obj_rsa_pub_key_attrs),

	PROP(TEE_TYPE_RSA_KEYPAIR, 1, 256, CFG_CORE_BIGNUM_MAX_BITS,
		sizeof(struct rsa_keypair),
		tee_cryp_obj_rsa_keypair_attrs),

	PROP(TEE_TYPE_DSA_PUBLIC_KEY, 64, 512, 3072,
		sizeof(struct dsa_public_key),
		tee_cryp_obj_dsa_pub_key_attrs),

	PROP(TEE_TYPE_DSA_KEYPAIR, 64, 512, 3072,
		sizeof(struct dsa_keypair),
		tee_cryp_obj_dsa_keypair_attrs),

	PROP(TEE_TYPE_DH_KEYPAIR, 1, 256, 2048,
		sizeof(struct dh_keypair),
		tee_cryp_obj_dh_keypair_attrs),

	PROP(TEE_TYPE_ECDSA_PUBLIC_KEY, 1, 192, 521,
		sizeof(struct ecc_public_key),
		tee_cryp_obj_ecc_pub_key_attrs),

	PROP(TEE_TYPE_ECDSA_KEYPAIR, 1, 192, 521,
		sizeof(struct ecc_keypair),
		tee_cryp_obj_ecc_keypair_attrs),

	PROP(TEE_TYPE_ECDH_PUBLIC_KEY, 1, 192, 521,
		sizeof(struct ecc_public_key),
		tee_cryp_obj_ecc_pub_key_attrs),

	PROP(TEE_TYPE_ECDH_KEYPAIR, 1, 192, 521,
		sizeof(struct ecc_keypair),
		tee_cryp_obj_ecc_keypair_attrs),

	PROP(TEE_TYPE_SM2_DSA_PUBLIC_KEY, 1, 256, 256,
	     sizeof(struct ecc_public_key),
	     tee_cryp_obj_ecc_pub_key_attrs),

	PROP(TEE_TYPE_SM2_DSA_KEYPAIR, 1, 256, 256,
	     sizeof(struct ecc_keypair),
	     tee_cryp_obj_ecc_keypair_attrs),

	PROP(TEE_TYPE_SM2_PKE_PUBLIC_KEY, 1, 256, 256,
	     sizeof(struct ecc_public_key),
	     tee_cryp_obj_ecc_pub_key_attrs),

	PROP(TEE_TYPE_SM2_PKE_KEYPAIR, 1, 256, 256,
	     sizeof(struct ecc_keypair),
	     tee_cryp_obj_ecc_keypair_attrs),

	PROP(TEE_TYPE_SM2_KEP_PUBLIC_KEY, 1, 256, 256,
	     sizeof(struct ecc_public_key),
	     tee_cryp_obj_ecc_pub_key_attrs),

	PROP(TEE_TYPE_SM2_KEP_KEYPAIR, 1, 256, 256,
	     sizeof(struct ecc_keypair),
	     tee_cryp_obj_ecc_keypair_attrs),
};

struct attr_ops {
	TEE_Result (*from_user)(void *attr, const void *buffer, size_t size);
	TEE_Result (*to_user)(void *attr, struct ts_session *sess,
			      void *buffer, uint64_t *size);
	TEE_Result (*to_binary)(void *attr, void *data, size_t data_len,
			    size_t *offs);
	bool (*from_binary)(void *attr, const void *data, size_t data_len,
			    size_t *offs);
	TEE_Result (*from_obj)(void *attr, void *src_attr);
	void (*free)(void *attr);
	void (*clear)(void *attr);
};

static TEE_Result op_u32_to_binary_helper(uint32_t v, uint8_t *data,
				    size_t data_len, size_t *offs)
{
	uint32_t field;
	size_t next_offs;

	if (ADD_OVERFLOW(*offs, sizeof(field), &next_offs))
		return TEE_ERROR_OVERFLOW;

	if (data && next_offs <= data_len) {
		field = TEE_U32_TO_BIG_ENDIAN(v);
		memcpy(data + *offs, &field, sizeof(field));
	}
	(*offs) = next_offs;

	return TEE_SUCCESS;
}

static bool op_u32_from_binary_helper(uint32_t *v, const uint8_t *data,
				      size_t data_len, size_t *offs)
{
	uint32_t field;

	if (!data || (*offs + sizeof(field)) > data_len)
		return false;

	memcpy(&field, data + *offs, sizeof(field));
	*v = TEE_U32_FROM_BIG_ENDIAN(field);
	(*offs) += sizeof(field);
	return true;
}

static TEE_Result op_attr_secret_value_from_user(void *attr, const void *buffer,
						 size_t size)
{
	struct tee_cryp_obj_secret *key = attr;

	/* Data size has to fit in allocated buffer */
	if (size > key->alloc_size)
		return TEE_ERROR_SECURITY;
	memcpy(key + 1, buffer, size);
	key->key_size = size;
	return TEE_SUCCESS;
}

static TEE_Result op_attr_secret_value_to_user(void *attr,
					       struct ts_session *sess __unused,
					       void *buffer, uint64_t *size)
{
	TEE_Result res;
	struct tee_cryp_obj_secret *key = attr;
	uint64_t s;
	uint64_t key_size;

	res = copy_from_user(&s, size, sizeof(s));
	if (res != TEE_SUCCESS)
		return res;

	key_size = key->key_size;
	res = copy_to_user(size, &key_size, sizeof(key_size));
	if (res != TEE_SUCCESS)
		return res;

	if (s < key->key_size || !buffer)
		return TEE_ERROR_SHORT_BUFFER;

	return copy_to_user(buffer, key + 1, key->key_size);
}

static TEE_Result op_attr_secret_value_to_binary(void *attr, void *data,
					   size_t data_len, size_t *offs)
{
	TEE_Result res;
	struct tee_cryp_obj_secret *key = attr;
	size_t next_offs;

	res = op_u32_to_binary_helper(key->key_size, data, data_len, offs);
	if (res != TEE_SUCCESS)
		return res;

	if (ADD_OVERFLOW(*offs, key->key_size, &next_offs))
		return TEE_ERROR_OVERFLOW;

	if (data && next_offs <= data_len)
		memcpy((uint8_t *)data + *offs, key + 1, key->key_size);
	(*offs) = next_offs;

	return TEE_SUCCESS;
}

static bool op_attr_secret_value_from_binary(void *attr, const void *data,
					     size_t data_len, size_t *offs)
{
	struct tee_cryp_obj_secret *key = attr;
	uint32_t s;

	if (!op_u32_from_binary_helper(&s, data, data_len, offs))
		return false;

	if ((*offs + s) > data_len)
		return false;

	/* Data size has to fit in allocated buffer */
	if (s > key->alloc_size)
		return false;
	key->key_size = s;
	memcpy(key + 1, (const uint8_t *)data + *offs, s);
	(*offs) += s;
	return true;
}


static TEE_Result op_attr_secret_value_from_obj(void *attr, void *src_attr)
{
	struct tee_cryp_obj_secret *key = attr;
	struct tee_cryp_obj_secret *src_key = src_attr;

	if (src_key->key_size > key->alloc_size)
		return TEE_ERROR_BAD_STATE;
	memcpy(key + 1, src_key + 1, src_key->key_size);
	key->key_size = src_key->key_size;
	return TEE_SUCCESS;
}

static void op_attr_secret_value_clear(void *attr)
{
	struct tee_cryp_obj_secret *key = attr;

	key->key_size = 0;
	memzero_explicit(key + 1, key->alloc_size);
}

static TEE_Result op_attr_bignum_from_user(void *attr, const void *buffer,
					   size_t size)
{
	struct bignum **bn = attr;

	return crypto_bignum_bin2bn(buffer, size, *bn);
}

static TEE_Result op_attr_bignum_to_user(void *attr,
					 struct ts_session *sess,
					 void *buffer, uint64_t *size)
{
	TEE_Result res = TEE_SUCCESS;
	struct bignum **bn = attr;
	uint64_t req_size = 0;
	uint64_t s = 0;

	res = copy_from_user(&s, size, sizeof(s));
	if (res != TEE_SUCCESS)
		return res;

	req_size = crypto_bignum_num_bytes(*bn);
	res = copy_to_user(size, &req_size, sizeof(req_size));
	if (res != TEE_SUCCESS)
		return res;
	if (!req_size)
		return TEE_SUCCESS;
	if (s < req_size || !buffer)
		return TEE_ERROR_SHORT_BUFFER;

	/* Check we can access data using supplied user mode pointer */
	res = vm_check_access_rights(&to_user_ta_ctx(sess->ctx)->uctx,
				     TEE_MEMORY_ACCESS_READ |
				     TEE_MEMORY_ACCESS_WRITE |
				     TEE_MEMORY_ACCESS_ANY_OWNER,
				     (uaddr_t)buffer, req_size);
	if (res != TEE_SUCCESS)
		return res;
	/*
	* Write the bignum (wich raw data points to) into an array of
	* bytes (stored in buffer)
	*/
	crypto_bignum_bn2bin(*bn, buffer);
	return TEE_SUCCESS;
}

static TEE_Result op_attr_bignum_to_binary(void *attr, void *data,
					   size_t data_len, size_t *offs)
{
	TEE_Result res;
	struct bignum **bn = attr;
	uint32_t n = crypto_bignum_num_bytes(*bn);
	size_t next_offs;

	res = op_u32_to_binary_helper(n, data, data_len, offs);
	if (res != TEE_SUCCESS)
		return res;

	if (ADD_OVERFLOW(*offs, n, &next_offs))
		return TEE_ERROR_OVERFLOW;

	if (data && next_offs <= data_len)
		crypto_bignum_bn2bin(*bn, (uint8_t *)data + *offs);
	(*offs) = next_offs;

	return TEE_SUCCESS;
}

static bool op_attr_bignum_from_binary(void *attr, const void *data,
				       size_t data_len, size_t *offs)
{
	struct bignum **bn = attr;
	uint32_t n;

	if (!op_u32_from_binary_helper(&n, data, data_len, offs))
		return false;

	if ((*offs + n) > data_len)
		return false;
	if (crypto_bignum_bin2bn((const uint8_t *)data + *offs, n, *bn))
		return false;
	(*offs) += n;
	return true;
}

static TEE_Result op_attr_bignum_from_obj(void *attr, void *src_attr)
{
	struct bignum **bn = attr;
	struct bignum **src_bn = src_attr;

	crypto_bignum_copy(*bn, *src_bn);
	return TEE_SUCCESS;
}

static void op_attr_bignum_clear(void *attr)
{
	struct bignum **bn = attr;

	crypto_bignum_clear(*bn);
}

static void op_attr_bignum_free(void *attr)
{
	struct bignum **bn = attr;

	crypto_bignum_free(*bn);
	*bn = NULL;
}

static TEE_Result op_attr_value_from_user(void *attr, const void *buffer,
					  size_t size)
{
	uint32_t *v = attr;

	if (size != sizeof(uint32_t) * 2)
		return TEE_ERROR_GENERIC; /* "can't happen */

	/* Note that only the first value is copied */
	memcpy(v, buffer, sizeof(uint32_t));
	return TEE_SUCCESS;
}

static TEE_Result op_attr_value_to_user(void *attr,
					struct ts_session *sess __unused,
					void *buffer, uint64_t *size)
{
	TEE_Result res;
	uint32_t *v = attr;
	uint64_t s;
	uint32_t value[2] = { *v };
	uint64_t req_size = sizeof(value);

	res = copy_from_user(&s, size, sizeof(s));
	if (res != TEE_SUCCESS)
		return res;

	if (s < req_size || !buffer)
		return TEE_ERROR_SHORT_BUFFER;

	return copy_to_user(buffer, value, req_size);
}

static TEE_Result op_attr_value_to_binary(void *attr, void *data,
					  size_t data_len, size_t *offs)
{
	uint32_t *v = attr;

	return op_u32_to_binary_helper(*v, data, data_len, offs);
}

static bool op_attr_value_from_binary(void *attr, const void *data,
				      size_t data_len, size_t *offs)
{
	uint32_t *v = attr;

	return op_u32_from_binary_helper(v, data, data_len, offs);
}

static TEE_Result op_attr_value_from_obj(void *attr, void *src_attr)
{
	uint32_t *v = attr;
	uint32_t *src_v = src_attr;

	*v = *src_v;
	return TEE_SUCCESS;
}

static void op_attr_value_clear(void *attr)
{
	uint32_t *v = attr;

	*v = 0;
}

static const struct attr_ops attr_ops[] = {
	[ATTR_OPS_INDEX_SECRET] = {
		.from_user = op_attr_secret_value_from_user,
		.to_user = op_attr_secret_value_to_user,
		.to_binary = op_attr_secret_value_to_binary,
		.from_binary = op_attr_secret_value_from_binary,
		.from_obj = op_attr_secret_value_from_obj,
		.free = op_attr_secret_value_clear, /* not a typo */
		.clear = op_attr_secret_value_clear,
	},
	[ATTR_OPS_INDEX_BIGNUM] = {
		.from_user = op_attr_bignum_from_user,
		.to_user = op_attr_bignum_to_user,
		.to_binary = op_attr_bignum_to_binary,
		.from_binary = op_attr_bignum_from_binary,
		.from_obj = op_attr_bignum_from_obj,
		.free = op_attr_bignum_free,
		.clear = op_attr_bignum_clear,
	},
	[ATTR_OPS_INDEX_VALUE] = {
		.from_user = op_attr_value_from_user,
		.to_user = op_attr_value_to_user,
		.to_binary = op_attr_value_to_binary,
		.from_binary = op_attr_value_from_binary,
		.from_obj = op_attr_value_from_obj,
		.free = op_attr_value_clear, /* not a typo */
		.clear = op_attr_value_clear,
	},
};

static TEE_Result get_user_u64_as_size_t(size_t *dst, uint64_t *src)
{
	uint64_t d = 0;
	TEE_Result res = copy_from_user(&d, src, sizeof(d));

	/*
	 * On 32-bit systems a size_t can't hold a uint64_t so we need to
	 * check that the value isn't too large.
	 */
	if (!res && ADD_OVERFLOW(0, d, dst))
		return TEE_ERROR_OVERFLOW;

	return res;
}

static TEE_Result put_user_u64(uint64_t *dst, size_t value)
{
	uint64_t v = value;

	return copy_to_user(dst, &v, sizeof(v));
}

TEE_Result syscall_cryp_obj_get_info(unsigned long obj, TEE_ObjectInfo *info)
{
	struct ts_session *sess = ts_get_current_session();
	TEE_Result res = TEE_SUCCESS;
	struct tee_obj *o = NULL;

	res = tee_obj_get(to_user_ta_ctx(sess->ctx),
			  uref_to_vaddr(obj), &o);
	if (res != TEE_SUCCESS)
		goto exit;

	res = copy_to_user_private(info, &o->info, sizeof(o->info));

exit:
	return res;
}

TEE_Result syscall_cryp_obj_restrict_usage(unsigned long obj,
			unsigned long usage)
{
	struct ts_session *sess = ts_get_current_session();
	TEE_Result res = TEE_SUCCESS;
	struct tee_obj *o = NULL;

	res = tee_obj_get(to_user_ta_ctx(sess->ctx), uref_to_vaddr(obj), &o);
	if (res != TEE_SUCCESS)
		goto exit;

	o->info.objectUsage &= usage;

exit:
	return res;
}

static int tee_svc_cryp_obj_find_type_attr_idx(
		uint32_t attr_id,
		const struct tee_cryp_obj_type_props *type_props)
{
	size_t n;

	for (n = 0; n < type_props->num_type_attrs; n++) {
		if (attr_id == type_props->type_attrs[n].attr_id)
			return n;
	}
	return -1;
}

static const struct tee_cryp_obj_type_props *tee_svc_find_type_props(
		TEE_ObjectType obj_type)
{
	size_t n;

	for (n = 0; n < ARRAY_SIZE(tee_cryp_obj_props); n++) {
		if (tee_cryp_obj_props[n].obj_type == obj_type)
			return tee_cryp_obj_props + n;
	}

	return NULL;
}

/* Set an attribute on an object */
static void set_attribute(struct tee_obj *o,
			  const struct tee_cryp_obj_type_props *props,
			  uint32_t attr)
{
	int idx = tee_svc_cryp_obj_find_type_attr_idx(attr, props);

	if (idx < 0)
		return;
	o->have_attrs |= BIT(idx);
}

/* Get an attribute on an object */
static uint32_t get_attribute(const struct tee_obj *o,
			      const struct tee_cryp_obj_type_props *props,
			      uint32_t attr)
{
	int idx = tee_svc_cryp_obj_find_type_attr_idx(attr, props);

	if (idx < 0)
		return 0;
	return o->have_attrs & BIT(idx);
}

TEE_Result syscall_cryp_obj_get_attr(unsigned long obj, unsigned long attr_id,
			void *buffer, uint64_t *size)
{
	struct ts_session *sess = ts_get_current_session();
	TEE_Result res = TEE_SUCCESS;
	struct tee_obj *o = NULL;
	const struct tee_cryp_obj_type_props *type_props = NULL;
	int idx = 0;
	const struct attr_ops *ops = NULL;
	void *attr = NULL;

	res = tee_obj_get(to_user_ta_ctx(sess->ctx), uref_to_vaddr(obj), &o);
	if (res != TEE_SUCCESS)
		return TEE_ERROR_ITEM_NOT_FOUND;

	/* Check that the object is initialized */
	if (!(o->info.handleFlags & TEE_HANDLE_FLAG_INITIALIZED))
		return TEE_ERROR_BAD_PARAMETERS;

	/* Check that getting the attribute is allowed */
	if (!(attr_id & TEE_ATTR_FLAG_PUBLIC) &&
	    !(o->info.objectUsage & TEE_USAGE_EXTRACTABLE))
		return TEE_ERROR_BAD_PARAMETERS;

	type_props = tee_svc_find_type_props(o->info.objectType);
	if (!type_props) {
		/* Unknown object type, "can't happen" */
		return TEE_ERROR_BAD_STATE;
	}

	idx = tee_svc_cryp_obj_find_type_attr_idx(attr_id, type_props);
	if ((idx < 0) || ((o->have_attrs & (1 << idx)) == 0))
		return TEE_ERROR_ITEM_NOT_FOUND;

	ops = attr_ops + type_props->type_attrs[idx].ops_index;
	attr = (uint8_t *)o->attr + type_props->type_attrs[idx].raw_offs;
	return ops->to_user(attr, sess, buffer, size);
}

void tee_obj_attr_free(struct tee_obj *o)
{
	const struct tee_cryp_obj_type_props *tp;
	size_t n;

	if (!o->attr)
		return;
	tp = tee_svc_find_type_props(o->info.objectType);
	if (!tp)
		return;

	for (n = 0; n < tp->num_type_attrs; n++) {
		const struct tee_cryp_obj_type_attrs *ta = tp->type_attrs + n;

		attr_ops[ta->ops_index].free((uint8_t *)o->attr + ta->raw_offs);
	}
}

void tee_obj_attr_clear(struct tee_obj *o)
{
	const struct tee_cryp_obj_type_props *tp;
	size_t n;

	if (!o->attr)
		return;
	tp = tee_svc_find_type_props(o->info.objectType);
	if (!tp)
		return;

	for (n = 0; n < tp->num_type_attrs; n++) {
		const struct tee_cryp_obj_type_attrs *ta = tp->type_attrs + n;

		attr_ops[ta->ops_index].clear((uint8_t *)o->attr +
					      ta->raw_offs);
	}
}

TEE_Result tee_obj_attr_to_binary(struct tee_obj *o, void *data,
				  size_t *data_len)
{
	const struct tee_cryp_obj_type_props *tp;
	size_t n;
	size_t offs = 0;
	size_t len = data ? *data_len : 0;
	TEE_Result res;

	if (o->info.objectType == TEE_TYPE_DATA) {
		*data_len = 0;
		return TEE_SUCCESS; /* pure data object */
	}
	if (!o->attr)
		return TEE_ERROR_BAD_STATE;
	tp = tee_svc_find_type_props(o->info.objectType);
	if (!tp)
		return TEE_ERROR_BAD_STATE;

	for (n = 0; n < tp->num_type_attrs; n++) {
		const struct tee_cryp_obj_type_attrs *ta = tp->type_attrs + n;
		void *attr = (uint8_t *)o->attr + ta->raw_offs;

		res = attr_ops[ta->ops_index].to_binary(attr, data, len, &offs);
		if (res != TEE_SUCCESS)
			return res;
	}

	*data_len = offs;
	if (data && offs > len)
		return TEE_ERROR_SHORT_BUFFER;
	return TEE_SUCCESS;
}

TEE_Result tee_obj_attr_from_binary(struct tee_obj *o, const void *data,
				    size_t data_len)
{
	const struct tee_cryp_obj_type_props *tp;
	size_t n;
	size_t offs = 0;

	if (o->info.objectType == TEE_TYPE_DATA)
		return TEE_SUCCESS; /* pure data object */
	if (!o->attr)
		return TEE_ERROR_BAD_STATE;
	tp = tee_svc_find_type_props(o->info.objectType);
	if (!tp)
		return TEE_ERROR_BAD_STATE;

	for (n = 0; n < tp->num_type_attrs; n++) {
		const struct tee_cryp_obj_type_attrs *ta = tp->type_attrs + n;
		void *attr = (uint8_t *)o->attr + ta->raw_offs;

		if (!attr_ops[ta->ops_index].from_binary(attr, data, data_len,
							 &offs))
			return TEE_ERROR_CORRUPT_OBJECT;
	}
	return TEE_SUCCESS;
}

TEE_Result tee_obj_attr_copy_from(struct tee_obj *o, const struct tee_obj *src)
{
	TEE_Result res;
	const struct tee_cryp_obj_type_props *tp;
	const struct tee_cryp_obj_type_attrs *ta;
	size_t n;
	uint32_t have_attrs = 0;
	void *attr;
	void *src_attr;

	if (o->info.objectType == TEE_TYPE_DATA)
		return TEE_SUCCESS; /* pure data object */
	if (!o->attr)
		return TEE_ERROR_BAD_STATE;
	tp = tee_svc_find_type_props(o->info.objectType);
	if (!tp)
		return TEE_ERROR_BAD_STATE;

	if (o->info.objectType == src->info.objectType) {
		have_attrs = src->have_attrs;
		for (n = 0; n < tp->num_type_attrs; n++) {
			ta = tp->type_attrs + n;
			attr = (uint8_t *)o->attr + ta->raw_offs;
			src_attr = (uint8_t *)src->attr + ta->raw_offs;
			res = attr_ops[ta->ops_index].from_obj(attr, src_attr);
			if (res != TEE_SUCCESS)
				return res;
		}
	} else {
		const struct tee_cryp_obj_type_props *tp_src;
		int idx;

		if (o->info.objectType == TEE_TYPE_RSA_PUBLIC_KEY) {
			if (src->info.objectType != TEE_TYPE_RSA_KEYPAIR)
				return TEE_ERROR_BAD_PARAMETERS;
		} else if (o->info.objectType == TEE_TYPE_DSA_PUBLIC_KEY) {
			if (src->info.objectType != TEE_TYPE_DSA_KEYPAIR)
				return TEE_ERROR_BAD_PARAMETERS;
		} else if (o->info.objectType == TEE_TYPE_ECDSA_PUBLIC_KEY) {
			if (src->info.objectType != TEE_TYPE_ECDSA_KEYPAIR)
				return TEE_ERROR_BAD_PARAMETERS;
		} else if (o->info.objectType == TEE_TYPE_ECDH_PUBLIC_KEY) {
			if (src->info.objectType != TEE_TYPE_ECDH_KEYPAIR)
				return TEE_ERROR_BAD_PARAMETERS;
		} else if (o->info.objectType == TEE_TYPE_SM2_DSA_PUBLIC_KEY) {
			if (src->info.objectType != TEE_TYPE_SM2_DSA_KEYPAIR)
				return TEE_ERROR_BAD_PARAMETERS;
		} else if (o->info.objectType == TEE_TYPE_SM2_PKE_PUBLIC_KEY) {
			if (src->info.objectType != TEE_TYPE_SM2_PKE_KEYPAIR)
				return TEE_ERROR_BAD_PARAMETERS;
		} else if (o->info.objectType == TEE_TYPE_SM2_KEP_PUBLIC_KEY) {
			if (src->info.objectType != TEE_TYPE_SM2_KEP_KEYPAIR)
				return TEE_ERROR_BAD_PARAMETERS;
		} else {
			return TEE_ERROR_BAD_PARAMETERS;
		}

		tp_src = tee_svc_find_type_props(src->info.objectType);
		if (!tp_src)
			return TEE_ERROR_BAD_STATE;

		have_attrs = BIT32(tp->num_type_attrs) - 1;
		for (n = 0; n < tp->num_type_attrs; n++) {
			ta = tp->type_attrs + n;

			idx = tee_svc_cryp_obj_find_type_attr_idx(ta->attr_id,
								  tp_src);
			if (idx < 0)
				return TEE_ERROR_BAD_STATE;

			attr = (uint8_t *)o->attr + ta->raw_offs;
			src_attr = (uint8_t *)src->attr +
				   tp_src->type_attrs[idx].raw_offs;
			res = attr_ops[ta->ops_index].from_obj(attr, src_attr);
			if (res != TEE_SUCCESS)
				return res;
		}
	}

	o->have_attrs = have_attrs;
	return TEE_SUCCESS;
}

static bool is_gp_legacy_des_key_size(TEE_ObjectType type, size_t sz)
{
	return IS_ENABLED(CFG_COMPAT_GP10_DES) &&
	       ((type == TEE_TYPE_DES && sz == 56) ||
		(type == TEE_TYPE_DES3 && (sz == 112 || sz == 168)));
}

static TEE_Result check_key_size(const struct tee_cryp_obj_type_props *props,
				 size_t key_size)
{
	size_t sz = key_size;

	/*
	 * In GP Internal API Specification 1.0 the partity bits aren't
	 * counted when telling the size of the key in bits so add them
	 * here if missing.
	 */
	if (is_gp_legacy_des_key_size(props->obj_type, sz))
		sz += sz / 7;

	if (sz % props->quanta != 0)
		return TEE_ERROR_NOT_SUPPORTED;
	if (sz < props->min_size)
		return TEE_ERROR_NOT_SUPPORTED;
	if (sz > props->max_size)
		return TEE_ERROR_NOT_SUPPORTED;

	return TEE_SUCCESS;
}

TEE_Result tee_obj_set_type(struct tee_obj *o, uint32_t obj_type,
			    size_t max_key_size)
{
	TEE_Result res = TEE_SUCCESS;
	const struct tee_cryp_obj_type_props *type_props;

	/* Can only set type for newly allocated objs */
	if (o->attr)
		return TEE_ERROR_BAD_STATE;

	/*
	 * Verify that maxKeySize is supported and find out how
	 * much should be allocated.
	 */

	if (obj_type == TEE_TYPE_DATA) {
		if (max_key_size)
			return TEE_ERROR_NOT_SUPPORTED;
	} else {
		/* Find description of object */
		type_props = tee_svc_find_type_props(obj_type);
		if (!type_props)
			return TEE_ERROR_NOT_SUPPORTED;

		/* Check that max_key_size follows restrictions */
		res = check_key_size(type_props, max_key_size);
		if (res)
			return res;

		o->attr = calloc(1, type_props->alloc_size);
		if (!o->attr)
			return TEE_ERROR_OUT_OF_MEMORY;
	}

	/* If we have a key structure, pre-allocate the bignums inside */
	switch (obj_type) {
	case TEE_TYPE_RSA_PUBLIC_KEY:
		res = crypto_acipher_alloc_rsa_public_key(o->attr,
							  max_key_size);
		break;
	case TEE_TYPE_RSA_KEYPAIR:
		res = crypto_acipher_alloc_rsa_keypair(o->attr, max_key_size);
		break;
	case TEE_TYPE_DSA_PUBLIC_KEY:
		res = crypto_acipher_alloc_dsa_public_key(o->attr,
							  max_key_size);
		break;
	case TEE_TYPE_DSA_KEYPAIR:
		res = crypto_acipher_alloc_dsa_keypair(o->attr, max_key_size);
		break;
	case TEE_TYPE_DH_KEYPAIR:
		res = crypto_acipher_alloc_dh_keypair(o->attr, max_key_size);
		break;
	case TEE_TYPE_ECDSA_PUBLIC_KEY:
	case TEE_TYPE_ECDH_PUBLIC_KEY:
	case TEE_TYPE_SM2_DSA_PUBLIC_KEY:
	case TEE_TYPE_SM2_PKE_PUBLIC_KEY:
	case TEE_TYPE_SM2_KEP_PUBLIC_KEY:
		res = crypto_acipher_alloc_ecc_public_key(o->attr, obj_type,
							  max_key_size);
		break;
	case TEE_TYPE_ECDSA_KEYPAIR:
	case TEE_TYPE_ECDH_KEYPAIR:
	case TEE_TYPE_SM2_DSA_KEYPAIR:
	case TEE_TYPE_SM2_PKE_KEYPAIR:
	case TEE_TYPE_SM2_KEP_KEYPAIR:
		res = crypto_acipher_alloc_ecc_keypair(o->attr, obj_type,
						       max_key_size);
		break;
	default:
		if (obj_type != TEE_TYPE_DATA) {
			struct tee_cryp_obj_secret *key = o->attr;

			key->alloc_size = type_props->alloc_size -
					  sizeof(*key);
		}
		break;
	}

	if (res != TEE_SUCCESS)
		return res;

	o->info.objectType = obj_type;
	o->info.maxKeySize = max_key_size;
	o->info.objectUsage = TEE_USAGE_DEFAULT;

	return TEE_SUCCESS;
}

TEE_Result syscall_cryp_obj_alloc(unsigned long obj_type,
			unsigned long max_key_size, uint32_t *obj)
{
	struct ts_session *sess = ts_get_current_session();
	TEE_Result res = TEE_SUCCESS;
	struct tee_obj *o = NULL;


	o = tee_obj_alloc();
	if (!o)
		return TEE_ERROR_OUT_OF_MEMORY;

	res = tee_obj_set_type(o, obj_type, max_key_size);
	if (res != TEE_SUCCESS) {
		tee_obj_free(o);
		return res;
	}

	tee_obj_add(to_user_ta_ctx(sess->ctx), o);

	res = copy_kaddr_to_uref(obj, o);
	if (res != TEE_SUCCESS)
		tee_obj_close(to_user_ta_ctx(sess->ctx), o);
	return res;
}

TEE_Result syscall_cryp_obj_close(unsigned long obj)
{
	struct ts_session *sess = ts_get_current_session();
	TEE_Result res = TEE_SUCCESS;
	struct tee_obj *o = NULL;

	res = tee_obj_get(to_user_ta_ctx(sess->ctx), uref_to_vaddr(obj), &o);
	if (res != TEE_SUCCESS)
		return res;

	/*
	 * If it's busy it's used by an operation, a client should never have
	 * this handle.
	 */
	if (o->busy)
		return TEE_ERROR_ITEM_NOT_FOUND;

	tee_obj_close(to_user_ta_ctx(sess->ctx), o);
	return TEE_SUCCESS;
}

TEE_Result syscall_cryp_obj_reset(unsigned long obj)
{
	struct ts_session *sess = ts_get_current_session();
	TEE_Result res = TEE_SUCCESS;
	struct tee_obj *o = NULL;

	res = tee_obj_get(to_user_ta_ctx(sess->ctx), uref_to_vaddr(obj), &o);
	if (res != TEE_SUCCESS)
		return res;

	if ((o->info.handleFlags & TEE_HANDLE_FLAG_PERSISTENT) == 0) {
		tee_obj_attr_clear(o);
		o->info.keySize = 0;
		o->info.objectUsage = TEE_USAGE_DEFAULT;
	} else {
		return TEE_ERROR_BAD_PARAMETERS;
	}

	/* the object is no more initialized */
	o->info.handleFlags &= ~TEE_HANDLE_FLAG_INITIALIZED;

	return TEE_SUCCESS;
}

static TEE_Result copy_in_attrs(struct user_ta_ctx *utc,
			const struct utee_attribute *usr_attrs,
			uint32_t attr_count, TEE_Attribute *attrs)
{
	TEE_Result res = TEE_SUCCESS;
	size_t size = 0;
	uint32_t n = 0;

	if (MUL_OVERFLOW(sizeof(struct utee_attribute), attr_count, &size))
		return TEE_ERROR_OVERFLOW;

	res = vm_check_access_rights(&utc->uctx,
				     TEE_MEMORY_ACCESS_READ |
				     TEE_MEMORY_ACCESS_ANY_OWNER,
				     (uaddr_t)usr_attrs, size);
	if (res != TEE_SUCCESS)
		return res;

	for (n = 0; n < attr_count; n++) {
		attrs[n].attributeID = usr_attrs[n].attribute_id;
		if (attrs[n].attributeID & TEE_ATTR_FLAG_VALUE) {
			attrs[n].content.value.a = usr_attrs[n].a;
			attrs[n].content.value.b = usr_attrs[n].b;
		} else {
			uintptr_t buf = usr_attrs[n].a;
			size_t len = usr_attrs[n].b;
			uint32_t flags = TEE_MEMORY_ACCESS_READ |
					 TEE_MEMORY_ACCESS_ANY_OWNER;

			res = vm_check_access_rights(&utc->uctx, flags, buf,
						     len);
			if (res != TEE_SUCCESS)
				return res;
			attrs[n].content.ref.buffer = (void *)buf;
			attrs[n].content.ref.length = len;
		}
	}

	return TEE_SUCCESS;
}

enum attr_usage {
	ATTR_USAGE_POPULATE,
	ATTR_USAGE_GENERATE_KEY
};

static TEE_Result tee_svc_cryp_check_attr(enum attr_usage usage,
					  const struct tee_cryp_obj_type_props
						*type_props,
					  const TEE_Attribute *attrs,
					  uint32_t attr_count)
{
	uint32_t required_flag = 0;
	uint32_t opt_flag = 0;
	bool all_opt_needed = false;
	uint32_t req_attrs = 0;
	uint32_t opt_grp_attrs = 0;
	uint32_t attrs_found = 0;
	size_t n = 0;
	uint32_t bit = 0;
	uint32_t flags = 0;
	int idx = 0;

	if (usage == ATTR_USAGE_POPULATE) {
		required_flag = TEE_TYPE_ATTR_REQUIRED;
		opt_flag = TEE_TYPE_ATTR_OPTIONAL_GROUP;
		all_opt_needed = true;
	} else {
		required_flag = TEE_TYPE_ATTR_GEN_KEY_REQ;
		opt_flag = TEE_TYPE_ATTR_GEN_KEY_OPT;
		all_opt_needed = false;
	}

	/*
	 * First find out which attributes are required and which belong to
	 * the optional group
	 */
	for (n = 0; n < type_props->num_type_attrs; n++) {
		bit = 1 << n;
		flags = type_props->type_attrs[n].flags;

		if (flags & required_flag)
			req_attrs |= bit;
		else if (flags & opt_flag)
			opt_grp_attrs |= bit;
	}

	/*
	 * Verify that all required attributes are in place and
	 * that the same attribute isn't repeated.
	 */
	for (n = 0; n < attr_count; n++) {
		idx = tee_svc_cryp_obj_find_type_attr_idx(
							attrs[n].attributeID,
							type_props);

		/* attribute not defined in current object type */
		if (idx < 0)
			return TEE_ERROR_ITEM_NOT_FOUND;

		bit = 1 << idx;

		/* attribute not repeated */
		if ((attrs_found & bit) != 0)
			return TEE_ERROR_ITEM_NOT_FOUND;

		/*
		 * Attribute not defined in current object type for this
		 * usage.
		 */
		if (!(bit & (req_attrs | opt_grp_attrs)))
			return TEE_ERROR_ITEM_NOT_FOUND;

		attrs_found |= bit;
	}
	/* Required attribute missing */
	if ((attrs_found & req_attrs) != req_attrs)
		return TEE_ERROR_ITEM_NOT_FOUND;

	/*
	 * If the flag says that "if one of the optional attributes are included
	 * all of them has to be included" this must be checked.
	 */
	if (all_opt_needed && (attrs_found & opt_grp_attrs) != 0 &&
	    (attrs_found & opt_grp_attrs) != opt_grp_attrs)
		return TEE_ERROR_ITEM_NOT_FOUND;

	return TEE_SUCCESS;
}

static TEE_Result get_ec_key_size(uint32_t curve, size_t *key_size)
{
	switch (curve) {
	case TEE_ECC_CURVE_NIST_P192:
		*key_size = 192;
		break;
	case TEE_ECC_CURVE_NIST_P224:
		*key_size = 224;
		break;
	case TEE_ECC_CURVE_NIST_P256:
		*key_size = 256;
		break;
	case TEE_ECC_CURVE_NIST_P384:
		*key_size = 384;
		break;
	case TEE_ECC_CURVE_NIST_P521:
		*key_size = 521;
		break;
	case TEE_ECC_CURVE_SM2:
		*key_size = 256;
		break;
	default:
		return TEE_ERROR_NOT_SUPPORTED;
	}

	return TEE_SUCCESS;
}

static size_t get_used_bits(const TEE_Attribute *a)
{
	int nbits = a->content.ref.length * 8;
	int v = 0;

	bit_ffs(a->content.ref.buffer, nbits, &v);
	return nbits - v;
}

static TEE_Result tee_svc_cryp_obj_populate_type(
		struct tee_obj *o,
		const struct tee_cryp_obj_type_props *type_props,
		const TEE_Attribute *attrs,
		uint32_t attr_count)
{
	TEE_Result res = TEE_SUCCESS;
	uint32_t have_attrs = 0;
	size_t obj_size = 0;
	size_t n = 0;
	int idx = 0;
	const struct attr_ops *ops = NULL;
	void *attr = NULL;

	for (n = 0; n < attr_count; n++) {
		idx = tee_svc_cryp_obj_find_type_attr_idx(
							attrs[n].attributeID,
							type_props);
		/* attribute not defined in current object type */
		if (idx < 0)
			return TEE_ERROR_ITEM_NOT_FOUND;

		have_attrs |= BIT32(idx);
		ops = attr_ops + type_props->type_attrs[idx].ops_index;
		attr = (uint8_t *)o->attr +
		       type_props->type_attrs[idx].raw_offs;
		if (attrs[n].attributeID & TEE_ATTR_FLAG_VALUE)
			res = ops->from_user(attr, &attrs[n].content.value,
					     sizeof(attrs[n].content.value));
		else
			res = ops->from_user(attr, attrs[n].content.ref.buffer,
					     attrs[n].content.ref.length);
		if (res != TEE_SUCCESS)
			return res;

		/*
		 * The attribute that gives the size of the object is
		 * flagged with TEE_TYPE_ATTR_SIZE_INDICATOR.
		 */
		if (type_props->type_attrs[idx].flags &
		    TEE_TYPE_ATTR_SIZE_INDICATOR) {
			/* There should be only one */
			if (obj_size)
				return TEE_ERROR_BAD_STATE;

			/*
			 * For ECDSA/ECDH we need to translate curve into
			 * object size
			 */
			if (attrs[n].attributeID == TEE_ATTR_ECC_CURVE) {
				res = get_ec_key_size(attrs[n].content.value.a,
						      &obj_size);
				if (res != TEE_SUCCESS)
					return res;
			} else {
				TEE_ObjectType obj_type = o->info.objectType;
				size_t sz = o->info.maxKeySize;

				obj_size = attrs[n].content.ref.length * 8;
				/* Drop the parity bits for legacy objects */
				if (is_gp_legacy_des_key_size(obj_type, sz))
					obj_size -= obj_size / 8;
			}
			if (obj_size > o->info.maxKeySize)
				return TEE_ERROR_BAD_STATE;
		}

		/*
		 * Bignum attributes limited by the number of bits in
		 * o->info.keySize are flagged with
		 * TEE_TYPE_ATTR_BIGNUM_MAXBITS.
		 */
		if (type_props->type_attrs[idx].flags &
		    TEE_TYPE_ATTR_BIGNUM_MAXBITS) {
			if (get_used_bits(attrs + n) > o->info.maxKeySize)
				return TEE_ERROR_BAD_STATE;
		}
	}

	o->have_attrs = have_attrs;
	o->info.keySize = obj_size;
	/*
	 * In GP Internal API Specification 1.0 the partity bits aren't
	 * counted when telling the size of the key in bits so remove the
	 * parity bits here.
	 */
	if (is_gp_legacy_des_key_size(o->info.objectType, o->info.maxKeySize))
		o->info.keySize -= o->info.keySize / 8;

	return TEE_SUCCESS;
}

TEE_Result syscall_cryp_obj_populate(unsigned long obj,
			struct utee_attribute *usr_attrs,
			unsigned long attr_count)
{
	struct ts_session *sess = ts_get_current_session();
	TEE_Result res = TEE_SUCCESS;
	struct tee_obj *o = NULL;
	const struct tee_cryp_obj_type_props *type_props = NULL;
	TEE_Attribute *attrs = NULL;
	size_t alloc_size = 0;

	res = tee_obj_get(to_user_ta_ctx(sess->ctx), uref_to_vaddr(obj), &o);
	if (res != TEE_SUCCESS)
		return res;

	/* Must be a transient object */
	if ((o->info.handleFlags & TEE_HANDLE_FLAG_PERSISTENT) != 0)
		return TEE_ERROR_BAD_PARAMETERS;

	/* Must not be initialized already */
	if ((o->info.handleFlags & TEE_HANDLE_FLAG_INITIALIZED) != 0)
		return TEE_ERROR_BAD_PARAMETERS;

	type_props = tee_svc_find_type_props(o->info.objectType);
	if (!type_props)
		return TEE_ERROR_NOT_IMPLEMENTED;

	if (MUL_OVERFLOW(sizeof(TEE_Attribute), attr_count, &alloc_size))
		return TEE_ERROR_OVERFLOW;

	attrs = malloc(alloc_size);
	if (!attrs)
		return TEE_ERROR_OUT_OF_MEMORY;

	res = copy_in_attrs(to_user_ta_ctx(sess->ctx), usr_attrs, attr_count,
			    attrs);
	if (res != TEE_SUCCESS)
		goto out;

	res = tee_svc_cryp_check_attr(ATTR_USAGE_POPULATE, type_props,
				      attrs, attr_count);
	if (res != TEE_SUCCESS)
		goto out;

	res = tee_svc_cryp_obj_populate_type(o, type_props, attrs, attr_count);
	if (res == TEE_SUCCESS)
		o->info.handleFlags |= TEE_HANDLE_FLAG_INITIALIZED;

out:
	free_wipe(attrs);
	return res;
}

TEE_Result syscall_cryp_obj_copy(unsigned long dst, unsigned long src)
{
	struct ts_session *sess = ts_get_current_session();
	TEE_Result res = TEE_SUCCESS;
	struct tee_obj *dst_o = NULL;
	struct tee_obj *src_o = NULL;

	res = tee_obj_get(to_user_ta_ctx(sess->ctx),
			  uref_to_vaddr(dst), &dst_o);
	if (res != TEE_SUCCESS)
		return res;

	res = tee_obj_get(to_user_ta_ctx(sess->ctx),
			  uref_to_vaddr(src), &src_o);
	if (res != TEE_SUCCESS)
		return res;

	if ((src_o->info.handleFlags & TEE_HANDLE_FLAG_INITIALIZED) == 0)
		return TEE_ERROR_BAD_PARAMETERS;
	if ((dst_o->info.handleFlags & TEE_HANDLE_FLAG_PERSISTENT) != 0)
		return TEE_ERROR_BAD_PARAMETERS;
	if ((dst_o->info.handleFlags & TEE_HANDLE_FLAG_INITIALIZED) != 0)
		return TEE_ERROR_BAD_PARAMETERS;

	res = tee_obj_attr_copy_from(dst_o, src_o);
	if (res != TEE_SUCCESS)
		return res;

	dst_o->info.handleFlags |= TEE_HANDLE_FLAG_INITIALIZED;
	dst_o->info.keySize = src_o->info.keySize;
	dst_o->info.objectUsage = src_o->info.objectUsage;
	return TEE_SUCCESS;
}

static TEE_Result check_pub_rsa_key(struct bignum *e)
{
	size_t n = crypto_bignum_num_bytes(e);
	uint8_t bin_key[256 / 8] = { 0 };

	/*
	 * NIST SP800-56B requires public RSA key to be an odd integer in
	 * the range 65537 <= e < 2^256.
	 */

	if (n > sizeof(bin_key) || n < 3)
		return TEE_ERROR_BAD_PARAMETERS;

	crypto_bignum_bn2bin(e, bin_key);

	if (!(bin_key[n - 1] & 1)) /* key must be odd */
		return TEE_ERROR_BAD_PARAMETERS;

	if (n == 3) {
		uint32_t key = 0;

		for (n = 0; n < 3; n++) {
			key <<= 8;
			key |= bin_key[n];
		}

		if (key < 65537)
			return TEE_ERROR_BAD_PARAMETERS;
	}

	/* key is larger than 65537 */
	return TEE_SUCCESS;
}

static TEE_Result tee_svc_obj_generate_key_rsa(
	struct tee_obj *o, const struct tee_cryp_obj_type_props *type_props,
	uint32_t key_size,
	const TEE_Attribute *params, uint32_t param_count)
{
	TEE_Result res = TEE_SUCCESS;
	struct rsa_keypair *key = o->attr;
	uint32_t e = TEE_U32_TO_BIG_ENDIAN(65537);

	/* Copy the present attributes into the obj before starting */
	res = tee_svc_cryp_obj_populate_type(o, type_props, params,
					     param_count);
	if (res != TEE_SUCCESS)
		return res;
	if (get_attribute(o, type_props, TEE_ATTR_RSA_PUBLIC_EXPONENT)) {
		res = check_pub_rsa_key(key->e);
		if (res)
			return res;
	} else {
		crypto_bignum_bin2bn((const uint8_t *)&e, sizeof(e), key->e);
	}
	res = crypto_acipher_gen_rsa_key(key, key_size);
	if (res != TEE_SUCCESS)
		return res;

	/* Set bits for all known attributes for this object type */
	o->have_attrs = (1 << type_props->num_type_attrs) - 1;

	return TEE_SUCCESS;
}

static TEE_Result tee_svc_obj_generate_key_dsa(
	struct tee_obj *o, const struct tee_cryp_obj_type_props *type_props,
	uint32_t key_size, const TEE_Attribute *params, uint32_t param_count)
{
	TEE_Result res;

	/* Copy the present attributes into the obj before starting */
	res = tee_svc_cryp_obj_populate_type(o, type_props, params,
					     param_count);
	if (res != TEE_SUCCESS)
		return res;

	res = crypto_acipher_gen_dsa_key(o->attr, key_size);
	if (res != TEE_SUCCESS)
		return res;

	/* Set bits for all known attributes for this object type */
	o->have_attrs = (1 << type_props->num_type_attrs) - 1;

	return TEE_SUCCESS;
}

static TEE_Result tee_svc_obj_generate_key_dh(
	struct tee_obj *o, const struct tee_cryp_obj_type_props *type_props,
	uint32_t key_size, const TEE_Attribute *params, uint32_t param_count)
{
	TEE_Result res;
	struct dh_keypair *tee_dh_key;
	struct bignum *dh_q = NULL;
	uint32_t dh_xbits = 0;

	/* Copy the present attributes into the obj before starting */
	res = tee_svc_cryp_obj_populate_type(o, type_props, params,
					     param_count);
	if (res != TEE_SUCCESS)
		return res;

	tee_dh_key = (struct dh_keypair *)o->attr;

	if (get_attribute(o, type_props, TEE_ATTR_DH_SUBPRIME))
		dh_q = tee_dh_key->q;
	if (get_attribute(o, type_props, TEE_ATTR_DH_X_BITS))
		dh_xbits = tee_dh_key->xbits;
	res = crypto_acipher_gen_dh_key(tee_dh_key, dh_q, dh_xbits, key_size);
	if (res != TEE_SUCCESS)
		return res;

	/* Set bits for the generated public and private key */
	set_attribute(o, type_props, TEE_ATTR_DH_PUBLIC_VALUE);
	set_attribute(o, type_props, TEE_ATTR_DH_PRIVATE_VALUE);
	set_attribute(o, type_props, TEE_ATTR_DH_X_BITS);
	return TEE_SUCCESS;
}

static TEE_Result tee_svc_obj_generate_key_ecc(
	struct tee_obj *o, const struct tee_cryp_obj_type_props *type_props,
	uint32_t key_size, const TEE_Attribute *params, uint32_t param_count)
{
	TEE_Result res;
	struct ecc_keypair *tee_ecc_key;

	/* Copy the present attributes into the obj before starting */
	res = tee_svc_cryp_obj_populate_type(o, type_props, params,
					     param_count);
	if (res != TEE_SUCCESS)
		return res;

	tee_ecc_key = (struct ecc_keypair *)o->attr;

	res = crypto_acipher_gen_ecc_key(tee_ecc_key, key_size);
	if (res != TEE_SUCCESS)
		return res;

	/* Set bits for the generated public and private key */
	set_attribute(o, type_props, TEE_ATTR_ECC_PRIVATE_VALUE);
	set_attribute(o, type_props, TEE_ATTR_ECC_PUBLIC_VALUE_X);
	set_attribute(o, type_props, TEE_ATTR_ECC_PUBLIC_VALUE_Y);
	set_attribute(o, type_props, TEE_ATTR_ECC_CURVE);
	return TEE_SUCCESS;
}

TEE_Result syscall_obj_generate_key(unsigned long obj, unsigned long key_size,
			const struct utee_attribute *usr_params,
			unsigned long param_count)
{
	struct ts_session *sess = ts_get_current_session();
	TEE_Result res = TEE_SUCCESS;
	const struct tee_cryp_obj_type_props *type_props = NULL;
	struct tee_obj *o = NULL;
	struct tee_cryp_obj_secret *key = NULL;
	size_t byte_size = 0;
	TEE_Attribute *params = NULL;
	size_t alloc_size = 0;

	res = tee_obj_get(to_user_ta_ctx(sess->ctx), uref_to_vaddr(obj), &o);
	if (res != TEE_SUCCESS)
		return res;

	/* Must be a transient object */
	if ((o->info.handleFlags & TEE_HANDLE_FLAG_PERSISTENT) != 0)
		return TEE_ERROR_BAD_STATE;

	/* Must not be initialized already */
	if ((o->info.handleFlags & TEE_HANDLE_FLAG_INITIALIZED) != 0)
		return TEE_ERROR_BAD_STATE;

	/* Find description of object */
	type_props = tee_svc_find_type_props(o->info.objectType);
	if (!type_props)
		return TEE_ERROR_NOT_SUPPORTED;

	/* Check that key_size follows restrictions */
	res = check_key_size(type_props, key_size);
	if (res)
		return res;

	if (MUL_OVERFLOW(sizeof(TEE_Attribute), param_count, &alloc_size))
		return TEE_ERROR_OVERFLOW;

	params = malloc(alloc_size);
	if (!params)
		return TEE_ERROR_OUT_OF_MEMORY;
	res = copy_in_attrs(to_user_ta_ctx(sess->ctx), usr_params, param_count,
			    params);
	if (res != TEE_SUCCESS)
		goto out;

	res = tee_svc_cryp_check_attr(ATTR_USAGE_GENERATE_KEY, type_props,
				      params, param_count);
	if (res != TEE_SUCCESS)
		goto out;

	switch (o->info.objectType) {
	case TEE_TYPE_AES:
	case TEE_TYPE_DES:
	case TEE_TYPE_DES3:
	case TEE_TYPE_SM4:
	case TEE_TYPE_HMAC_MD5:
	case TEE_TYPE_HMAC_SHA1:
	case TEE_TYPE_HMAC_SHA224:
	case TEE_TYPE_HMAC_SHA256:
	case TEE_TYPE_HMAC_SHA384:
	case TEE_TYPE_HMAC_SHA512:
	case TEE_TYPE_HMAC_SM3:
	case TEE_TYPE_GENERIC_SECRET:
		byte_size = key_size / 8;

		/*
		 * In GP Internal API Specification 1.0 the partity bits
		 * aren't counted when telling the size of the key in bits.
		 */
		if (is_gp_legacy_des_key_size(o->info.objectType, key_size))
			byte_size = (key_size + key_size / 7) / 8;

		key = (struct tee_cryp_obj_secret *)o->attr;
		if (byte_size > key->alloc_size) {
			res = TEE_ERROR_EXCESS_DATA;
			goto out;
		}

		res = crypto_rng_read((void *)(key + 1), byte_size);
		if (res != TEE_SUCCESS)
			goto out;

		key->key_size = byte_size;

		/* Set bits for all known attributes for this object type */
		o->have_attrs = (1 << type_props->num_type_attrs) - 1;

		break;

	case TEE_TYPE_RSA_KEYPAIR:
		res = tee_svc_obj_generate_key_rsa(o, type_props, key_size,
						   params, param_count);
		if (res != TEE_SUCCESS)
			goto out;
		break;

	case TEE_TYPE_DSA_KEYPAIR:
		res = tee_svc_obj_generate_key_dsa(o, type_props, key_size,
						   params, param_count);
		if (res != TEE_SUCCESS)
			goto out;
		break;

	case TEE_TYPE_DH_KEYPAIR:
		res = tee_svc_obj_generate_key_dh(o, type_props, key_size,
						  params, param_count);
		if (res != TEE_SUCCESS)
			goto out;
		break;

	case TEE_TYPE_ECDSA_KEYPAIR:
	case TEE_TYPE_ECDH_KEYPAIR:
	case TEE_TYPE_SM2_PKE_KEYPAIR:
		res = tee_svc_obj_generate_key_ecc(o, type_props, key_size,
						  params, param_count);
		if (res != TEE_SUCCESS)
			goto out;
		break;

	default:
		res = TEE_ERROR_BAD_FORMAT;
	}

out:
	free_wipe(params);
	if (res == TEE_SUCCESS) {
		o->info.keySize = key_size;
		o->info.handleFlags |= TEE_HANDLE_FLAG_INITIALIZED;
	}
	return res;
}

static TEE_Result tee_svc_cryp_get_state(struct ts_session *sess,
					 vaddr_t state_id,
					 struct tee_cryp_state **state)
{
	struct tee_cryp_state *s;
	struct user_ta_ctx *utc = to_user_ta_ctx(sess->ctx);

	TAILQ_FOREACH(s, &utc->cryp_states, link) {
		if (state_id == (vaddr_t)s) {
			*state = s;
			return TEE_SUCCESS;
		}
	}
	return TEE_ERROR_BAD_PARAMETERS;
}

static void cryp_state_free(struct user_ta_ctx *utc, struct tee_cryp_state *cs)
{
	struct tee_obj *o;

	if (tee_obj_get(utc, cs->key1, &o) == TEE_SUCCESS)
		tee_obj_close(utc, o);
	if (tee_obj_get(utc, cs->key2, &o) == TEE_SUCCESS)
		tee_obj_close(utc, o);

	TAILQ_REMOVE(&utc->cryp_states, cs, link);
	if (cs->ctx_finalize != NULL)
		cs->ctx_finalize(cs->ctx);

	switch (TEE_ALG_GET_CLASS(cs->algo)) {
	case TEE_OPERATION_CIPHER:
		crypto_cipher_free_ctx(cs->ctx);
		break;
	case TEE_OPERATION_AE:
		crypto_authenc_free_ctx(cs->ctx);
		break;
	case TEE_OPERATION_DIGEST:
		crypto_hash_free_ctx(cs->ctx);
		break;
	case TEE_OPERATION_MAC:
		crypto_mac_free_ctx(cs->ctx);
		break;
	default:
		assert(!cs->ctx);
	}

	free(cs);
}

static TEE_Result tee_svc_cryp_check_key_type(const struct tee_obj *o,
					      uint32_t algo,
					      TEE_OperationMode mode)
{
	uint32_t req_key_type;
	uint32_t req_key_type2 = 0;

	switch (TEE_ALG_GET_MAIN_ALG(algo)) {
	case TEE_MAIN_ALGO_MD5:
		req_key_type = TEE_TYPE_HMAC_MD5;
		break;
	case TEE_MAIN_ALGO_SHA1:
		req_key_type = TEE_TYPE_HMAC_SHA1;
		break;
	case TEE_MAIN_ALGO_SHA224:
		req_key_type = TEE_TYPE_HMAC_SHA224;
		break;
	case TEE_MAIN_ALGO_SHA256:
		req_key_type = TEE_TYPE_HMAC_SHA256;
		break;
	case TEE_MAIN_ALGO_SHA384:
		req_key_type = TEE_TYPE_HMAC_SHA384;
		break;
	case TEE_MAIN_ALGO_SHA512:
		req_key_type = TEE_TYPE_HMAC_SHA512;
		break;
	case TEE_MAIN_ALGO_SM3:
		req_key_type = TEE_TYPE_HMAC_SM3;
		break;
	case TEE_MAIN_ALGO_AES:
		req_key_type = TEE_TYPE_AES;
		break;
	case TEE_MAIN_ALGO_DES:
		req_key_type = TEE_TYPE_DES;
		break;
	case TEE_MAIN_ALGO_DES3:
		req_key_type = TEE_TYPE_DES3;
		break;
	case TEE_MAIN_ALGO_SM4:
		req_key_type = TEE_TYPE_SM4;
		break;
	case TEE_MAIN_ALGO_RSA:
		req_key_type = TEE_TYPE_RSA_KEYPAIR;
		if (mode == TEE_MODE_ENCRYPT || mode == TEE_MODE_VERIFY)
			req_key_type2 = TEE_TYPE_RSA_PUBLIC_KEY;
		break;
	case TEE_MAIN_ALGO_DSA:
		req_key_type = TEE_TYPE_DSA_KEYPAIR;
		if (mode == TEE_MODE_ENCRYPT || mode == TEE_MODE_VERIFY)
			req_key_type2 = TEE_TYPE_DSA_PUBLIC_KEY;
		break;
	case TEE_MAIN_ALGO_DH:
		req_key_type = TEE_TYPE_DH_KEYPAIR;
		break;
	case TEE_MAIN_ALGO_ECDSA:
		req_key_type = TEE_TYPE_ECDSA_KEYPAIR;
		if (mode == TEE_MODE_VERIFY)
			req_key_type2 = TEE_TYPE_ECDSA_PUBLIC_KEY;
		break;
	case TEE_MAIN_ALGO_ECDH:
		req_key_type = TEE_TYPE_ECDH_KEYPAIR;
		break;
	case TEE_MAIN_ALGO_SM2_PKE:
		if (mode == TEE_MODE_ENCRYPT)
			req_key_type = TEE_TYPE_SM2_PKE_PUBLIC_KEY;
		else
			req_key_type = TEE_TYPE_SM2_PKE_KEYPAIR;
		break;
	case TEE_MAIN_ALGO_SM2_DSA_SM3:
		if (mode == TEE_MODE_VERIFY)
			req_key_type = TEE_TYPE_SM2_DSA_PUBLIC_KEY;
		else
			req_key_type = TEE_TYPE_SM2_DSA_KEYPAIR;
		break;
#if defined(CFG_CRYPTO_SM2_KEP)
	case TEE_MAIN_ALGO_SM2_KEP:
		req_key_type = TEE_TYPE_SM2_KEP_KEYPAIR;
		req_key_type2 = TEE_TYPE_SM2_KEP_PUBLIC_KEY;
		break;
#endif
#if defined(CFG_CRYPTO_HKDF)
	case TEE_MAIN_ALGO_HKDF:
		req_key_type = TEE_TYPE_HKDF_IKM;
		break;
#endif
#if defined(CFG_CRYPTO_CONCAT_KDF)
	case TEE_MAIN_ALGO_CONCAT_KDF:
		req_key_type = TEE_TYPE_CONCAT_KDF_Z;
		break;
#endif
#if defined(CFG_CRYPTO_PBKDF2)
	case TEE_MAIN_ALGO_PBKDF2:
		req_key_type = TEE_TYPE_PBKDF2_PASSWORD;
		break;
#endif
	default:
		return TEE_ERROR_BAD_PARAMETERS;
	}

	if (req_key_type != o->info.objectType &&
	    req_key_type2 != o->info.objectType)
		return TEE_ERROR_BAD_PARAMETERS;
	return TEE_SUCCESS;
}

TEE_Result syscall_cryp_state_alloc(unsigned long algo, unsigned long mode,
			unsigned long key1, unsigned long key2,
			uint32_t *state)
{
	struct ts_session *sess = ts_get_current_session();
	struct user_ta_ctx *utc = to_user_ta_ctx(sess->ctx);
	TEE_Result res = TEE_SUCCESS;
	struct tee_cryp_state *cs = NULL;
	struct tee_obj *o1 = NULL;
	struct tee_obj *o2 = NULL;

	if (key1 != 0) {
		res = tee_obj_get(utc, uref_to_vaddr(key1), &o1);
		if (res != TEE_SUCCESS)
			return res;
		if (o1->busy)
			return TEE_ERROR_BAD_PARAMETERS;
		res = tee_svc_cryp_check_key_type(o1, algo, mode);
		if (res != TEE_SUCCESS)
			return res;
	}
	if (key2 != 0) {
		res = tee_obj_get(utc, uref_to_vaddr(key2), &o2);
		if (res != TEE_SUCCESS)
			return res;
		if (o2->busy)
			return TEE_ERROR_BAD_PARAMETERS;
		res = tee_svc_cryp_check_key_type(o2, algo, mode);
		if (res != TEE_SUCCESS)
			return res;
	}

	cs = calloc(1, sizeof(struct tee_cryp_state));
	if (!cs)
		return TEE_ERROR_OUT_OF_MEMORY;
	TAILQ_INSERT_TAIL(&utc->cryp_states, cs, link);
	cs->algo = algo;
	cs->mode = mode;
	cs->state = CRYP_STATE_UNINITIALIZED;

	switch (TEE_ALG_GET_CLASS(algo)) {
	case TEE_OPERATION_CIPHER:
		if ((algo == TEE_ALG_AES_XTS && (key1 == 0 || key2 == 0)) ||
		    (algo != TEE_ALG_AES_XTS && (key1 == 0 || key2 != 0))) {
			res = TEE_ERROR_BAD_PARAMETERS;
		} else {
			res = crypto_cipher_alloc_ctx(&cs->ctx, algo);
			if (res != TEE_SUCCESS)
				break;
		}
		break;
	case TEE_OPERATION_AE:
		if (key1 == 0 || key2 != 0) {
			res = TEE_ERROR_BAD_PARAMETERS;
		} else {
			res = crypto_authenc_alloc_ctx(&cs->ctx, algo);
			if (res != TEE_SUCCESS)
				break;
		}
		break;
	case TEE_OPERATION_MAC:
		if (key1 == 0 || key2 != 0) {
			res = TEE_ERROR_BAD_PARAMETERS;
		} else {
			res = crypto_mac_alloc_ctx(&cs->ctx, algo);
			if (res != TEE_SUCCESS)
				break;
		}
		break;
	case TEE_OPERATION_DIGEST:
		if (key1 != 0 || key2 != 0) {
			res = TEE_ERROR_BAD_PARAMETERS;
		} else {
			res = crypto_hash_alloc_ctx(&cs->ctx, algo);
			if (res != TEE_SUCCESS)
				break;
		}
		break;
	case TEE_OPERATION_ASYMMETRIC_CIPHER:
	case TEE_OPERATION_ASYMMETRIC_SIGNATURE:
		if (algo == TEE_ALG_RSASSA_PKCS1_V1_5 &&
		    !IS_ENABLED(CFG_CRYPTO_RSASSA_NA1)) {
			res = TEE_ERROR_NOT_SUPPORTED;
			break;
		}
		if (key1 == 0 || key2 != 0)
			res = TEE_ERROR_BAD_PARAMETERS;
		break;
	case TEE_OPERATION_KEY_DERIVATION:
		if (algo == TEE_ALG_SM2_KEP) {
			if (key1 == 0 || key2 == 0)
				res = TEE_ERROR_BAD_PARAMETERS;
		} else {
			if (key1 == 0 || key2 != 0)
				res = TEE_ERROR_BAD_PARAMETERS;
		}
		break;
	default:
		res = TEE_ERROR_NOT_SUPPORTED;
		break;
	}
	if (res != TEE_SUCCESS)
		goto out;

	res = copy_kaddr_to_uref(state, cs);
	if (res != TEE_SUCCESS)
		goto out;

	/* Register keys */
	if (o1 != NULL) {
		o1->busy = true;
		cs->key1 = (vaddr_t)o1;
	}
	if (o2 != NULL) {
		o2->busy = true;
		cs->key2 = (vaddr_t)o2;
	}

out:
	if (res != TEE_SUCCESS)
		cryp_state_free(utc, cs);
	return res;
}

TEE_Result syscall_cryp_state_copy(unsigned long dst, unsigned long src)
{
	struct ts_session *sess = ts_get_current_session();
	TEE_Result res = TEE_SUCCESS;
	struct tee_cryp_state *cs_dst = NULL;
	struct tee_cryp_state *cs_src = NULL;

	res = tee_svc_cryp_get_state(sess, uref_to_vaddr(dst), &cs_dst);
	if (res != TEE_SUCCESS)
		return res;

	res = tee_svc_cryp_get_state(sess, uref_to_vaddr(src), &cs_src);
	if (res != TEE_SUCCESS)
		return res;
	if (cs_dst->algo != cs_src->algo || cs_dst->mode != cs_src->mode)
		return TEE_ERROR_BAD_PARAMETERS;

	switch (TEE_ALG_GET_CLASS(cs_src->algo)) {
	case TEE_OPERATION_CIPHER:
		crypto_cipher_copy_state(cs_dst->ctx, cs_src->ctx);
		break;
	case TEE_OPERATION_AE:
		crypto_authenc_copy_state(cs_dst->ctx, cs_src->ctx);
		break;
	case TEE_OPERATION_DIGEST:
		crypto_hash_copy_state(cs_dst->ctx, cs_src->ctx);
		break;
	case TEE_OPERATION_MAC:
		crypto_mac_copy_state(cs_dst->ctx, cs_src->ctx);
		break;
	default:
		return TEE_ERROR_BAD_STATE;
	}

	cs_dst->state = cs_src->state;
	cs_dst->ctx_finalize = cs_src->ctx_finalize;

	return TEE_SUCCESS;
}

void tee_svc_cryp_free_states(struct user_ta_ctx *utc)
{
	struct tee_cryp_state_head *states = &utc->cryp_states;

	while (!TAILQ_EMPTY(states))
		cryp_state_free(utc, TAILQ_FIRST(states));
}

TEE_Result syscall_cryp_state_free(unsigned long state)
{
	struct ts_session *sess = ts_get_current_session();
	TEE_Result res = TEE_SUCCESS;
	struct tee_cryp_state *cs = NULL;

	res = tee_svc_cryp_get_state(sess, uref_to_vaddr(state), &cs);
	if (res != TEE_SUCCESS)
		return res;
	cryp_state_free(to_user_ta_ctx(sess->ctx), cs);
	return TEE_SUCCESS;
}

TEE_Result syscall_hash_init(unsigned long state,
			     const void *iv __maybe_unused,
			     size_t iv_len __maybe_unused)
{
	struct ts_session *sess = ts_get_current_session();
	TEE_Result res = TEE_SUCCESS;
	struct tee_cryp_state *cs = NULL;

	res = tee_svc_cryp_get_state(sess, uref_to_vaddr(state), &cs);
	if (res != TEE_SUCCESS)
		return res;

	switch (TEE_ALG_GET_CLASS(cs->algo)) {
	case TEE_OPERATION_DIGEST:
		res = crypto_hash_init(cs->ctx);
		if (res != TEE_SUCCESS)
			return res;
		break;
	case TEE_OPERATION_MAC:
		{
			struct tee_obj *o;
			struct tee_cryp_obj_secret *key;

			res = tee_obj_get(to_user_ta_ctx(sess->ctx),
					  cs->key1, &o);
			if (res != TEE_SUCCESS)
				return res;
			if ((o->info.handleFlags &
			     TEE_HANDLE_FLAG_INITIALIZED) == 0)
				return TEE_ERROR_BAD_PARAMETERS;

			key = (struct tee_cryp_obj_secret *)o->attr;
			res = crypto_mac_init(cs->ctx, (void *)(key + 1),
					      key->key_size);
			if (res != TEE_SUCCESS)
				return res;
			break;
		}
	default:
		return TEE_ERROR_BAD_PARAMETERS;
	}

	cs->state = CRYP_STATE_INITIALIZED;

	return TEE_SUCCESS;
}

TEE_Result syscall_hash_update(unsigned long state, const void *chunk,
			size_t chunk_size)
{
	struct ts_session *sess = ts_get_current_session();
	struct tee_cryp_state *cs = NULL;
	TEE_Result res = TEE_SUCCESS;

	/* No data, but size provided isn't valid parameters. */
	if (!chunk && chunk_size)
		return TEE_ERROR_BAD_PARAMETERS;

	/* Zero length hash is valid, but nothing we need to do. */
	if (!chunk_size)
		return TEE_SUCCESS;

	res = vm_check_access_rights(&to_user_ta_ctx(sess->ctx)->uctx,
				     TEE_MEMORY_ACCESS_READ |
				     TEE_MEMORY_ACCESS_ANY_OWNER,
				     (uaddr_t)chunk, chunk_size);
	if (res != TEE_SUCCESS)
		return res;

	res = tee_svc_cryp_get_state(sess, uref_to_vaddr(state), &cs);
	if (res != TEE_SUCCESS)
		return res;

	if (cs->state != CRYP_STATE_INITIALIZED)
		return TEE_ERROR_BAD_STATE;

	switch (TEE_ALG_GET_CLASS(cs->algo)) {
	case TEE_OPERATION_DIGEST:
		res = crypto_hash_update(cs->ctx, chunk, chunk_size);
		if (res != TEE_SUCCESS)
			return res;
		break;
	case TEE_OPERATION_MAC:
		res = crypto_mac_update(cs->ctx, chunk, chunk_size);
		if (res != TEE_SUCCESS)
			return res;
		break;
	default:
		return TEE_ERROR_BAD_PARAMETERS;
	}

	return TEE_SUCCESS;
}

TEE_Result syscall_hash_final(unsigned long state, const void *chunk,
			size_t chunk_size, void *hash, uint64_t *hash_len)
{
	struct ts_session *sess = ts_get_current_session();
	struct tee_cryp_state *cs = NULL;
	TEE_Result res2 = TEE_SUCCESS;
	TEE_Result res = TEE_SUCCESS;
	size_t hash_size = 0;
	size_t hlen = 0;

	/* No data, but size provided isn't valid parameters. */
	if (!chunk && chunk_size)
		return TEE_ERROR_BAD_PARAMETERS;

	res = vm_check_access_rights(&to_user_ta_ctx(sess->ctx)->uctx,
				     TEE_MEMORY_ACCESS_READ |
				     TEE_MEMORY_ACCESS_ANY_OWNER,
				     (uaddr_t)chunk, chunk_size);
	if (res != TEE_SUCCESS)
		return res;

	res = get_user_u64_as_size_t(&hlen, hash_len);
	if (res != TEE_SUCCESS)
		return res;

	res = vm_check_access_rights(&to_user_ta_ctx(sess->ctx)->uctx,
				     TEE_MEMORY_ACCESS_READ |
				     TEE_MEMORY_ACCESS_WRITE |
				     TEE_MEMORY_ACCESS_ANY_OWNER,
				     (uaddr_t)hash, hlen);
	if (res != TEE_SUCCESS)
		return res;

	res = tee_svc_cryp_get_state(sess, uref_to_vaddr(state), &cs);
	if (res != TEE_SUCCESS)
		return res;

	if (cs->state != CRYP_STATE_INITIALIZED)
		return TEE_ERROR_BAD_STATE;

	switch (TEE_ALG_GET_CLASS(cs->algo)) {
	case TEE_OPERATION_DIGEST:
		res = tee_alg_get_digest_size(cs->algo, &hash_size);
		if (res != TEE_SUCCESS)
			return res;
		if (hlen < hash_size) {
			res = TEE_ERROR_SHORT_BUFFER;
			goto out;
		}

		if (chunk_size) {
			res = crypto_hash_update(cs->ctx, chunk, chunk_size);
			if (res != TEE_SUCCESS)
				return res;
		}

		res = crypto_hash_final(cs->ctx, hash, hash_size);
		if (res != TEE_SUCCESS)
			return res;
		break;

	case TEE_OPERATION_MAC:
		res = tee_alg_get_digest_size(cs->algo, &hash_size);
		if (res != TEE_SUCCESS)
			return res;
		if (hlen < hash_size) {
			res = TEE_ERROR_SHORT_BUFFER;
			goto out;
		}

		if (chunk_size) {
			res = crypto_mac_update(cs->ctx, chunk, chunk_size);
			if (res != TEE_SUCCESS)
				return res;
		}

		res = crypto_mac_final(cs->ctx, hash, hash_size);
		if (res != TEE_SUCCESS)
			return res;
		break;

	default:
		return TEE_ERROR_BAD_PARAMETERS;
	}
out:
	res2 = put_user_u64(hash_len, hash_size);
	if (res2 != TEE_SUCCESS)
		return res2;
	return res;
}

TEE_Result syscall_cipher_init(unsigned long state, const void *iv,
			size_t iv_len)
{
	struct ts_session *sess = ts_get_current_session();
	struct user_ta_ctx *utc = to_user_ta_ctx(sess->ctx);
	struct tee_cryp_obj_secret *key1 = NULL;
	struct tee_cryp_state *cs = NULL;
	TEE_Result res = TEE_SUCCESS;
	struct tee_obj *o = NULL;

	res = tee_svc_cryp_get_state(sess, uref_to_vaddr(state), &cs);
	if (res != TEE_SUCCESS)
		return res;

	if (TEE_ALG_GET_CLASS(cs->algo) != TEE_OPERATION_CIPHER)
		return TEE_ERROR_BAD_STATE;

	res = vm_check_access_rights(&utc->uctx,
				     TEE_MEMORY_ACCESS_READ |
				     TEE_MEMORY_ACCESS_ANY_OWNER,
				     (uaddr_t)iv, iv_len);
	if (res != TEE_SUCCESS)
		return res;

	res = tee_obj_get(utc, cs->key1, &o);
	if (res != TEE_SUCCESS)
		return res;
	if ((o->info.handleFlags & TEE_HANDLE_FLAG_INITIALIZED) == 0)
		return TEE_ERROR_BAD_PARAMETERS;

	key1 = o->attr;

	if (tee_obj_get(utc, cs->key2, &o) == TEE_SUCCESS) {
		struct tee_cryp_obj_secret *key2 = o->attr;

		if ((o->info.handleFlags & TEE_HANDLE_FLAG_INITIALIZED) == 0)
			return TEE_ERROR_BAD_PARAMETERS;

		res = crypto_cipher_init(cs->ctx, cs->mode,
					 (uint8_t *)(key1 + 1), key1->key_size,
					 (uint8_t *)(key2 + 1), key2->key_size,
					 iv, iv_len);
	} else {
		res = crypto_cipher_init(cs->ctx, cs->mode,
					 (uint8_t *)(key1 + 1), key1->key_size,
					 NULL, 0, iv, iv_len);
	}
	if (res != TEE_SUCCESS)
		return res;

	cs->ctx_finalize = crypto_cipher_final;
	cs->state = CRYP_STATE_INITIALIZED;

	return TEE_SUCCESS;
}

static TEE_Result tee_svc_cipher_update_helper(unsigned long state,
			bool last_block, const void *src, size_t src_len,
			void *dst, uint64_t *dst_len)
{
	struct ts_session *sess = ts_get_current_session();
	struct tee_cryp_state *cs = NULL;
	TEE_Result res = TEE_SUCCESS;
	size_t dlen = 0;

	res = tee_svc_cryp_get_state(sess, uref_to_vaddr(state), &cs);
	if (res != TEE_SUCCESS)
		return res;

	if (cs->state != CRYP_STATE_INITIALIZED)
		return TEE_ERROR_BAD_STATE;

	res = vm_check_access_rights(&to_user_ta_ctx(sess->ctx)->uctx,
				     TEE_MEMORY_ACCESS_READ |
				     TEE_MEMORY_ACCESS_ANY_OWNER,
				     (uaddr_t)src, src_len);
	if (res != TEE_SUCCESS)
		return res;

	if (!dst_len) {
		dlen = 0;
	} else {
		struct user_mode_ctx *uctx = &to_user_ta_ctx(sess->ctx)->uctx;
		uint32_t flags = TEE_MEMORY_ACCESS_READ |
				 TEE_MEMORY_ACCESS_WRITE |
				 TEE_MEMORY_ACCESS_ANY_OWNER;

		res = get_user_u64_as_size_t(&dlen, dst_len);
		if (res != TEE_SUCCESS)
			return res;

		res = vm_check_access_rights(uctx, flags, (uaddr_t)dst, dlen);
		if (res != TEE_SUCCESS)
			return res;
	}

	if (dlen < src_len) {
		res = TEE_ERROR_SHORT_BUFFER;
		goto out;
	}

	if (src_len > 0) {
		/* Permit src_len == 0 to finalize the operation */
		res = tee_do_cipher_update(cs->ctx, cs->algo, cs->mode,
					   last_block, src, src_len, dst);
	}

	if (last_block && cs->ctx_finalize != NULL) {
		cs->ctx_finalize(cs->ctx);
		cs->ctx_finalize = NULL;
	}

out:
	if ((res == TEE_SUCCESS || res == TEE_ERROR_SHORT_BUFFER) &&
	    dst_len != NULL) {
		TEE_Result res2;

		res2 = put_user_u64(dst_len, src_len);
		if (res2 != TEE_SUCCESS)
			res = res2;
	}

	return res;
}

TEE_Result syscall_cipher_update(unsigned long state, const void *src,
			size_t src_len, void *dst, uint64_t *dst_len)
{
	return tee_svc_cipher_update_helper(state, false /* last_block */,
					    src, src_len, dst, dst_len);
}

TEE_Result syscall_cipher_final(unsigned long state, const void *src,
			size_t src_len, void *dst, uint64_t *dst_len)
{
	return tee_svc_cipher_update_helper(state, true /* last_block */,
					    src, src_len, dst, dst_len);
}

#if defined(CFG_CRYPTO_HKDF)
static TEE_Result get_hkdf_params(const TEE_Attribute *params,
				  uint32_t param_count,
				  void **salt, size_t *salt_len, void **info,
				  size_t *info_len, size_t *okm_len)
{
	size_t n;
	enum { SALT = 0x1, LENGTH = 0x2, INFO = 0x4 };
	uint8_t found = 0;

	*salt = *info = NULL;
	*salt_len = *info_len = *okm_len = 0;

	for (n = 0; n < param_count; n++) {
		switch (params[n].attributeID) {
		case TEE_ATTR_HKDF_SALT:
			if (!(found & SALT)) {
				*salt = params[n].content.ref.buffer;
				*salt_len = params[n].content.ref.length;
				found |= SALT;
			}
			break;
		case TEE_ATTR_HKDF_OKM_LENGTH:
			if (!(found & LENGTH)) {
				*okm_len = params[n].content.value.a;
				found |= LENGTH;
			}
			break;
		case TEE_ATTR_HKDF_INFO:
			if (!(found & INFO)) {
				*info = params[n].content.ref.buffer;
				*info_len = params[n].content.ref.length;
				found |= INFO;
			}
			break;
		default:
			/* Unexpected attribute */
			return TEE_ERROR_BAD_PARAMETERS;
		}

	}

	if (!(found & LENGTH))
		return TEE_ERROR_BAD_PARAMETERS;

	return TEE_SUCCESS;
}
#endif

#if defined(CFG_CRYPTO_CONCAT_KDF)
static TEE_Result get_concat_kdf_params(const TEE_Attribute *params,
					uint32_t param_count,
					void **other_info,
					size_t *other_info_len,
					size_t *derived_key_len)
{
	size_t n;
	enum { LENGTH = 0x1, INFO = 0x2 };
	uint8_t found = 0;

	*other_info = NULL;
	*other_info_len = *derived_key_len = 0;

	for (n = 0; n < param_count; n++) {
		switch (params[n].attributeID) {
		case TEE_ATTR_CONCAT_KDF_OTHER_INFO:
			if (!(found & INFO)) {
				*other_info = params[n].content.ref.buffer;
				*other_info_len = params[n].content.ref.length;
				found |= INFO;
			}
			break;
		case TEE_ATTR_CONCAT_KDF_DKM_LENGTH:
			if (!(found & LENGTH)) {
				*derived_key_len = params[n].content.value.a;
				found |= LENGTH;
			}
			break;
		default:
			/* Unexpected attribute */
			return TEE_ERROR_BAD_PARAMETERS;
		}
	}

	if (!(found & LENGTH))
		return TEE_ERROR_BAD_PARAMETERS;

	return TEE_SUCCESS;
}
#endif

#if defined(CFG_CRYPTO_PBKDF2)
static TEE_Result get_pbkdf2_params(const TEE_Attribute *params,
				   uint32_t param_count, void **salt,
				   size_t *salt_len, size_t *derived_key_len,
				   size_t *iteration_count)
{
	size_t n;
	enum { SALT = 0x1, LENGTH = 0x2, COUNT = 0x4 };
	uint8_t found = 0;

	*salt = NULL;
	*salt_len = *derived_key_len = *iteration_count = 0;

	for (n = 0; n < param_count; n++) {
		switch (params[n].attributeID) {
		case TEE_ATTR_PBKDF2_SALT:
			if (!(found & SALT)) {
				*salt = params[n].content.ref.buffer;
				*salt_len = params[n].content.ref.length;
				found |= SALT;
			}
			break;
		case TEE_ATTR_PBKDF2_DKM_LENGTH:
			if (!(found & LENGTH)) {
				*derived_key_len = params[n].content.value.a;
				found |= LENGTH;
			}
			break;
		case TEE_ATTR_PBKDF2_ITERATION_COUNT:
			if (!(found & COUNT)) {
				*iteration_count = params[n].content.value.a;
				found |= COUNT;
			}
			break;
		default:
			/* Unexpected attribute */
			return TEE_ERROR_BAD_PARAMETERS;
		}
	}

	if ((found & (LENGTH|COUNT)) != (LENGTH|COUNT))
		return TEE_ERROR_BAD_PARAMETERS;

	return TEE_SUCCESS;
}
#endif

#if defined(CFG_CRYPTO_SM2_KEP)
static TEE_Result get_sm2_kep_params(const TEE_Attribute *params,
				     uint32_t param_count,
				     struct ecc_public_key *peer_key,
				     struct ecc_public_key *peer_eph_key,
				     struct sm2_kep_parms *kep_parms)
{
	TEE_Result res = TEE_ERROR_GENERIC;
	size_t n;
	enum {
		IS_INITIATOR,
		PEER_KEY_X,
		PEER_KEY_Y,
		PEER_EPH_KEY_X,
		PEER_EPH_KEY_Y,
		INITIATOR_ID,
		RESPONDER_ID,
	};
	uint8_t mandatory = BIT(IS_INITIATOR) | BIT(PEER_KEY_X) |
		BIT(PEER_KEY_Y) | BIT(PEER_EPH_KEY_X) | BIT(PEER_EPH_KEY_Y) |
		BIT(INITIATOR_ID) | BIT(RESPONDER_ID);
	uint8_t found = 0;

	res = crypto_acipher_alloc_ecc_public_key(peer_key,
						  TEE_TYPE_SM2_KEP_PUBLIC_KEY,
						  256);
	if (res)
		return res;

	res = crypto_acipher_alloc_ecc_public_key(peer_eph_key,
						  TEE_TYPE_SM2_KEP_PUBLIC_KEY,
						  256);
	if (res)
		goto out_p;

	peer_key->curve = TEE_ECC_CURVE_SM2;
	peer_eph_key->curve = TEE_ECC_CURVE_SM2;

	for (n = 0; n < param_count; n++) {
		const TEE_Attribute *p = &params[n];

		switch (p->attributeID) {
		case TEE_ATTR_SM2_KEP_USER:
			kep_parms->is_initiator = !p->content.value.a;
			found |= BIT(IS_INITIATOR);
			break;
		case TEE_ATTR_ECC_PUBLIC_VALUE_X:
			crypto_bignum_bin2bn(p->content.ref.buffer,
					     p->content.ref.length,
					     peer_key->x);
			found |= BIT(PEER_KEY_X);
			break;
		case TEE_ATTR_ECC_PUBLIC_VALUE_Y:
			crypto_bignum_bin2bn(p->content.ref.buffer,
					     p->content.ref.length,
					     peer_key->y);
			found |= BIT(PEER_KEY_Y);
			break;
		case TEE_ATTR_ECC_EPHEMERAL_PUBLIC_VALUE_X:
			crypto_bignum_bin2bn(p->content.ref.buffer,
					     p->content.ref.length,
					     peer_eph_key->x);
			found |= BIT(PEER_EPH_KEY_X);
			break;
		case TEE_ATTR_ECC_EPHEMERAL_PUBLIC_VALUE_Y:
			crypto_bignum_bin2bn(p->content.ref.buffer,
					     p->content.ref.length,
					     peer_eph_key->y);
			found |= BIT(PEER_EPH_KEY_Y);
			break;
		case TEE_ATTR_SM2_ID_INITIATOR:
			kep_parms->initiator_id = p->content.ref.buffer;
			kep_parms->initiator_id_len = p->content.ref.length;
			found |= BIT(INITIATOR_ID);
			break;
		case TEE_ATTR_SM2_ID_RESPONDER:
			kep_parms->responder_id = p->content.ref.buffer;
			kep_parms->responder_id_len = p->content.ref.length;
			found |= BIT(RESPONDER_ID);
			break;
		case TEE_ATTR_SM2_KEP_CONFIRMATION_IN:
			kep_parms->conf_in = p->content.ref.buffer;
			kep_parms->conf_in_len = p->content.ref.length;
			break;
		case TEE_ATTR_SM2_KEP_CONFIRMATION_OUT:
			kep_parms->conf_out = p->content.ref.buffer;
			kep_parms->conf_out_len = p->content.ref.length;
			break;
		default:
			/* Unexpected attribute */
			res = TEE_ERROR_BAD_PARAMETERS;
			goto out;
		}
	}

	if ((found & mandatory) != mandatory) {
		res = TEE_ERROR_BAD_PARAMETERS;
		goto out;
	}

	return TEE_SUCCESS;
out:
	crypto_acipher_free_ecc_public_key(peer_eph_key);
out_p:
	crypto_acipher_free_ecc_public_key(peer_key);
	return res;
}
#endif

TEE_Result syscall_cryp_derive_key(unsigned long state,
			const struct utee_attribute *usr_params,
			unsigned long param_count, unsigned long derived_key)
{
	struct ts_session *sess = ts_get_current_session();
	struct user_ta_ctx *utc = to_user_ta_ctx(sess->ctx);
	TEE_Result res = TEE_ERROR_NOT_SUPPORTED;
	struct tee_obj *ko = NULL;
	struct tee_obj *so = NULL;
	struct tee_cryp_state *cs = NULL;
	struct tee_cryp_obj_secret *sk = NULL;
	const struct tee_cryp_obj_type_props *type_props = NULL;
	TEE_Attribute *params = NULL;
	size_t alloc_size = 0;

	res = tee_svc_cryp_get_state(sess, uref_to_vaddr(state), &cs);
	if (res != TEE_SUCCESS)
		return res;

	if (MUL_OVERFLOW(sizeof(TEE_Attribute), param_count, &alloc_size))
		return TEE_ERROR_OVERFLOW;

	params = malloc(alloc_size);
	if (!params)
		return TEE_ERROR_OUT_OF_MEMORY;
	res = copy_in_attrs(utc, usr_params, param_count, params);
	if (res != TEE_SUCCESS)
		goto out;

	/* Get key set in operation */
	res = tee_obj_get(utc, cs->key1, &ko);
	if (res != TEE_SUCCESS)
		goto out;

	res = tee_obj_get(utc, uref_to_vaddr(derived_key), &so);
	if (res != TEE_SUCCESS)
		goto out;

	/* Find information needed about the object to initialize */
	sk = so->attr;

	/* Find description of object */
	type_props = tee_svc_find_type_props(so->info.objectType);
	if (!type_props) {
		res = TEE_ERROR_NOT_SUPPORTED;
		goto out;
	}

	if (cs->algo == TEE_ALG_DH_DERIVE_SHARED_SECRET) {
		struct bignum *pub = NULL;
		struct bignum *ss = NULL;
		size_t bin_size = 0;

		if (param_count != 1 ||
		    params[0].attributeID != TEE_ATTR_DH_PUBLIC_VALUE) {
			res = TEE_ERROR_BAD_PARAMETERS;
			goto out;
		}

		bin_size = params[0].content.ref.length;

		if (MUL_OVERFLOW(bin_size, 8, &alloc_size)) {
			res = TEE_ERROR_OVERFLOW;
			goto out;
		}

		pub = crypto_bignum_allocate(alloc_size);
		ss = crypto_bignum_allocate(alloc_size);
		if (pub && ss) {
			crypto_bignum_bin2bn(params[0].content.ref.buffer,
					     bin_size, pub);
			res = crypto_acipher_dh_shared_secret(ko->attr,
							      pub, ss);
			if (res == TEE_SUCCESS) {
				sk->key_size = crypto_bignum_num_bytes(ss);
				crypto_bignum_bn2bin(ss, (uint8_t *)(sk + 1));
				so->info.handleFlags |=
						TEE_HANDLE_FLAG_INITIALIZED;
				set_attribute(so, type_props,
					      TEE_ATTR_SECRET_VALUE);
			}
		} else {
			res = TEE_ERROR_OUT_OF_MEMORY;
		}
		crypto_bignum_free(pub);
		crypto_bignum_free(ss);
	} else if (TEE_ALG_GET_MAIN_ALG(cs->algo) == TEE_MAIN_ALGO_ECDH) {
		struct ecc_public_key key_public;
		uint8_t *pt_secret;
		unsigned long pt_secret_len;
		uint32_t key_type = TEE_TYPE_ECDH_PUBLIC_KEY;

		if (param_count != 2 ||
		    params[0].attributeID != TEE_ATTR_ECC_PUBLIC_VALUE_X ||
		    params[1].attributeID != TEE_ATTR_ECC_PUBLIC_VALUE_Y) {
			res = TEE_ERROR_BAD_PARAMETERS;
			goto out;
		}

		switch (cs->algo) {
		case TEE_ALG_ECDH_P192:
			alloc_size = 192;
			break;
		case TEE_ALG_ECDH_P224:
			alloc_size = 224;
			break;
		case TEE_ALG_ECDH_P256:
			alloc_size = 256;
			break;
		case TEE_ALG_ECDH_P384:
			alloc_size = 384;
			break;
		case TEE_ALG_ECDH_P521:
			alloc_size = 521;
			break;
		default:
			res = TEE_ERROR_NOT_IMPLEMENTED;
			goto out;
		}

		/* Create the public key */
		res = crypto_acipher_alloc_ecc_public_key(&key_public, key_type,
							  alloc_size);
		if (res != TEE_SUCCESS)
			goto out;
		key_public.curve = ((struct ecc_keypair *)ko->attr)->curve;
		crypto_bignum_bin2bn(params[0].content.ref.buffer,
				     params[0].content.ref.length,
				     key_public.x);
		crypto_bignum_bin2bn(params[1].content.ref.buffer,
				     params[1].content.ref.length,
				     key_public.y);

		pt_secret = (uint8_t *)(sk + 1);
		pt_secret_len = sk->alloc_size;
		res = crypto_acipher_ecc_shared_secret(ko->attr, &key_public,
						       pt_secret,
						       &pt_secret_len);

		if (res == TEE_SUCCESS) {
			sk->key_size = pt_secret_len;
			so->info.handleFlags |= TEE_HANDLE_FLAG_INITIALIZED;
			set_attribute(so, type_props, TEE_ATTR_SECRET_VALUE);
		}

		/* free the public key */
		crypto_acipher_free_ecc_public_key(&key_public);
	}
#if defined(CFG_CRYPTO_HKDF)
	else if (TEE_ALG_GET_MAIN_ALG(cs->algo) == TEE_MAIN_ALGO_HKDF) {
		void *salt, *info;
		size_t salt_len, info_len, okm_len;
		uint32_t hash_id = TEE_ALG_GET_DIGEST_HASH(cs->algo);
		struct tee_cryp_obj_secret *ik = ko->attr;
		const uint8_t *ikm = (const uint8_t *)(ik + 1);

		res = get_hkdf_params(params, param_count, &salt, &salt_len,
				      &info, &info_len, &okm_len);
		if (res != TEE_SUCCESS)
			goto out;

		/* Requested size must fit into the output object's buffer */
		if (okm_len > ik->alloc_size) {
			res = TEE_ERROR_BAD_PARAMETERS;
			goto out;
		}

		res = tee_cryp_hkdf(hash_id, ikm, ik->key_size, salt, salt_len,
				    info, info_len, (uint8_t *)(sk + 1),
				    okm_len);
		if (res == TEE_SUCCESS) {
			sk->key_size = okm_len;
			so->info.handleFlags |= TEE_HANDLE_FLAG_INITIALIZED;
			set_attribute(so, type_props, TEE_ATTR_SECRET_VALUE);
		}
	}
#endif
#if defined(CFG_CRYPTO_CONCAT_KDF)
	else if (TEE_ALG_GET_MAIN_ALG(cs->algo) == TEE_MAIN_ALGO_CONCAT_KDF) {
		void *info;
		size_t info_len, derived_key_len;
		uint32_t hash_id = TEE_ALG_GET_DIGEST_HASH(cs->algo);
		struct tee_cryp_obj_secret *ss = ko->attr;
		const uint8_t *shared_secret = (const uint8_t *)(ss + 1);

		res = get_concat_kdf_params(params, param_count, &info,
					    &info_len, &derived_key_len);
		if (res != TEE_SUCCESS)
			goto out;

		/* Requested size must fit into the output object's buffer */
		if (derived_key_len > ss->alloc_size) {
			res = TEE_ERROR_BAD_PARAMETERS;
			goto out;
		}

		res = tee_cryp_concat_kdf(hash_id, shared_secret, ss->key_size,
					  info, info_len, (uint8_t *)(sk + 1),
					  derived_key_len);
		if (res == TEE_SUCCESS) {
			sk->key_size = derived_key_len;
			so->info.handleFlags |= TEE_HANDLE_FLAG_INITIALIZED;
			set_attribute(so, type_props, TEE_ATTR_SECRET_VALUE);
		}
	}
#endif
#if defined(CFG_CRYPTO_PBKDF2)
	else if (TEE_ALG_GET_MAIN_ALG(cs->algo) == TEE_MAIN_ALGO_PBKDF2) {
		void *salt;
		size_t salt_len, iteration_count, derived_key_len;
		uint32_t hash_id = TEE_ALG_GET_DIGEST_HASH(cs->algo);
		struct tee_cryp_obj_secret *ss = ko->attr;
		const uint8_t *password = (const uint8_t *)(ss + 1);

		res = get_pbkdf2_params(params, param_count, &salt, &salt_len,
					&derived_key_len, &iteration_count);
		if (res != TEE_SUCCESS)
			goto out;

		/* Requested size must fit into the output object's buffer */
		if (derived_key_len > ss->alloc_size) {
			res = TEE_ERROR_BAD_PARAMETERS;
			goto out;
		}

		res = tee_cryp_pbkdf2(hash_id, password, ss->key_size, salt,
				      salt_len, iteration_count,
				      (uint8_t *)(sk + 1), derived_key_len);
		if (res == TEE_SUCCESS) {
			sk->key_size = derived_key_len;
			so->info.handleFlags |= TEE_HANDLE_FLAG_INITIALIZED;
			set_attribute(so, type_props, TEE_ATTR_SECRET_VALUE);
		}
	}
#endif
#if defined(CFG_CRYPTO_SM2_KEP)
	else if (cs->algo == TEE_ALG_SM2_KEP) {
		struct ecc_public_key peer_eph_key = { };
		struct ecc_public_key peer_key = { };
		struct sm2_kep_parms kep_parms = {
			.out = (uint8_t *)(sk + 1),
			.out_len = so->info.maxKeySize,
		};
		struct tee_obj *ko2 = NULL;

		res = tee_obj_get(utc, cs->key2, &ko2);
		if (res != TEE_SUCCESS)
			goto out;

		res = get_sm2_kep_params(params, param_count, &peer_key,
					 &peer_eph_key, &kep_parms);
		if (res != TEE_SUCCESS)
			goto out;

		/*
		 * key1 is our private keypair, key2 is our ephemeral public key
		 */
		res = crypto_acipher_sm2_kep_derive(ko->attr, /* key1 */
						    ko2->attr, /* key2 */
						    &peer_key, &peer_eph_key,
						    &kep_parms);

		if (res == TEE_SUCCESS) {
			sk->key_size = kep_parms.out_len;
			so->info.handleFlags |= TEE_HANDLE_FLAG_INITIALIZED;
			set_attribute(so, type_props, TEE_ATTR_SECRET_VALUE);
		}
		crypto_acipher_free_ecc_public_key(&peer_key);
		crypto_acipher_free_ecc_public_key(&peer_eph_key);
	}
#endif
	else
		res = TEE_ERROR_NOT_SUPPORTED;

out:
	free_wipe(params);
	return res;
}

TEE_Result syscall_cryp_random_number_generate(void *buf, size_t blen)
{
	struct ts_session *sess = ts_get_current_session();
	TEE_Result res = TEE_SUCCESS;

	res = vm_check_access_rights(&to_user_ta_ctx(sess->ctx)->uctx,
				     TEE_MEMORY_ACCESS_WRITE,
				     (uaddr_t)buf, blen);
	if (res != TEE_SUCCESS)
		return res;

	res = crypto_rng_read(buf, blen);
	if (res != TEE_SUCCESS)
		return res;

	return res;
}

TEE_Result syscall_authenc_init(unsigned long state, const void *nonce,
				size_t nonce_len, size_t tag_len,
				size_t aad_len, size_t payload_len)
{
	struct ts_session *sess = ts_get_current_session();
	struct tee_cryp_obj_secret *key = NULL;
	struct tee_cryp_state *cs = NULL;
	TEE_Result res = TEE_SUCCESS;
	struct tee_obj *o = NULL;

	res = vm_check_access_rights(&to_user_ta_ctx(sess->ctx)->uctx,
				     TEE_MEMORY_ACCESS_READ |
				     TEE_MEMORY_ACCESS_ANY_OWNER,
				     (uaddr_t)nonce, nonce_len);
	if (res != TEE_SUCCESS)
		return res;

	res = tee_svc_cryp_get_state(sess, uref_to_vaddr(state), &cs);
	if (res != TEE_SUCCESS)
		return res;

	res = tee_obj_get(to_user_ta_ctx(sess->ctx), cs->key1, &o);
	if (res != TEE_SUCCESS)
		return res;
	if ((o->info.handleFlags & TEE_HANDLE_FLAG_INITIALIZED) == 0)
		return TEE_ERROR_BAD_PARAMETERS;

	key = o->attr;
	res = crypto_authenc_init(cs->ctx, cs->mode, (uint8_t *)(key + 1),
				  key->key_size, nonce, nonce_len, tag_len,
				  aad_len, payload_len);
	if (res != TEE_SUCCESS)
		return res;

	cs->ctx_finalize = crypto_authenc_final;
	cs->state = CRYP_STATE_INITIALIZED;

	return TEE_SUCCESS;
}

TEE_Result syscall_authenc_update_aad(unsigned long state,
				      const void *aad_data, size_t aad_data_len)
{
	struct ts_session *sess = ts_get_current_session();
	TEE_Result res = TEE_SUCCESS;
	struct tee_cryp_state *cs = NULL;

	res = vm_check_access_rights(&to_user_ta_ctx(sess->ctx)->uctx,
				     TEE_MEMORY_ACCESS_READ |
				     TEE_MEMORY_ACCESS_ANY_OWNER,
				     (uaddr_t)aad_data, aad_data_len);
	if (res != TEE_SUCCESS)
		return res;

	res = tee_svc_cryp_get_state(sess, uref_to_vaddr(state), &cs);
	if (res != TEE_SUCCESS)
		return res;

	if (cs->state != CRYP_STATE_INITIALIZED)
		return TEE_ERROR_BAD_STATE;

	if (TEE_ALG_GET_CLASS(cs->algo) != TEE_OPERATION_AE)
		return TEE_ERROR_BAD_STATE;

	res = crypto_authenc_update_aad(cs->ctx, cs->mode, aad_data,
					aad_data_len);
	if (res != TEE_SUCCESS)
		return res;

	return TEE_SUCCESS;
}

TEE_Result syscall_authenc_update_payload(unsigned long state,
					  const void *src_data,
					  size_t src_len, void *dst_data,
					  uint64_t *dst_len)
{
	struct ts_session *sess = ts_get_current_session();
	struct tee_cryp_state *cs = NULL;
	TEE_Result res = TEE_SUCCESS;
	size_t dlen = 0;

	res = tee_svc_cryp_get_state(sess, uref_to_vaddr(state), &cs);
	if (res != TEE_SUCCESS)
		return res;

	if (cs->state != CRYP_STATE_INITIALIZED)
		return TEE_ERROR_BAD_STATE;

	if (TEE_ALG_GET_CLASS(cs->algo) != TEE_OPERATION_AE)
		return TEE_ERROR_BAD_STATE;

	res = vm_check_access_rights(&to_user_ta_ctx(sess->ctx)->uctx,
				     TEE_MEMORY_ACCESS_READ |
				     TEE_MEMORY_ACCESS_ANY_OWNER,
				     (uaddr_t)src_data, src_len);
	if (res != TEE_SUCCESS)
		return res;

	res = get_user_u64_as_size_t(&dlen, dst_len);
	if (res != TEE_SUCCESS)
		return res;

	res = vm_check_access_rights(&to_user_ta_ctx(sess->ctx)->uctx,
				     TEE_MEMORY_ACCESS_READ |
				     TEE_MEMORY_ACCESS_WRITE |
				     TEE_MEMORY_ACCESS_ANY_OWNER,
				     (uaddr_t)dst_data, dlen);
	if (res != TEE_SUCCESS)
		return res;

	if (dlen < src_len) {
		res = TEE_ERROR_SHORT_BUFFER;
		goto out;
	}

	res = crypto_authenc_update_payload(cs->ctx, cs->mode, src_data,
					    src_len, dst_data, &dlen);
out:
	if (res == TEE_SUCCESS || res == TEE_ERROR_SHORT_BUFFER) {
		TEE_Result res2 = put_user_u64(dst_len, dlen);

		if (res2 != TEE_SUCCESS)
			res = res2;
	}

	return res;
}

TEE_Result syscall_authenc_enc_final(unsigned long state, const void *src_data,
				     size_t src_len, void *dst_data,
				     uint64_t *dst_len, void *tag,
				     uint64_t *tag_len)
{
	struct ts_session *sess = ts_get_current_session();
	struct user_mode_ctx *uctx = &to_user_ta_ctx(sess->ctx)->uctx;
	struct tee_cryp_state *cs = NULL;
	TEE_Result res = TEE_SUCCESS;
	size_t dlen = 0;
	size_t tlen = 0;

	res = tee_svc_cryp_get_state(sess, uref_to_vaddr(state), &cs);
	if (res != TEE_SUCCESS)
		return res;

	if (cs->state != CRYP_STATE_INITIALIZED)
		return TEE_ERROR_BAD_STATE;

	if (cs->mode != TEE_MODE_ENCRYPT)
		return TEE_ERROR_BAD_PARAMETERS;

	if (TEE_ALG_GET_CLASS(cs->algo) != TEE_OPERATION_AE)
		return TEE_ERROR_BAD_STATE;

	res = vm_check_access_rights(uctx,
				     TEE_MEMORY_ACCESS_READ |
				     TEE_MEMORY_ACCESS_ANY_OWNER,
				     (uaddr_t)src_data, src_len);
	if (res != TEE_SUCCESS)
		return res;

	if (!dst_len) {
		dlen = 0;
	} else {
		res = get_user_u64_as_size_t(&dlen, dst_len);
		if (res != TEE_SUCCESS)
			return res;

		res = vm_check_access_rights(uctx,
					     TEE_MEMORY_ACCESS_READ |
					     TEE_MEMORY_ACCESS_WRITE |
					     TEE_MEMORY_ACCESS_ANY_OWNER,
					     (uaddr_t)dst_data, dlen);
		if (res != TEE_SUCCESS)
			return res;
	}

	if (dlen < src_len) {
		res = TEE_ERROR_SHORT_BUFFER;
		goto out;
	}

	res = get_user_u64_as_size_t(&tlen, tag_len);
	if (res != TEE_SUCCESS)
		return res;

	res = vm_check_access_rights(uctx,
				     TEE_MEMORY_ACCESS_READ |
				     TEE_MEMORY_ACCESS_WRITE |
				     TEE_MEMORY_ACCESS_ANY_OWNER,
				     (uaddr_t)tag, tlen);
	if (res != TEE_SUCCESS)
		return res;

	res = crypto_authenc_enc_final(cs->ctx, src_data, src_len, dst_data,
				       &dlen, tag, &tlen);

out:
	if (res == TEE_SUCCESS || res == TEE_ERROR_SHORT_BUFFER) {
		TEE_Result res2 = TEE_SUCCESS;

		if (dst_len != NULL) {
			res2 = put_user_u64(dst_len, dlen);
			if (res2 != TEE_SUCCESS)
				return res2;
		}

		res2 = put_user_u64(tag_len, tlen);
		if (res2 != TEE_SUCCESS)
			return res2;
	}

	return res;
}

TEE_Result syscall_authenc_dec_final(unsigned long state,
			const void *src_data, size_t src_len, void *dst_data,
			uint64_t *dst_len, const void *tag, size_t tag_len)
{
	struct ts_session *sess = ts_get_current_session();
	struct user_mode_ctx *uctx = &to_user_ta_ctx(sess->ctx)->uctx;
	struct tee_cryp_state *cs = NULL;
	TEE_Result res = TEE_SUCCESS;
	size_t dlen = 0;

	res = tee_svc_cryp_get_state(sess, uref_to_vaddr(state), &cs);
	if (res != TEE_SUCCESS)
		return res;

	if (cs->state != CRYP_STATE_INITIALIZED)
		return TEE_ERROR_BAD_STATE;

	if (cs->mode != TEE_MODE_DECRYPT)
		return TEE_ERROR_BAD_PARAMETERS;

	if (TEE_ALG_GET_CLASS(cs->algo) != TEE_OPERATION_AE)
		return TEE_ERROR_BAD_STATE;

	res = vm_check_access_rights(uctx,
				     TEE_MEMORY_ACCESS_READ |
				     TEE_MEMORY_ACCESS_ANY_OWNER,
				     (uaddr_t)src_data, src_len);
	if (res != TEE_SUCCESS)
		return res;

	if (!dst_len) {
		dlen = 0;
	} else {
		res = get_user_u64_as_size_t(&dlen, dst_len);
		if (res != TEE_SUCCESS)
			return res;

		res = vm_check_access_rights(uctx,
					     TEE_MEMORY_ACCESS_READ |
					     TEE_MEMORY_ACCESS_WRITE |
					     TEE_MEMORY_ACCESS_ANY_OWNER,
					     (uaddr_t)dst_data, dlen);
		if (res != TEE_SUCCESS)
			return res;
	}

	if (dlen < src_len) {
		res = TEE_ERROR_SHORT_BUFFER;
		goto out;
	}

	res = vm_check_access_rights(uctx, TEE_MEMORY_ACCESS_READ,
				     (uaddr_t)tag, tag_len);
	if (res != TEE_SUCCESS)
		return res;

	res = crypto_authenc_dec_final(cs->ctx, src_data, src_len, dst_data,
				       &dlen, tag, tag_len);

out:
	if ((res == TEE_SUCCESS || res == TEE_ERROR_SHORT_BUFFER) &&
	    dst_len != NULL) {
		TEE_Result res2 = put_user_u64(dst_len, dlen);

		if (res2 != TEE_SUCCESS)
			return res2;
	}

	return res;
}

static int pkcs1_get_salt_len(const TEE_Attribute *params, uint32_t num_params,
			      size_t default_len)
{
	size_t n;

	assert(default_len < INT_MAX);

	for (n = 0; n < num_params; n++) {
		if (params[n].attributeID == TEE_ATTR_RSA_PSS_SALT_LENGTH) {
			if (params[n].content.value.a < INT_MAX)
				return params[n].content.value.a;
			break;
		}
	}
	/*
	 * If salt length isn't provided use the default value which is
	 * the length of the digest.
	 */
	return default_len;
}

TEE_Result syscall_asymm_operate(unsigned long state,
			const struct utee_attribute *usr_params,
			size_t num_params, const void *src_data, size_t src_len,
			void *dst_data, uint64_t *dst_len)
{
	struct ts_session *sess = ts_get_current_session();
	struct user_ta_ctx *utc = to_user_ta_ctx(sess->ctx);
	TEE_Result res = TEE_SUCCESS;
	struct tee_cryp_state *cs = NULL;
	size_t dlen = 0;
	struct tee_obj *o = NULL;
	void *label = NULL;
	size_t label_len = 0;
	size_t n = 0;
	int salt_len = 0;
	TEE_Attribute *params = NULL;
	size_t alloc_size = 0;

	res = tee_svc_cryp_get_state(sess, uref_to_vaddr(state), &cs);
	if (res != TEE_SUCCESS)
		return res;

	res = vm_check_access_rights(&utc->uctx,
				     TEE_MEMORY_ACCESS_READ |
				     TEE_MEMORY_ACCESS_ANY_OWNER,
				     (uaddr_t)src_data, src_len);
	if (res != TEE_SUCCESS)
		return res;

	res = get_user_u64_as_size_t(&dlen, dst_len);
	if (res != TEE_SUCCESS)
		return res;

	res = vm_check_access_rights(&utc->uctx,
				     TEE_MEMORY_ACCESS_READ |
				     TEE_MEMORY_ACCESS_WRITE |
				     TEE_MEMORY_ACCESS_ANY_OWNER,
				     (uaddr_t)dst_data, dlen);
	if (res != TEE_SUCCESS)
		return res;

	if (MUL_OVERFLOW(sizeof(TEE_Attribute), num_params, &alloc_size))
		return TEE_ERROR_OVERFLOW;

	params = malloc(alloc_size);
	if (!params)
		return TEE_ERROR_OUT_OF_MEMORY;
	res = copy_in_attrs(utc, usr_params, num_params, params);
	if (res != TEE_SUCCESS)
		goto out;

	res = tee_obj_get(utc, cs->key1, &o);
	if (res != TEE_SUCCESS)
		goto out;
	if ((o->info.handleFlags & TEE_HANDLE_FLAG_INITIALIZED) == 0) {
		res = TEE_ERROR_GENERIC;
		goto out;
	}

	switch (cs->algo) {
	case TEE_ALG_RSA_NOPAD:
		if (cs->mode == TEE_MODE_ENCRYPT) {
			res = crypto_acipher_rsanopad_encrypt(o->attr, src_data,
							      src_len, dst_data,
							      &dlen);
		} else if (cs->mode == TEE_MODE_DECRYPT) {
			res = crypto_acipher_rsanopad_decrypt(o->attr, src_data,
							      src_len, dst_data,
							      &dlen);
		} else {
			/*
			 * We will panic because "the mode is not compatible
			 * with the function"
			 */
			res = TEE_ERROR_GENERIC;
		}
		break;

	case TEE_ALG_SM2_PKE:
		if (cs->mode == TEE_MODE_ENCRYPT) {
			res = crypto_acipher_sm2_pke_encrypt(o->attr, src_data,
							     src_len, dst_data,
							     &dlen);
		} else if (cs->mode == TEE_MODE_DECRYPT) {
			res = crypto_acipher_sm2_pke_decrypt(o->attr, src_data,
							     src_len, dst_data,
							     &dlen);
		} else {
			res = TEE_ERROR_GENERIC;
		}
		break;

	case TEE_ALG_RSAES_PKCS1_V1_5:
	case TEE_ALG_RSAES_PKCS1_OAEP_MGF1_SHA1:
	case TEE_ALG_RSAES_PKCS1_OAEP_MGF1_SHA224:
	case TEE_ALG_RSAES_PKCS1_OAEP_MGF1_SHA256:
	case TEE_ALG_RSAES_PKCS1_OAEP_MGF1_SHA384:
	case TEE_ALG_RSAES_PKCS1_OAEP_MGF1_SHA512:
		for (n = 0; n < num_params; n++) {
			if (params[n].attributeID == TEE_ATTR_RSA_OAEP_LABEL) {
				label = params[n].content.ref.buffer;
				label_len = params[n].content.ref.length;
				break;
			}
		}

		if (cs->mode == TEE_MODE_ENCRYPT) {
			res = crypto_acipher_rsaes_encrypt(cs->algo, o->attr,
							   label, label_len,
							   src_data, src_len,
							   dst_data, &dlen);
		} else if (cs->mode == TEE_MODE_DECRYPT) {
			res = crypto_acipher_rsaes_decrypt(
					cs->algo, o->attr, label, label_len,
					src_data, src_len, dst_data, &dlen);
		} else {
			res = TEE_ERROR_BAD_PARAMETERS;
		}
		break;

#if defined(CFG_CRYPTO_RSASSA_NA1)
	case TEE_ALG_RSASSA_PKCS1_V1_5:
#endif
	case TEE_ALG_RSASSA_PKCS1_V1_5_MD5:
	case TEE_ALG_RSASSA_PKCS1_V1_5_SHA1:
	case TEE_ALG_RSASSA_PKCS1_V1_5_SHA224:
	case TEE_ALG_RSASSA_PKCS1_V1_5_SHA256:
	case TEE_ALG_RSASSA_PKCS1_V1_5_SHA384:
	case TEE_ALG_RSASSA_PKCS1_V1_5_SHA512:
	case TEE_ALG_RSASSA_PKCS1_PSS_MGF1_SHA1:
	case TEE_ALG_RSASSA_PKCS1_PSS_MGF1_SHA224:
	case TEE_ALG_RSASSA_PKCS1_PSS_MGF1_SHA256:
	case TEE_ALG_RSASSA_PKCS1_PSS_MGF1_SHA384:
	case TEE_ALG_RSASSA_PKCS1_PSS_MGF1_SHA512:
		if (cs->mode != TEE_MODE_SIGN) {
			res = TEE_ERROR_BAD_PARAMETERS;
			break;
		}
		salt_len = pkcs1_get_salt_len(params, num_params, src_len);
		res = crypto_acipher_rsassa_sign(cs->algo, o->attr, salt_len,
						 src_data, src_len, dst_data,
						 &dlen);
		break;

	case TEE_ALG_DSA_SHA1:
	case TEE_ALG_DSA_SHA224:
	case TEE_ALG_DSA_SHA256:
		res = crypto_acipher_dsa_sign(cs->algo, o->attr, src_data,
					      src_len, dst_data, &dlen);
		break;
	case TEE_ALG_ECDSA_P192:
	case TEE_ALG_ECDSA_P224:
	case TEE_ALG_ECDSA_P256:
	case TEE_ALG_ECDSA_P384:
	case TEE_ALG_ECDSA_P521:
	case TEE_ALG_SM2_DSA_SM3:
		res = crypto_acipher_ecc_sign(cs->algo, o->attr, src_data,
					      src_len, dst_data, &dlen);
		break;
	default:
		res = TEE_ERROR_BAD_PARAMETERS;
		break;
	}

out:
	free_wipe(params);

	if (res == TEE_SUCCESS || res == TEE_ERROR_SHORT_BUFFER) {
		TEE_Result res2 = put_user_u64(dst_len, dlen);

		if (res2 != TEE_SUCCESS)
			return res2;
	}

	return res;
}

TEE_Result syscall_asymm_verify(unsigned long state,
			const struct utee_attribute *usr_params,
			size_t num_params, const void *data, size_t data_len,
			const void *sig, size_t sig_len)
{
	struct ts_session *sess = ts_get_current_session();
	struct user_ta_ctx *utc = to_user_ta_ctx(sess->ctx);
	struct tee_cryp_state *cs = NULL;
	TEE_Result res = TEE_SUCCESS;
	TEE_Attribute *params = NULL;
	struct tee_obj *o = NULL;
	size_t hash_size = 0;
	uint32_t hash_algo = 0;
	int salt_len = 0;
	size_t alloc_size = 0;

	res = tee_svc_cryp_get_state(sess, uref_to_vaddr(state), &cs);
	if (res != TEE_SUCCESS)
		return res;

	if (cs->mode != TEE_MODE_VERIFY)
		return TEE_ERROR_BAD_PARAMETERS;

	res = vm_check_access_rights(&utc->uctx,
				     TEE_MEMORY_ACCESS_READ |
				     TEE_MEMORY_ACCESS_ANY_OWNER,
				     (uaddr_t)data, data_len);
	if (res != TEE_SUCCESS)
		return res;

	res = vm_check_access_rights(&utc->uctx,
				     TEE_MEMORY_ACCESS_READ |
				     TEE_MEMORY_ACCESS_ANY_OWNER,
				     (uaddr_t)sig, sig_len);
	if (res != TEE_SUCCESS)
		return res;

	if (MUL_OVERFLOW(sizeof(TEE_Attribute), num_params, &alloc_size))
		return TEE_ERROR_OVERFLOW;

	params = malloc(alloc_size);
	if (!params)
		return TEE_ERROR_OUT_OF_MEMORY;
	res = copy_in_attrs(utc, usr_params, num_params, params);
	if (res != TEE_SUCCESS)
		goto out;

	res = tee_obj_get(utc, cs->key1, &o);
	if (res != TEE_SUCCESS)
		goto out;
	if ((o->info.handleFlags & TEE_HANDLE_FLAG_INITIALIZED) == 0) {
		res = TEE_ERROR_BAD_PARAMETERS;
		goto out;
	}

	switch (TEE_ALG_GET_MAIN_ALG(cs->algo)) {
	case TEE_MAIN_ALGO_RSA:
		if (cs->algo != TEE_ALG_RSASSA_PKCS1_V1_5) {
			hash_algo = TEE_DIGEST_HASH_TO_ALGO(cs->algo);
			res = tee_alg_get_digest_size(hash_algo, &hash_size);
			if (res != TEE_SUCCESS)
				break;
			if (data_len != hash_size) {
				res = TEE_ERROR_BAD_PARAMETERS;
				break;
			}
			salt_len = pkcs1_get_salt_len(params, num_params,
						      hash_size);
		}
		res = crypto_acipher_rsassa_verify(cs->algo, o->attr, salt_len,
						   data, data_len, sig,
						   sig_len);
		break;

	case TEE_MAIN_ALGO_DSA:
		hash_algo = TEE_DIGEST_HASH_TO_ALGO(cs->algo);
		res = tee_alg_get_digest_size(hash_algo, &hash_size);
		if (res != TEE_SUCCESS)
			break;

		if (data_len != hash_size) {
			struct dsa_public_key *key = o->attr;

			/*
			 * Depending on the DSA algorithm (NIST), the
			 * digital signature output size may be truncated
			 * to the size of a key pair (Q prime size). Q
			 * prime size must be less or equal than the hash
			 * output length of the hash algorithm involved.
			 *
			 * We're checking here in order to be able to
			 * return this particular error code, which will
			 * cause TEE_AsymmetricVerifyDigest() to panic as
			 * required by GP. crypto_acipher_dsa_verify() is
			 * implemented in the glue layer of the crypto
			 * library and it might be a bit harder to catch
			 * this particular case there or lead to duplicated
			 * code in different crypto glue layers.
			 *
			 * The GP spec says that we SHOULD panic if
			 * data_len != hash_size, but that would break a
			 * few of the DSA tests in xtest where the
			 * hash_size is larger than possible data_len. So
			 * the compromise is in case data_len != hash_size
			 * check that it's not smaller than what makes
			 * sense.
			 */
			if (data_len != crypto_bignum_num_bytes(key->q)) {
				res = TEE_ERROR_BAD_PARAMETERS;
				break;
			}
		}
		res = crypto_acipher_dsa_verify(cs->algo, o->attr, data,
						data_len, sig, sig_len);
		break;

	case TEE_MAIN_ALGO_ECDSA:
	case TEE_MAIN_ALGO_SM2_DSA_SM3:
		res = crypto_acipher_ecc_verify(cs->algo, o->attr, data,
						data_len, sig, sig_len);
		break;

	default:
		res = TEE_ERROR_NOT_SUPPORTED;
	}

out:
	free_wipe(params);
	return res;
}
