- Added new feature: Oaisim Security Algorithms (see README in openair2/UTIL/OSA/README
- Fix uint64_t formatting in openair_proc.c - Removed duplicate call to pdcp_fifo_read_input_sdus pre-ci tests passed git-svn-id: http://svn.eurecom.fr/openair4G/trunk@4158 818b1a75-f10b-46b9-bf7c-635c3b92a50f
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
OAISIM Security Algorithms v0.1
|
||||
|
||||
Dependencies:
|
||||
- nettle >= 2.5
|
||||
- openssl >= 1.0.1
|
||||
|
||||
Compilation:
|
||||
|
||||
When compiling oaisim, add SECU=1 to enable security features. The makefile will
|
||||
check for required dependencies and disable this feature if not all dependencies
|
||||
are met.
|
||||
|
||||
OSA is based on 3GPP TS 33.401.
|
||||
Note that only EEA2 and EIA2 algorithms are supported, SNOW-3G is not implemented.
|
||||
|
||||
Currently the kenb used to derive kRRCint, kRRCenc and kUPenc is composed of the
|
||||
UE_id. In the future this parameter can be either obtained by deriving KASME received
|
||||
from core epc (in the S1AP initial context setup request)
|
||||
or configurable via command line or XML file.
|
||||
@@ -0,0 +1,129 @@
|
||||
/*******************************************************************************
|
||||
|
||||
Eurecom OpenAirInterface
|
||||
Copyright(c) 1999 - 2013 Eurecom
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it
|
||||
under the terms and conditions of the GNU General Public License,
|
||||
version 2, as published by the Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
The full GNU General Public License is included in this distribution in
|
||||
the file called "COPYING".
|
||||
|
||||
Contact Information
|
||||
Openair Admin: openair_admin@eurecom.fr
|
||||
Openair Tech : openair_tech@eurecom.fr
|
||||
Forums : http://forums.eurecom.fr/openairinterface
|
||||
Address : EURECOM, Campus SophiaTech, 450 Route des Chappes
|
||||
06410 Biot FRANCE
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
/*!
|
||||
* \file secu_defs.h
|
||||
* \brief Openair security algorithms functions implementing 3GPP TS 33.401
|
||||
* \note HMAC computations are done by nettle/openssl library
|
||||
* \author Sebastien ROUX
|
||||
* \date 2013
|
||||
* \version 0.1
|
||||
* @ingroup security
|
||||
*/
|
||||
|
||||
#ifndef SECU_DEFS_H_
|
||||
#define SECU_DEFS_H_
|
||||
|
||||
#define EIA0_ALG_ID 0x00
|
||||
#define EIA1_128_ALG_ID 0x01
|
||||
#define EIA2_128_ALG_ID 0x02
|
||||
|
||||
#define EEA0_ALG_ID EIA0_ALG_ID
|
||||
#define EEA1_128_ALG_ID EIA1_128_ALG_ID
|
||||
#define EEA2_128_ALG_ID EIA2_128_ALG_ID
|
||||
|
||||
#define SECU_DIRECTION_UPLINK 0
|
||||
#define SECU_DIRECTION_DOWNLINK 1
|
||||
|
||||
typedef enum {
|
||||
NAS_ENC_ALG = 0x01,
|
||||
NAS_INT_ALG = 0x02,
|
||||
RRC_ENC_ALG = 0x03,
|
||||
RRC_INT_ALG = 0x04,
|
||||
UP_ENC_ALG = 0x05
|
||||
} algorithm_type_dist_t;
|
||||
|
||||
int derive_keNB(const uint8_t kasme[32], const uint32_t nas_count, uint8_t **keNB);
|
||||
|
||||
int derive_key(algorithm_type_dist_t nas_alg_type, uint8_t nas_enc_alg_id,
|
||||
const uint8_t key[32], uint8_t **out);
|
||||
|
||||
#define derive_key_nas_enc(aLGiD, kEY, kNAS) \
|
||||
derive_key(NAS_ENC_ALG, aLGiD, kEY, kNAS)
|
||||
|
||||
#define derive_key_nas_int(aLGiD, kEY, kNAS) \
|
||||
derive_key(NAS_INT_ALG, aLGiD, kEY, kNAS)
|
||||
|
||||
#define derive_key_rrc_enc(aLGiD, kEY, kNAS) \
|
||||
derive_key(RRC_ENC_ALG, aLGiD, kEY, kNAS)
|
||||
|
||||
#define derive_key_rrc_int(aLGiD, kEY, kNAS) \
|
||||
derive_key(RRC_INT_ALG, aLGiD, kEY, kNAS)
|
||||
|
||||
#define derive_key_up_enc(aLGiD, kEY, kNAS) \
|
||||
derive_key(UP_ENC_ALG, aLGiD, kEY, kNAS)
|
||||
|
||||
#define derive_key_up_int(aLGiD, kEY, kNAS) \
|
||||
derive_key(UP_INT_ALG, aLGiD, kEY, kNAS)
|
||||
|
||||
typedef struct {
|
||||
uint8_t *key;
|
||||
uint32_t key_length;
|
||||
uint32_t count;
|
||||
uint8_t bearer;
|
||||
uint8_t direction;
|
||||
uint8_t *message;
|
||||
/* length in bits */
|
||||
uint32_t blength;
|
||||
} stream_cipher_t;
|
||||
|
||||
/*!
|
||||
* @brief Encrypt/Decrypt a block of data based on the provided algorithm
|
||||
* @param[in] algorithm Algorithm used to encrypt the data
|
||||
* Possible values are:
|
||||
* - EIA0_ALG_ID for NULL encryption
|
||||
* - EIA1_128_ALG_ID for SNOW-3G encryption (not avalaible right now)
|
||||
* - EIA2_128_ALG_ID for 128 bits AES LTE encryption
|
||||
* @param[in] stream_cipher All parameters used to compute the encrypted block of data
|
||||
* @param[out] out The encrypted block of data dynamically allocated
|
||||
* @return 0 if everything OK, -1 if something failed
|
||||
*/
|
||||
int stream_encrypt(uint8_t algorithm, stream_cipher_t *stream_cipher, uint8_t **out);
|
||||
#define stream_decrypt stream_encrypt
|
||||
|
||||
int stream_compute_integrity(uint8_t algorithm, stream_cipher_t *stream_cipher, uint8_t out[4]);
|
||||
|
||||
/*!
|
||||
* @brief Decrypt a block of data based on the provided algorithm
|
||||
* @param[in] algorithm Algorithm used to encrypt the data
|
||||
* Possible values are:
|
||||
* - EIA0_ALG_ID for NULL encryption
|
||||
* - EIA1_128_ALG_ID for SNOW-3G encryption (not avalaible right now)
|
||||
* - EIA2_128_ALG_ID for 128 bits AES LTE encryption
|
||||
* @param[in] stream_cipher All parameters used to compute the decrypted block of data
|
||||
* @param[out] out The decrypted block of data dynamically allocated
|
||||
* @return 0 if everything OK, -1 if something failed
|
||||
*/
|
||||
int stream_decrypt(uint8_t algorithm, stream_cipher_t *stream_cipher, uint8_t **out);
|
||||
|
||||
int stream_check_integrity(uint8_t algorithm, stream_cipher_t *stream_cipher, uint8_t *expected);
|
||||
#undef SECU_DEBUG
|
||||
|
||||
#endif /* SECU_DEFS_H_ */
|
||||
@@ -0,0 +1,26 @@
|
||||
#ifndef OSA_INTERNAL_H_
|
||||
#define OSA_INTERNAL_H_
|
||||
|
||||
#define FC_KENB (0x11)
|
||||
#define FC_NH (0x12)
|
||||
#define FC_KENB_STAR (0x13)
|
||||
/* 33401 #A.7 Algorithm for key derivation function.
|
||||
* This FC should be used for:
|
||||
* - NAS Encryption algorithm
|
||||
* - NAS Integrity algorithm
|
||||
* - RRC Encryption algorithm
|
||||
* - RRC Integrity algorithm
|
||||
* - User Plane Encryption algorithm
|
||||
*/
|
||||
#define FC_ALG_KEY_DER (0x15)
|
||||
#define FC_KASME_TO_CK (0x16)
|
||||
|
||||
#ifndef hton_int32
|
||||
# define hton_int32(x) \
|
||||
(((x & 0x000000FF) << 24) | ((x & 0x0000FF00) << 8) | \
|
||||
((x & 0x00FF0000) >> 8) | ((x & 0xFF000000) >> 24))
|
||||
#endif
|
||||
|
||||
#define SECU_DEBUG
|
||||
|
||||
#endif /* OSA_INTERNAL_H_ */
|
||||
@@ -0,0 +1,113 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include <nettle/hmac.h>
|
||||
|
||||
#include "osa_defs.h"
|
||||
#include "osa_internal.h"
|
||||
#include "UTIL/LOG/log.h"
|
||||
|
||||
static inline
|
||||
void kdf(const uint8_t *s, const uint32_t s_length, const uint8_t *key,
|
||||
const uint32_t key_length, uint8_t **out, uint32_t out_length)
|
||||
{
|
||||
struct hmac_sha256_ctx ctx;
|
||||
|
||||
uint8_t *buffer;
|
||||
|
||||
buffer = malloc(sizeof(uint8_t) * out_length);
|
||||
|
||||
hmac_sha256_set_key(&ctx, key_length, key);
|
||||
hmac_sha256_update(&ctx, s_length, s);
|
||||
hmac_sha256_digest(&ctx, out_length, buffer);
|
||||
|
||||
*out = buffer;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Derive the keys from kasme and perform truncate on the generated key to
|
||||
* reduce his size to 128 bits. Definition of the derivation function can
|
||||
* be found in 3GPP TS.33401 #A.7
|
||||
* @param[in] alg_type Algorithm distinguisher
|
||||
* @param[in] alg_id Algorithm identifier.
|
||||
* Possible values are:
|
||||
* - 0 for EIA0 algorithm (Null Integrity Protection algorithm)
|
||||
* - 1 for 128-EIA1 SNOW 3G
|
||||
* - 2 for 128-EIA2 AES
|
||||
* @param[in] key The top key used to derive other subkeys
|
||||
* @param[out] out Pointer to reference where output of KDF will be stored.
|
||||
* NOTE: knas is dynamically allocated by the KDF function
|
||||
*/
|
||||
int derive_key(algorithm_type_dist_t alg_type, uint8_t alg_id,
|
||||
const uint8_t key[32], uint8_t **out)
|
||||
{
|
||||
uint8_t string[7];
|
||||
#if defined(SECU_DEBUG)
|
||||
int i;
|
||||
#endif
|
||||
|
||||
/* FC */
|
||||
string[0] = FC_ALG_KEY_DER;
|
||||
|
||||
/* P0 = algorithm type distinguisher */
|
||||
string[1] = (uint8_t)(alg_type & 0xFF);
|
||||
|
||||
/* L0 = length(P0) = 1 */
|
||||
string[2] = 0x00;
|
||||
string[3] = 0x01;
|
||||
|
||||
/* P1 */
|
||||
string[4] = alg_id;
|
||||
|
||||
/* L1 = length(P1) = 1 */
|
||||
string[5] = 0x00;
|
||||
string[6] = 0x01;
|
||||
|
||||
#if defined(SECU_DEBUG)
|
||||
{
|
||||
int i;
|
||||
char payload[6 * sizeof(string) + 1];
|
||||
int index = 0;
|
||||
|
||||
for (i = 0; i < sizeof(string); i++)
|
||||
index += sprintf(&payload[index], "0x%02x ", string[i]);
|
||||
LOG_D(OSA, "Key deriver input string: %s\n", payload);
|
||||
}
|
||||
#endif
|
||||
|
||||
kdf(string, 7, key, 32, out, 32);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int derive_keNB(const uint8_t kasme[32], const uint32_t nas_count, uint8_t **keNB)
|
||||
{
|
||||
uint8_t string[7];
|
||||
|
||||
/* FC */
|
||||
string[0] = FC_KENB;
|
||||
/* P0 = Uplink NAS count */
|
||||
string[1] = (nas_count & 0xff000000) >> 24;
|
||||
string[2] = (nas_count & 0x00ff0000) >> 16;
|
||||
string[3] = (nas_count & 0x0000ff00) >> 8;
|
||||
string[4] = (nas_count & 0x000000ff);
|
||||
|
||||
/* Length of NAS count */
|
||||
string[5] = 0x00;
|
||||
string[6] = 0x04;
|
||||
|
||||
#if defined(SECU_DEBUG)
|
||||
{
|
||||
int i;
|
||||
char payload[6 * sizeof(string) + 1];
|
||||
int index = 0;
|
||||
|
||||
for (i = 0; i < sizeof(string); i++)
|
||||
index += sprintf(&payload[index], "0x%02x ", string[i]);
|
||||
LOG_D(OSA, "KeNB deriver input string: %s\n", payload);
|
||||
}
|
||||
#endif
|
||||
|
||||
kdf(string, 7, kasme, 32, keNB, 32);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <nettle/nettle-meta.h>
|
||||
#include <nettle/aes.h>
|
||||
#include <nettle/ctr.h>
|
||||
|
||||
#include "UTIL/LOG/log.h"
|
||||
|
||||
#include "assertions.h"
|
||||
#include "osa_defs.h"
|
||||
#include "osa_internal.h"
|
||||
|
||||
int stream_encrypt_eea2(stream_cipher_t *stream_cipher, uint8_t **out)
|
||||
{
|
||||
uint8_t m[16];
|
||||
uint32_t local_count;
|
||||
|
||||
void *ctx;
|
||||
uint8_t *data = NULL;
|
||||
|
||||
uint32_t zero_bit = 0;
|
||||
uint32_t byte_length;
|
||||
|
||||
DevAssert(stream_cipher != NULL);
|
||||
DevAssert(out != NULL);
|
||||
|
||||
LOG_D(OSA, "Entering stream_encrypt_eea2, bits length %u, bearer %u, "
|
||||
"count %u, direction %s\n", stream_cipher->blength,
|
||||
stream_cipher->bearer, stream_cipher->count, stream_cipher->direction == SECU_DIRECTION_DOWNLINK ?
|
||||
"Downlink" : "Uplink");
|
||||
|
||||
zero_bit = stream_cipher->blength & 0x7;
|
||||
|
||||
byte_length = stream_cipher->blength >> 3;
|
||||
if (zero_bit > 0)
|
||||
byte_length += 1;
|
||||
|
||||
ctx = malloc(nettle_aes128.context_size);
|
||||
|
||||
if (*out == NULL) {
|
||||
/* User provided output buffer */
|
||||
data = malloc(byte_length);
|
||||
*out = data;
|
||||
} else {
|
||||
data = *out;
|
||||
}
|
||||
|
||||
local_count = hton_int32(stream_cipher->count);
|
||||
|
||||
memset(m, 0, sizeof(m));
|
||||
memcpy(&m[0], &local_count, 4);
|
||||
m[4] = ((stream_cipher->bearer & 0x1F) << 3) |
|
||||
((stream_cipher->direction & 0x01) << 2);
|
||||
/* Other bits are 0 */
|
||||
|
||||
#if defined(SECU_DEBUG)
|
||||
{
|
||||
int i;
|
||||
char payload[6 * byte_length + 1];
|
||||
int index = 0;
|
||||
|
||||
for (i = 0; i < byte_length; i++)
|
||||
index += sprintf(&payload[index], "0x%02x ", stream_cipher->message[i]);
|
||||
LOG_D(OSA, "Original message: %s\n", payload);
|
||||
}
|
||||
#endif
|
||||
|
||||
nettle_aes128.set_encrypt_key(ctx, stream_cipher->key_length,
|
||||
stream_cipher->key);
|
||||
|
||||
nettle_ctr_crypt(ctx, nettle_aes128.encrypt,
|
||||
nettle_aes128.block_size, m,
|
||||
byte_length, data, stream_cipher->message);
|
||||
|
||||
if (zero_bit > 0)
|
||||
data[byte_length - 1] = data[byte_length - 1] & (uint8_t)(0xFF << (8 - zero_bit));
|
||||
|
||||
free(ctx);
|
||||
|
||||
#if defined(SECU_DEBUG)
|
||||
{
|
||||
int i;
|
||||
char payload[6 * byte_length + 1];
|
||||
int index = 0;
|
||||
|
||||
for (i = 0; i < byte_length; i++)
|
||||
index += sprintf(&payload[index], "0x%02x ", data[i]);
|
||||
LOG_D(OSA, "Encrypted message: %s\n", payload);
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int stream_encrypt(uint8_t algorithm, stream_cipher_t *stream_cipher, uint8_t **out)
|
||||
{
|
||||
if (algorithm == EEA0_ALG_ID) {
|
||||
// return stream_encrypt_eea0(stream_cipher, out);
|
||||
} else if (algorithm == EEA1_128_ALG_ID) {
|
||||
LOG_E(OSA, "SNOW-3G algorithms are currently not implemented\n");
|
||||
return -1;
|
||||
} else if (algorithm == EEA2_128_ALG_ID) {
|
||||
return stream_encrypt_eea2(stream_cipher, out);
|
||||
}
|
||||
LOG_E(OSA, "Provided algorithm is currently not supported = %u\n", algorithm);
|
||||
return -1;
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "assertions.h"
|
||||
|
||||
#include <openssl/aes.h>
|
||||
#include <openssl/cmac.h>
|
||||
#include <openssl/evp.h>
|
||||
|
||||
#include "UTIL/LOG/log.h"
|
||||
|
||||
#include "osa_defs.h"
|
||||
#include "osa_internal.h"
|
||||
|
||||
/*!
|
||||
* @brief Create integrity cmac t for a given message.
|
||||
* @param[in] stream_cipher Structure containing various variables to setup encoding
|
||||
* @param[out] out For EIA2 the output string is 32 bits long
|
||||
*/
|
||||
int stream_compute_integrity_eia2(stream_cipher_t *stream_cipher, uint8_t out[4])
|
||||
{
|
||||
uint8_t *m;
|
||||
uint32_t local_count;
|
||||
size_t size = 4;
|
||||
|
||||
uint8_t data[16];
|
||||
|
||||
CMAC_CTX *cmac_ctx;
|
||||
|
||||
uint32_t zero_bit = 0;
|
||||
uint32_t m_length;
|
||||
|
||||
DevAssert(stream_cipher != NULL);
|
||||
DevAssert(stream_cipher->key != NULL);
|
||||
|
||||
memset(data, 0, 16);
|
||||
|
||||
zero_bit = stream_cipher->blength & 0x7;
|
||||
|
||||
m_length = stream_cipher->blength >> 3;
|
||||
if (zero_bit > 0)
|
||||
m_length += 1;
|
||||
|
||||
local_count = hton_int32(stream_cipher->count);
|
||||
|
||||
m = calloc(m_length + 8, sizeof(uint8_t));
|
||||
|
||||
memcpy(&m[0], &local_count, 4);
|
||||
m[4] = ((stream_cipher->bearer & 0x1F) << 3) | ((stream_cipher->direction & 0x01) << 2);
|
||||
|
||||
memcpy(&m[8], stream_cipher->message, m_length);
|
||||
|
||||
#if defined(SECU_DEBUG)
|
||||
{
|
||||
int i;
|
||||
char payload[6 * sizeof(m) + 1];
|
||||
int index = 0;
|
||||
LOG_T(OSA, "Blength: %u, Zero bits: %u\n",
|
||||
stream_cipher->blength, zero_bit);
|
||||
for (i = 0; i < sizeof(m); i++)
|
||||
index += sprintf(&payload[index], "0x%02x ", m[i]);
|
||||
LOG_T(OSA, "Payload: %s\n", payload);
|
||||
}
|
||||
#endif
|
||||
|
||||
cmac_ctx = CMAC_CTX_new();
|
||||
|
||||
CMAC_Init(cmac_ctx, stream_cipher->key, stream_cipher->key_length, EVP_aes_128_cbc(), NULL);
|
||||
CMAC_Update(cmac_ctx, m, m_length + 8);
|
||||
|
||||
CMAC_Final(cmac_ctx, data, &size);
|
||||
|
||||
CMAC_CTX_free(cmac_ctx);
|
||||
|
||||
memcpy(out, data, 4);
|
||||
free(m);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int stream_compute_integrity(uint8_t algorithm, stream_cipher_t *stream_cipher, uint8_t out[4])
|
||||
{
|
||||
if (algorithm == EIA0_ALG_ID) {
|
||||
// return stream_encrypt_eea0(stream_cipher, out);
|
||||
} else if (algorithm == EIA1_128_ALG_ID) {
|
||||
LOG_E(OSA, "SNOW-3G algorithms are currently not implemented\n");
|
||||
return -1;
|
||||
} else if (algorithm == EIA2_128_ALG_ID) {
|
||||
return stream_compute_integrity_eia2(stream_cipher, out);
|
||||
}
|
||||
LOG_E(OSA, "Provided algorithm is currently not supported = %u\n", algorithm);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int stream_check_integrity(uint8_t algorithm, stream_cipher_t *stream_cipher, uint8_t *expected)
|
||||
{
|
||||
uint8_t result[4];
|
||||
|
||||
if (stream_compute_integrity(algorithm, stream_cipher, result) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (memcmp(result, expected, 4) != 0) {
|
||||
LOG_E(OSA, "Mismatch found in integrity for algorithm %u,\n"
|
||||
"\tgot %02x.%02x.%02x.%02x, expecting %02x.%02x.%02x.%02x\n",
|
||||
algorithm, result[0], result[1], result[2], result[3], expected[0],
|
||||
expected[1], expected[2], expected[3]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Integrity verification succeeded */
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user