/*
 * Copyright (c) 2017-2018 The Linux Foundation. All rights reserved.
 *
 * Permission to use, copy, modify, and/or distribute this software for
 * any purpose with or without fee is hereby granted, provided that the
 * above copyright notice and this permission notice appear in all
 * copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 */
#include <palTypes.h>
#include <lim_fils_defs.h>
#include <lim_process_fils.h>
#include <limSendMessages.h>
#include <limTypes.h>
#include <limUtils.h>
#include <limPropExtsUtils.h>
#include <limAssocUtils.h>
#include <limSession.h>
#include <ieee80211_defines.h>
#include <qdf_crypto.h>
#include <limSerDesUtils.h>
#ifdef WLAN_FEATURE_FILS_SK

#define SHA256_DIGEST_SIZE 32
#define SHA384_DIGEST_SIZE 48

#define HMAC_SHA256_CRYPTO_TYPE "hmac(sha256)"
#define HMAC_SHA386_CRYPTO_TYPE "hmac(sha384)"

int alloc_tfm(uint8_t *type);

#ifdef WLAN_FILS_DEBUG
/**
 * lim_fils_data_dump()- dump fils data
 * @type: Data name
 * @data: pointer to data buffer
 * @len: data len
 *
 * Return: None
 */
void lim_fils_data_dump(char *type, uint8_t *data, uint32_t len)
{

	VOS_TRACE(VOS_MODULE_ID_PE, VOS_TRACE_LEVEL_INFO,
			("%s : length %d"), type, len);
	vos_trace_hex_dump(VOS_MODULE_ID_PE, VOS_TRACE_LEVEL_INFO, data, len);
}
#else
static void lim_fils_data_dump(char *type, uint8_t *data, uint32_t len)
{ }
#endif

/**
 * lim_get_crypto_digest_len()- This API return hash length based on crypto type
 * @type: Crypto type
 *
 * Return: hash length
 */
static int lim_get_crypto_digest_len(uint8_t *type)
{
	if (!strcmp(type, HMAC_SHA386_CRYPTO_TYPE))
		return SHA384_DIGEST_SIZE;
	else if (!strcmp(type, HMAC_SHA256_CRYPTO_TYPE))
		return SHA256_DIGEST_SIZE;
	return -EINVAL;
}

/**
 * lim_get_auth_tag_len()- This API return auth tag len based on crypto suit
 * used to encrypt erp packet.
 * @crypto_suite: Crtpto suit
 *
 * Return: tag length
 */
static uint8_t lim_get_auth_tag_len(enum fils_erp_cryptosuite crypto_suite)
{
	switch (crypto_suite) {
		case HMAC_SHA256_64:
			return -EINVAL;
		case HMAC_SHA256_128:
			return FILS_SHA256_128_AUTH_TAG;
		case HMAC_SHA256_256:
			return FILS_SHA256_256_AUTH_TAG;
		default:
			return -EINVAL;
	}
}

/**
 * lim_copy_u16_be()- This API reads u16 value from network byte order buffer
 * @ptr: pointer to buffer
 *
 * Return: 16bit value
 */
static uint16_t limGetU16_be(uint8_t *buf)
{
	return (buf[0] << 8) | buf[1];
}
/**
 * lim_get_crypto_type()- This API returns crypto type based on akm suite used
 * @akm: akm used for authentication
 *
 * Return: Crypto type
 */
static uint8_t *lim_get_crypto_type(uint8_t akm)
{
	switch (akm) {
		case eCSR_AUTH_TYPE_FILS_SHA384:
		case eCSR_AUTH_TYPE_FT_FILS_SHA384:
			return FILS_SHA384_CRYPTO_TYPE;
		case eCSR_AUTH_TYPE_FILS_SHA256:
		case eCSR_AUTH_TYPE_FT_FILS_SHA256:
		default:
			return FILS_SHA256_CRYPTO_TYPE;
	}
}

/**
 * lim_get_pmk_length()- This API returns pmk length based on akm used
 * @akm: akm used for authentication
 *
 * Return: PMK length
 */
static uint8_t lim_get_pmk_length(int akm_type)
{
	switch (akm_type) {
		case eCSR_AUTH_TYPE_FILS_SHA256:
		case eCSR_AUTH_TYPE_FT_FILS_SHA256:
			return FILS_SHA256_PKM_LEN;
		case eCSR_AUTH_TYPE_FILS_SHA384:
		case eCSR_AUTH_TYPE_FT_FILS_SHA384:
			return FILS_SHA384_PKM_LEN;
		default:
			return FILS_SHA256_PKM_LEN;
	}
}

/**
 * lim_get_kek_len()- This API returns kek length based on akm used
 * @akm: akm used for authentication
 *
 * Return: KEK length
 */
static uint8_t lim_get_kek_len(uint8_t akm)
{
	switch (akm) {
		case eCSR_AUTH_TYPE_FILS_SHA384:
		case eCSR_AUTH_TYPE_FT_FILS_SHA384:
			return FILS_SHA384_KEK_LEN;
		case eCSR_AUTH_TYPE_FILS_SHA256:
		case eCSR_AUTH_TYPE_FT_FILS_SHA256:
			return FILS_SHA256_KEK_LEN;
		default:
			return FILS_SHA256_KEK_LEN;
	}
}

/**
 * lim_get_tk_len()- This API returns tk length based on cypher used
 * @akm: cypher used
 *
 * Return: TK length
 */
static uint8_t lim_get_tk_len(int cypher_suite)
{
	switch (cypher_suite) {
		case eSIR_ED_TKIP:
			return TK_LEN_TKIP;
		case eSIR_ED_CCMP:
			return TK_LEN_CCMP;
		case eSIR_ED_AES_128_CMAC:
			return TK_LEN_AES_128_CMAC;
		default:
			return 0;
	}
}

/**
 * lim_get_ick_len()- This API returns ick length based on akm used
 * @akm: akm used for authentication
 *
 * Return: ICK length
 */
static int lim_get_ick_len(uint8_t akm)
{
	switch (akm) {
		case eCSR_AUTH_TYPE_FILS_SHA384:
		case eCSR_AUTH_TYPE_FT_FILS_SHA384:
			return FILS_SHA384_ICK_LEN;
		case eCSR_AUTH_TYPE_FILS_SHA256:
		case eCSR_AUTH_TYPE_FT_FILS_SHA256:
		default:
			return FILS_SHA256_ICK_LEN;
	}
}

/**
 * lim_get_key_from_prf()- This API returns key data using PRF-X as defined in
 * 11.6.1.7.2 ieee-80211-2012.
 * @type: crypto type needs to be used
 * @secret: key which needs to be used in crypto
 * @secret_len: key_len of secret
 * @label: PRF label
 * @optional_data: Data used for hash
 * @optional_data_len: data length
 * @key: key data output
 * @keylen: key data length
 *
 * Return: VOS_STATUS
 */
static VOS_STATUS lim_get_key_from_prf(uint8_t *type, uint8_t *secret,
		uint32_t secret_len, uint8_t *label, uint8_t *optional_data,
		uint32_t optional_data_len, uint8_t *key, uint32_t keylen)
{
	uint8_t count[2];
	uint8_t *addr[4];
	uint32_t len[4];
	uint16_t key_bit_length = keylen * 8;
	uint8_t key_length[2];
	uint32_t i = 0, remain_len;
	uint16_t interation;
	uint8_t crypto_digest_len = lim_get_crypto_digest_len(type);
	uint8_t tmp_hash[SHA384_DIGEST_SIZE] = {0};

	addr[0] = count;
	len[0] = sizeof(count);

	addr[1] = label;
	len[1] = strlen(label);

	addr[2] = optional_data;
	len[2] = optional_data_len;

	vos_mem_copy(key_length, &key_bit_length, sizeof(key_bit_length));
	addr[3] = key_length;
	len[3] = sizeof(key_length);

	for (interation = 1; i < keylen; interation++) {
		remain_len = keylen - i;
		vos_mem_copy(count, &interation, sizeof(interation));

		if (remain_len >= crypto_digest_len)
			remain_len = crypto_digest_len;

		if(!alloc_tfm(type))
		{
			if (qdf_get_hmac_hash(type, secret, secret_len, 4,
						addr, len, tmp_hash) < 0) {
				VOS_TRACE(VOS_MODULE_ID_PE, VOS_TRACE_LEVEL_ERROR,
						FL("qdf_get_hmac_hash failed"));
				return VOS_STATUS_E_FAILURE;
			}
		}
		vos_mem_copy(&key[i], tmp_hash, remain_len);
		i += crypto_digest_len;
	}
	return VOS_STATUS_SUCCESS;
}

#define MAX_PRF_INTERATIONS_COUNT 255

/**
 * lim_default_hmac_sha256_kdf()- This API calculates key data using default kdf
 * defined in RFC4306.
 * @secret: key which needs to be used in crypto
 * @secret_len: key_len of secret
 * @label: PRF label
 * @optional_data: Data used for hash
 * @optional_data_len: data length
 * @key: key data output
 * @keylen: key data length
 *
 * This API creates default KDF as defined in RFC4306
 * PRF+ (K,S) = T1 | T2 | T3 | T4 | ...
 * T1 = PRF (K, S | 0x01)
 * T2 = PRF (K, T1 | S | 0x02)
 * T3 = PRF (K, T2 | S | 0x03)
 * T4 = PRF (K, T3 | S | 0x04)
 *
 * for every iteration its creates 32 bit of hash
 *
 * Return: VOS_STATUS
 */
	static VOS_STATUS
