The main work of this commit is to rewrite cipher_aes_128_cbc_cmac() which was too slow and created issues when running the 4G eNB on some machines (LLL when using a b210). Instead of creating the security context, use it, and delete it, all in cipher_aes_128_cbc_cmac(), we introduce the function init_aes_128_cbc_cmac() to create it, and we change cipher_aes_128_cbc_cmac() to just use it, and we also adapt free_aes_128_cbc_cmac(). The function aes_128_cbc_cmac() is removed, it does not follow the new init/use/free API. Doing so, on the primary test machine used to do this work (a powerful and fast machine where it is possible to set the CPUs' frequencies) it was possible to limit the CPUs' frequencies to 500 MHz (it was not possible to go lower) without any LLL (using a b210, quectel UE in idle, no data traffic, only periodic measurement reports). Before this commit, it was necessary to set a frequency of at least 1.8 GHz otherwise some LLL would appear. Then we need to adapt the rest of the code to take into account those changes. A notion of security container (stream_security_container_t) containing ciphering and integrity contexts is introduced. And the code is harmonized for, hopefully, better readability. The (opaque) type stream_security_context_t is introduced, used by both integrity and ciphering contexts. The idea is to use a init()/use()/free() API for all the security procedures, identical for all the ciphering and integrity procedures.
1435 lines
58 KiB
C
1435 lines
58 KiB
C
/*
|
|
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
|
* contributor license agreements. See the NOTICE file distributed with
|
|
* this work for additional information regarding copyright ownership.
|
|
* The OpenAirInterface Software Alliance licenses this file to You under
|
|
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
|
|
* except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.openairinterface.org/?page_id=698
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*-------------------------------------------------------------------------------
|
|
* For more information about the OpenAirInterface (OAI) Software Alliance:
|
|
* contact@openairinterface.org
|
|
*/
|
|
|
|
/*****************************************************************************
|
|
|
|
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 = NULL;
|
|
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 (security) {
|
|
emm_security_context = (emm_security_context_t*)security;
|
|
#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 = NULL;
|
|
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 (security) {
|
|
emm_security_context = (emm_security_context_t*)security;
|
|
#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, (int)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
|
|
if (emm_security_context == NULL)
|
|
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 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);
|
|
(void) size; // prevent warning of unused variable, but we have to decode it
|
|
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.context = emm_security_context->security_container->ciphering_context;
|
|
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;
|
|
stream_compute_encrypt(EEA1_128_ALG_ID, &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);
|
|
(void) size; // prevent warning of unused variable, but we have to decode it
|
|
|
|
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.context = emm_security_context->security_container->ciphering_context;
|
|
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;
|
|
stream_compute_encrypt(EEA2_128_ALG_ID, &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);
|
|
(void) size; // prevent warning of unused variable, but we have to decode it
|
|
|
|
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);
|
|
(void) size; // prevent warning of unused variable, but we have to decode it
|
|
|
|
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);
|
|
(void) size; // prevent warning of unused variable, but we have to decode it
|
|
|
|
LOG_FUNC_RETURN (header.protocol_discriminator);
|
|
break;
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
LOG_TRACE(ERROR,
|
|
"Unknown security header type %u", security_header_type);
|
|
LOG_FUNC_RETURN (0);
|
|
};
|
|
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.context = emm_security_context->security_container->ciphering_context;
|
|
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;
|
|
stream_compute_encrypt(EEA1_128_ALG_ID, &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.context = emm_security_context->security_container->ciphering_context;
|
|
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;
|
|
stream_compute_encrypt(EEA2_128_ALG_ID, &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;
|
|
|
|
#ifdef DEBUG_NAS_MESSAGE
|
|
int i,bytes = 0;
|
|
#endif
|
|
|
|
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);
|
|
|
|
#ifdef DEBUG_NAS_MESSAGE
|
|
fprintf(stderr, "\n[NAS]\t");
|
|
|
|
for (i=0; i < length; i++) {
|
|
fprintf(stderr, "%.2hhx ", (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);
|
|
#endif
|
|
|
|
stream_cipher.context = emm_security_context->security_container->integrity_context;
|
|
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;
|
|
stream_compute_integrity(EIA1_128_ALG_ID, &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.context = emm_security_context->security_container->integrity_context;
|
|
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;
|
|
stream_compute_integrity(EIA2_128_ALG_ID, &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);
|
|
}
|
|
|
|
|