/*******************************************************************************
    OpenAirInterface
    Copyright(c) 1999 - 2014 Eurecom

    OpenAirInterface is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.


    OpenAirInterface is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with OpenAirInterface.The full GNU General Public License is
   included in this distribution in the file called "COPYING". If not,
   see <http://www.gnu.org/licenses/>.

  Contact Information
  OpenAirInterface Admin: openair_admin@eurecom.fr
  OpenAirInterface Tech : openair_tech@eurecom.fr
  OpenAirInterface Dev  : openair4g-devel@eurecom.fr

  Address      : Eurecom, Compus SophiaTech 450, route des chappes, 06451 Biot, France.

 *******************************************************************************/
/*****************************************************************************

Source		nas_message.h

Version		0.1

Date		2012/26/09

Product		NAS stack

Subsystem	Application Programming Interface

Author		Frederic Maurel

Description	Defines the layer 3 messages supported by the NAS sublayer
		protocol and functions used to encode and decode

*****************************************************************************/

#include "nas_message.h"
#include "nas_log.h"

#include "TLVDecoder.h"
#include "TLVEncoder.h"

#include <stdlib.h>	// malloc, free
#include <string.h>	// memcpy

#if ((defined(EPC_BUILD) && defined(NAS_MME)) || (defined(ENABLE_NAS_UE_LOGGING) && defined(UE_BUILD) && defined(NAS_UE)))
# include "nas_itti_messaging.h"
#endif
#include "secu_defs.h"
#include "emmData.h"

/****************************************************************************/
/****************  E X T E R N A L    D E F I N I T I O N S  ****************/
/****************************************************************************/

/****************************************************************************/
/*******************  L O C A L    D E F I N I T I O N S  *******************/
/****************************************************************************/


/* Functions used to decode layer 3 NAS messages */
static int _nas_message_header_decode(const char* buffer,
    nas_message_security_header_t* header, int length);
static int _nas_message_plain_decode(const char* buffer,
    const nas_message_security_header_t* header,
    nas_message_plain_t* msg, int length);
static int _nas_message_protected_decode(const char* buffer,
    nas_message_security_header_t* header,
    nas_message_plain_t* msg, int length,
    const emm_security_context_t * const emm_security_context);

/* Functions used to encode layer 3 NAS messages */
static int _nas_message_header_encode(char* buffer,
    const nas_message_security_header_t* header, int length);
static int _nas_message_plain_encode(char* buffer,
    const nas_message_security_header_t* header,
    const nas_message_plain_t* msg, int length);

static int _nas_message_protected_encode(
    char                                    *buffer,
    const nas_message_security_protected_t  *msg,
    int                                      length,
    void                                    *security);

/* Functions used to decrypt and encrypt layer 3 NAS messages */
static int _nas_message_decrypt(
    char* dest,
    const char* src,
    UInt8_t type,
    UInt32_t code,
    UInt8_t seq,
    int length,
    const emm_security_context_t * const emm_security_context);


static int
_nas_message_encrypt(
    char       *dest,
    const char *src,
    UInt8_t     type,
    UInt32_t    code,
    UInt8_t     seq,
    int         const direction,
    int         length,
    const emm_security_context_t * const emm_security_context);

/* Functions used for integrity protection of layer 3 NAS messages */
static UInt32_t _nas_message_get_mac(
    const char                   * const buffer,
    int                            const length,
    int                            const direction,
    const emm_security_context_t * const emm_security_context);

/****************************************************************************/
/******************  E X P O R T E D    F U N C T I O N S  ******************/
/****************************************************************************/

/****************************************************************************
 **                                                                        **
 ** Name:	 nas_message_encrypt()                                     **
 **                                                                        **
 ** Description: Encripts plain NAS message into security protected NAS    **
 **		 message                                                   **
 **                                                                        **
 ** Inputs:	 inbuf:		Input buffer containing plain NAS message  **
 **		 header:	Security protected header to be applied    **
 ** 		 length:	Number of bytes that should be encrypted   **
 **		 Others:	None                                       **
 **                                                                        **
 ** Outputs:	 outbuf:	Output buffer containing security protec-  **
 **				ted message                                **
 ** 		 Return:	The number of bytes in the output buffer   **
 **				if the input buffer has been successfully  **
 **				encrypted; Negative error code otherwise.  **
 **		 Others:	None                                       **
 **                                                                        **
 ***************************************************************************/
int
nas_message_encrypt(
    const char                          *inbuf,
    char                                *outbuf,
    const nas_message_security_header_t *header,
    int                                  length,
    void                                *security)
{
    LOG_FUNC_IN;
    emm_security_context_t *emm_security_context   = (emm_security_context_t*)security;
    int bytes = length;

    /* Encode the header */
    int size = _nas_message_header_encode(outbuf, header, length);

    if (size < 0) {
        LOG_FUNC_RETURN (TLV_ENCODE_BUFFER_TOO_SHORT);
    }
    else if (size > 1) {
        /* Encrypt the plain NAS message */
        bytes = _nas_message_encrypt(outbuf + size, inbuf,
            header->security_header_type,
            header->message_authentication_code,
            header->sequence_number,
#ifdef NAS_MME
                SECU_DIRECTION_DOWNLINK,
#else
                SECU_DIRECTION_UPLINK,
#endif
            length - size,
            emm_security_context);
        /* Integrity protected the NAS message */
        if (bytes > 0) {
            /* Compute offset of the sequence number field */
            int offset = size - sizeof(UInt8_t);
            /* Compute the NAS message authentication code */
            UInt32_t mac = _nas_message_get_mac(
                outbuf + offset,
                bytes + size - offset,
#ifdef NAS_MME
                SECU_DIRECTION_DOWNLINK,
#else
                SECU_DIRECTION_UPLINK,
#endif
                emm_security_context);
            /* Set the message authentication code of the NAS message */
            *(UInt32_t*)(outbuf + sizeof(UInt8_t)) = htonl(mac);
        }
    }
    else {
        /* The input buffer does not need to be encrypted */
        memcpy(outbuf, inbuf, length);
    }

#ifdef NAS_MME
	/* TS 124.301, section 4.4.3.1
	 * The NAS sequence number part of the NAS COUNT shall be
	 * exchanged between the UE and the MME as part of the
	 * NAS signalling. After each new or retransmitted outbound
	 * security protected NAS message, the sender shall increase
	 * the NAS COUNT number by one. Specifically, on the sender
	 * side, the NAS sequence number shall be increased by one,
	 * and if the result is zero (due to wrap around), the NAS
	 * overflow counter shall also be incremented by one (see
	 * subclause 4.4.3.5).
	 */
	emm_security_context->dl_count.seq_num += 1;
	if ( ! emm_security_context->dl_count.seq_num) {
		emm_security_context->dl_count.overflow += 1;
	}
    LOG_TRACE(DEBUG,
        "Incremented emm_security_context.dl_count.seq_num -> %u",
        emm_security_context->dl_count.seq_num);
#else
	emm_security_context->ul_count.seq_num += 1;
	if ( ! emm_security_context->ul_count.seq_num) {
		emm_security_context->ul_count.overflow += 1;
	}
    LOG_TRACE(DEBUG,
        "Incremented emm_security_context.ul_count.seq_num -> %u",
        emm_security_context->ul_count.seq_num);
#endif

    if (bytes < 0) {
        LOG_FUNC_RETURN (bytes);
    }
    if (size > 1) {
        LOG_FUNC_RETURN (size + bytes);
    }
    LOG_FUNC_RETURN (bytes);
}