lim_default_hmac_sha256_kdf(uint8_t *secret, uint32_t secret_len,
		uint8_t *label, uint8_t *optional_data,
		uint32_t optional_data_len, uint8_t *key, uint32_t keylen)
{
	uint8_t tmp_hash[SHA256_DIGEST_SIZE] = {0};
	uint8_t count = 1;
	uint8_t *addr[4];
	uint32_t len[4];
	uint32_t current_position = 0, remaining_data = SHA256_DIGEST_SIZE;

	addr[0] = tmp_hash;
	len[0] = SHA256_DIGEST_SIZE;
	addr[1] = label;
	len[1] = strlen(label) + 1;
	addr[2] = optional_data;
	len[2] = optional_data_len;
	addr[3] = &count;
	len[3] = 1;

	if (keylen == 0 ||
			(keylen > (MAX_PRF_INTERATIONS_COUNT * SHA256_DIGEST_SIZE))) {
		VOS_TRACE(VOS_MODULE_ID_PE, VOS_TRACE_LEVEL_ERROR,
				FL("invalid key length %d"), keylen);
		return VOS_STATUS_E_FAILURE;
	}

	if(!alloc_tfm(FILS_SHA256_CRYPTO_TYPE))
	{
		/* Create T1 */
		if (qdf_get_hmac_hash(FILS_SHA256_CRYPTO_TYPE, secret, secret_len, 3,
					&addr[1], &len[1], tmp_hash) < 0) {
			VOS_TRACE(VOS_MODULE_ID_PE, VOS_TRACE_LEVEL_ERROR,
					FL("failed to get hmac hash"));
			return VOS_STATUS_E_FAILURE;
		}
	}
	/* Update hash from tmp_hash */
	vos_mem_copy(key + current_position, tmp_hash, remaining_data);
	current_position += remaining_data;

	for (count = 2; current_position < keylen; count++) {
		remaining_data = keylen - current_position;
		if (remaining_data > SHA256_DIGEST_SIZE)
			remaining_data = SHA256_DIGEST_SIZE;

		if(!alloc_tfm(FILS_SHA256_CRYPTO_TYPE))
		{
			/* Create T-n */
			if (qdf_get_hmac_hash(FILS_SHA256_CRYPTO_TYPE, secret,
						secret_len, 4, addr, len, tmp_hash) < 0) {
				VOS_TRACE(VOS_MODULE_ID_PE, VOS_TRACE_LEVEL_ERROR,
						FL("failed to get hmac hash"));
				return VOS_STATUS_E_FAILURE;
			}
		}
		/* Update hash from tmp_hash */
		vos_mem_copy(key + current_position, tmp_hash, remaining_data);
		current_position += remaining_data;
	}
	return VOS_STATUS_SUCCESS;
}

/**
 * lim_process_fils_eap_tlv()- This API process eap tlv available in auth resp
 * and returns remaining length.
 * @pe_session: PE session
 * @wrapped_data: wrapped data
 * @data_len: length of tlv
 *
 * Return: remaining length
 */
static uint32_t lim_process_fils_eap_tlv(tpPESession pe_session,
		uint8_t *wrapped_data, uint32_t data_len)
{
	struct fils_eap_tlv *tlv;
	struct fils_auth_rsp_info *auth_info;
	uint8_t auth_tag_len;

	auth_info = &pe_session->fils_info->auth_info;
	/* Minimum */
	auth_tag_len = lim_get_auth_tag_len(HMAC_SHA256_128);

	while (data_len > (auth_tag_len + 1)) {
		tlv = (struct fils_eap_tlv *) wrapped_data;

		VOS_TRACE(VOS_MODULE_ID_PE, VOS_TRACE_LEVEL_INFO,
				FL("tlv type %x len %u total %u"),
				tlv->type, tlv->length, data_len);
		switch (tlv->type) {
			case SIR_FILS_EAP_TLV_KEYNAME_NAI:
				auth_info->keyname = vos_mem_malloc(tlv->length);
				if (!auth_info->keyname) {
					VOS_TRACE(VOS_MODULE_ID_PE, VOS_TRACE_LEVEL_INFO,
							FL("failed to alloc memory"));
					return 0;
				}
				vos_mem_copy(auth_info->keyname,
						tlv->data, tlv->length);
				auth_info->keylength = tlv->length;
				data_len -= (tlv->length + 2);
				wrapped_data += (tlv->length + 2);
				break;
			case SIR_FILS_EAP_TLV_R_RK_LIFETIME:
				/* TODO check this */
				auth_info->r_rk_lifetime = limGetU32(tlv->data);
				data_len -= (tlv->length + 2);
				wrapped_data += (tlv->length + 2);
				break;
			case SIR_FILS_EAP_TLV_R_MSK_LIFETIME:
				/* TODO check this */
				auth_info->r_msk_lifetime = limGetU32(tlv->data);
				data_len -= (tlv->length + 2);
				wrapped_data += (tlv->length + 2);
				break;
			case SIR_FILS_EAP_TLV_DOMAIN_NAME:
				auth_info->domain_name = vos_mem_malloc(tlv->length);
				if (!auth_info->domain_name) {
					VOS_TRACE(VOS_MODULE_ID_PE, VOS_TRACE_LEVEL_INFO,
							FL("failed to alloc memory"));
					return 0;
				}
				vos_mem_copy(auth_info->domain_name,
						tlv->data, tlv->length);
				auth_info->domain_len = tlv->length;
				data_len -= (tlv->length + 2);
				wrapped_data += (tlv->length + 2);
				break;
				/* TODO process these now */
			case SIR_FILS_EAP_TLV_CRYPTO_LIST:
			case SIR_FILS_EAP_TLV_AUTH_INDICATION:
				data_len -= (tlv->length + 2);
				wrapped_data += (tlv->length + 2);
				break;
			default:
				VOS_TRACE(VOS_MODULE_ID_PE, VOS_TRACE_LEVEL_ERROR,
						FL("Unknown type"));
				return data_len;
		}
	}
	return data_len;
}

/**
 * lim_generate_key_data()- This API generates key data using prf
 * FILS-Key-Data = KDF-X(PMK, "FILS PTK Derivation", SPA||AA||SNonce||ANonce)
 * @fils_info: fils session info
 * @key_label: label used
 * @data: data buffer
 * @data_len: data buffer len
 * @key_data: hash data needs to be generated
 * @key_data_len: hash data len
 *
 * Return: VOS_STATUS
 */
static VOS_STATUS lim_generate_key_data(struct pe_fils_session *fils_info,
		uint8_t *key_label, uint8_t *data, uint32_t data_len,
		uint8_t *key_data, uint32_t key_data_len)
{
	VOS_STATUS status;

	if (!fils_info)
		return VOS_STATUS_E_FAILURE;

	status = lim_get_key_from_prf(lim_get_crypto_type(fils_info->akm),
			fils_info->fils_pmk,
			fils_info->fils_pmk_len,
			key_label, data, data_len, key_data, key_data_len);
	if (status != VOS_STATUS_SUCCESS)
		VOS_TRACE(VOS_MODULE_ID_PE, VOS_TRACE_LEVEL_ERROR,
				FL("failed to generate keydata"));
	return status;
}

/**
 * lim_generate_ap_key_auth()- This API generates ap auth data which needs to be
 * verified in assoc response.
 * @pe_session: pe session pointer
 *
 * Return: None
 */
static void lim_generate_ap_key_auth(tpPESession pe_session)
{
	uint8_t *buf, *addr[1];
	uint32_t len;
	struct pe_fils_session *fils_info = pe_session->fils_info;
	uint8_t data[SIR_FILS_NONCE_LENGTH + SIR_FILS_NONCE_LENGTH
		+ QDF_MAC_ADDR_SIZE + QDF_MAC_ADDR_SIZE] = {0};

	if (!fils_info)
		return;
	len = SIR_FILS_NONCE_LENGTH + SIR_FILS_NONCE_LENGTH +
		QDF_MAC_ADDR_SIZE +  QDF_MAC_ADDR_SIZE;
	addr[0] = data;
	buf = data;
	vos_mem_copy(buf, fils_info->auth_info.fils_nonce,
			SIR_FILS_NONCE_LENGTH);
	buf += SIR_FILS_NONCE_LENGTH;
	vos_mem_copy(buf, fils_info->fils_nonce, SIR_FILS_NONCE_LENGTH);
	buf += SIR_FILS_NONCE_LENGTH;
	vos_mem_copy(buf, pe_session->bssId, QDF_MAC_ADDR_SIZE);
	buf += QDF_MAC_ADDR_SIZE;
	vos_mem_copy(buf, pe_session->selfMacAddr, QDF_MAC_ADDR_SIZE);
	buf += QDF_MAC_ADDR_SIZE;

	if(!alloc_tfm(lim_get_crypto_type(fils_info->akm)))
	{
		if (qdf_get_hmac_hash(lim_get_crypto_type(fils_info->akm),
					fils_info->ick, fils_info->ick_len, 1, &addr[0],
					&len, fils_info->ap_key_auth_data) < 0)
			VOS_TRACE(VOS_MODULE_ID_PE, VOS_TRACE_LEVEL_ERROR,
					FL("failed to generate PMK id"));
	}
	fils_info->ap_key_auth_len = lim_get_crypto_digest_len(
			lim_get_crypto_type(fils_info->akm));
	lim_fils_data_dump("AP Key Auth", fils_info->ap_key_auth_data,
			fils_info->ap_key_auth_len);
}

