//
// Embedded 802.11b wireless network interface
// MAC layer data processing functions
// Copyright 2003 Digi International
//
#include "mac.h"
#include "wln_api.h"

// LLC/SNAP (802.2) header on data frames
static const LLC_SNAP llc_snap = { SAP_SNAP, SAP_SNAP, XDLC_U, {0, 0, 0}, 0 };

// Receive cache entry
typedef struct {
	MacAddr	addr;		// Transmitter address
	uint16	seq;		// Frame sequence number
	uint16	frags;		// Number of fragments received
	uint32	time;		// Time first fragment received
	MacQueue bufs;		// List of fragments
} RecvCacheEntry;

// Receive cache.
// This collects fragments of MSDUs until all have been received.
// It also stores the most recent MSDUs received so that duplicate
// frames can be detected.
static RecvCacheEntry recvCache[RXCACHE_NUM];

static void RecvData (MacBuffer *sbuf);
static MacBuffer *RecvCache (MacBuffer *buf);
static BOOL SearchRecvCache (MacAddr addr, uint16 seq, RecvCacheEntry **rce);

// Supported encryption ciphers
static const MacCipher *ciphers[] = {
	&openCipher,
	&wepCipher,
	&tkipCipher,
#ifdef AES_ENABLED
	&ccmpCipher,
#else
	NULL,
#endif
};

static const MacCipher *pairCipher;		// Pairwise (station to station) cipher
static const MacCipher *newPairCipher;	// Pairsize cipher after next frame
static const MacCipher *groupCipher;	// Group (broadcast) cipher

MacStats macStats;		// Statistics counters
int maxFragSize;		// Max data fragment size

//
// Initialize the MAC data module
//
void InitData()
{
	memset (recvCache, 0, sizeof recvCache);
	memset (&macStats, 0, sizeof macStats);

	pairCipher = NULL;
	newPairCipher = NULL;
	groupCipher = NULL;
	maxFragSize = macParams.frag_thresh;
}

//
// Set an encryption key
// @param type key type, one of WLN_KEY_xxx
// @param addr peer station if pairwise key, broadcast address if group key
// @param id key index (0-3)
// @param tx TRUE if key is used to transmit
// @param key key data
// @param key_len key length in bytes
// @param seq packet sequence number, NULL if none
//
int wln_set_key (wln_key_type type, mac_addr addr, int id, BOOL tx,
				 uint8 *key, int key_len, uint8 *seq)
{
	// Delete previous key
	if (IS_GROUP_ADDR (addr))
	{
		if (groupCipher)
			groupCipher->SetKey (id, FALSE, NULL, 0, NULL);
	}
	else
	{
		if (pairCipher)
			pairCipher->SetKey (id, FALSE, NULL, 0, NULL);
		pairCipher = newPairCipher = NULL;
	}

	if (type == WLN_KEY_NONE)
	{
		macStatus.encrypt = 0;
		return 0;
	}

	// Set new key
	else if (type <= WLN_KEY_CCMP)
	{
		if (ciphers[type] == NULL)
			return -1;

		if (!ciphers[type]->SetKey (id, tx, key, key_len, seq))
			return -1;

		// Setting a key to non-default value indicates sucessful authentication
		if (macStatus.state == WLN_ST_ASSOC_ESS &&
			(key_len != macParams.wep_key_len ||
			 memcmp (key, macParams.wep_key[id], key_len) != 0))
		{
			macStatus.state = WLN_ST_AUTH_ESS;

			// Guess at authentication type from WPA info
			if (macStatus.authen == WLN_AUTH_LEAP)
				;
            else if (macStatus.wpa_info_len == 0 && macStatus.rsn_info_len == 0)
				macStatus.authen = WLN_AUTH_WEP_8021X;
			else if ((macStatus.rsn_info_len != 0 && macStatus.rsn_info[19] == 2) ||
                     (macStatus.wpa_info_len != 0 && macStatus.wpa_info[23] == 2))
				macStatus.authen = WLN_AUTH_WPA_PSK;
			else
				macStatus.authen = WLN_AUTH_WPA_8021X;
		}

		if (IS_GROUP_ADDR (addr) || type == WLN_KEY_WEP)
			groupCipher = ciphers[type];
		else
			// WPA app queues an 802.1X frame, then sets the pairwise key.
			// Delay setting pairCipher until after sending the frame,
			// or before receiving a new frame.
			newPairCipher = ciphers[type];

		macStatus.encrypt |= 1 << type;
		return 0;
	}

	return -1;
}

