Something went wrong on our end
Forked from
oai / openairinterface5G
25282 commits behind the upstream repository.
-
Cédric Roux authoredCédric Roux authored
nas_message.c 58.05 KiB
/*
* 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 = (unsigned char *)src;
/* length in bits */
stream_cipher.blength = length << 3;
nas_stream_encrypt_eea1(&stream_cipher, (unsigned char *)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 = (unsigned char *)src;
/* length in bits */
stream_cipher.blength = length << 3;
nas_stream_encrypt_eea1(&stream_cipher, (unsigned char *)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 = (unsigned char *)src;
/* length in bits */
stream_cipher.blength = length << 3;
nas_stream_encrypt_eea1(&stream_cipher, (unsigned char *)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 = (unsigned char *)src;
/* length in bits */
stream_cipher.blength = length << 3;
nas_stream_encrypt_eea2(&stream_cipher, (unsigned char *)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 = (unsigned char *)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 = (unsigned char *)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);
}