/**
 * lim_generate_key_auth()- This API generates sta auth data which needs to be
 * send to AP in assoc request, AP will generate the same the verify it.
 * @pe_session: pe session pointer
 *
 * Return: None
 */
static void lim_generate_key_auth(tpPESession pe_session)
{
	uint8_t *buf, *addr[1];
	uint32_t len;
	struct pe_fils_session *fils_info = pe_session->fils_info;
	uint8_t data[SIR_FILS_NONCE_LENGTH + SIR_FILS_NONCE_LENGTH
		+ QDF_MAC_ADDR_SIZE + QDF_MAC_ADDR_SIZE] = {0};

	if (!fils_info)
		return;

	len = SIR_FILS_NONCE_LENGTH + SIR_FILS_NONCE_LENGTH +
		QDF_MAC_ADDR_SIZE +  QDF_MAC_ADDR_SIZE;
	addr[0] = data;
	buf = data;
	vos_mem_copy(buf, fils_info->fils_nonce, SIR_FILS_NONCE_LENGTH);
	buf += SIR_FILS_NONCE_LENGTH;
	vos_mem_copy(buf, fils_info->auth_info.fils_nonce,
			SIR_FILS_NONCE_LENGTH);
	buf += SIR_FILS_NONCE_LENGTH;
	vos_mem_copy(buf, pe_session->selfMacAddr, QDF_MAC_ADDR_SIZE);
	buf += QDF_MAC_ADDR_SIZE;
	vos_mem_copy(buf, pe_session->bssId, QDF_MAC_ADDR_SIZE);
	buf += QDF_MAC_ADDR_SIZE;

	if(!alloc_tfm(lim_get_crypto_type(fils_info->akm)))
	{
		if (qdf_get_hmac_hash(lim_get_crypto_type(fils_info->akm),
					fils_info->ick, fils_info->ick_len, 1,
					&addr[0], &len, fils_info->key_auth) < 0)
			VOS_TRACE(VOS_MODULE_ID_PE, VOS_TRACE_LEVEL_ERROR,
					FL("failed to generate key auth"));
	}
	fils_info->key_auth_len = lim_get_crypto_digest_len(
			lim_get_crypto_type(fils_info->akm));
	lim_fils_data_dump("STA Key Auth",
			fils_info->key_auth, fils_info->key_auth_len);
}

/**
 * lim_get_keys()- This API generates keys keydata which is generated after
 * parsing of auth response.
 * KCK = L(FILS-Key-Data, 0, KCK_bits)
 * KEK = L(FILS-Key-Data, KCK_bits, KEK_bits)
 * TK = L(FILS-Key-Data, KCK_bits + KEK_bits, TK_bits)
 * FILS-FT = L(FILS-Key-Data, KCK_bits + KEK_bits + TK_bits, FILS-FT_bits)
 * @pe_session: pe session pointer
 *
 * Return: None
 */
static void lim_get_keys(tpPESession pe_session)
{
	uint8_t key_label[] = PTK_KEY_LABEL;
	uint8_t *data;
	uint8_t data_len;
	struct pe_fils_session *fils_info = pe_session->fils_info;
	uint8_t key_data[MAX_ICK_LEN + MAX_KEK_LEN + MAX_TK_LEN] = {0};
	uint8_t key_data_len;
	uint8_t ick_len = lim_get_ick_len(fils_info->akm);
	uint8_t kek_len = lim_get_kek_len(fils_info->akm);
	uint8_t tk_len = lim_get_tk_len(pe_session->encryptType);
	uint8_t *buf;

	if (!fils_info)
		return;
	key_data_len = ick_len + kek_len + tk_len;

	data_len = 2 * SIR_FILS_NONCE_LENGTH + 2 * QDF_MAC_ADDR_SIZE;
	data = vos_mem_malloc(data_len);
	if (!data) {
		VOS_TRACE(VOS_MODULE_ID_PE, VOS_TRACE_LEVEL_ERROR,
				FL("failed to alloc memory"));
		return;
	}

	/* Update data */
	buf = data;
	vos_mem_copy(buf, pe_session->selfMacAddr, QDF_MAC_ADDR_SIZE);
	buf += QDF_MAC_ADDR_SIZE;
	vos_mem_copy(buf, pe_session->bssId, QDF_MAC_ADDR_SIZE);
	buf += QDF_MAC_ADDR_SIZE;
	vos_mem_copy(buf, fils_info->fils_nonce, SIR_FILS_NONCE_LENGTH);
	buf += SIR_FILS_NONCE_LENGTH;
	vos_mem_copy(buf, fils_info->auth_info.fils_nonce,
			SIR_FILS_NONCE_LENGTH);
	lim_generate_key_data(fils_info, key_label, data, data_len,
			key_data, key_data_len);
	buf = key_data;
	vos_mem_copy(fils_info->ick, buf, ick_len);
	fils_info->ick_len = ick_len;
	buf += ick_len;
	vos_mem_copy(fils_info->kek, buf, kek_len);
	fils_info->kek_len = kek_len;
	buf += kek_len;
	vos_mem_copy(fils_info->tk, buf, tk_len);
	fils_info->tk_len = tk_len;
	lim_fils_data_dump("Key Data", key_data, key_data_len);
	lim_fils_data_dump("ICK", fils_info->ick, ick_len);
	lim_fils_data_dump("KEK", fils_info->kek, kek_len);
	lim_fils_data_dump("TK", fils_info->tk, tk_len);
	vos_mem_free(data);
}

/**
 * lim_generate_pmkid()- This API generates PMKID using hash of erp auth packet
 * parsing of auth response.
 * PMKID = Truncate-128(Hash(EAP-Initiate/Reauth))
 * @pe_session: pe session pointer
 *
 * Return: None
 */
static void lim_generate_pmkid(tpPESession pe_session)
{
	uint8_t hash[SHA384_DIGEST_SIZE];
	struct pe_fils_session *fils_info = pe_session->fils_info;

	if (!fils_info)
		return;

	if(!alloc_tfm(lim_get_crypto_type(fils_info->akm)))
	{
		qdf_get_hash(lim_get_crypto_type(fils_info->akm), 1,
				&fils_info->fils_erp_reauth_pkt,
				&fils_info->fils_erp_reauth_pkt_len, hash);
		vos_mem_copy(fils_info->fils_pmkid, hash, PMKID_LEN);
		lim_fils_data_dump("PMKID", fils_info->fils_pmkid, PMKID_LEN);
	}
}

/**
 * lim_generate_pmk()- This API generates PMK using hmac hash of rmsk data
 * anonce, snonce will be used as key for this
 * PMK = HMAC-Hash(SNonce || ANonce, rMSK [ || DHss ])
 * @pe_session: pe session pointer
 *
 * Return: None
 */
static void lim_generate_pmk(tpPESession pe_session)
{
	uint8_t nounce[2 * SIR_FILS_NONCE_LENGTH] = {0};
	uint8_t nounce_len = 2 * SIR_FILS_NONCE_LENGTH;
	uint8_t *addr[1];
	uint32_t len[1];
	struct pe_fils_session *fils_info = pe_session->fils_info;

	/* Snounce */
	vos_mem_copy(nounce, fils_info->fils_nonce,
			SIR_FILS_NONCE_LENGTH);
	/* anounce */
	vos_mem_copy(nounce + SIR_FILS_NONCE_LENGTH,
			fils_info->auth_info.fils_nonce,
			SIR_FILS_NONCE_LENGTH);
	fils_info->fils_pmk_len = lim_get_pmk_length(fils_info->akm);

	if (fils_info->fils_pmk)
		vos_mem_free(fils_info->fils_pmk);

	fils_info->fils_pmk = vos_mem_malloc(fils_info->fils_pmk_len);
	if (!fils_info->fils_pmk) {
		VOS_TRACE(VOS_MODULE_ID_PE, VOS_TRACE_LEVEL_ERROR,
				FL("failed to alloc memory"));
		return;
	}
	addr[0] = fils_info->fils_rmsk;
	len[0] = fils_info->fils_rmsk_len;
	lim_fils_data_dump("Nonce", nounce, nounce_len);

	if(!alloc_tfm(lim_get_crypto_type(fils_info->akm)))
	{
		if (qdf_get_hmac_hash(lim_get_crypto_type(fils_info->akm), nounce,
					nounce_len, 1,
					&addr[0], &len[0], fils_info->fils_pmk) < 0)
			VOS_TRACE(VOS_MODULE_ID_PE, VOS_TRACE_LEVEL_ERROR,
					FL("failed to generate PMK"));
	}
	lim_fils_data_dump("PMK", fils_info->fils_pmk, fils_info->fils_pmk_len);
}

/**
 * lim_generate_rmsk_data()- This API generates RMSK data using
 * default kdf as defined in RFC4306.
 * @pe_session: pe session pointer
 *
 * Return: None
 */