//
// Initialize non-WPA encryption
//
void SetDefaultKeys (void)
{
	int i;

	pairCipher = newPairCipher = NULL;

	// Initialize WEP using default keys.
	// These may be used by shared key authentication or WEP encryption.
	if (macParams.wep_key_len > 0)
		for (i = 0; i < WLN_KEY_NUM; i++)
			wepCipher.SetKey (i, i == macParams.wep_key_id,
				macParams.wep_key[i], macParams.wep_key_len, NULL);

	// Select encryption method
	if (macStatus.bss_caps & CAP_PRIVACY)
	{
		groupCipher = &wepCipher;
		macStatus.encrypt = WLN_ENCR_WEP;
	}
	else
	{
		groupCipher = &openCipher;
		macStatus.encrypt = WLN_ENCR_OPEN;
	}
}

//
// Send a data MSDU, fragmenting and encrypting as needed.
// @param dst Destination MAC address
// @param epType Ethernet protocol type
// @param data Data to send
// @param size Size of data in bytes
// @return One of ERR_xxx
//
MacReturn MacSendData (MacAddr dst, int epType, uint8 *data, int size)
{
	MacBuffer *sbuf;
	MacBuffer *buf;
	const MacCipher *cipher;
	int fragSize;
	int frag;
	int i;

	// Drop data frame if not associated
	if (staState != SS_ASSOC)
	{
		macStats.txDropAssoc++;
		return ERR_ASSOC;
	}

    // Not an 802.1x (EAPOL) frame, set new pair cipher
    if (epType != EP_8021X)
        pairCipher = newPairCipher;

	// Decide which encryption cipher to use
	// Don't encrypt 802.1x (EAPOL) frames if no pairwise key
	if (epType == EP_8021X && pairCipher == NULL)
		cipher = &openCipher;
	// No pair key, or ad-hoc broadcast
	else if (pairCipher == NULL ||
		(IS_GROUP_ADDR (dst) && (macStatus.bss_caps & CAP_IBSS)))
		cipher = groupCipher;
	else
		cipher = pairCipher;

	if (cipher == NULL)
		return ERR_ENCRYPT;

	// Fragment directed frames, all frames sent to AP
	size += SNAP_SIZE;
	if (size > macParams.frag_thresh - DATA_SIZE &&
		(!IS_GROUP_ADDR (dst) || (macStatus.bss_caps & CAP_ESS)))
	{
		// More than one fragment of size maxFragSize
		fragSize = maxFragSize - DATA_SIZE;
		if (size > MAX_FRAGS * fragSize)
			return ERR_SIZE;
	}
	else
	{
		// One fragment of size up to BUF_SIZE
		fragSize = BUF_SIZE;
		if (size > fragSize)
			return ERR_SIZE;
	}

	// Allocate a buffer for each fragment
	sbuf = NULL;
	for (i = 0; i < size; i += fragSize)
	{
		buf = MacGetBuffer();
		if (buf == NULL)
		{
			MacFreeBuffers (sbuf);
			return ERR_MEMORY;
		}

		// Add to buffer list
		buf->next = sbuf;
		sbuf = buf;
	}

	// Set MAC header fields on first fragment
	sbuf->macHdr.fc.type = TYPE_DATA;
	SET_ADDR (sbuf->macHdr.addr2, macParams.sta_addr);
	if (macStatus.bss_caps & CAP_ESS)
	{
		sbuf->macHdr.fc.toDS = TRUE;
		SET_ADDR (sbuf->macHdr.addr1, macStatus.bss_addr);
		SET_ADDR (sbuf->macHdr.addr3, dst);
	}
	else
	{
		SET_ADDR (sbuf->macHdr.addr1, dst);
		SET_ADDR (sbuf->macHdr.addr3, macStatus.bss_addr);
	}

	// Prepare each fragment
	frag = 0;
	for (buf = sbuf; buf; buf = buf->next)
	{
		uint8 *body = buf->body + cipher->ivSize;
		int length = size;
		SeqControlU squ;

		// Limit length to fragment size
		if (length > fragSize)
			length = fragSize;
		buf->length = length + DATA_SIZE;

		// Copy MAC header from first fragment
		if (frag > 0)
			buf->macHdr = sbuf->macHdr;

		// Set fragment fields
		squ.sq.seq = 0;
		squ.sq.frag = frag;
		buf->macHdr.squ.sq16 = HTON16 (squ.sq16);
		buf->macHdr.fc.moreFrag = (buf->next != NULL);

		// Copy LLC/SNAP header to first fragment
		if (frag == 0)
		{
			LLC_SNAP *ls = (LLC_SNAP *) body;
			*ls = llc_snap;
			ls->eptype = epType;

			body   += SNAP_SIZE;
			length -= SNAP_SIZE;
			size   -= SNAP_SIZE;
		}

		// Copy data to fragment
		memcpy (body, data, length);
		data += length;
		size -= length;
		frag++;
	}

	// Add message integrity check
	if (!cipher->AddMIC (sbuf, dst))
	{
		MacFreeBuffers (sbuf);
		return ERR_ENCRYPT;
	}

	// Encrypt each fragment after headers and MIC are set
	for (buf = sbuf; buf; buf = buf->next)
		if (!cipher->Encrypt (buf))
		{
			MacFreeBuffers (sbuf);
			return ERR_ENCRYPT;
		}

	// Done with frame, set new pair cipher
	pairCipher = newPairCipher;

	SendFrames (sbuf, dst);

	return ERR_OK;
}

