/*
 * Copyright (c) 2015, EURECOM (www.eurecom.fr)
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * The views and conclusions contained in the software and documentation are those
 * of the authors and should not be interpreted as representing official policies,
 * either expressed or implied, of the FreeBSD Project.
 */
/*****************************************************************************

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(NAS_BUILT_IN_EPC) && defined(NAS_MME)) || (defined(ENABLE_NAS_UE_LOGGING) && defined(NAS_BUILT_IN_UE) && 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 (emm_security_context) {
#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) Type of security context %u",
                header->message_authentication_code,header->message_authentication_code,
                mac,mac, (emm_security_context!=NULL)?emm_security_context->type:88);
#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
                   );

#define NAS_CODE_TO_BE_MODIFIED 1
#ifdef NAS_CODE_TO_BE_MODIFIED

    // According to 3GPP TS 24.301 version 10.15.0 Release 10, 4.4.4.3 Integrity checking of NAS signalling messages in the MME
    if ((!emm_security_context) && (mac == 0) && (msg->header.message_authentication_code != 0)) {
      // force mac to be the same, but we should check for message types.
      // TODO Reverse order of processing in NAS code: decode message type then check MAC
      LOG_TRACE(DEBUG,
                "Forced computed MAC to be the MSG MAC %04x",
                msg->header.message_authentication_code);
      mac = msg->header.message_authentication_code;
      LOG_TRACE(DEBUG,
                "Forced computed MAC to be the same as MAC in message %04x",
                msg->header.message_authentication_code);
    }

#endif

    /* 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(NAS_BUILT_IN_EPC) && defined(NAS_MME)) || (defined(ENABLE_NAS_UE_LOGGING) && defined(NAS_BUILT_IN_UE) && 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(NAS_BUILT_IN_EPC) && defined(NAS_MME)) || (defined(ENABLE_NAS_UE_LOGGING) && defined(NAS_BUILT_IN_UE) && 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*)calloc(1,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*)calloc(1,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 length %u according to security header type 0x%02x",
              length, security_header_type);
    memcpy(dest, src, length);
    DECODE_U8(dest, *(uint8_t*)(&header), size);
    LOG_FUNC_RETURN (header.protocol_discriminator);
    //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(NAS_BUILT_IN_EPC) || defined(NAS_BUILT_IN_UE)
    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(NAS_BUILT_IN_EPC) || defined(NAS_BUILT_IN_UE)
    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);
}