/****************************************************************************
 **                                                                        **
 ** Name:	 nas_message_decrypt()                                     **
 **                                                                        **
 ** Description: Decripts security protected NAS message into plain NAS    **
 **		 message                                                   **
 **                                                                        **
 ** Inputs:	 inbuf:		Input buffer containing security protected **
 **				NAS message                                **
 ** 		 length:	Number of bytes that should be decrypted   **
 **		 Others:	None                                       **
 **                                                                        **
 ** Outputs:	 outbuf:	Output buffer containing plain NAS message **
 **		 header:	Security protected header applied          **
 ** 		 Return:	The number of bytes in the output buffer   **
 **				if the input buffer has been successfully  **
 **				decrypted; Negative error code otherwise.  **
 **		 Others:	None                                       **
 **                                                                        **
 ***************************************************************************/
int nas_message_decrypt(
    const char                         *inbuf,
    char                               *outbuf,
    nas_message_security_header_t      *header,
    int                                 length,
    void                               *security)
{
    LOG_FUNC_IN;

    emm_security_context_t *emm_security_context   = (emm_security_context_t*)security;
    int                     bytes                  = length;

    /* Decode the header */
    int size = _nas_message_header_decode(inbuf, header, length);

    if (size < 0) {
    	LOG_TRACE(DEBUG, "MESSAGE TOO SHORT");
        LOG_FUNC_RETURN (TLV_DECODE_BUFFER_TOO_SHORT);
    }
    else if (size > 1) {
#if defined(NAS_MME)
    	if (emm_security_context->ul_count.seq_num > header->sequence_number) {
    	    emm_security_context->ul_count.overflow += 1;
    	}
    	emm_security_context->ul_count.seq_num = header->sequence_number;

#else
    	if (emm_security_context->dl_count.seq_num > header->sequence_number) {
    		emm_security_context->dl_count.overflow += 1;
    	}
    	emm_security_context->dl_count.seq_num = header->sequence_number;
#endif
        /* Compute offset of the sequence number field */
        int offset = size - sizeof(UInt8_t);
        /* Compute the NAS message authentication code */
        UInt32_t mac = _nas_message_get_mac(
            inbuf + offset,
            length - offset,
#ifdef NAS_MME
            SECU_DIRECTION_UPLINK,
#else
            SECU_DIRECTION_DOWNLINK,
#endif
            emm_security_context);


        /* Check NAS message integrity */
        if (mac != header->message_authentication_code) {
        	LOG_TRACE(DEBUG,
        	        "MAC Failure MSG:%08X(%u) <> INT ALGO:%08X(%u)",
        	        header->message_authentication_code,
        	        header->message_authentication_code,
        	        mac,
        	        mac);
#if defined(NAS_MME)
            LOG_FUNC_RETURN (TLV_DECODE_MAC_MISMATCH);
#else
#warning "added test on integrity algorithm because of SECURITY_MODE_COMMAND not correctly handled in UE (check integrity)"
            if (emm_security_context->selected_algorithms.integrity !=
        		    NAS_SECURITY_ALGORITHMS_EIA0) {
            	LOG_FUNC_RETURN (TLV_DECODE_MAC_MISMATCH);
            } else {
                LOG_TRACE(WARNING,
                		"MAC failure but continue due to EIA0 selected");
            }
#endif
        } else {
        	LOG_TRACE(DEBUG, "Integrity: MAC Success");
        }

        /* Decrypt the security protected NAS message */
        header->protocol_discriminator =
            _nas_message_decrypt(outbuf, inbuf + size,
                header->security_header_type,
                header->message_authentication_code,
                header->sequence_number,
                length - size,
                emm_security_context);
        bytes = length - size;
    }
    else {
    	LOG_TRACE(DEBUG, "Plain NAS message found");
        /* The input buffer contains a plain NAS message */
        memcpy(outbuf, inbuf, length);
    }

    LOG_FUNC_RETURN (bytes);
}

/****************************************************************************
 **                                                                        **
 ** Name:	 nas_message_decode()                                      **
 **                                                                        **
 ** Description: Decode layer 3 NAS message                                **
 **                                                                        **
 ** Inputs:	 buffer:	Pointer to the buffer containing layer 3   **
 **				NAS message data                           **
 ** 		 length:	Number of bytes that should be decoded     **
 **		 Others:	None                                       **
 **                                                                        **
 ** Outputs:	 msg:		L3 NAS message structure to be filled      **
 ** 		 Return:	The number of bytes in the buffer if the   **
 **				data have been successfully decoded;       **
 **				A negative error code otherwise.           **
 **		 Others:	None                                       **
 **                                                                        **
 ***************************************************************************/