//
// Send a list of data MPDUs or a management MPDU
// @param sbuf Start of buffer list.
// @param dst Destination MAC address
//
void SendFrames (MacBuffer *sbuf, MacAddr dst)
{
	MacBuffer *buf;
	int rate, rate2, dur, level;

	rate = rate2 = rateTx;

	// Set MAC address fields for management frames.
	// Send at lowest rate.
	if (IS_MGMT (sbuf))
	{
		SET_ADDR (sbuf->macHdr.addr1, dst);
		SET_ADDR (sbuf->macHdr.addr2, macParams.sta_addr);
		if (staState >= SS_JOIN)
			SET_ADDR (sbuf->macHdr.addr3, macStatus.bss_addr);
		rate2 = rateMin;
	}

	// If broadcast, send at broadcast rate
	else if (IS_GROUP_ADDR (sbuf->macHdr.addr1))
		rate2 = rateBC;

	// If protection enabled and multiple fragments,
	// send all but last fragment at broadcast rate
	else if (erpInfo & ERP_USEPROTECT)
		rate = rateBC;

	// Set PLCP header for each fragment
	for (buf = sbuf; buf->next; buf = buf->next)
		SetPLCP (buf, rate);
	SetPLCP (buf, rate2);

	// Set duration for initial fragments
	for (buf = sbuf; buf->next; buf = buf->next)
	{
		// Set this fragment's duration to its ACK plus
		// next fragment's data and ACK
		dur = GetAckDuration (rate) + GetDuration (buf->next);
		buf->macHdr.duration = HTON16(dur);
	}

	// Set duration for final (or only) fragment
	if (IS_GROUP_ADDR (buf->macHdr.addr1))
		buf->macHdr.duration = 0;
	else
	{
		dur = GetAckDuration (rate2);
		buf->macHdr.duration = HTON16(dur);
	}

	// Add each fragment to transmit queue
	level = DISABLE();
	while (sbuf)
	{
		buf = sbuf->next;
		TXQ_PUT (sbuf);
		sbuf = buf;
	}

	// Start sending first fragment
	if (txState == ST_IDLE)
		SendQueue();

	ENABLE (level);
}

//
// Copy data from a list of MPDUs, defragmenting as needed.
// @param buf Start of buffer list
// @param data Destination to copy data from buffers
// @param size Size of data in bytes
// @return Number of bytes copied, may be less than size
// if end of buffer list is reached
//
int MacGetData (MacBuffer *buf, uint8 *data, int size)
{
	int total = 0;

	// Copy buffers until size is reached
	while (buf && size > 0)
	{
		uint8 *body = buf->body;
		int length = buf->length-DATA_SIZE;

		// Skip over IV
		if (buf->macHdr.fc.protected)
		{
			if (body[3] & EXT_IV)
				body += EXTIV_SIZE;
			else
				body += IV_SIZE;
		}

		// Skip over LLC/SNAP header if first fragment
		if (total == 0)
		{
			body   += SNAP_SIZE;
			length -= SNAP_SIZE;
		}

		if (length > size)
			length = size;
		memcpy (data, body, length);

		buf = buf->next;
		data += length;
		size -= length;
		total += length;
	}

	return total;
}