static void lim_generate_rmsk_data(tpPESession pe_session)
{
	uint8_t optional_data[4] = {0};
	uint8_t rmsk_label[] = RMSK_LABEL;
	struct pe_fils_session *fils_info = pe_session->fils_info;
	struct fils_auth_rsp_info *auth_info;

	if (!fils_info)
		return;
	auth_info = &(pe_session->fils_info->auth_info);
	fils_info->fils_rmsk_len = fils_info->fils_r_rk_len;
	fils_info->fils_rmsk = vos_mem_malloc(fils_info->fils_r_rk_len);
	if (!fils_info->fils_rmsk) {
		VOS_TRACE(VOS_MODULE_ID_PE, VOS_TRACE_LEVEL_ERROR,
				FL("failed to alloc memory"));
		return;
	}
	/*
	 * Sequence number sent in EAP-INIT packet,
	 * it should be in network byte order
	 */
	lim_copy_u16_be(&optional_data[0], fils_info->sequence_number);
	lim_copy_u16_be(&optional_data[2], fils_info->fils_r_rk_len);
	lim_default_hmac_sha256_kdf(fils_info->fils_r_rk,
			fils_info->fils_r_rk_len, rmsk_label,
			optional_data, sizeof(optional_data),
			fils_info->fils_rmsk, fils_info->fils_rmsk_len);
	lim_fils_data_dump("RMSK", fils_info->fils_rmsk,
			fils_info->fils_rmsk_len);
}

/**
 * lim_process_auth_wrapped_data()- This API process wrapped data element
 * of auth response.
 * @pe_session: pe session pointer
 * @wrapped_data: wrapped data pointer
 * @data_len: wrapped data len
 *
 * Return: None
 */
static VOS_STATUS lim_process_auth_wrapped_data(tpPESession pe_session,
		uint8_t *wrapped_data, uint32_t data_len)
{
	uint8_t code;
	uint8_t identifier;
	uint16_t length;
	uint8_t type;
	unsigned long flags;
	struct pe_fils_session *fils_info;
	uint8_t hash[32], crypto;
	uint32_t remaining_len = data_len, new_len;
	uint8_t *input_data[1];
	uint32_t input_len[1];
	uint8_t auth_tag_len;

	fils_info = pe_session->fils_info;
	if (!fils_info)
		return VOS_STATUS_E_FAILURE;
	input_data[0] = wrapped_data;
	input_len[0] = data_len;

	VOS_TRACE(VOS_MODULE_ID_PE, VOS_TRACE_LEVEL_INFO,
			FL("trying to process the wrappped data"));

	code = *wrapped_data;
	wrapped_data++;
	remaining_len--;
	identifier = *wrapped_data;
	wrapped_data++;
	remaining_len--;

	length = limGetU16(wrapped_data);
	wrapped_data += sizeof(uint16_t);
	remaining_len -= sizeof(uint16_t);

	type = *wrapped_data; /* val should be 2 here */
	wrapped_data++;
	remaining_len--;

	flags = *wrapped_data;
	if (test_bit(7, &flags)) {
		VOS_TRACE(VOS_MODULE_ID_PE, VOS_TRACE_LEVEL_ERROR,
				FL("R bit is set in flag, error"));
		return VOS_STATUS_E_FAILURE;
	}
	wrapped_data++;
	remaining_len--;

	fils_info->auth_info.sequence = limGetU16_be(wrapped_data);
	wrapped_data += sizeof(uint16_t);
	remaining_len -= sizeof(uint16_t);
	/* Validate Auth sequence number */
	if (fils_info->auth_info.sequence < fils_info->sequence_number) {
		VOS_TRACE(VOS_MODULE_ID_PE, VOS_TRACE_LEVEL_ERROR,
				FL("sequence EAP-finish:%d is less than EAP-init:%d"),
				fils_info->auth_info.sequence,
				fils_info->sequence_number);
		return VOS_STATUS_E_FAILURE;
	}

	/* Parse attached TLVs */
	new_len = lim_process_fils_eap_tlv(pe_session,
			wrapped_data, remaining_len);
	VOS_TRACE(VOS_MODULE_ID_PE, VOS_TRACE_LEVEL_ERROR,
			FL("new len=%d remain=%d"), new_len, remaining_len);

	wrapped_data += remaining_len - new_len;
	remaining_len = new_len;
	/* Remove cryptosuite */
	crypto = *wrapped_data;
	wrapped_data++;
	remaining_len--;

	auth_tag_len = lim_get_auth_tag_len(crypto);
	input_len[0] -= auth_tag_len;
	/* if we have auth tag remaining */
	if (remaining_len == auth_tag_len) {

		if(!alloc_tfm(FILS_SHA256_CRYPTO_TYPE))
		{
			qdf_get_hmac_hash(FILS_SHA256_CRYPTO_TYPE, fils_info->fils_r_ik,
					fils_info->fils_r_ik_len, 1,
					input_data, input_len, hash);
		}
	} else {
		VOS_TRACE(VOS_MODULE_ID_PE, VOS_TRACE_LEVEL_ERROR,
				FL("invalid remaining len %d"),
				remaining_len);
	}
	if (!vos_mem_compare(wrapped_data, hash, auth_tag_len)) {
		VOS_TRACE(VOS_MODULE_ID_PE, VOS_TRACE_LEVEL_ERROR,
				FL("integratity check failed for auth, crypto %d"),
				crypto);
		return VOS_STATUS_E_FAILURE;
	}

	lim_generate_rmsk_data(pe_session);
	lim_generate_pmk(pe_session);
	lim_generate_pmkid(pe_session);
	return VOS_STATUS_SUCCESS;
}

/**
 * lim_is_valid_fils_auth_frame()- This API check whether auth frame is a
 * valid frame.
 * @mac_ctx: mac context
 * @pe_session: pe session pointer
 * @rx_auth_frm_body: pointer to autherntication frame
 *
 * Return: true if frame is valid or fils is disable, false otherwise
 */
bool lim_is_valid_fils_auth_frame(tpAniSirGlobal mac_ctx,
		tpPESession pe_session,
		tSirMacAuthFrameBody *rx_auth_frm_body)
{
	if (!pe_session->fils_info)
		return true;
	if (pe_session->fils_info->is_fils_connection == false)
		return true;

	if (!vos_mem_compare(rx_auth_frm_body->session,
				pe_session->fils_info->fils_session,
				SIR_FILS_SESSION_LENGTH)) {
		lim_fils_data_dump("Current FILS session",
				pe_session->fils_info->fils_session,
				SIR_FILS_SESSION_LENGTH);
		lim_fils_data_dump("FILS Session in pkt",
				rx_auth_frm_body->session,
				SIR_FILS_SESSION_LENGTH);
		return false;
	}
	vos_mem_copy(pe_session->fils_info->auth_info.fils_nonce,
			rx_auth_frm_body->nonce, SIR_FILS_NONCE_LENGTH);
	pe_session->fils_info->auth_info.assoc_delay =
		rx_auth_frm_body->assoc_delay_info;
	return true;
}

/**
 * lim_create_fils_r_ik()- This API create rik using rrk coming from
 * supplicant.
 * rIK = KDF (K, S), where
 * K = rRK and
 * S = rIK Label + "\0" + cryptosuite + length
 * The rIK Label is the 8-bit ASCII string:
 * Re-authentication Integrity Key@ietf.org
 * @fils_info: fils session info
 *
 * Return: None
 */
static VOS_STATUS lim_create_fils_r_ik(struct pe_fils_session *fils_info)
{
	uint8_t optional_data[SIR_FILS_OPTIONAL_DATA_LEN];
	uint8_t label[] = SIR_FILS_RIK_LABEL;

	if (!fils_info)
		return VOS_STATUS_E_FAILURE;
	optional_data[0] = HMAC_SHA256_128;
	/* basic validation */
	if (fils_info->fils_r_rk_len <= 0) {
		VOS_TRACE(VOS_MODULE_ID_PE, VOS_TRACE_LEVEL_INFO,
				FL("invalid r_rk length %d"), fils_info->fils_r_rk_len);
		return VOS_STATUS_E_FAILURE;
	}
	lim_copy_u16_be(&optional_data[1], fils_info->fils_r_rk_len);
	fils_info->fils_r_ik = vos_mem_malloc(fils_info->fils_r_rk_len);
	if (!fils_info->fils_r_ik) {
		VOS_TRACE(VOS_MODULE_ID_PE, VOS_TRACE_LEVEL_INFO,
				FL("failed to alloc memory"));
		return VOS_STATUS_E_FAILURE;
	}
	if (lim_default_hmac_sha256_kdf(fils_info->fils_r_rk,
				fils_info->fils_r_rk_len, label,
				optional_data, sizeof(optional_data),
				fils_info->fils_r_ik, fils_info->fils_r_rk_len)
			!= VOS_STATUS_SUCCESS) {
		VOS_TRACE(VOS_MODULE_ID_PE, VOS_TRACE_LEVEL_INFO,
				FL("failed to create r_ik"));
		return VOS_STATUS_E_FAILURE;
	}
	fils_info->fils_r_ik_len = fils_info->fils_r_rk_len;
	lim_fils_data_dump("rRk", fils_info->fils_r_rk,
			fils_info->fils_r_rk_len);
	lim_fils_data_dump("rIk", fils_info->fils_r_ik,
			fils_info->fils_r_rk_len);

	return VOS_STATUS_SUCCESS;
}

/**
 * lim_create_fils_wrapper_data()- This API create warpped data which will be
 * sent in auth request.
 * @fils_info: fils session info
 *
 * Return: None
 */