int nas_message_decode(
    const char* const   buffer,
    nas_message_t      *msg,
    int                 length,
    void               *security)
{
    LOG_FUNC_IN;
    emm_security_context_t *emm_security_context   = (emm_security_context_t*)security;
    int bytes;

    /* Decode the header */
    int size = _nas_message_header_decode(buffer, &msg->header, length);

    if (size < 0) {
        LOG_FUNC_RETURN (TLV_DECODE_BUFFER_TOO_SHORT);
    }
    else if (size > 1) {
        if (emm_security_context) {
#if defined(NAS_MME)
            if (emm_security_context->ul_count.seq_num > msg->header.sequence_number) {
                emm_security_context->ul_count.overflow += 1;
            }
            emm_security_context->ul_count.seq_num = msg->header.sequence_number;

#else
            if (emm_security_context->dl_count.seq_num > msg->header.sequence_number) {
                emm_security_context->dl_count.overflow += 1;
            }
            emm_security_context->dl_count.seq_num = msg->header.sequence_number;
#endif
        }
        /* Compute offset of the sequence number field */
        int offset = size - sizeof(UInt8_t);
        /* Compute the NAS message authentication code */
        UInt32_t mac = _nas_message_get_mac(
            buffer + offset,
            length - offset,
#ifdef NAS_MME
            SECU_DIRECTION_UPLINK,
#else
            SECU_DIRECTION_DOWNLINK,
#endif
            emm_security_context
            );

        /* Check NAS message integrity */
        if (mac != msg->header.message_authentication_code) {
            LOG_TRACE(DEBUG,
                "msg->header.message_authentication_code = %04X computed = %04X",
                msg->header.message_authentication_code,
                mac);
            LOG_FUNC_RETURN (TLV_DECODE_MAC_MISMATCH);
        }

#if ((defined(EPC_BUILD) && defined(NAS_MME)) || (defined(ENABLE_NAS_UE_LOGGING) && defined(UE_BUILD) && defined(NAS_UE)))
        /* Log message header */
#endif

        /* Decode security protected NAS message */
        bytes = _nas_message_protected_decode(buffer + size,
                &msg->header,
                &msg->plain,
                length - size,
                emm_security_context);
    }
    else {
        /* Decode plain NAS message */
        bytes = _nas_message_plain_decode(buffer,
                &msg->header,
                &msg->plain,
                length);
    }

    if (bytes < 0) {
        LOG_FUNC_RETURN (bytes);
    }
    if (size > 1) {
        LOG_FUNC_RETURN (size + bytes);
    }
    LOG_FUNC_RETURN (bytes);
}

/****************************************************************************
 **                                                                        **
 ** Name:	 nas_message_encode()                                      **
 **                                                                        **
 ** Description: Encode layer 3 NAS message                                **
 **                                                                        **
 ** Inputs	 msg:		L3 NAS message structure to encode         **
 **		 length:	Maximal capacity of the output buffer      **
 **		 Others:	None                                       **
 **                                                                        **
 ** Outputs:	 buffer:	Pointer to the encoded data buffer         **
 ** 		 Return:	The number of bytes in the buffer if the   **
 **				data have been successfully encoded;       **
 **				A negative error code otherwise.           **
 **		 Others:	None                                       **
 **                                                                        **
 ***************************************************************************/
int nas_message_encode(
    char                *buffer,
    const nas_message_t *const msg,
    int                  length,
    void                *security)
{
    LOG_FUNC_IN;

    emm_security_context_t *emm_security_context   = (emm_security_context_t*)security;
    int bytes;


    /* Encode the header */
    int size = _nas_message_header_encode(buffer, &msg->header, length);

    if (size < 0) {
        LOG_FUNC_RETURN (TLV_ENCODE_BUFFER_TOO_SHORT);
    }
    else if (size > 1) {
        /* Encode security protected NAS message */
        bytes = _nas_message_protected_encode(
            buffer + size,
            &msg->security_protected,
            length - size,
            emm_security_context);
        /* Integrity protect the NAS message */
        if (bytes > 0) {
            /* Compute offset of the sequence number field */
            int offset = size - sizeof(UInt8_t);
            /* Compute the NAS message authentication code */
            LOG_TRACE(DEBUG,
                "offset %d = %d - %d, hdr encode = %d, length = %d bytes = %d",
                offset, size, sizeof(UInt8_t),
                size, length, bytes);
            UInt32_t mac = _nas_message_get_mac(
                buffer + offset,
                bytes + size - offset,
#ifdef NAS_MME
                SECU_DIRECTION_DOWNLINK,
#else
                SECU_DIRECTION_UPLINK,
#endif
                emm_security_context);
            /* Set the message authentication code of the NAS message */
            *(UInt32_t*)(buffer + sizeof(UInt8_t)) = htonl(mac);

            if (emm_security_context) {
#ifdef NAS_MME
                /* TS 124.301, section 4.4.3.1
                 * The NAS sequence number part of the NAS COUNT shall be
                 * exchanged between the UE and the MME as part of the
                 * NAS signalling. After each new or retransmitted outbound
                 * security protected NAS message, the sender shall increase
                 * the NAS COUNT number by one. Specifically, on the sender
                 * side, the NAS sequence number shall be increased by one,
                 * and if the result is zero (due to wrap around), the NAS
                 * overflow counter shall also be incremented by one (see
                 * subclause 4.4.3.5).
                 */

                emm_security_context->dl_count.seq_num += 1;
                if ( ! emm_security_context->dl_count.seq_num) {
                    emm_security_context->dl_count.overflow += 1;
                }
                LOG_TRACE(DEBUG,
                    "Incremented emm_security_context.dl_count.seq_num -> %u",
                    emm_security_context->dl_count.seq_num);
#else
                emm_security_context->ul_count.seq_num += 1;
                if ( ! emm_security_context->ul_count.seq_num) {
                    emm_security_context->ul_count.overflow += 1;
                }
                LOG_TRACE(DEBUG,
                    "Incremented emm_security_context.ul_count.seq_num -> %u",
                    emm_security_context->ul_count.seq_num);
#endif
            } else {
                LOG_TRACE(DEBUG,
                    "Did not increment emm_security_context.dl_count.seq_num because no security context");
            }
        }
#if ((defined(EPC_BUILD) && defined(NAS_MME)) || (defined(ENABLE_NAS_UE_LOGGING) && defined(UE_BUILD) && defined(NAS_UE)))
        /* Log message header */
#endif
    }
    else {
        /* Encode plain NAS message */
        bytes = _nas_message_plain_encode(buffer, &msg->header,
                          &msg->plain, length);
    }

    if (bytes < 0) {
        LOG_FUNC_RETURN (bytes);
    }
    if (size > 1) {
        LOG_FUNC_RETURN (size + bytes);
    }
    LOG_FUNC_RETURN (bytes);
}

/****************************************************************************/
/*********************  L O C A L    F U N C T I O N S  *********************/
/****************************************************************************/

/*
 * -----------------------------------------------------------------------------
 *		Functions used to decode layer 3 NAS messages
 * -----------------------------------------------------------------------------
 */