//
// Process a frame from receive queue
// @param buf Received frame
//
void RecvFrame (MacBuffer *buf)
{
    extern ep_types types[];
    int i;
#if 0
#ifdef DEBUG
	// Check for correct FCS
	if (
#ifdef AES_ENABLED
		!(hwAES && buf->macHdr.fc.protected) &&
#endif
		CalcCRC32 ((uint8 *) &buf->macHdr, buf->length) != GOOD_CRC32)
	{
		MacFreeBuffers (buf);
		macStats.rxDropInvalid++;
		DPRINTF ("Bad CRC %d\n", macStats.rxDropInvalid);
		return;
	}
#endif
#endif
    GET_MUTEX();
    if (monitorMode)
    {
	    for (i = 0; i < MAX_TYPES; i++)
	    {
		    if (types[i].ep_type == WLN_MONITOR_TYPE && types[i].func != NULL)
		    {
		    types[i].func (types[i].context, NULL, (uint8 *)&buf->macHdr, buf->length);
			MacFreeBuffers (buf);
			DPRINTF ("Passed the received frame to application\n");
			PUT_MUTEX();
			return;
		    }
	    }
	}
	PUT_MUTEX();

#if 1	 //these are the fixes for UNH test, vantive 23309, 23308, and 23298

	//Should send a deauthentication frame if
	//  1. not authenticated and not associated, and
	//  2. Received frame with unicast address in the address 1 field from sender, and
	//  3. The received frame is Class 2 -- association request/response; reassociation request/response;
	//      disassociation.

	//Should send a deauthentication frame if
	//  1. not authenticated and not associated, and
	//  2. Received frame with unicast address in the address 1 field from sender, and
	//  3. The received frame is Class 3 -- PS_POLL;
	//      any data frames, including those with either the ToDS or FromDS bits set.

	if ( (staState < SS_AUTH) &&
	        EQUAL_ADDR (buf->macHdr.addr1, macParams.sta_addr) &&
	        ((IS_MGMT(buf) && ( buf->macHdr.fc.type == TYPE_ASSOC_REQ ||
	                            buf->macHdr.fc.type == TYPE_ASSOC_RESP ||
	                            buf->macHdr.fc.type == TYPE_REASSOC_REQ ||
	                            buf->macHdr.fc.type == TYPE_REASSOC_RESP ||
	                            buf->macHdr.fc.type == TYPE_DISASSOC )) ||
	        (((IS_DATA (buf) && (buf->macHdr.fc.toDS || buf->macHdr.fc.fromDS )) ||
	        buf->macHdr.fc.type == TYPE_PSPOLL ))))
	{
	    force_disassociate(buf, REAS_INCORRECT_FRAME_UNAUTH, 1);
	    MacFreeBuffers (buf);
        macStats.rxDropInvalid++;
		return;
	}

	//Should send a disassociation frame if
	//  1. is authenticated and not associated, and
	//  2. Received frame with unicast address in the address 1 field from sender, and
	//  3. The received frame is Class 3 -- PS_POLL;
	//      any data frames, including those with either the ToDS or FromDS bits set.
	if ( (staState == SS_AUTH) &&
	        EQUAL_ADDR (buf->macHdr.addr1, macParams.sta_addr) &&
	        (((IS_DATA (buf) && (buf->macHdr.fc.toDS || buf->macHdr.fc.fromDS )) ||
	        buf->macHdr.fc.type == TYPE_PSPOLL )))
	{
	    force_disassociate(buf, REAS_INCORRECT_FRAME_UNASSO, 0);
	    MacFreeBuffers (buf);
        macStats.rxDropInvalid++;
		return;
	}

#endif

    // Discard received frame if:
	//   To DS
	//   Broadcast originally from this station
	//   Data and not associated or from another BSS
	if (buf->macHdr.fc.toDS ||
		(IS_GROUP_ADDR (buf->macHdr.addr1) && EQUAL_ADDR (buf->macHdr.addr3, macParams.sta_addr)) ||
		(IS_DATA (buf) &&
		 (staState != SS_ASSOC ||
		  (buf->macHdr.fc.fromDS  && !EQUAL_ADDR (buf->macHdr.addr2, macStatus.bss_addr)) ||
		  (!buf->macHdr.fc.fromDS && !EQUAL_ADDR (buf->macHdr.addr3, macStatus.bss_addr)))))
	{
		MacFreeBuffers (buf);
		macStats.rxDropInvalid++;
		return;
	}

	// Cache directed frames until all fragments received
	if (!IS_GROUP_ADDR (buf->macHdr.addr1))
	{
		buf = RecvCache (buf);
		if (buf == NULL)
			return;
	}

#if defined(BSP_WANT_AUTO_ADJUST_MAC_AGC)
    adjustMaxAgc_wi9c(buf);
#endif

#if (USB_WIFI_ENABLED == 1)
    adjustMaxAgc_wiwave(buf);
#endif

	// Have complete list of fragments
	if (IS_DATA (buf))
		RecvData (buf);
	else
		RecvMgmtFrame (buf);
}