static int lim_create_fils_wrapper_data(struct pe_fils_session *fils_info)
{
	uint8_t *buf;
	uint8_t auth_tag[FILS_AUTH_TAG_MAX_LENGTH] = {0};
	uint32_t length = 0;
	int buf_len =
		/* code + identifier */
		sizeof(uint8_t) * 2 +
		/* length */
		sizeof(uint16_t) +
		/* type + flags */
		sizeof(uint8_t) * 2 +
		/* sequence */
		sizeof(uint16_t) +
		/* tlv : type, length, data */
		sizeof(uint8_t) * 2 + fils_info->keyname_nai_length +
		/* cryptosuite + auth_tag */
		sizeof(uint8_t) + lim_get_auth_tag_len(HMAC_SHA256_128);

	if (!fils_info)
		return 0;
	fils_info->fils_erp_reauth_pkt = vos_mem_malloc(buf_len);
	if (!fils_info->fils_erp_reauth_pkt) {
		VOS_TRACE(VOS_MODULE_ID_PE, VOS_TRACE_LEVEL_ERROR,
				FL("failed to allocate memory"));
		return -EINVAL;
	}
	buf = fils_info->fils_erp_reauth_pkt;
	*buf = 5;
	buf++;
	/* Identifier */
	*buf = 0;
	buf++;
	/* Length */
	lim_copy_u16_be(buf, buf_len);
	buf += sizeof(uint16_t);
	/* type */
	*buf = SIR_FILS_EAP_INIT_PACKET_TYPE;
	buf++;

	/**
	 *  flag
	 *  0 1 2  <-- 5 -->
	 * ----------------
	 * |R|B|L| Reserved|
	 * -----------------
	 */
	*buf = 0x20; /* l=1, b=0, r=0 */
	buf++;
	/* sequence */
	lim_copy_u16_be(buf, fils_info->sequence_number);
	buf += sizeof(uint16_t);

	/* tlv */
	/* Type */
	*buf = SIR_FILS_EAP_TLV_KEYNAME_NAI;
	buf++;
	/* NAI Length */
	*buf = fils_info->keyname_nai_length;
	buf++;
	/* NAI Data */
	vos_mem_copy(buf, fils_info->keyname_nai_data,
			fils_info->keyname_nai_length);
	buf += fils_info->keyname_nai_length;

	/* cryptosuite */
	*buf = HMAC_SHA256_128;
	buf++;

	/*
	 * This should be moved to just after sending probe to save time
	 * lim_process_switch_channel_join_req ??
	 */
	lim_create_fils_r_ik(fils_info);
	fils_info->fils_erp_reauth_pkt_len = buf_len;
	length = fils_info->fils_erp_reauth_pkt_len -
		lim_get_auth_tag_len(HMAC_SHA256_128);
	if(!alloc_tfm(FILS_SHA256_CRYPTO_TYPE))
	{
		qdf_get_hmac_hash(FILS_SHA256_CRYPTO_TYPE,
				fils_info->fils_r_ik, fils_info->fils_r_ik_len, 1,
				&fils_info->fils_erp_reauth_pkt, &length, auth_tag);
	}
	lim_fils_data_dump("Auth tag", auth_tag,
			lim_get_auth_tag_len(HMAC_SHA256_128));
	lim_fils_data_dump("EAP init pkt", fils_info->fils_erp_reauth_pkt,
			fils_info->fils_erp_reauth_pkt_len);

	vos_mem_copy(buf, auth_tag, lim_get_auth_tag_len(HMAC_SHA256_128));
	buf += lim_get_auth_tag_len(HMAC_SHA256_128);

	return buf_len;
}

/**
 * lim_add_fils_data_to_auth_frame()- This API add fils data to auth frame.
 * Following will be added in this.
 * RSNIE
 * SNonce
 * Session
 * Wrapped data
 * @session: PE session
 * @body: pointer to auth frame where data needs to be added
 *
 * Return: None
 */
void lim_add_fils_data_to_auth_frame(tpPESession session,
		uint8_t *body)
{
	struct pe_fils_session *fils_info;

	fils_info = session->fils_info;
	if (!fils_info)
		return;
	/* RSN IE */
	vos_mem_copy(body, fils_info->rsn_ie, fils_info->rsn_ie_len);
	body += fils_info->rsn_ie_len;
	lim_fils_data_dump("FILS RSN", fils_info->rsn_ie,
			fils_info->rsn_ie_len);

	/* ***Nounce*** */
	/* Add element id */
	*body = SIR_MAX_ELEMENT_ID;
	body++;
	/* Add nounce length + 1 for ext element id */
	*body = SIR_FILS_NONCE_LENGTH + 1;
	body++;
	/* Add ext element */
	*body = SIR_FILS_NONCE_EXT_EID;
	body++;
	/* Add data */
	vos_rand_get_bytes(0, fils_info->fils_nonce, SIR_FILS_NONCE_LENGTH);
	vos_mem_copy(body, fils_info->fils_nonce, SIR_FILS_NONCE_LENGTH);
	body = body + SIR_FILS_NONCE_LENGTH;
	/* Dump data */
	lim_fils_data_dump("fils anonce", fils_info->fils_nonce,
			SIR_FILS_NONCE_LENGTH);

	/*   *** Session ***  */
	/* Add element id */
	*body = SIR_MAX_ELEMENT_ID;
	body++;
	/* Add nounce length + 1 for ext element id */
	*body = SIR_FILS_SESSION_LENGTH + 1;
	body++;
	/* Add ext element */
	*body = SIR_FILS_SESSION_EXT_EID;
	body++;
	/* Add data */
	vos_rand_get_bytes(0, fils_info->fils_session, SIR_FILS_SESSION_LENGTH);
	vos_mem_copy(body, fils_info->fils_session, SIR_FILS_SESSION_LENGTH);
	body = body + SIR_FILS_SESSION_LENGTH;
	/* dump data */
	lim_fils_data_dump("Fils Session",
			fils_info->fils_session, SIR_FILS_SESSION_LENGTH);

	/*  ERP Packet  */
	/* Add element id */
	*body = SIR_MAX_ELEMENT_ID;
	body++;
	/* Add packet length + 1 for ext element id */
	*body = fils_info->fils_erp_reauth_pkt_len + 1;
	body++;
	/* Add ext element */
	*body = SIR_FILS_WRAPPED_DATA_EXT_EID;
	body++;
	/* Copy data */
	vos_mem_copy(body, fils_info->fils_erp_reauth_pkt,
			fils_info->fils_erp_reauth_pkt_len);
	lim_fils_data_dump("Fils ERP reauth Pkt",
			fils_info->fils_erp_reauth_pkt,
			fils_info->fils_erp_reauth_pkt_len);
	body = body + fils_info->fils_erp_reauth_pkt_len;
}

/**
 * lim_process_fils_auth_frame2()- This API process fils data from auth response
 * @mac_ctx: mac context
 * @session: PE session
 * @rx_auth_frm_body: pointer to auth frame
 *
 * Return: true if fils data needs to be processed else false
 */
bool lim_process_fils_auth_frame2(tpAniSirGlobal mac_ctx,
		tpPESession pe_session,
		tSirMacAuthFrameBody *rx_auth_frm_body)
{
	bool pmkid_found = false;
	int i;
	tDot11fIERSN dot11f_ie_rsn = {0};

	if (rx_auth_frm_body->authAlgoNumber != eSIR_FILS_SK_WITHOUT_PFS)
		return false;

	if (!pe_session->fils_info)
		return false;

	if (dot11fUnpackIeRSN(mac_ctx,
			&rx_auth_frm_body->rsn_ie.info[0],
			rx_auth_frm_body->rsn_ie.length,
			&dot11f_ie_rsn) != DOT11F_PARSE_SUCCESS) {
		return false;
	}

	for (i = 0; i < dot11f_ie_rsn.pmkid_count; i++) {
		if (vos_mem_compare(dot11f_ie_rsn.pmkid[i],
					pe_session->fils_info->fils_pmkid,
					IEEE80211_PMKID_LEN)) {
			pmkid_found = true;
			VOS_TRACE(VOS_MODULE_ID_PE,
					VOS_TRACE_LEVEL_DEBUG,
					FL("pmkid match in rsn ie total_count %d"),
					dot11f_ie_rsn.pmkid_count);
			break;
		}
	}
	if (!pmkid_found) {
		if (VOS_STATUS_SUCCESS !=
				lim_process_auth_wrapped_data(pe_session,
					rx_auth_frm_body->wrapped_data,
					rx_auth_frm_body->wrapped_data_len))
			return false;
	} else {
		lim_fils_data_dump("PMK",
				pe_session->fils_info->fils_pmk,
				pe_session->fils_info->fils_pmk_len);
	}
	lim_get_keys(pe_session);
	lim_generate_key_auth(pe_session);
	lim_generate_ap_key_auth(pe_session);
	return true;
}

/**
 * lim_update_fils_config()- This API update fils session info to csr config
 * from join request.
 * @session: PE session
 * @sme_join_req: pointer to join request
 *
 * Return: None
 */
