git-svn-id: http://svn.eurecom.fr/openair4G/trunk@7886 818b1a75-f10b-46b9-bf7c-635c3b92a50f
1437 lines
58 KiB
C
1437 lines
58 KiB
C
/*
|
|
* 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);
|
|
}
|
|
|
|
|