//
// Process a data MSDU
// @param sbuf List of buffers
//
static void RecvData (MacBuffer *sbuf)
{
	MacBuffer *buf;
	const MacCipher *cipher;
	LLC_SNAP *ls;
	int length;

	// Update pair cipher before trying to decrypt
	if (!IS_GROUP_ADDR (sbuf->macHdr.addr1))
		pairCipher = newPairCipher;

	// Decide which decryption cipher to use
	if (!sbuf->macHdr.fc.protected)
		cipher = &openCipher;
	else if (IS_GROUP_ADDR (sbuf->macHdr.addr1) || pairCipher == NULL)
		cipher = groupCipher;
	else
		cipher = pairCipher;

	if (cipher == NULL)
	{
		MacFreeBuffers (sbuf);
		macStats.rxDropDecrypt++;
		return;
	}

	// Decrypt each fragment
	for (buf = sbuf; buf; buf = buf->next)
		if (!cipher->Decrypt (buf))
		{
			MacFreeBuffers (sbuf);
			macStats.rxDropDecrypt++;
			return;
		}

	// Verify message integrity check
	if (!cipher->VerifyMIC (sbuf))
	{
		MacFreeBuffers (sbuf);
		macStats.rxDropDecrypt++;
		return;
	}

	// Calculate total length
	length = 0;
	for (buf = sbuf; buf; buf = buf->next)
    	length += buf->length - DATA_SIZE;

	// Verify LLC/SNAP header
	ls = (LLC_SNAP *) (sbuf->body + cipher->ivSize);
	if (length < SNAP_SIZE || memcmp (ls, &llc_snap, SNAP_SIZE-2) != 0)
	{
		MacFreeBuffers (sbuf);
		macStats.rxDropInvalid++;
		return;
	}

    // Drop if not encrypted when required.
    // Always allow 802.1x (EAPOL) frames.
	if (!sbuf->macHdr.fc.protected && (macStatus.bss_caps & CAP_PRIVACY) && ls->eptype != EP_8021X)
	{
		MacFreeBuffers (sbuf);
		macStats.rxDropDecrypt++;
		return;
	}

    // Send data frame to upper layer
	MacRecvData (sbuf, sbuf->macHdr.addr1,
		sbuf->macHdr.fc.fromDS ? sbuf->macHdr.addr3 : sbuf->macHdr.addr2,
		ls->eptype, length-SNAP_SIZE);
	MacFreeBuffers (sbuf);
}