void lim_update_fils_config(tpPESession session,
		tpSirSmeJoinReq sme_join_req)
{
	struct pe_fils_session *csr_fils_info;
	struct cds_fils_connection_info *fils_config_info;

	fils_config_info = &sme_join_req->fils_con_info;
	csr_fils_info = session->fils_info;
	if (!csr_fils_info){
		VOS_TRACE(VOS_MODULE_ID_PE,
				VOS_TRACE_LEVEL_DEBUG,
				FL(" csr fils info not present exiting....."));
		return;
	}
	if (fils_config_info->is_fils_connection == false){
		VOS_TRACE(VOS_MODULE_ID_PE,
				VOS_TRACE_LEVEL_DEBUG,
				FL(" is_fils_connection is false....."));
		return;
	}

	csr_fils_info->is_fils_connection =
		fils_config_info->is_fils_connection;
	csr_fils_info->keyname_nai_length =
		fils_config_info->key_nai_length;
	csr_fils_info->fils_r_rk_len =
		fils_config_info->r_rk_length;
	csr_fils_info->akm = fils_config_info->akm_type;
	csr_fils_info->auth = fils_config_info->auth_type;
	csr_fils_info->sequence_number = fils_config_info->sequence_number;
	csr_fils_info->keyname_nai_data =
		vos_mem_malloc(fils_config_info->key_nai_length);
	if (!csr_fils_info->keyname_nai_data) {
		VOS_TRACE(VOS_MODULE_ID_PE,
				VOS_TRACE_LEVEL_DEBUG,
				FL("failed to alloc memory"));
		return;
	}
	vos_mem_copy(csr_fils_info->keyname_nai_data,
			fils_config_info->keyname_nai,
			fils_config_info->key_nai_length);
	csr_fils_info->fils_r_rk =
		vos_mem_malloc(fils_config_info->r_rk_length);
	if (!csr_fils_info->fils_r_rk) {
		VOS_TRACE(VOS_MODULE_ID_PE,
				VOS_TRACE_LEVEL_DEBUG,
				FL("failed to alloc memory"));
		vos_mem_free(csr_fils_info->keyname_nai_data);
		return;
	}
	vos_mem_copy(csr_fils_info->fils_r_rk,
			fils_config_info->r_rk,
			fils_config_info->r_rk_length);

	vos_mem_copy(csr_fils_info->fils_pmkid,
			fils_config_info->pmkid, PMKID_LEN);

	csr_fils_info->rsn_ie_len = sme_join_req->rsnIE.length;
	vos_mem_copy(csr_fils_info->rsn_ie,
			sme_join_req->rsnIE.rsnIEdata,
			sme_join_req->rsnIE.length);

	csr_fils_info->fils_pmk_len = fils_config_info->pmk_len;
	if (fils_config_info->pmk_len)
	{
		csr_fils_info->fils_pmk =
			vos_mem_malloc(fils_config_info->pmk_len);
		if (!csr_fils_info->fils_pmk) {
			vos_mem_free(csr_fils_info->keyname_nai_data);
			vos_mem_free(csr_fils_info->fils_r_rk);
			VOS_TRACE(VOS_MODULE_ID_PE,
					VOS_TRACE_LEVEL_DEBUG,
					FL("failed to alloc memory"));
			return;
		}
	}
	vos_mem_copy(csr_fils_info->fils_pmk, fils_config_info->pmk,
			fils_config_info->pmk_len);

	VOS_TRACE(VOS_MODULE_ID_PE, VOS_TRACE_LEVEL_INFO,
			FL("fils=%d nai-len=%d rrk_len=%d akm=%d auth=%d pmk_len=%d"),
			fils_config_info->is_fils_connection,
			fils_config_info->key_nai_length,
			fils_config_info->r_rk_length,
			fils_config_info->akm_type,
			fils_config_info->auth_type,
			fils_config_info->pmk_len);
}

#define EXTENDED_IE_HEADER_LEN 3
/**
 * lim_create_fils_auth_data()- This API creates the fils auth data
 * which needs to be sent in auth req.
 * @mac_ctx: mac context
 * @auth_frame: pointer to auth frame
 * @session: PE session
 *
 * Return: length of fils data
 */
uint32_t lim_create_fils_auth_data(tpAniSirGlobal mac_ctx,
		tpSirMacAuthFrameBody auth_frame,
		tpPESession session)
{
	uint32_t frame_len = 0;
	uint32_t wrapped_data_len;

	if (!session->fils_info)
		return 0;
	if (auth_frame->authAlgoNumber == eSIR_FILS_SK_WITHOUT_PFS) {
		frame_len += session->fils_info->rsn_ie_len;
		/* FILS nounce */
		frame_len += SIR_FILS_NONCE_LENGTH + EXTENDED_IE_HEADER_LEN;
		/* FILS Session */
		frame_len += SIR_FILS_SESSION_LENGTH + EXTENDED_IE_HEADER_LEN;
		/* Calculate data/length for FILS Wrapped Data */
		wrapped_data_len =
			lim_create_fils_wrapper_data(session->fils_info);
		if (wrapped_data_len < 0) {
			VOS_TRACE(VOS_MODULE_ID_PE, VOS_TRACE_LEVEL_INFO,
					FL("failed to create warpped data"));
			return 0;
		}
		frame_len += wrapped_data_len + EXTENDED_IE_HEADER_LEN;
	}
	return frame_len;
}

void populate_fils_connect_params(tpAniSirGlobal mac_ctx,
		tpPESession session,
		tpSirSmeJoinRsp sme_join_rsp)
{
	struct fils_join_rsp_params *fils_join_rsp;
	struct pe_fils_session *fils_info = (session->fils_info);

	if (!fils_info)
		return;

	if (!lim_is_fils_connection(session))
		return;

	if (!fils_info->fils_pmk_len ||
			!fils_info->tk_len || !fils_info->gtk_len ||
			!fils_info->fils_pmk || !fils_info->kek_len) {
		VOS_TRACE(VOS_MODULE_ID_PE, VOS_TRACE_LEVEL_INFO,
			FL("Invalid FILS info pmk len %d kek len %d tk len %d gtk len %d"),
				fils_info->fils_pmk_len,
				fils_info->kek_len,
				fils_info->tk_len,
				fils_info->gtk_len);
		return;
	}

	sme_join_rsp->fils_join_rsp = vos_mem_malloc(sizeof(*fils_join_rsp));
	if (!sme_join_rsp->fils_join_rsp) {
		VOS_TRACE(VOS_MODULE_ID_PE, VOS_TRACE_LEVEL_INFO,
				FL("fils_join_rsp malloc fails!"));
		pe_delete_fils_info(session);
		return;
	}

	fils_join_rsp = sme_join_rsp->fils_join_rsp;
	fils_join_rsp->fils_pmk = vos_mem_malloc(fils_info->fils_pmk_len);
	if (!fils_join_rsp->fils_pmk) {
		VOS_TRACE(VOS_MODULE_ID_PE, VOS_TRACE_LEVEL_INFO,
				FL("fils_pmk malloc fails!"));
		vos_mem_free(fils_join_rsp);
		pe_delete_fils_info(session);
		return;
	}

	fils_join_rsp->fils_pmk_len = fils_info->fils_pmk_len;
	vos_mem_copy(fils_join_rsp->fils_pmk, fils_info->fils_pmk,
			fils_info->fils_pmk_len);

	vos_mem_copy(fils_join_rsp->fils_pmkid, fils_info->fils_pmkid,
			IEEE80211_PMKID_LEN);

	fils_join_rsp->kek_len = fils_info->kek_len;
	vos_mem_copy(fils_join_rsp->kek, fils_info->kek, fils_info->kek_len);

	fils_join_rsp->tk_len = fils_info->tk_len;
	vos_mem_copy(fils_join_rsp->tk, fils_info->tk, fils_info->tk_len);

	fils_join_rsp->gtk_len = fils_info->gtk_len;
	vos_mem_copy(fils_join_rsp->gtk, fils_info->gtk, fils_info->gtk_len);

	VOS_TRACE(VOS_MODULE_ID_PE, VOS_TRACE_LEVEL_INFO,
			FL("FILS connect params copied lim"));
	pe_delete_fils_info(session);
}

/**
 * lim_parse_kde_elements() - Parse Key Delivery Elements
 * @mac_ctx: mac context
 * @fils_info: FILS info
 * @kde_list: KDE list buffer
 * @kde_list_len: Length of @kde_list
 *
 * This API is used to parse the Key Delivery Elements from buffer
 * and populate them in PE FILS session struct i.e @fils_info
 *
 *             Key Delivery Element[KDE] format
 * +----------+--------+-----------+------------+----------+
 * | ID(0xDD) | length |  KDE OUI  |  data type |  IE data |
 * |----------|--------|-----------|------------|----------|
 * |  1 byte  | 1 byte |  3 bytes  |   1 byte   | variable |
 * +----------+--------+-----------+------------+----------+
 *
 * there can be multiple KDE present inside KDE list.
 * the IE data could be GTK, IGTK etc based on the data type
 *
 * Return: QDF_STATUS_SUCCESS if we parse GTK successfully,
 *         QDF_STATUS_E_FAILURE otherwise
 */
