/*
 * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
 *
 * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
 *
 *
 * 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.
 */
/*
 * Copyright (c) 2012, The Linux Foundation. All rights reserved.
 *
 * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
 *
 *
 * 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.
 */

/*
 * Woodside Networks, Inc proprietary. All rights reserved.
 * File: $Header: //depot/software/projects/feature_branches/gen5_phase1/os/linux/classic/ap/apps/ssm/lib/aniSsmEapol.c#2 $ 
 *
 * Contains definitions of various utilities for EAPoL frame
 * parsing and creation.
 *
 * Author:      Mayank D. Upadhyay
 * Date:        19-June-2002
 * History:-
 * Date         Modified by     Modification Information
 * ------------------------------------------------------
 *
 */
#include "vos_utils.h"
#include <bapRsnAsfPacket.h>
#include <bapRsnErrors.h>
#include <bapRsnSsmEapol.h>
#include "bapRsn8021xFsm.h"
#include "vos_memory.h"

//#include "aniSsmUtils.h"


//TODO: Put these in an array after EAPOL_TYPE is made an enum
#define ANI_EAPOL_TYPE_PACKET_STR    "EAP"
#define ANI_EAPOL_TYPE_START_STR     "START"
#define ANI_EAPOL_TYPE_LOGOFF_STR    "LOGOFF"
#define ANI_EAPOL_TYPE_KEY_STR       "KEY"
#define ANI_EAPOL_TYPE_ASF_ALERT_STR "ALERT"
#define ANI_EAPOL_TYPE_UNKNOWN_STR   "UNKNOWN"

/**
 * The EAPOL packet is structured as follows:
 */
#define DST_MAC_POS 0
#define SRC_MAC_POS 6
#define ETHER_PROTO_POS 12
#define EAPOL_VERSION_POS 14
#define ANI_EAPOL_TYPE_POS 15
#define EAPOL_BODY_LEN_POS 16
#define EAPOL_BODY_POS EAPOL_RX_HEADER_SIZE

#define EAPOL_BODY_LEN_SIZE 2

#define ANI_SSM_LEGACY_RC4_KEY_SIGN_OFFSET (EAPOL_BODY_POS + 28)

/**
 * Bitmasks for the RSN Key Information field
 */
#define ANI_SSM_RSN_KEY_DESC_VERS_MASK 0x0007
#define ANI_SSM_RSN_UNICAST_MASK       0x0008
#define ANI_SSM_RSN_KEY_INDEX_MASK     0x0030
#define ANI_SSM_RSN_INSTALL_MASK       0x0040
#define ANI_SSM_RSN_ACK_MASK           0x0080
#define ANI_SSM_RSN_MIC_MASK           0x0100
#define ANI_SSM_RSN_SECURE_MASK        0x0200
#define ANI_SSM_RSN_ERROR_MASK         0x0400
#define ANI_SSM_RSN_REQUEST_MASK       0x0800
#define ANI_SSM_RSN_ENC_KEY_DATA_MASK  0x1000

#define ANI_SSM_RSN_KEY_DESC_VERS_OFFSET  0
#define ANI_SSM_RSN_KEY_INDEX_OFFSET      4

#define ANI_SSM_RSN_KEY_MIC_OFFSET (EAPOL_BODY_POS + 77)

/**
 * Other hard coded values for convenience:
 */