/****************************************************************************
 **                                                                        **
 ** Name:	 _nas_message_header_decode()                              **
 **                                                                        **
 ** Description: Decode header of a security protected NAS message         **
 **                                                                        **
 ** Inputs:	 buffer:	Pointer to the buffer containing layer 3   **
 **				message data                               **
 ** 		 length:	Number of bytes that should be decoded     **
 **		 Others:	None                                       **
 **                                                                        **
 ** Outputs:	 header:	Security header structure to be filled     **
 ** 		 Return:	The size in bytes of the security header   **
 **				if data have been successfully decoded;    **
 **				1, if the header is not a security header  **
 **				(header of plain NAS message);             **
 **				-1 otherwise.                              **
 **		 Others:	None                                       **
 **                                                                        **
 ***************************************************************************/
static int
_nas_message_header_decode(
    const char                         *buffer,
    nas_message_security_header_t      *header,
    int                                 length)
{
    LOG_FUNC_IN;

    int size = 0;

    /* Decode the first octet of the header (security header type or EPS bearer
     * identity, and protocol discriminator) */
    DECODE_U8(buffer, *(UInt8_t*)(header), size);

    if (header->protocol_discriminator == EPS_MOBILITY_MANAGEMENT_MESSAGE) {
        if (header->security_header_type != SECURITY_HEADER_TYPE_NOT_PROTECTED)
        {
            if (length < NAS_MESSAGE_SECURITY_HEADER_SIZE) {
                /* The buffer is not big enough to contain security header */
                LOG_TRACE(WARNING, "NET-API   - The size of the header (%u) "
                    "exceeds the buffer length (%u)",
                    NAS_MESSAGE_SECURITY_HEADER_SIZE, length);
                LOG_FUNC_RETURN (-1);
            }
            /* Decode the message authentication code */
            DECODE_U32(buffer+size, header->message_authentication_code, size);
            /* Decode the sequence number */
            DECODE_U8(buffer+size, header->sequence_number, size);
        }
    }

    LOG_FUNC_RETURN (size);
}

/****************************************************************************
 **                                                                        **
 ** Name:	 _nas_message_plain_decode()                               **
 **                                                                        **
 ** Description: Decode plain NAS message                                  **
 **                                                                        **
 ** Inputs:	 buffer:	Pointer to the buffer containing plain NAS **
 **				message data                               **
 **		 header:	Header of the plain NAS message            **
 ** 		 length:	Number of bytes that should be decoded     **
 **		 Others:	None                                       **
 **                                                                        **
 ** Outputs:	 msg:		Decoded NAS message                        **
 ** 		 Return:	The number of bytes in the buffer if the   **
 **				data have been successfully decoded;       **
 **				A negative error code otherwise.           **
 **		 Others:	None                                       **
 **                                                                        **
 ***************************************************************************/
static int
_nas_message_plain_decode(
    const char                          *buffer,
    const nas_message_security_header_t *header,
    nas_message_plain_t                 *msg,
    int                                 length)
{
    LOG_FUNC_IN;

    int bytes = TLV_DECODE_PROTOCOL_NOT_SUPPORTED;

    if (header->protocol_discriminator == EPS_MOBILITY_MANAGEMENT_MESSAGE) {
        /* Decode EPS Mobility Management L3 message */
        bytes = emm_msg_decode(&msg->emm, (uint8_t *)buffer, length);
    }
    else if (header->protocol_discriminator == EPS_SESSION_MANAGEMENT_MESSAGE) {
        /* Decode EPS Session Management L3 message */
        bytes = esm_msg_decode(&msg->esm, (uint8_t *)buffer, length);
    }
    else {
        /* Discard L3 messages with not supported protocol discriminator */
        LOG_TRACE(WARNING,"NET-API   - Protocol discriminator 0x%x is "
            "not supported", header->protocol_discriminator);
    }

    LOG_FUNC_RETURN (bytes);
}

/****************************************************************************
 **                                                                        **
 ** Name:	 _nas_message_protected_decode()                               **
 **                                                                        **
 ** Description: Decode security protected NAS message                     **
 **                                                                        **
 ** Inputs:  buffer:  Pointer to the buffer containing the secu-           **
 **                     rity protected NAS message data                    **
 **          header:	Header of the security protected NAS message       **
 ** 		 length:	Number of bytes that should be decoded             **
 ** 		 emm_security_context: security context                       **
 **		 Others:	None                                       **
 **                                                                        **
 ** Outputs:	 msg:		Decoded NAS message                        **
 ** 		 Return:	The number of bytes in the buffer if the   **
 **				data have been successfully decoded;       **
 **				A negative error code otherwise.           **
 **		 Others:	None                                       **
 **                                                                        **
 ***************************************************************************/
static int _nas_message_protected_decode(
    const char                         *buffer,
    nas_message_security_header_t      *header,
    nas_message_plain_t                *msg,
    int                                 length,
    const emm_security_context_t * const emm_security_context)
{
    LOG_FUNC_IN;

    int bytes = TLV_DECODE_BUFFER_TOO_SHORT;

    char* plain_msg = (char*)malloc(length);
    if (plain_msg)
    {
        /* Decrypt the security protected NAS message */
        header->protocol_discriminator =
            _nas_message_decrypt(plain_msg,
                buffer,
                header->security_header_type,
                header->message_authentication_code,
                header->sequence_number,
                length,
                emm_security_context);
        /* Decode the decrypted message as plain NAS message */
        bytes = _nas_message_plain_decode(plain_msg, header, msg, length);
        free(plain_msg);
    }

    LOG_FUNC_RETURN (bytes);
}

/*
 * -----------------------------------------------------------------------------
 *		Functions used to encode layer 3 NAS messages
 * -----------------------------------------------------------------------------
 */

/****************************************************************************
 **                                                                        **
 ** Name:	 _nas_message_header_encode()                              **
 **                                                                        **
 ** Description: Encode header of a security protected NAS message         **
 **                                                                        **
 ** Inputs	 header:	Security header structure to encode        **
 **		 length:	Maximal capacity of the output buffer      **
 **		 Others:	None                                       **
 **                                                                        **
 ** Outputs:	 buffer:	Pointer to the encoded data buffer         **
 ** 		 Return:	The number of bytes in the buffer if the   **
 **				data have been successfully encoded;       **
 **				1, if the header is not a security header  **
 **				(header of plain NAS message);             **
 **				-1 otherwise.                              **
 **		 Others:	None                                       **
 **                                                                        **
 ***************************************************************************/