static VOS_STATUS lim_parse_kde_elements(tpAniSirGlobal mac_ctx,
		struct pe_fils_session *fils_info,
		uint8_t *kde_list,
		uint8_t kde_list_len)
{
	uint8_t rem_len = kde_list_len;
	uint8_t *temp_ie = kde_list;
	uint8_t elem_id, data_type, data_len, *ie_data = NULL, *current_ie;
	uint16_t elem_len;

	if (!kde_list_len || !kde_list) {
		limLog(mac_ctx, LOGE, FL("kde_list_len %d"), kde_list_len);
		return VOS_STATUS_E_FAILURE;
	}

	while (rem_len >= 2) {
		current_ie = temp_ie;
		elem_id = *temp_ie++;
		elem_len = *temp_ie++;
		rem_len -= 2;

		if (elem_len < KDE_IE_DATA_OFFSET) {
			limLog(mac_ctx, LOGE, FL("Not enough len to parse elem_len %d"),
			       elem_len);
			return VOS_STATUS_E_FAILURE;
		}
		if (lim_check_if_vendor_oui_match(mac_ctx, KDE_OUI_TYPE,
					KDE_OUI_TYPE_SIZE, current_ie, elem_len)) {
			data_type = *(temp_ie + KDE_DATA_TYPE_OFFSET);
			ie_data = (temp_ie + KDE_IE_DATA_OFFSET);
			data_len = (elem_len - KDE_IE_DATA_OFFSET);
			switch(data_type) {
				/* TODO - is anymore KDE type expected */
				case DATA_TYPE_GTK:
					if (data_len < GTK_OFFSET) {
						limLog(mac_ctx, LOGE,
						       FL("Invalid KDE data_len %d"),
						       data_len);
						return VOS_STATUS_E_FAILURE;
					}
					limLog(mac_ctx, LOG1, FL("GTK found "));
					vos_mem_copy(fils_info->gtk, (ie_data +
							GTK_OFFSET), (data_len -
							GTK_OFFSET));
					fils_info->gtk_len = (data_len - GTK_OFFSET);
					break;
				case DATA_TYPE_IGTK:
					if (data_len < IGTK_OFFSET) {
						limLog(mac_ctx, LOGE,
						       FL("Invalid KDE data_len %d"),
						       data_len);
						return VOS_STATUS_E_FAILURE;
					}
					limLog(mac_ctx, LOG1, FL("IGTK found"));
					fils_info->igtk_len = (data_len - IGTK_OFFSET);
					vos_mem_copy(fils_info->igtk, (ie_data +
							IGTK_OFFSET), (data_len -
							IGTK_OFFSET));
					vos_mem_copy(fils_info->ipn, (ie_data +
							IPN_OFFSET), IPN_LEN);
					break;
				default:
					limLog(mac_ctx, LOGE, FL("Unknown KDE data type %x"),
							data_type);
					break;
			}
		}
		temp_ie += elem_len;
		rem_len -= elem_len;
		ie_data = NULL;
	}
	/* Expecting GTK in KDE */
	if (!fils_info->gtk_len) {
		limLog(mac_ctx, LOGE, FL("GTK not found in KDE"));
		return VOS_STATUS_E_FAILURE;
	}

	return VOS_STATUS_SUCCESS;
}

bool lim_verify_fils_params_assoc_rsp(tpAniSirGlobal mac_ctx,
		tpPESession session_entry,
		tpSirAssocRsp assoc_rsp,
		tLimMlmAssocCnf *assoc_cnf)
{
	struct pe_fils_session *fils_info = (session_entry->fils_info);
	tDot11fIEfils_session fils_session = assoc_rsp->fils_session;
	tDot11fIEfils_key_confirmation fils_key_auth = assoc_rsp->fils_key_auth;
	tDot11fIEfils_kde fils_kde = assoc_rsp->fils_kde;
	VOS_STATUS status;

	if (!fils_info){
		return true;
	}
	if (!lim_is_fils_connection(session_entry)){
		return true;
	}
	if (!assoc_rsp->fils_session.present) {
		limLog(mac_ctx, LOGE, FL("FILS IE not present"));
		goto verify_fils_params_fails;
	}

	/* Compare FILS session */
	if (adf_os_mem_cmp(fils_info->fils_session,
				fils_session.session, DOT11F_IE_FILS_SESSION_MAX_LEN)) {
		limLog(mac_ctx, LOGE, FL("FILS session mismatch"));
		goto verify_fils_params_fails;
	}

	/* Compare FILS key auth */
	if ((fils_key_auth.num_key_auth != fils_info->key_auth_len) ||
			adf_os_mem_cmp(fils_info->ap_key_auth_data, fils_key_auth.key_auth,
				fils_info->ap_key_auth_len)) {
		lim_fils_data_dump("session keyauth",
				fils_info->ap_key_auth_data,
				fils_info->ap_key_auth_len);
		lim_fils_data_dump("Pkt keyauth",
				fils_key_auth.key_auth,
				fils_key_auth.num_key_auth);
		goto verify_fils_params_fails;
	}

	/* Verify the Key Delivery Element presence */
	if (!fils_kde.num_kde_list) {
		limLog(mac_ctx, LOGE, FL("FILS KDE list absent"));
		goto verify_fils_params_fails;
	}

	/* Derive KDE elements */
	status = lim_parse_kde_elements(mac_ctx, fils_info, fils_kde.kde_list,
			fils_kde.num_kde_list);
	if (!VOS_IS_STATUS_SUCCESS(status)) {
		limLog(mac_ctx, LOGE, FL("KDE Parsing fails"));
		goto verify_fils_params_fails;
	}
	return true;

verify_fils_params_fails:
	assoc_cnf->resultCode = eSIR_SME_ASSOC_REFUSED;
	assoc_cnf->protStatusCode = eSIR_MAC_UNSPEC_FAILURE_STATUS;
	return false;
}

/**
 * find_ie_data_after_fils_session_ie() - Find IE pointer after FILS Session IE
 * @mac_ctx: MAC context
 * @buf: IE buffer
 * @buf_len: Length of @buf
 * @ie: Pointer to update the found IE pointer after FILS session IE
 * @ie_len: length of the IE data after FILS session IE
 *
 * This API is used to find the IE data ptr and length after FILS session IE
 *
 * Return: VOS_STATUS_SUCCESS if found, else VOS_STATUS_E_FAILURE
 */
static VOS_STATUS find_ie_data_after_fils_session_ie(tpAniSirGlobal mac_ctx,
		uint8_t *buf,
		uint32_t buf_len,
		uint8_t **ie,
		uint32_t *ie_len)
{
	uint32_t left = buf_len;
	uint8_t *ptr = buf;
	uint8_t elem_id, elem_len;

	if (NULL == buf || 0 == buf_len)
		return VOS_STATUS_E_FAILURE;

	while (left >= 2) {
		elem_id = ptr[0];
		elem_len = ptr[1];
		left -= 2;
		if (elem_len > left)
			return VOS_STATUS_E_FAILURE;

		if (elem_id == SIR_MAC_REQUEST_EID_MAX &&
				ptr[2] == SIR_FILS_SESSION_EXT_EID) {
			(*ie) = ((&ptr[1]) + ptr[1] + 1);
			(*ie_len) = (left - elem_len);
			return VOS_STATUS_SUCCESS;
		}
		left -= elem_len;
		ptr += (elem_len + 2);
	}
	return VOS_STATUS_E_FAILURE;
}

/**
 * fils_aead_encrypt() - API to do FILS AEAD encryption
 *
 * @kek: Pointer to KEK
 * @kek_len: KEK length
 * @own_mac: Pointer to own MAC address
 * @bssid: Bssid
 * @snonce: Supplicant Nonce
 * @anonce: Authenticator Nonce
 * @data: Pointer to data after MAC header
 * @data_len: length of @data
 * @plain_text: Pointer to data after FILS Session IE
 * @plain_text_len: length of @plain_text
 * @out: Pointer to the encrypted data
 *
 * length of AEAD encryption @out is @plain_text_len + AES_BLOCK_SIZE[16 bytes]
 *
 * Return: zero on success, error otherwise
 */
static int fils_aead_encrypt(const uint8_t *kek, unsigned int kek_len,
		const uint8_t *own_mac, const uint8_t *bssid,
		const uint8_t *snonce, const uint8_t *anonce,
		const uint8_t *data, size_t data_len, uint8_t *plain_text,
		size_t plain_text_len, uint8_t *out)
{
	uint8_t v[AES_BLOCK_SIZE];
	const uint8_t *aad[6];
	size_t aad_len[6];
	uint8_t *buf;
	int ret;

	/* SIV Encrypt/Decrypt takes input key of length 256, 384 or 512 bits */
	if (kek_len != 32 && kek_len != 48 && kek_len != 64) {
		VOS_TRACE(VOS_MODULE_ID_PE, VOS_TRACE_LEVEL_ERROR,
				FL("Invalid key length: %u"), kek_len);
		return -EINVAL;
	}

	if (own_mac == NULL || bssid == NULL || snonce == NULL ||
			anonce == NULL || data_len == 0 || plain_text_len == 0 ||
			out == NULL) {
		VOS_TRACE(VOS_MODULE_ID_PE, VOS_TRACE_LEVEL_ERROR,
				FL("Error missing params mac:%pK bssid:%pK snonce:%pK"
				    "anonce:%pK data_len:%zu"
				    "plain_text_len:%zu out:%pK"),
				own_mac, bssid, snonce, anonce, data_len,
				plain_text_len, out);
		return -EINVAL;
	}

	if (plain_text == out) {
		buf = vos_mem_malloc(plain_text_len);
		if (buf == NULL) {
			VOS_TRACE(VOS_MODULE_ID_PE, VOS_TRACE_LEVEL_ERROR,
					FL("Failed to allocate memory"));
			return -ENOMEM;
		}
		vos_mem_copy(buf, plain_text, plain_text_len);
	} else {
		buf = plain_text;
	}

	aad[0] = own_mac;
	aad_len[0] = QDF_MAC_ADDR_SIZE;
	aad[1] = bssid;
	aad_len[1] = QDF_MAC_ADDR_SIZE;
	aad[2] = snonce;
	aad_len[2] = SIR_FILS_NONCE_LENGTH;
	aad[3] = anonce;
	aad_len[3] = SIR_FILS_NONCE_LENGTH;
	aad[4] = data;
	aad_len[4] = data_len;
	/* Plain text, P, is Sn in AES-SIV */
	aad[5] = buf;
	aad_len[5] = plain_text_len;

	/* AES-SIV S2V */
	/* K1 = leftmost(K, len(K)/2) */
	ret = qdf_aes_s2v(kek, kek_len/2, aad, aad_len, 6, v);
	if (ret)
		goto error;

	/* out = SIV || C (Synthetic Initialization Vector || Ciphered text) */
	vos_mem_copy(out, v, AES_BLOCK_SIZE);

	/* AES-SIV CTR */
	/* K2 = rightmost(K, len(K)/2) */
	/* Clear 31st and 63rd bits in counter synthetic iv */
	v[12] &= 0x7F;
	v[8] &= 0x7F;

	ret = qdf_aes_ctr(kek + kek_len/2, kek_len/2, v, buf, plain_text_len,
			out + AES_BLOCK_SIZE, true);

error:
	if (plain_text == out)
		vos_mem_free(buf);
	return ret;
}