static const v_U8_t 
ANI_ETH_P_EAPOL_BYTES[2] = {0x00, 0x03};//BT-AMP security type{0x88, 0x8e};
static const v_U8_t 
EAPOL_VERSION_BYTES[1] = {EAPOL_VERSION_1};
static const v_U8_t 
ANI_EAPOL_TYPE_PACKET_BYTES[1] = {ANI_EAPOL_TYPE_PACKET};
static const v_U8_t 
ANI_EAPOL_TYPE_START_BYTES[1] = {ANI_EAPOL_TYPE_START};
static const v_U8_t 
ANI_EAPOL_TYPE_LOGOFF_BYTES[1] = {ANI_EAPOL_TYPE_LOGOFF};
static const v_U8_t 
ANI_EAPOL_TYPE_KEY_BYTES[1] = {ANI_EAPOL_TYPE_KEY};
static const v_U8_t 
ANI_EAPOL_TYPE_ASF_ALERT_BYTES[1] = {ANI_EAPOL_TYPE_ASF_ALERT};
static const v_U8_t 
ZERO_BYTES[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
static v_U8_t BAP_RSN_LLC_HEADER[] =  {0xAA, 0xAA, 0x03, 0x00, 0x19, 0x58 };



static int
parseRsnKeyDesc(tAniPacket *packet, 
                tAniEapolRsnKeyDesc **rsnDescPtr);

static int
parseRsnKeyInfo(tAniPacket *packet, 
                tAniRsnKeyInfo *info);

static int
writeRsnKeyDesc(tAniPacket *packet,
                tAniEapolRsnKeyDesc *rsnDesc,
                v_U8_t keyDescType);

static int
writeRsnKeyInfo(tAniPacket *packet, tAniRsnKeyInfo *info);

static int
writeRsnKeyMic(v_U32_t cryptHandle,
               tAniPacket *eapolFrame, 
               tAniEapolRsnKeyDesc *rsnDesc,
               v_U8_t *micKey, 
               v_U32_t micKeyLen);

static int
checkRsnKeyMic(v_U32_t cryptHandle,
               tAniPacket *eapolFrame,
               tAniEapolRsnKeyDesc *rsnDesc,
               v_U8_t *micKey,
               v_U32_t micKeyLen);

extern void authEapolHandler( tAuthRsnFsm *fsm, tAniPacket *eapolFrame,
                 tAniMacAddr dstMac, 
                 tAniMacAddr srcMac,
                 v_U8_t *type);
extern void suppEapolHandler( tSuppRsnFsm *fsm, tAniPacket *eapolFrame,
                 tAniMacAddr dstMac, 
                 tAniMacAddr srcMac,
                 v_U8_t *type);

/**
 * addEapolHeaders
 *
 * FUNCTION:
 * Prepends the EAPOL header to a packet.
 *
 * ASSUMPTIONS:
 * The packet has enough space available for prepending the EAPOL
 * header.
 *
 * @param packet the packet to prepend to
 * @param dstMac the MAC address of the destination (authenticator)
 * @param srcMac the MAC address of the source (supplicant)
 * @param eapolType the EAPOL-Type field
 *
 * @return ANI_OK if the operation succeeds
 */
static int
addEapolHeaders(tAniPacket *packet, 
                tAniMacAddr dstMac, 
                tAniMacAddr srcMac, 
                v_U8_t eapolType)
{
    int retVal;
    v_U16_t len;
  
    do
    {
        retVal = aniAsfPacketGetLen(packet);
        if( !ANI_IS_STATUS_SUCCESS( retVal ) ) break;

        len = retVal;
        retVal = aniAsfPacketPrepend16(packet, len);

        retVal = aniAsfPacketPrepend8(packet, eapolType);
        if( !ANI_IS_STATUS_SUCCESS( retVal ) ) break;

        retVal = aniAsfPacketPrependBuffer(packet, EAPOL_VERSION_BYTES, 1);
        if( !ANI_IS_STATUS_SUCCESS( retVal ) ) break;

        retVal = aniAsfPacketPrependBuffer(packet, ANI_ETH_P_EAPOL_BYTES, 2);
        if( !ANI_IS_STATUS_SUCCESS( retVal ) ) break;

        //Since TL expects SNAP header in all packets we send, put it in
        retVal = aniAsfPacketPrependBuffer(packet, BAP_RSN_LLC_HEADER, sizeof(BAP_RSN_LLC_HEADER));
        if( !ANI_IS_STATUS_SUCCESS( retVal ) ) break;

        //packet length
        len += 6/*length + eapolType+version + eth_type*/ + sizeof(BAP_RSN_LLC_HEADER);
        retVal = aniAsfPacketPrepend16(packet, len);
        if( !ANI_IS_STATUS_SUCCESS( retVal ) ) break;

        retVal = aniAsfPacketPrependBuffer(packet, srcMac, sizeof(tAniMacAddr));
        if( !ANI_IS_STATUS_SUCCESS( retVal ) ) break;

        retVal = aniAsfPacketPrependBuffer(packet, dstMac, sizeof(tAniMacAddr));
        if( !ANI_IS_STATUS_SUCCESS( retVal ) ) break;
    }while( 0 );

    return retVal;
}

/**
 * aniEapolWriteStart
 *
 * FUNCTION:
 * Writes an EAPOL-Start frame to the packet. It is only used by the
 * supplicant.
 *
 * LOGIC:
 * Prepend the appropriate EAPOL header to the packet. There is no
 * EAPOL payload for this kind of frame.
 *
 * ASSUMPTIONS:
 * The packet has enough space available for prepending the header.
 *
 * @param packet the packet to which the frame should be written
 * @param dstMac the MAC address of the destination (authenticator)
 * @param srcMac the MAC address of the source (supplicant)
 *
 * @return ANI_OK if the operation succeeds
 */
int
aniEapolWriteStart(tAniPacket *packet, 
                   tAniMacAddr dstMac,
                   tAniMacAddr srcMac)
{
    return ( addEapolHeaders(packet, dstMac, srcMac, ANI_EAPOL_TYPE_START) );
}

/**
 * aniEapolWriteEapPacket
 *
 * FUNCTION:
 * Writes the EAPOL/EAP-Packet frame headers. It is used
 * by both the authenticator and the supplicant. This creates an EAPOL
 * frame that is carrying an EAP message as its payload.
 *
 * LOGIC:
 * Prepend the appropriate EAPOL header to the packet.
 *
 * ASSUMPTIONS:
 * The EAP message (ie., the payload) is already available in the
 * packet and that the packet has enough space available for
 * prepending the EAPOL header.
 *
 * @param packet the packet containing the EAP message
 * @param dstMac the MAC address of the destination (authenticator)
 * @param srcMac the MAC address of the source (supplicant)
 *
 * @return ANI_OK if the operation succeeds
 */
int
aniEapolWriteEapPacket(tAniPacket *eapPacket, 
                       tAniMacAddr dstMac, 
                       tAniMacAddr srcMac)
{
    return( addEapolHeaders(eapPacket, dstMac, srcMac, ANI_EAPOL_TYPE_PACKET) );
}

/**
 * aniEapolParse
 *
 * FUNCTION:
 * Parses an EAPoL frame to the first level of headers (no EAP
 * headers are parsed). 
 *
 * NOTE: This is a non-destructive read, that is the
 * headers are not stripped off the packet. However, any additional
 * data at  the end of the packet, beyond what the EAPoL headers encode
 * will be stripped off.
 *
 * @param packet the packet containing the EAPoL frame to parse
 * @param dstMac a pointer to set to the location of the destination
 * MAC address
 * @param srcMac a pointer to set to the location of the source
 * MAC address
 * @param type a pointer to set to the location of the EAPOL type
 * field.
 *
 * @return the non-negative length of the EAPOL payload if the operation
 * succeeds
 */
int 
aniEapolParse(tAniPacket *packet,
              v_U8_t **dstMac, 
              v_U8_t **srcMac, 
              v_U8_t **type)
{
    v_U16_t frameType;
    v_U8_t *ptr;
    int retVal;
    int tmp;

    if (aniAsfPacketGetLen(packet) < EAPOL_BODY_POS)
        return ANI_E_ILLEGAL_ARG;

    retVal = aniAsfPacketGetBytes(packet, &ptr);
    if( !ANI_IS_STATUS_SUCCESS( retVal ) )
    {
        return retVal;
    }

    VOS_TRACE( VOS_MODULE_ID_BAP, VOS_TRACE_LEVEL_INFO,
            "Supp parsing EAPOL packet of len %d: \n",
                retVal);

    frameType = (ptr[ETHER_PROTO_POS] << 8) + ptr[ETHER_PROTO_POS+1];

    /*
     * Validate the EAPOL-FRAME
     */

    if (frameType != ANI_ETH_P_EAPOL)
        return ANI_E_ILLEGAL_ARG;

    *dstMac = ptr + DST_MAC_POS;
    *srcMac = ptr + SRC_MAC_POS;

    //    if (ptr[EAPOL_VERSION_POS] != EAPOL_VERSION_1)
    //        return ANI_E_ILLEGAL_ARG;

    *type = ptr + ANI_EAPOL_TYPE_POS;
    retVal = (ptr[EAPOL_BODY_LEN_POS] << 8) + ptr[EAPOL_BODY_LEN_POS + 1];
 
    /* 
     * Validate the length of the body. Allow for longer 
     * packets than encoded, but encoding should not be larger than
     * packet.
     * Note: EAPOL body len does not include headers
     */
    tmp = aniAsfPacketGetLen(packet) - EAPOL_RX_HEADER_SIZE;
    if (retVal > tmp) 
    {
        retVal = ANI_E_ILLEGAL_ARG;
    } 
    else {
        if (retVal < tmp) 
        {
            retVal = aniAsfPacketTruncateFromRear(packet, tmp - retVal);
        }
    }

    return retVal;
}

/**
 * aniEapolWriteKey
 *
 * Writes out a complete EAPOL-Key frame. The key descriptor is
 * appended to the packet and the EAPOL header is prepended to it. If
 * a micKey is passed in, then a MIC is calculated and inserted into
 * the frame.
 *
 * @param packet the packet to write to
 * @param dstMac the destination MAC address
 * @param srcMac the source MAC address
 * @param descType the key descriptor type
 * (ANI_EAPOL_KEY_DESC_TYPE_LEGACY_RC4 or
 * ANI_EAPOL_KEY_DESC_TYPE_RSN).
 * @param keyDescData the key descriptor data corresponding to the
 * above descType. The signature field is ignored and will be
 * generated in the packet. The key bytes are expected to be encrypted
 * if they need to be encrypted.
 * @param micKey the MIC key
 * @param micKeyLen the number of bytes in the MIC key
 *
 * @return ANI_OK if the operation succeeds
 *
 */
int
aniEapolWriteKey(v_U32_t cryptHandle,
                 tAniPacket *packet,
                 tAniMacAddr dstMac, 
                 tAniMacAddr srcMac, 
                 int descType,
                 void *keyDescData,
                 v_U8_t *micKey,
                 v_U32_t micKeyLen)
{
    int retVal;

    if (packet == NULL)
        return ANI_E_NULL_VALUE;

    do
    {
        if ((descType == ANI_EAPOL_KEY_DESC_TYPE_RSN_NEW) 
                || (descType == ANI_EAPOL_KEY_DESC_TYPE_RSN)) 
        {

            retVal = writeRsnKeyDesc(packet,
                                     (tAniEapolRsnKeyDesc *) keyDescData,
                                     // Indicate 
                                     // ANI_EAPOL_KEY_DESC_TYPE_RSN_NEW 
                                     // or ANI_EAPOL_KEY_DESC_TYPE_RSN
                                     descType);
            if( !ANI_IS_STATUS_SUCCESS( retVal ) )
            {
                break;
            }

            retVal = addEapolHeaders(packet, dstMac, srcMac, ANI_EAPOL_TYPE_KEY);
            if( !ANI_IS_STATUS_SUCCESS(retVal) ) break;

            retVal = writeRsnKeyMic(cryptHandle,
                                    packet, 
                                    (tAniEapolRsnKeyDesc *) keyDescData,
                                    micKey, micKeyLen);
            if( !ANI_IS_STATUS_SUCCESS(retVal) ) break;

        } 
        else {
            VOS_ASSERT( 0 );
            return ANI_E_ILLEGAL_ARG;
        }
    }while( 0 );

    return retVal;
}


/**
 * aniEapolParseKey
 *
 * Parses and verifies a complete EAPOL-Key frame. The key descriptor
 * type is returned and so is a newly allocated key descriptor structure
 * that is appropriate for the type.
 *
 * NOTE: This is a non-destructive read. That is, the packet headers
 * will be unchanged at the end of this read operation. This is so
 * that a followup MIC check may be done on the complete packet. If
 * the packet parsing fails, the packet headers are not guaranteed to
 * be unchanged.
 *
 * @param packet the packet to read from. Note that the frame is not
 * expected to contain any additional padding at the end other than
 * the exact number of key bytes. (The aniEapolParse function will
 * ensure this.)
 * @param descType is set to the key descriptor type
 * (ANI_EAPOL_KEY_DESC_TYPE_LEGACY_RC4 or
 * ANI_EAPOL_KEY_DESC_TYPE_RSN).
 * @param keyDescData is set to a newly allocated key descriptor
 * corresponding to the above descType. The signature field is
 * verified. The key bytes will be returned encrypted. It is the
 * responsibility of the caller to free this structure and the data
 * contained therein.
 *
 * @return ANI_OK if the operation succeeds
 */
int
aniEapolParseKey(tAniPacket *packet,
                 int *descType,
                 void **keyDescData)
{
    int retVal;
    v_U8_t *bytes;
    v_U32_t eapolFrameLen;

    if (packet == NULL)
        return ANI_E_NULL_VALUE;

    do
    {
        eapolFrameLen = aniAsfPacketGetLen(packet);

        VOS_TRACE( VOS_MODULE_ID_BAP, VOS_TRACE_LEVEL_INFO, "Supp parsing EAPOL-Key frame of len %d\n", 
                      eapolFrameLen);

        retVal = aniAsfPacketTruncateFromFront(packet, EAPOL_RX_HEADER_SIZE);
        if( !ANI_IS_STATUS_SUCCESS(retVal) ) break;
        
        retVal = aniAsfPacketGetBytes(packet, &bytes);
        if( !ANI_IS_STATUS_SUCCESS(retVal) ) break;

        if (*bytes == ANI_EAPOL_KEY_DESC_TYPE_RSN ||
                   *bytes == ANI_EAPOL_KEY_DESC_TYPE_RSN_NEW) 
        {
            tAniEapolRsnKeyDesc *rsnDesc = NULL;

            //*descType = ANI_EAPOL_KEY_DESC_TYPE_RSN;
            *descType = (*bytes == ANI_EAPOL_KEY_DESC_TYPE_RSN_NEW ?  
                 ANI_EAPOL_KEY_DESC_TYPE_RSN_NEW :  ANI_EAPOL_KEY_DESC_TYPE_RSN) ;
            retVal = parseRsnKeyDesc(packet, &rsnDesc);
            if( !ANI_IS_STATUS_SUCCESS(retVal) ) break;
            *keyDescData = rsnDesc;
        } 
        else 
        {
            VOS_TRACE( VOS_MODULE_ID_BAP, VOS_TRACE_LEVEL_ERROR, 
                "Supp received unknown EAPOL-Key descriptor: %d\n",
                        *bytes);
            retVal = ANI_E_ILLEGAL_ARG;
            break;
        }

        aniAsfPacketMoveLeft(packet, eapolFrameLen);
    }while( 0 );
    
    return retVal;
}



static int
parseRsnKeyDesc(tAniPacket *packet, 
                tAniEapolRsnKeyDesc **rsnDescPtr)
{
    int retVal = ANI_OK;
    int len;
    v_U8_t *bytes;
    tAniEapolRsnKeyDesc *rsnDesc = NULL;

    do
    {
        aniAsfPacketTruncateFromFront(packet, 1); // Desc-Type

        rsnDesc = (tAniEapolRsnKeyDesc *) 
            vos_mem_malloc( sizeof(tAniEapolRsnKeyDesc) );

        if (rsnDesc == NULL) 
        {
            VOS_TRACE( VOS_MODULE_ID_BAP, VOS_TRACE_LEVEL_ERROR,
                    "Supp could not malloc EAPOL-Key Descriptor for RSN\n");
            retVal = ANI_E_MALLOC_FAILED;
            break;
        }

        retVal = parseRsnKeyInfo(packet, &rsnDesc->info);
        if (retVal != ANI_OK) break;

        retVal = aniAsfPacketGet16(packet, &rsnDesc->keyLen);
        if (retVal != ANI_OK)
        {
            break;
        }

        len = sizeof(rsnDesc->replayCounter);
        retVal = aniAsfPacketGetN(packet, len, &bytes);
        if (retVal != ANI_OK)
        {
            break;
        }
        vos_mem_copy(rsnDesc->replayCounter, bytes, len);

        len = sizeof(rsnDesc->keyNonce);
        retVal = aniAsfPacketGetN(packet, len, &bytes);
        if (retVal != ANI_OK)
        {
            break;
        }
        vos_mem_copy(rsnDesc->keyNonce, bytes, len);

        len = sizeof(rsnDesc->keyIv);
        retVal = aniAsfPacketGetN(packet, len, &bytes);
        if (retVal != ANI_OK)
        {
            break;
        }
        vos_mem_copy(rsnDesc->keyIv, bytes, len);

        len = sizeof(rsnDesc->keyRecvSeqCounter);
        retVal = aniAsfPacketGetN(packet, len, &bytes);
        if (retVal != ANI_OK)
        {
            break;
        }
        vos_mem_copy(rsnDesc->keyRecvSeqCounter, bytes, len);    

        len = sizeof(rsnDesc->keyId);
        retVal = aniAsfPacketGetN(packet, len, &bytes);
        if (retVal != ANI_OK)
        {
            break;
        }
        vos_mem_copy(rsnDesc->keyId, bytes, len);

        len = sizeof(rsnDesc->keyMic);
        retVal = aniAsfPacketGetN(packet, len, &bytes);
        if (retVal != ANI_OK)
        {
            break;
        }
        vos_mem_copy(rsnDesc->keyMic, bytes, len);

        retVal = aniAsfPacketGet16(packet, &rsnDesc->keyDataLen);
        if (retVal != ANI_OK)
        {
            break;
        }

        len = rsnDesc->keyDataLen;
        if (len > 0) {
            // We have a key
            retVal = aniAsfPacketGetN(packet, len, &bytes);
            if (retVal != ANI_OK)
            {
             break;
            }
            rsnDesc->keyData = (v_U8_t*)vos_mem_malloc(len);
            if (rsnDesc->keyData == NULL) 
            {
                VOS_TRACE( VOS_MODULE_ID_BAP, VOS_TRACE_LEVEL_ERROR, "Could not allocate RSN key bytes!\n");
                VOS_ASSERT( 0 );
                retVal = ANI_E_MALLOC_FAILED;
                break;
            }
            vos_mem_copy(rsnDesc->keyData, bytes, len);
        } 
        else {
            rsnDesc->keyData = NULL;
        }

        *rsnDescPtr = rsnDesc;

    }while( 0 );

    if( !ANI_IS_STATUS_SUCCESS( retVal ) )
    {
        vos_mem_free(rsnDesc);
    }

    return retVal;
}

static int
parseRsnKeyInfo(tAniPacket *packet, 
                tAniRsnKeyInfo *info)
{
    v_U16_t tmp;
    int retVal;

    retVal = aniAsfPacketGet16(packet, &tmp);
    if( !ANI_IS_STATUS_SUCCESS( retVal ) )
    {
        return retVal;
    }

    info->keyDescVers = (tmp & ANI_SSM_RSN_KEY_DESC_VERS_MASK) 
        >> ANI_SSM_RSN_KEY_DESC_VERS_OFFSET;
    if (info->keyDescVers != ANI_EAPOL_KEY_DESC_VERS_RC4 &&
        info->keyDescVers != ANI_EAPOL_KEY_DESC_VERS_AES)
        return ANI_E_ILLEGAL_ARG;

    info->unicastFlag = (tmp & ANI_SSM_RSN_UNICAST_MASK) ? 
        eANI_BOOLEAN_TRUE : eANI_BOOLEAN_FALSE;
    info->keyId    = (tmp & ANI_SSM_RSN_KEY_INDEX_MASK)
        >> ANI_SSM_RSN_KEY_INDEX_OFFSET;
    info->installFlag = (tmp & ANI_SSM_RSN_INSTALL_MASK) ?
        eANI_BOOLEAN_TRUE : eANI_BOOLEAN_FALSE;
    info->ackFlag     = (tmp & ANI_SSM_RSN_ACK_MASK) ?
        eANI_BOOLEAN_TRUE : eANI_BOOLEAN_FALSE;
    info->micFlag     = (tmp & ANI_SSM_RSN_MIC_MASK) ?
        eANI_BOOLEAN_TRUE : eANI_BOOLEAN_FALSE;
    info->secureFlag  = (tmp & ANI_SSM_RSN_SECURE_MASK) ?
        eANI_BOOLEAN_TRUE : eANI_BOOLEAN_FALSE;
    info->errorFlag   = (tmp & ANI_SSM_RSN_ERROR_MASK) ?
        eANI_BOOLEAN_TRUE: eANI_BOOLEAN_FALSE;
    info->requestFlag = (tmp & ANI_SSM_RSN_REQUEST_MASK) ?
        eANI_BOOLEAN_TRUE : eANI_BOOLEAN_FALSE;
    info->encKeyDataFlag = (tmp & ANI_SSM_RSN_ENC_KEY_DATA_MASK) ?
        eANI_BOOLEAN_TRUE : eANI_BOOLEAN_FALSE;

    return ANI_OK;
}


static int
writeRsnKeyDesc(tAniPacket *packet, 
                tAniEapolRsnKeyDesc *rsnDesc,
                v_U8_t keyDescType)
{
    int retVal;

    do
    {
        // This can be either ANI_EAPOL_KEY_DESC_TYPE_RSN
        // or ANI_EAPOL_KEY_DESC_TYPE_RSN_NEW
        retVal = aniAsfPacketAppend8(packet, keyDescType);
        if( !ANI_IS_STATUS_SUCCESS( retVal ) ) break;

        retVal = writeRsnKeyInfo(packet, &rsnDesc->info);
        if( !ANI_IS_STATUS_SUCCESS( retVal ) ) break;

        retVal = aniAsfPacketAppend16(packet, rsnDesc->keyLen);
        if( !ANI_IS_STATUS_SUCCESS( retVal ) ) break;

        retVal = aniAsfPacketAppendBuffer(packet, 
                                          rsnDesc->replayCounter,
                                          sizeof(rsnDesc->replayCounter));
        if( !ANI_IS_STATUS_SUCCESS( retVal ) ) break;

        retVal = aniAsfPacketAppendBuffer(packet, 
                                          rsnDesc->keyNonce, 
                                          sizeof(rsnDesc->keyNonce));
        if( !ANI_IS_STATUS_SUCCESS( retVal ) ) break;

        retVal = aniAsfPacketAppendBuffer(packet, 
                                          rsnDesc->keyIv, 
                                          sizeof(rsnDesc->keyIv));
        if( !ANI_IS_STATUS_SUCCESS( retVal ) ) break;

        retVal = aniAsfPacketAppendBuffer(packet, 
                                          rsnDesc->keyRecvSeqCounter, 
                                          sizeof(rsnDesc->keyRecvSeqCounter));
        if( !ANI_IS_STATUS_SUCCESS( retVal ) ) break;

        retVal = aniAsfPacketAppendBuffer(packet, 
                                          rsnDesc->keyId, 
                                          sizeof(rsnDesc->keyId));
        if( !ANI_IS_STATUS_SUCCESS( retVal ) ) break;

        // Zero out the key MIC
        retVal = aniAsfPacketAppendBuffer(packet, 
                                          ZERO_BYTES, 
                                          sizeof(rsnDesc->keyMic));
        if( !ANI_IS_STATUS_SUCCESS( retVal ) ) break;

        retVal = aniAsfPacketAppend16(packet, rsnDesc->keyDataLen);
        if( !ANI_IS_STATUS_SUCCESS( retVal ) ) break;

        if (rsnDesc->keyDataLen != 0) 
        {
            retVal = aniAsfPacketAppendBuffer(packet, 
                                              rsnDesc->keyData, 
                                              rsnDesc->keyDataLen);
            if( !ANI_IS_STATUS_SUCCESS( retVal ) ) break;
        }
    }while( 0 );

    return retVal;
}

static int
writeRsnKeyInfo(tAniPacket *packet, tAniRsnKeyInfo *info)
{
    int retVal;
    v_U16_t tmp;
    v_U16_t infoValue;

    infoValue = 0;

    tmp = (v_U16_t)info->keyDescVers;
    tmp = tmp << ANI_SSM_RSN_KEY_DESC_VERS_OFFSET;
    infoValue |= (tmp & ANI_SSM_RSN_KEY_DESC_VERS_MASK);

    if (info->unicastFlag)
        infoValue |= ANI_SSM_RSN_UNICAST_MASK;

    tmp = info->keyId;
    tmp = tmp << ANI_SSM_RSN_KEY_INDEX_OFFSET;
    infoValue |= (tmp & ANI_SSM_RSN_KEY_INDEX_MASK);

    if (info->installFlag)
        infoValue |= ANI_SSM_RSN_INSTALL_MASK;

    if (info->ackFlag)
        infoValue |= ANI_SSM_RSN_ACK_MASK;

    if (info->micFlag)
        infoValue |= ANI_SSM_RSN_MIC_MASK;

    if (info->secureFlag)
        infoValue |= ANI_SSM_RSN_SECURE_MASK;

    if (info->errorFlag)
        infoValue |= ANI_SSM_RSN_ERROR_MASK;

    if (info->requestFlag)
        infoValue |= ANI_SSM_RSN_REQUEST_MASK;
    
    if (info->encKeyDataFlag)
        infoValue |= ANI_SSM_RSN_ENC_KEY_DATA_MASK;
    
    retVal = aniAsfPacketAppend16(packet, infoValue);

    return retVal;
}


static int
writeRsnKeyMic(v_U32_t cryptHandle,
               tAniPacket *eapolFrame, 
               tAniEapolRsnKeyDesc *rsnDesc,
               v_U8_t *micKey, 
               v_U32_t micKeyLen)
{
    int retVal = ANI_OK;
    int len;

    v_U8_t *ptr = NULL;
    v_U8_t *micPos = NULL;
    v_U8_t result[VOS_DIGEST_SHA1_SIZE]; // Larger of the two

    // Sanity check the arguments and return if no MIC generation is
    // needed
    if (micKey != NULL) 
    {
        if (micKeyLen == 0 || !rsnDesc->info.micFlag) 
        {
            VOS_TRACE( VOS_MODULE_ID_BAP, VOS_TRACE_LEVEL_ERROR,
                "Supp MIC key provided but micKeyLen or micFlag is not set!\n");
            VOS_ASSERT( 0 );
            return ANI_E_ILLEGAL_ARG;
        }
    } 
    else {
        if (rsnDesc->info.micFlag) 
        {
            VOS_TRACE( VOS_MODULE_ID_BAP, VOS_TRACE_LEVEL_ERROR,
                "Supp micFlag is set but MIC key not provided!\n");
            VOS_ASSERT( 0 );
            return ANI_E_ILLEGAL_ARG;
        }
        // Normal condition where MIC is not desired by the caller
        return ANI_OK;
    }

    len = aniAsfPacketGetBytes(eapolFrame, &ptr);
    if( !ANI_IS_STATUS_SUCCESS( len ) )
    {
        return len;
    }

    micPos = ptr + ANI_SSM_RSN_KEY_MIC_OFFSET + SNAP_HEADER_SIZE;

    // Clear the MIC field in the packet before the MIC computation
    vos_mem_zero( micPos, VOS_DIGEST_MD5_SIZE);

    // Skip to the EAPOL version field for MIC computation
    ptr += EAPOL_VERSION_POS + SNAP_HEADER_SIZE;
    len -= (EAPOL_VERSION_POS + SNAP_HEADER_SIZE);

    if (rsnDesc->info.keyDescVers == ANI_EAPOL_KEY_DESC_VERS_AES) 
    {
        if( VOS_IS_STATUS_SUCCESS( vos_sha1_hmac_str(cryptHandle, ptr, len, micKey, micKeyLen, result) ) )
        {
            retVal = ANI_OK;
        }
        else
        {
            retVal = ANI_ERROR;
        }
    } 
    else {
        VOS_ASSERT( 0 );
        retVal = ANI_E_ILLEGAL_ARG;
    }

    if (retVal == ANI_OK) 
    {
        // Copy only 16B which is the smaller of the two and the same as
        // ANI_EAPOL_KEY_RSN_MIC_SIZE
        vos_mem_copy(micPos, result, VOS_DIGEST_MD5_SIZE);
    }

    return retVal;
}

/**
 * aniEapolKeyCheckMic
 *
 * @param eapolFrame the complete EAPOL-Key packet
 * @param descType the key descriptor type
 * @param keyDescData the key descriptor
 * @param micKey the MIC key
 * @param micKeyLen the number of bytes in the MIC key
 *
 * @return ANI_OK if the operation succeeds; ANI_E_MIC_FAILED if the
 * MIC check fails.
 */
int
aniEapolKeyCheckMic(v_U32_t cryptHandle,
                    tAniPacket *eapolFrame,
                    int descType,
                    void *keyDescData,
                    v_U8_t *micKey,
                    v_U32_t micKeyLen)
{
    if (descType == ANI_EAPOL_KEY_DESC_TYPE_RSN_NEW) 
    {
        return checkRsnKeyMic(cryptHandle, eapolFrame, keyDescData, micKey, micKeyLen);
    } 
    else {
        VOS_ASSERT( 0 );
        return ANI_E_ILLEGAL_ARG;
    }
}


static int
checkRsnKeyMic(v_U32_t cryptHandle,
               tAniPacket *eapolFrame,
               tAniEapolRsnKeyDesc *rsnDesc,
               v_U8_t *micKey,
               v_U32_t micKeyLen) 
{
    int retVal = ANI_ERROR;
    int len;

    v_U8_t *ptr = NULL;
    v_U8_t *micPos = NULL;

    v_U8_t result[VOS_DIGEST_SHA1_SIZE]; // Larger of the two
    v_U8_t incomingMic[ANI_EAPOL_KEY_RSN_MIC_SIZE];

    if (!rsnDesc->info.micFlag) 
    {
        VOS_ASSERT( 0 );
        return ANI_E_ILLEGAL_ARG;
    }

    len = aniAsfPacketGetBytes(eapolFrame, &ptr);
    if( ANI_IS_STATUS_SUCCESS( len ) )
    {
        micPos = ptr + ANI_SSM_RSN_KEY_MIC_OFFSET;

        // Skip to the EAPOL version field for MIC computation
        ptr += EAPOL_VERSION_POS;
        len -= EAPOL_VERSION_POS;

        // Copy existing MIC to temporary location and zero it out
        vos_mem_copy( incomingMic, micPos, ANI_EAPOL_KEY_RSN_MIC_SIZE );
        vos_mem_zero( micPos, ANI_EAPOL_KEY_RSN_MIC_SIZE );

        if (rsnDesc->info.keyDescVers == ANI_EAPOL_KEY_DESC_VERS_AES) 
        {
            if( VOS_IS_STATUS_SUCCESS( vos_sha1_hmac_str(cryptHandle, ptr, len, micKey, micKeyLen, result) ) )
            {
                retVal = ANI_OK;
            }
        } 
        else {
            VOS_ASSERT( 0 );
            retVal = ANI_E_ILLEGAL_ARG;
        }

        if (retVal == ANI_OK) 
        {
            if ( !vos_mem_compare(incomingMic, result, ANI_EAPOL_KEY_RSN_MIC_SIZE))
            {
                retVal = ANI_E_MIC_FAILED;
            }
        }
    }

    return retVal;
}

/**
 * aniEapolKeyFreeDesc
 *
 * Frees the EAPOL key descriptor and the key bytes contained within it.
 *
 * @param descType the key descriptor type
 * @param keyDescData the key descriptor
 *
 * @return ANI_OK if the operation succeeds
 */
int
aniEapolKeyFreeDesc(int descType, void *keyDescData)
{
    tAniEapolRsnKeyDesc *rsnDesc;

    if( keyDescData )
    {
        if ((descType == ANI_EAPOL_KEY_DESC_TYPE_RSN_NEW) 
                || (descType == ANI_EAPOL_KEY_DESC_TYPE_RSN)) 
        {

            rsnDesc = (tAniEapolRsnKeyDesc *) keyDescData;
            if (rsnDesc->keyData != NULL)
                vos_mem_free(rsnDesc->keyData);

        } 
        else {

            VOS_TRACE( VOS_MODULE_ID_BAP, VOS_TRACE_LEVEL_ERROR,
                "Supp asked to free illegal type: %d\n", descType);

        }

        vos_mem_free(keyDescData);
    }

    return ANI_OK;
}

v_U8_t *
aniEapolType2Str(v_U8_t type)
{
    switch (type) 
    {
    case ANI_EAPOL_TYPE_PACKET:
        return (v_U8_t *)ANI_EAPOL_TYPE_PACKET_STR;
        break;
    case ANI_EAPOL_TYPE_START:
        return (v_U8_t *)ANI_EAPOL_TYPE_START_STR;
        break;
    case ANI_EAPOL_TYPE_LOGOFF:
        return (v_U8_t *)ANI_EAPOL_TYPE_LOGOFF_STR;
        break;
    case ANI_EAPOL_TYPE_KEY:
        return (v_U8_t *)ANI_EAPOL_TYPE_KEY_STR;
        break;
    case ANI_EAPOL_TYPE_ASF_ALERT:
        return (v_U8_t *)ANI_EAPOL_TYPE_ASF_ALERT_STR;
        break;
    default:
        return (v_U8_t *)ANI_EAPOL_TYPE_UNKNOWN_STR;
        break;
    }
}


void bapRsnEapolHandler( v_PVOID_t pvFsm, tAniPacket *packet, v_BOOL_t fIsAuth )
{
    int retVal;
    v_U8_t *dstMac = NULL;
    v_U8_t *srcMac = NULL;
    v_U8_t *type = NULL;

    retVal = aniEapolParse(packet, &dstMac, &srcMac, &type);
    if ( retVal >= 0 )
    {
        retVal = ANI_OK;

        // Sanity check that a PAE role has been assigned to it,
        // and then dispatch to the appropriate handler

        if( fIsAuth )
        {
            tAuthRsnFsm *fsm = (tAuthRsnFsm *)pvFsm;
            authEapolHandler( fsm, packet, dstMac, srcMac, type );
        }
        else
        {
            tSuppRsnFsm *fsm = (tSuppRsnFsm *)pvFsm;
            suppEapolHandler(fsm, packet, dstMac, srcMac, type);
        } // switch statement
    } // Successfully parsed EAPOL
    else
    {
        VOS_TRACE( VOS_MODULE_ID_BAP, VOS_TRACE_LEVEL_ERROR,
            "eapolHandler Received bad EAPOL message of len %d (status=%d)\n",
                      aniAsfPacketGetLen(packet), retVal );
    } 
    aniAsfPacketFree( packet );
}


int bapRsnFormPktFromVosPkt( tAniPacket **ppPacket, vos_pkt_t *pVosPacket )
{
    int retVal = ANI_ERROR;
    VOS_STATUS status;
    v_U16_t uPktLen;
#define BAP_RSN_SNAP_TYPE_OFFSET 20
#define BAP_RSN_ETHERNET_3_HEADER_LEN   22
    v_U8_t *pFrame;
    tAniPacket *pAniPacket = NULL;

    do
    {
        status = vos_pkt_get_packet_length( pVosPacket, &uPktLen );
        if( !VOS_IS_STATUS_SUCCESS(status) ) break;
        if( (uPktLen < BAP_RSN_ETHERNET_3_HEADER_LEN) )
        {
            VOS_TRACE( VOS_MODULE_ID_BAP, VOS_TRACE_LEVEL_ERROR,
                " authRsnRxFrameHandler receive eapol packet size (%d) too small (%d)\n", 
                uPktLen, BAP_RSN_ETHERNET_3_HEADER_LEN );
            break;
        }
        status = vos_pkt_peek_data( pVosPacket, 0, (v_VOID_t *)&pFrame, uPktLen );
        if( !VOS_IS_STATUS_SUCCESS(status) ) break;
        retVal = aniAsfPacketAllocateExplicit(&pAniPacket, uPktLen, 0 );
        if( !ANI_IS_STATUS_SUCCESS( retVal ) )
        {
            VOS_TRACE( VOS_MODULE_ID_BAP, VOS_TRACE_LEVEL_ERROR,
                " authRsnRxFrameHandler failed to get buffer size (%d) \n", 
                uPktLen );
            break;
        }
        aniAsfPacketEmptyExplicit( pAniPacket, 0 );
        pFrame[ETHER_PROTO_POS] = pFrame[BAP_RSN_SNAP_TYPE_OFFSET];
        pFrame[ETHER_PROTO_POS + 1] = pFrame[BAP_RSN_SNAP_TYPE_OFFSET + 1];
        //push ethernet II header in
        retVal = aniAsfPacketAppendBuffer( pAniPacket, pFrame, ETHER_PROTO_POS + 2 );
        if( !ANI_IS_STATUS_SUCCESS( retVal ) ) break;
        //Get the rest of the data in
        uPktLen -= BAP_RSN_ETHERNET_3_HEADER_LEN;
        VOS_ASSERT( uPktLen > 0 );
        retVal = aniAsfPacketAppendBuffer( pAniPacket, pFrame + BAP_RSN_ETHERNET_3_HEADER_LEN, 
                            uPktLen );
        if( !ANI_IS_STATUS_SUCCESS( retVal ) ) 
        {
            VOS_TRACE( VOS_MODULE_ID_BAP, VOS_TRACE_LEVEL_ERROR,
                " authRsnRxFrameHandler cannot retrieve eapol payload size (%d)\n", 
                uPktLen );
            break;
        }
    }while( 0 );

    if( ANI_IS_STATUS_SUCCESS( retVal ) )
    {
        *ppPacket = pAniPacket;
    }
    else if( pAniPacket )
    {
        aniAsfPacketFree( pAniPacket );
    }

    return retVal;
}