static int _nas_message_header_encode(
    char                                       *buffer,
    const nas_message_security_header_t        *header,
    int                                         length)
{
    LOG_FUNC_IN;

    int size = 0;

    /* Encode the first octet of the header (security header type or EPS bearer
     * identity, and protocol discriminator) */
    ENCODE_U8(buffer, *(UInt8_t*)(header), size);

    if (header->protocol_discriminator == EPS_MOBILITY_MANAGEMENT_MESSAGE) {
        if (header->security_header_type != SECURITY_HEADER_TYPE_NOT_PROTECTED)
        {
            //static uint8_t seq = 0;
            if (length < NAS_MESSAGE_SECURITY_HEADER_SIZE) {
            /* The buffer is not big enough to contain security header */
            LOG_TRACE(WARNING, "NET-API   - The size of the header (%u) "
                  "exceeds the buffer length (%u)",
                  NAS_MESSAGE_SECURITY_HEADER_SIZE, length);
            LOG_FUNC_RETURN (-1);
            }
            /* Encode the message authentication code */
            ENCODE_U32(buffer+size, header->message_authentication_code, size);
            /* Encode the sequence number */
            ENCODE_U8(buffer+size, header->sequence_number, size);
            //ENCODE_U8(buffer+size, seq, size);
            //seq++;
        }
    }

    LOG_FUNC_RETURN (size);
}

/****************************************************************************
 **                                                                        **
 ** Name:	 _nas_message_plain_encode()                               **
 **                                                                        **
 ** Description: Encode plain NAS message                                  **
 **                                                                        **
 ** Inputs:	 pd:		Protocol discriminator of the NAS message  **
 **				to encode                                  **
 ** 		 msg:		Plain NAS message structure to encode      **
 **		 length:	Maximal capacity of the output buffer      **
 **		 Others:	None                                       **
 **                                                                        **
 ** Outputs:	 buffer:	Pointer to the encoded data buffer         **
 ** 		 Return:	The number of bytes in the buffer if the   **
 **				data have been successfully encoded;       **
 **				A negative error code otherwise.           **
 **		 Others:	None                                       **
 **                                                                        **
 ***************************************************************************/
static int _nas_message_plain_encode(
    char                                *buffer,
    const nas_message_security_header_t *header,
    const nas_message_plain_t           *msg,
    int                                  length)
{
    LOG_FUNC_IN;

    int bytes = TLV_ENCODE_PROTOCOL_NOT_SUPPORTED;

    if (header->protocol_discriminator == EPS_MOBILITY_MANAGEMENT_MESSAGE) {
        /* Encode EPS Mobility Management L3 message */
        bytes = emm_msg_encode((EMM_msg*)(&msg->emm), (uint8_t*)buffer, length);
    }
    else if (header->protocol_discriminator == EPS_SESSION_MANAGEMENT_MESSAGE) {
        /* Encode EPS Session Management L3 message */
        bytes = esm_msg_encode((ESM_msg*)(&msg->esm), (uint8_t*)buffer, length);
    }
    else {
        /* Discard L3 messages with not supported protocol discriminator */
        LOG_TRACE(WARNING,"NET-API   - Protocol discriminator 0x%x is "
            "not supported", header->protocol_discriminator);
    }

    LOG_FUNC_RETURN (bytes);
}

/****************************************************************************
 **                                                                        **
 ** Name:	 _nas_message_protected_encode()                               **
 **                                                                        **
 ** Description: Encode security protected NAS message                     **
 **                                                                        **
 ** Inputs    msg:     Security protected NAS message structure            **
 **                    to encode                                           **
 **           length:  Maximal capacity of the output buffer               **
 **           Others:  None                                                **
 **                                                                        **
 ** Outputs:  buffer:  Pointer to the encoded data buffer                  **
 ** Return:   The number of bytes in the buffer if the                     **
 **           data have been successfully encoded;                         **
 **           A negative error code otherwise.                             **
 ** Others:   None                                                         **
 **                                                                        **
 ***************************************************************************/
static int _nas_message_protected_encode(
    char                                       *buffer,
    const nas_message_security_protected_t     *msg,
    int                                         length,
    void                                       *security)
{
    LOG_FUNC_IN;

    emm_security_context_t *emm_security_context   = (emm_security_context_t*)security;
    int bytes = TLV_ENCODE_BUFFER_TOO_SHORT;

    char* plain_msg = (char*)malloc(length);
    if (plain_msg) {
        /* Encode the security protected NAS message as plain NAS message */
        int size = _nas_message_plain_encode(plain_msg, &msg->header,
            &msg->plain, length);
        if (size > 0) {
            //static uint8_t seq = 0;
            /* Encrypt the encoded plain NAS message */
            bytes = _nas_message_encrypt(buffer,
                plain_msg,
                msg->header.security_header_type,
                msg->header.message_authentication_code,
                msg->header.sequence_number,
#ifdef NAS_MME
                SECU_DIRECTION_DOWNLINK,
#else
                SECU_DIRECTION_UPLINK,
#endif
                size,
                emm_security_context);
            //seq, size);
            //seq ++;
        }
        free(plain_msg);
    }

    LOG_FUNC_RETURN (bytes);
}

/*
 * -----------------------------------------------------------------------------
 *	    Functions used to decrypt and encrypt layer 3 NAS messages
 * -----------------------------------------------------------------------------
 */

/****************************************************************************
 **                                                                        **
 ** Name:	 _nas_message_decrypt()                                    **
 **                                                                        **
 ** Description: Decrypt security protected NAS message                    **
 **                                                                        **
 ** Inputs	 src:		Pointer to the encrypted data buffer       **
 **		 security_header_type:		The security header type                   **
 **		 code:		The message authentication code            **
 **		 seq:		The sequence number                        **
 **		 length:	Maximal capacity of the output buffer      **
 **		 Others:	None                                       **
 **                                                                        **
 ** Outputs:	 dest:		Pointer to the decrypted data buffer       **
 ** 		 Return:	The protocol discriminator of the message  **
 **				that has been decrypted;                   **
 **		 Others:	None                                       **
 **                                                                        **
 ***************************************************************************/