VOS_STATUS aead_encrypt_assoc_req(tpAniSirGlobal mac_ctx,
		tpPESession pe_session,
		uint8_t *frm, uint32_t *frm_len)
{
	uint8_t *plain_text = NULL, *data;
	uint32_t plain_text_len = 0, data_len;
	VOS_STATUS status;
	struct pe_fils_session *fils_info = (pe_session->fils_info);

	if (!fils_info)
		return VOS_STATUS_E_FAILURE;

	/*
	 * data is the packet data after MAC header till
	 * FILS session IE(inclusive)
	 */
	data = frm + sizeof(tSirMacMgmtHdr);

	/*
	 * plain_text is the packet data after FILS session IE
	 * which needs to be encrypted. Get plain_text ptr and
	 * plain_text_len values using find_ptr_aft_fils_session_ie()
	 */
	status = find_ie_data_after_fils_session_ie(mac_ctx, data +
			FIXED_PARAM_OFFSET_ASSOC_REQ,
			(*frm_len -
			 FIXED_PARAM_OFFSET_ASSOC_REQ),
			&plain_text, &plain_text_len);
	if (!VOS_IS_STATUS_SUCCESS(status)) {
		limLog(mac_ctx, LOGE, FL("Could not find FILS session IE"));
		return VOS_STATUS_E_FAILURE;
	}
	data_len = ((*frm_len) - plain_text_len);

	/* Overwrite the AEAD encrypted output @ plain_text */
	if (fils_aead_encrypt(fils_info->kek, fils_info->kek_len,
				pe_session->selfMacAddr, pe_session->bssId,
				fils_info->fils_nonce,
				fils_info->auth_info.fils_nonce,
				data, data_len, plain_text, plain_text_len,
				plain_text)) {
		limLog(mac_ctx, LOGE, FL("AEAD Encryption fails !"));
		return VOS_STATUS_E_FAILURE;
	}

	/*
	 * AEAD encrypted output(cipher_text) will have length equals to
	 * plain_text_len + AES_BLOCK_SIZE(AEAD encryption header info).
	 * Add this to frm_len
	 */
	(*frm_len) += (AES_BLOCK_SIZE);

	return VOS_STATUS_SUCCESS;
}

/**
 * fils_aead_decrypt() - API to do AEAD decryption
 *
 * @kek: Pointer to KEK
 * @kek_len: KEK length
 * @own_mac: Pointer to own MAC address
 * @bssid: Bssid
 * @snonce: Supplicant Nonce
 * @anonce: Authenticator Nonce
 * @data: Pointer to data after MAC header
 * @data_len: length of @data
 * @plain_text: Pointer to data after FILS Session IE
 * @plain_text_len: length of @plain_text
 * @out: Pointer to the encrypted data
 *
 * Return: zero on success, error otherwise
 */
static int fils_aead_decrypt(const uint8_t *kek, unsigned int kek_len,
		const uint8_t *own_mac, const uint8_t *bssid,
		const uint8_t *snonce, const uint8_t *anonce,
		const uint8_t *data, size_t data_len, uint8_t *ciphered_text,
		size_t ciphered_text_len, uint8_t *plain_text)
{
	const uint8_t *aad[6];
	size_t aad_len[6];
	uint8_t *buf;
	size_t buf_len;
	uint8_t v[AES_BLOCK_SIZE];
	uint8_t siv[AES_BLOCK_SIZE];
	int ret;

	/* SIV Encrypt/Decrypt takes input key of length 256, 384 or 512 bits */
	if (kek_len != 32 && kek_len != 48 && kek_len != 64) {
		VOS_TRACE(VOS_MODULE_ID_PE, VOS_TRACE_LEVEL_ERROR,
				FL("Invalid key length: %u"), kek_len);
		return -EINVAL;
	}

	if (own_mac == NULL || bssid == NULL || snonce == NULL ||
			anonce == NULL || data_len == 0 || ciphered_text_len == 0 ||
			plain_text == NULL) {
		VOS_TRACE(VOS_MODULE_ID_PE, VOS_TRACE_LEVEL_ERROR,
				FL("Error missing params mac:%pK bssid:%pK snonce:%pK"
				    "anonce:%pK data_len:%zu ciphered_text_len:%zu"
				    "plain_text:%pK"),
				own_mac, bssid, snonce, anonce, data_len,
				ciphered_text_len, plain_text);
		return -EINVAL;
	}

	vos_mem_copy(v, ciphered_text, AES_BLOCK_SIZE);
	vos_mem_copy(siv, ciphered_text, AES_BLOCK_SIZE);
	v[12] &= 0x7F;
	v[8] &= 0x7F;

	buf_len = ciphered_text_len - AES_BLOCK_SIZE;
	if (ciphered_text == plain_text) {
		/* in place decryption */
		buf = vos_mem_malloc(buf_len);
		if (buf == NULL) {
			VOS_TRACE(VOS_MODULE_ID_PE, VOS_TRACE_LEVEL_ERROR,
					FL("Failed to allocate memory"));
			return -ENOMEM;
		}
		vos_mem_copy(buf, ciphered_text + AES_BLOCK_SIZE, buf_len);
	} else {
		buf = ciphered_text + AES_BLOCK_SIZE;
	}

	/* AES-SIV CTR */
	/* K2 = rightmost(K, len(K)/2) */
	ret = qdf_aes_ctr(kek + kek_len/2, kek_len/2, v, buf, buf_len,
			plain_text, false);
	if (ret)
		goto error;

	aad[0] = bssid;
	aad_len[0] = QDF_MAC_ADDR_SIZE;
	aad[1] = own_mac;
	aad_len[1] = QDF_MAC_ADDR_SIZE;
	aad[2] = anonce;
	aad_len[2] = SIR_FILS_NONCE_LENGTH;
	aad[3] = snonce;
	aad_len[3] = SIR_FILS_NONCE_LENGTH;
	aad[4] = data;
	aad_len[4] = data_len;
	aad[5] = plain_text;
	aad_len[5] = buf_len;

	/* AES-SIV S2V */
	/* K1 = leftmost(K, len(K)/2) */
	ret = qdf_aes_s2v(kek, kek_len/2, aad, aad_len, 6, v);
	if (ret)
		goto error;

	/* compare the iv generated against the one sent by AP */
	if (memcmp(v, siv, AES_BLOCK_SIZE) != 0) {
		VOS_TRACE(VOS_MODULE_ID_PE, VOS_TRACE_LEVEL_ERROR,
				FL("siv not same as frame siv"));
		ret = -EINVAL;
	}

error:
	if (ciphered_text == plain_text)
		vos_mem_free(buf);
	return ret;
}

VOS_STATUS aead_decrypt_assoc_rsp(tpAniSirGlobal mac_ctx,
		tpPESession session,
		tDot11fAssocResponse *ar,
		uint8_t *p_frame, uint32_t *n_frame)
{
	VOS_STATUS status;
	uint32_t data_len, fils_ies_len;
	uint8_t *fils_ies;
	struct pe_fils_session *fils_info = (session->fils_info);

	lim_fils_data_dump("Assoc Rsp :", p_frame, *n_frame);
	status = find_ie_data_after_fils_session_ie(mac_ctx, p_frame +
			FIXED_PARAM_OFFSET_ASSOC_RSP,
			((*n_frame) -
			 FIXED_PARAM_OFFSET_ASSOC_RSP),
			&fils_ies, &fils_ies_len);
	if (!VOS_IS_STATUS_SUCCESS(status)) {
		limLog(mac_ctx, LOGE, FL("FILS session IE not present"));
		return status;
	}

	data_len = (*n_frame) - fils_ies_len;

	if (fils_aead_decrypt(fils_info->kek, fils_info->kek_len,
				session->selfMacAddr, session->bssId,
				fils_info->fils_nonce,
				fils_info->auth_info.fils_nonce,
				p_frame, data_len,
				fils_ies, fils_ies_len, fils_ies)){
		limLog(mac_ctx, LOGE, FL("AEAD decryption fails"));
		return VOS_STATUS_E_FAILURE;
	}

	/* Dump the output of AEAD decrypt */
	lim_fils_data_dump("Plain text: ", fils_ies,
			fils_ies_len - AES_BLOCK_SIZE);

	(*n_frame) -= AES_BLOCK_SIZE;
	return status;
}
#endif