//
// Add a frame to the receive cache.
// Discards duplicate frames and out of order fragments.
// @param Received frame
// @return List of fragments in MSDU if all fragments received, else NULL.
//
static MacBuffer *RecvCache (MacBuffer *buf)
{
	RecvCacheEntry *rce;
	BOOL found;
	SeqControlU squ;

	// Find <addr2,seq> in receive cache, or an entry to replace
	squ.sq16 = NTOH16 (buf->macHdr.squ.sq16);
	found = SearchRecvCache (buf->macHdr.addr2, squ.sq.seq, &rce);

	// First or only fragment
	if (squ.sq.frag == 0)
	{
		// Discard if duplicate and retry bit set
		if (found && buf->macHdr.fc.retry && rce->frags == 0)
		{
			MacFreeBuffers (buf);
			macStats.rxDropDup++;
			return NULL;
		}

		// Free any previous buffers
		if (rce->bufs.head)
		{
			MacFreeBuffers (rce->bufs.head);
			rce->bufs.head = NULL;
			macStats.rxDropAge++;
		}

		// Set new cache entry
		SET_ADDR (rce->addr, buf->macHdr.addr2);
		rce->seq = squ.sq.seq;
		rce->frags = 1;
		rce->time = GET_TIME();
		PutQueue (&rce->bufs, buf);
	}

	// Other fragments:
	// Add to cache entry if next expected fragment
	else if (found && squ.sq.frag == rce->frags)
	{
		rce->frags++;
		PutQueue (&rce->bufs, buf);
	}
	else
	{
		MacFreeBuffers (buf);
		macStats.rxDropDup++;
		return NULL;
	}

	// Return list of buffers if all fragments received
	if (!buf->macHdr.fc.moreFrag)
	{
		// Remove buffers from cache, but leave entry for duplicate detection
		buf = rce->bufs.head;
		rce->bufs.head = NULL;
		rce->frags = 0;
		return buf;
	}
	else
		return NULL;
}

//
// Find the entry with <addr,seq> in receive cache.
// If not found, select an entry to replace.
// @param addr Transmitter address
// @param seq Frame sequence number
// @param rce Returned pointer to cache entry
// @return TRUE if entry found, FALSE to replace the entry at rce.
//
static BOOL SearchRecvCache (MacAddr addr, uint16 seq, RecvCacheEntry **rce)
{
	int i;
	uint32 oldest = recvCache[0].time;  // Oldest entry to replace
	*rce = &recvCache[0];

	for (i = 0; i < RXCACHE_NUM; i++)
	{
		// Found a match
		if (recvCache[i].seq == seq && EQUAL_ADDR (recvCache[i].addr, addr))
		{
			*rce = &recvCache[i];
			return TRUE;
		}
		// Found an older entry
		else if (recvCache[i].time < oldest)
		{
			*rce = &recvCache[i];
			oldest = recvCache[i].time;
		}
	}

	return FALSE;
}

//
// Free buffers associated with old receive cache entries
//
void AgeRecvCache()
{
	int i;
	uint32 now = GET_TIME();

	for (i = 0; i < RXCACHE_NUM; i++)
		// Has buffers and time is old
		if (recvCache[i].bufs.head && now-recvCache[i].time >= MAX_RX_LIFETIME)
		{
			// Free buffers, but leave entry for duplicate detection
			MacFreeBuffers (recvCache[i].bufs.head);
			recvCache[i].bufs.head = NULL;
			recvCache[i].frags = 0;
			macStats.rxDropAge++;
		}
}


//
// Open cipher (no encryption) implementation
//
static BOOL OpenSetKey (int id, BOOL tx, uint8 *key, int len, uint8 *seq)
{
    NA_UNUSED_PARAM(id);  /* added to remove GNU warning. */
    NA_UNUSED_PARAM(tx);  /* added to remove GNU warning. */
    NA_UNUSED_PARAM(key);  /* added to remove GNU warning. */
    NA_UNUSED_PARAM(len);  /* added to remove GNU warning. */
    NA_UNUSED_PARAM(seq);  /* added to remove GNU warning. */
	return FALSE;
}

static BOOL OpenEncrypt (MacBuffer *buf)
{
    NA_UNUSED_PARAM(buf);  /* added to remove GNU warning. */
	return TRUE;
}

static BOOL OpenDecrypt (MacBuffer *buf)
{
    NA_UNUSED_PARAM(buf);  /* added to remove GNU warning. */
	return !buf->macHdr.fc.protected;
}

BOOL OpenAddMIC (MacBuffer *buf, MacAddr dst)
{
    NA_UNUSED_PARAM(buf);  /* added to remove GNU warning. */
    NA_UNUSED_PARAM(dst);  /* added to remove GNU warning. */
	return TRUE;
}

BOOL OpenVerifyMIC (MacBuffer *buf)
{
    NA_UNUSED_PARAM(buf);  /* added to remove GNU warning. */
	return TRUE;
}

MacCipher openCipher = {
	0,
	OpenSetKey,
	OpenEncrypt,
	OpenDecrypt,
	OpenAddMIC,
	OpenVerifyMIC
};