static int _nas_message_decrypt(
    char               *dest,
    const char         *src,
    UInt8_t             security_header_type,
    UInt32_t            code,
    UInt8_t             seq,
    int                 length,
    const emm_security_context_t * const emm_security_context)
{
    nas_stream_cipher_t stream_cipher;
    uint32_t            count;
    uint8_t             direction;

    LOG_FUNC_IN;

    int size = 0;
    nas_message_security_header_t header;

#ifdef NAS_MME
    direction = SECU_DIRECTION_UPLINK;
#else
    direction = SECU_DIRECTION_DOWNLINK;
#endif

    switch (security_header_type) {
    case SECURITY_HEADER_TYPE_NOT_PROTECTED:
    case SECURITY_HEADER_TYPE_SERVICE_REQUEST:
    case SECURITY_HEADER_TYPE_INTEGRITY_PROTECTED:
    case SECURITY_HEADER_TYPE_INTEGRITY_PROTECTED_NEW:
        LOG_TRACE(DEBUG,
                "No decryption of message according to security header type 0x%02x",
                security_header_type);
        memcpy(dest, src, length);
        LOG_FUNC_RETURN (length);
        break;

    case SECURITY_HEADER_TYPE_INTEGRITY_PROTECTED_CYPHERED:
    case SECURITY_HEADER_TYPE_INTEGRITY_PROTECTED_CYPHERED_NEW:
        switch (emm_security_context->selected_algorithms.encryption) {

            case NAS_SECURITY_ALGORITHMS_EEA1: {
                if (direction == SECU_DIRECTION_UPLINK) {
                    count = 0x00000000 ||
                            ((emm_security_context->ul_count.overflow && 0x0000FFFF) << 8) ||
                            (emm_security_context->ul_count.seq_num & 0x000000FF);
                } else {
                    count = 0x00000000 ||
                            ((emm_security_context->dl_count.overflow && 0x0000FFFF) << 8) ||
                            (emm_security_context->dl_count.seq_num & 0x000000FF);
                }
                LOG_TRACE(DEBUG,
                        "NAS_SECURITY_ALGORITHMS_EEA1 dir %s count.seq_num %u count %u",
                        (direction == SECU_DIRECTION_UPLINK) ? "UPLINK":"DOWNLINK",
                                (direction == SECU_DIRECTION_UPLINK) ? emm_security_context->ul_count.seq_num:emm_security_context->dl_count.seq_num,
                                        count);
                stream_cipher.key        = emm_security_context->knas_enc.value;
                stream_cipher.key_length = AUTH_KNAS_ENC_SIZE;
                stream_cipher.count      = count;
                stream_cipher.bearer     = 0x00; //33.401 section 8.1.1
                stream_cipher.direction  = direction;
                stream_cipher.message    = src;
                /* length in bits */
                stream_cipher.blength    = length << 3;
                nas_stream_encrypt_eea1(&stream_cipher, dest);
                /* Decode the first octet (security header type or EPS bearer identity,
                 * and protocol discriminator) */
                DECODE_U8(dest, *(UInt8_t*)(&header), size);

                LOG_FUNC_RETURN (header.protocol_discriminator);

            }break;

            case NAS_SECURITY_ALGORITHMS_EEA2: {
                if (direction == SECU_DIRECTION_UPLINK) {
                    count = 0x00000000 ||
                            ((emm_security_context->ul_count.overflow && 0x0000FFFF) << 8) ||
                            (emm_security_context->ul_count.seq_num & 0x000000FF);
                } else {
                    count = 0x00000000 ||
                            ((emm_security_context->dl_count.overflow && 0x0000FFFF) << 8) ||
                            (emm_security_context->dl_count.seq_num & 0x000000FF);
                }
                LOG_TRACE(DEBUG,
                        "NAS_SECURITY_ALGORITHMS_EEA2 dir %s count.seq_num %u count %u",
                        (direction == SECU_DIRECTION_UPLINK) ? "UPLINK":"DOWNLINK",
                                (direction == SECU_DIRECTION_UPLINK) ? emm_security_context->ul_count.seq_num:emm_security_context->dl_count.seq_num,
                                        count);
                stream_cipher.key        = emm_security_context->knas_enc.value;
                stream_cipher.key_length = AUTH_KNAS_ENC_SIZE;
                stream_cipher.count      = count;
                stream_cipher.bearer     = 0x00; //33.401 section 8.1.1
                stream_cipher.direction  = direction;
                stream_cipher.message    = src;
                /* length in bits */
                stream_cipher.blength    = length << 3;
                nas_stream_encrypt_eea1(&stream_cipher, dest);
                /* Decode the first octet (security header type or EPS bearer identity,
                 * and protocol discriminator) */
                DECODE_U8(dest, *(UInt8_t*)(&header), size);

                LOG_FUNC_RETURN (header.protocol_discriminator);
            }break;

            case NAS_SECURITY_ALGORITHMS_EEA0:
                LOG_TRACE(DEBUG,
                        "NAS_SECURITY_ALGORITHMS_EEA0 dir %d ul_count.seq_num %d dl_count.seq_num %d",
                        direction,
                        emm_security_context->ul_count.seq_num,
                        emm_security_context->dl_count.seq_num);
                memcpy(dest, src, length);
                /* Decode the first octet (security header type or EPS bearer identity,
                 * and protocol discriminator) */
                DECODE_U8(dest, *(UInt8_t*)(&header), size);

                LOG_FUNC_RETURN (header.protocol_discriminator);

                break;

            default:
                LOG_TRACE(ERROR,
                        "Unknown Cyphering protection algorithm %d",
                        emm_security_context->selected_algorithms.encryption);
                memcpy(dest, src, length);
                /* Decode the first octet (security header type or EPS bearer identity,
                 * and protocol discriminator) */
                DECODE_U8(dest, *(UInt8_t*)(&header), size);

                LOG_FUNC_RETURN (header.protocol_discriminator);
                break;
        }
        break;
    default:
        LOG_TRACE(ERROR,
            "Unknown security header type %u", security_header_type);
        LOG_FUNC_RETURN (0);
    };

}

/****************************************************************************
 **                                                                        **
 ** Name:	 _nas_message_encrypt()                                    **
 **                                                                        **
 ** Description: Encrypt plain NAS message                                 **
 **                                                                        **
 ** Inputs	 src:		Pointer to the decrypted data buffer       **
 **		 security_header_type:		The security header type                   **
 **		 code:		The message authentication code            **
 **		 seq:		The sequence number                        **
 **		 direction:	The sequence number                        **
 **		 length:	Maximal capacity of the output buffer      **
 **		 Others:	None                                       **
 **                                                                        **
 ** Outputs:	 dest:		Pointer to the encrypted data buffer       **
 ** 		 Return:	The number of bytes in the output buffer   **
 **				if data have been successfully encrypted;  **
 **				RETURNerror otherwise.                     **
 **		 Others:	None                                       **
 **                                                                        **
 ***************************************************************************/
static int _nas_message_encrypt(
    char        *dest,
    const char  *src,
    UInt8_t     security_header_type,
    UInt32_t    code,
    UInt8_t     seq,
    int         const direction,
    int         length,
    const emm_security_context_t * const emm_security_context)
{
    nas_stream_cipher_t stream_cipher;
    uint32_t            count;
    LOG_FUNC_IN;

    if (!emm_security_context) {
        LOG_TRACE(ERROR,
            "No security context set for encryption protection algorithm");
        LOG_FUNC_RETURN (0);
    }

    switch (security_header_type) {
    case SECURITY_HEADER_TYPE_NOT_PROTECTED:
    case SECURITY_HEADER_TYPE_SERVICE_REQUEST:
    case SECURITY_HEADER_TYPE_INTEGRITY_PROTECTED:
    case SECURITY_HEADER_TYPE_INTEGRITY_PROTECTED_NEW:
        LOG_TRACE(DEBUG,
                "No encryption of message according to security header type 0x%02x",
                security_header_type);
        memcpy(dest, src, length);
        LOG_FUNC_RETURN (length);
        break;

    case SECURITY_HEADER_TYPE_INTEGRITY_PROTECTED_CYPHERED:
    case SECURITY_HEADER_TYPE_INTEGRITY_PROTECTED_CYPHERED_NEW:
        switch (emm_security_context->selected_algorithms.encryption) {

            case NAS_SECURITY_ALGORITHMS_EEA1: {
                if (direction == SECU_DIRECTION_UPLINK) {
                    count = 0x00000000 ||
                            ((emm_security_context->ul_count.overflow && 0x0000FFFF) << 8) ||
                            (emm_security_context->ul_count.seq_num & 0x000000FF);
                } else {
                    count = 0x00000000 ||
                            ((emm_security_context->dl_count.overflow && 0x0000FFFF) << 8) ||
                            (emm_security_context->dl_count.seq_num & 0x000000FF);
                }
                LOG_TRACE(DEBUG,
                        "NAS_SECURITY_ALGORITHMS_EEA1 dir %s count.seq_num %u count %u",
                        (direction == SECU_DIRECTION_UPLINK) ? "UPLINK":"DOWNLINK",
                                (direction == SECU_DIRECTION_UPLINK) ? emm_security_context->ul_count.seq_num:emm_security_context->dl_count.seq_num,
                                        count);
                stream_cipher.key        = emm_security_context->knas_enc.value;
                stream_cipher.key_length = AUTH_KNAS_ENC_SIZE;
                stream_cipher.count      = count;
                stream_cipher.bearer     = 0x00; //33.401 section 8.1.1
                stream_cipher.direction  = direction;
                stream_cipher.message    = src;
                /* length in bits */
                stream_cipher.blength    = length << 3;
                nas_stream_encrypt_eea1(&stream_cipher, dest);

                LOG_FUNC_RETURN (length);

            }break;

            case NAS_SECURITY_ALGORITHMS_EEA2: {
                if (direction == SECU_DIRECTION_UPLINK) {
                    count = 0x00000000 ||
                            ((emm_security_context->ul_count.overflow && 0x0000FFFF) << 8) ||
                            (emm_security_context->ul_count.seq_num & 0x000000FF);
                } else {
                    count = 0x00000000 ||
                            ((emm_security_context->dl_count.overflow && 0x0000FFFF) << 8) ||
                            (emm_security_context->dl_count.seq_num & 0x000000FF);
                }
                LOG_TRACE(DEBUG,
                        "NAS_SECURITY_ALGORITHMS_EEA2 dir %s count.seq_num %u count %u",
                        (direction == SECU_DIRECTION_UPLINK) ? "UPLINK":"DOWNLINK",
                                (direction == SECU_DIRECTION_UPLINK) ? emm_security_context->ul_count.seq_num:emm_security_context->dl_count.seq_num,
                                        count);
                stream_cipher.key        = emm_security_context->knas_enc.value;
                stream_cipher.key_length = AUTH_KNAS_ENC_SIZE;
                stream_cipher.count      = count;
                stream_cipher.bearer     = 0x00; //33.401 section 8.1.1
                stream_cipher.direction  = direction;
                stream_cipher.message    = src;
                /* length in bits */
                stream_cipher.blength    = length << 3;
                nas_stream_encrypt_eea2(&stream_cipher, dest);

                LOG_FUNC_RETURN (length);

            }break;

            case NAS_SECURITY_ALGORITHMS_EEA0:
                LOG_TRACE(DEBUG,
                        "NAS_SECURITY_ALGORITHMS_EEA0 dir %d ul_count.seq_num %d dl_count.seq_num %d",
                        direction,
                        emm_security_context->ul_count.seq_num,
                        emm_security_context->dl_count.seq_num);
                memcpy(dest, src, length);
                LOG_FUNC_RETURN (length);

                break;

            default:
                LOG_TRACE(ERROR,
                        "Unknown Cyphering protection algorithm %d",
                        emm_security_context->selected_algorithms.encryption);
                break;
        }
        break;

    default:
        LOG_TRACE(ERROR,
            "Unknown security header type %u", security_header_type);
        LOG_FUNC_RETURN (0);
    }
    LOG_FUNC_RETURN (length);
}

/*
 * -----------------------------------------------------------------------------
 *	Functions used for integrity protection of layer 3 NAS messages
 * -----------------------------------------------------------------------------
 */

/****************************************************************************
 **                                                                        **
 ** Name:	 _nas_message_get_mac()                                        **
 **                                                                        **
 ** Description: Run integrity algorithm onto cyphered or uncyphered NAS   **
 **		 message encoded in the input buffer and return the compu-         **
 **		 ted message authentication code                                   **
 **                                                                        **
 ** Inputs	 buffer:	Pointer to the integrity protected data            **
 **				buffer                                                     **
 **		 count:		Value of the uplink NAS counter                        **
 **		 length:	Length of the input buffer                             **
 **	     direction                                                         **
 **		 Others:	None                                                   **
 **                                                                        **
 ** Outputs:	 None                                                      **
 ** 		 Return:	The message authentication code                    **
 **		 Others:	None                                                   **
 **                                                                        **
 ***************************************************************************/
static UInt32_t _nas_message_get_mac(
    const char                   * const buffer,
    int                            const length,
    int                            const direction,
    const emm_security_context_t * const emm_security_context)
{

    LOG_FUNC_IN;

    if (!emm_security_context) {
        LOG_TRACE(DEBUG,
            "No security context set for integrity protection algorithm");
#if defined(EPC_BUILD) || defined(UE_BUILD)
        LOG_FUNC_RETURN (0);
#else
        LOG_FUNC_RETURN (0xabababab);
#endif
    }

    switch (emm_security_context->selected_algorithms.integrity) {

        case NAS_SECURITY_ALGORITHMS_EIA1: {
            UInt8_t             mac[4];
            nas_stream_cipher_t stream_cipher;
            UInt32_t            count;
            UInt32_t           *mac32;

            int i,bytes = 0;

            if (direction == SECU_DIRECTION_UPLINK) {
                count = 0x00000000 ||
                    ((emm_security_context->ul_count.overflow && 0x0000FFFF) << 8) ||
                    (emm_security_context->ul_count.seq_num & 0x000000FF);
            } else {
                count = 0x00000000 ||
                    ((emm_security_context->dl_count.overflow && 0x0000FFFF) << 8) ||
                    (emm_security_context->dl_count.seq_num & 0x000000FF);
            }
            LOG_TRACE(DEBUG,
                "NAS_SECURITY_ALGORITHMS_EIA1 dir %s count.seq_num %u count %u",
                (direction == SECU_DIRECTION_UPLINK) ? "UPLINK":"DOWNLINK",
                (direction == SECU_DIRECTION_UPLINK) ? emm_security_context->ul_count.seq_num:emm_security_context->dl_count.seq_num,
                count);

        	fprintf(stderr, "\n[NAS]\t");

        	for (i=0; i < length; i++)
        	{
        	    fprintf(stderr, "%.2hx ", (const unsigned char) buffer[i]);
        	    /* Add new line when the number of displayed bytes exceeds
        	     * the line's size */
        	    if ( ++bytes > (16 - 1) ) {
        		bytes = 0;
        		fprintf(stderr, "\n[NAS]\t");
        	    }
        	}
        	if (bytes % 16) {
        	    fprintf(stderr, "\n");
        	}
        	fprintf(stderr, "\n");
        	fflush(stderr);

        	stream_cipher.key        = emm_security_context->knas_int.value;
            stream_cipher.key_length = AUTH_KNAS_INT_SIZE;
            stream_cipher.count      = count;
            stream_cipher.bearer     = 0x00; //33.401 section 8.1.1
            stream_cipher.direction  = direction;
            stream_cipher.message    = buffer;
                /* length in bits */
            stream_cipher.blength    = length << 3;

            nas_stream_encrypt_eia1(
                &stream_cipher,
                mac);
            LOG_TRACE(DEBUG,
                "NAS_SECURITY_ALGORITHMS_EIA1 returned MAC %x.%x.%x.%x(%u) for length %d direction %d, count %d",
                mac[0], mac[1], mac[2],mac[3],
                *((UInt32_t*)&mac),
                length,
                direction,
                count);
            mac32 = (UInt32_t*)&mac;
            LOG_FUNC_RETURN (ntohl(*mac32));
        }break;

        case NAS_SECURITY_ALGORITHMS_EIA2: {
                UInt8_t             mac[4];
                nas_stream_cipher_t stream_cipher;
                UInt32_t            count;
                UInt32_t           *mac32;

                if (direction == SECU_DIRECTION_UPLINK) {
                    count = 0x00000000 ||
                        ((emm_security_context->ul_count.overflow && 0x0000FFFF) << 8) ||
                        (emm_security_context->ul_count.seq_num & 0x000000FF);
                } else {
                    count = 0x00000000 ||
                        ((emm_security_context->dl_count.overflow && 0x0000FFFF) << 8) ||
                        (emm_security_context->dl_count.seq_num & 0x000000FF);
                }
                LOG_TRACE(DEBUG,
                    "NAS_SECURITY_ALGORITHMS_EIA2 dir %s count.seq_num %u count %u",
                    (direction == SECU_DIRECTION_UPLINK) ? "UPLINK":"DOWNLINK",
                    (direction == SECU_DIRECTION_UPLINK) ? emm_security_context->ul_count.seq_num:emm_security_context->dl_count.seq_num,
                    count);

                stream_cipher.key        = emm_security_context->knas_int.value;
                stream_cipher.key_length = AUTH_KNAS_INT_SIZE;
                stream_cipher.count      = count;
                stream_cipher.bearer     = 0x00; //33.401 section 8.1.1
                stream_cipher.direction  = direction;
                stream_cipher.message    = buffer;
                    /* length in bits */
                stream_cipher.blength    = length << 3;

                nas_stream_encrypt_eia2(
                    &stream_cipher,
                    mac);
                LOG_TRACE(DEBUG,
                    "NAS_SECURITY_ALGORITHMS_EIA2 returned MAC %x.%x.%x.%x(%u) for length %d direction %d, count %d",
                    mac[0], mac[1], mac[2],mac[3],
                    *((UInt32_t*)&mac),
                    length,
                    direction,
                    count);
                mac32 = (UInt32_t*)&mac;
                LOG_FUNC_RETURN (ntohl(*mac32));
            }break;

        case NAS_SECURITY_ALGORITHMS_EIA0:
            LOG_TRACE(DEBUG,
                "NAS_SECURITY_ALGORITHMS_EIA0 dir %s count.seq_num %u",
                (direction == SECU_DIRECTION_UPLINK) ? "UPLINK":"DOWNLINK",
                (direction == SECU_DIRECTION_UPLINK) ? emm_security_context->ul_count.seq_num:emm_security_context->dl_count.seq_num
           );

#if defined(EPC_BUILD) || defined(UE_BUILD)
            LOG_FUNC_RETURN (0);
#else
            LOG_FUNC_RETURN (0xabababab);
#endif
            break;

        default:
        	LOG_TRACE(ERROR,
              "Unknown integrity protection algorithm %d",
              emm_security_context->selected_algorithms.integrity);
        	break;
    }
    LOG_FUNC_RETURN (0);
}


