mirror of https://github.com/pvnis/srsRAN_4G.git
Added ue nas base class with security functions
parent
7fa85ab336
commit
505225d845
@ -0,0 +1,67 @@
|
||||
/**
|
||||
*
|
||||
* \section COPYRIGHT
|
||||
*
|
||||
* Copyright 2013-2021 Software Radio Systems Limited
|
||||
*
|
||||
* By using this file, you agree to the terms and conditions set
|
||||
* forth in the LICENSE file which can be found at the top level of
|
||||
* the distribution.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SRSUE_NAS_BASE_H
|
||||
#define SRSUE_NAS_BASE_H
|
||||
|
||||
#include "srsran/asn1/liblte_mme.h"
|
||||
#include "srsran/common/buffer_pool.h"
|
||||
#include "srsran/common/common.h"
|
||||
#include "srsran/common/nas_pcap.h"
|
||||
#include "srsran/common/security.h"
|
||||
#include "srsran/common/string_helpers.h"
|
||||
#include "srsran/config.h"
|
||||
|
||||
using srsran::byte_buffer_t;
|
||||
|
||||
namespace srsue {
|
||||
|
||||
class nas_base
|
||||
{
|
||||
public:
|
||||
nas_base(const std::string& type_);
|
||||
// PCAP
|
||||
void start_pcap(srsran::nas_pcap* pcap_) { pcap = pcap_; }
|
||||
|
||||
protected:
|
||||
srslog::basic_logger& logger;
|
||||
// PCAP
|
||||
srsran::nas_pcap* pcap = nullptr;
|
||||
|
||||
// Security context
|
||||
struct nas_sec_ctxt {
|
||||
uint8_t ksi;
|
||||
uint8_t k_asme[32];
|
||||
uint32_t tx_count;
|
||||
uint32_t rx_count;
|
||||
uint32_t k_enb_count;
|
||||
srsran::CIPHERING_ALGORITHM_ID_ENUM cipher_algo;
|
||||
srsran::INTEGRITY_ALGORITHM_ID_ENUM integ_algo;
|
||||
LIBLTE_MME_EPS_MOBILE_ID_GUTI_STRUCT guti;
|
||||
};
|
||||
|
||||
nas_sec_ctxt ctxt = {};
|
||||
uint8_t k_nas_enc[32] = {};
|
||||
uint8_t k_nas_int[32] = {};
|
||||
|
||||
int parse_security_algorithm_list(std::string algorithm_string, bool* algorithm_caps);
|
||||
|
||||
// Security
|
||||
void
|
||||
integrity_generate(uint8_t* key_128, uint32_t count, uint8_t direction, uint8_t* msg, uint32_t msg_len, uint8_t* mac);
|
||||
bool integrity_check(srsran::byte_buffer_t* pdu);
|
||||
void cipher_encrypt(srsran::byte_buffer_t* pdu);
|
||||
void cipher_decrypt(srsran::byte_buffer_t* pdu);
|
||||
};
|
||||
|
||||
} // namespace srsue
|
||||
#endif
|
@ -0,0 +1,238 @@
|
||||
/**
|
||||
*
|
||||
* \section COPYRIGHT
|
||||
*
|
||||
* Copyright 2013-2021 Software Radio Systems Limited
|
||||
*
|
||||
* By using this file, you agree to the terms and conditions set
|
||||
* forth in the LICENSE file which can be found at the top level of
|
||||
* the distribution.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "srsue/hdr/stack/upper/nas_base.h"
|
||||
|
||||
using namespace srsran;
|
||||
namespace srsue {
|
||||
nas_base::nas_base(const std::string& type_) : logger(srslog::fetch_basic_logger(type_)) {}
|
||||
|
||||
int nas_base::parse_security_algorithm_list(std::string algorithm_string, bool* algorithm_caps)
|
||||
{
|
||||
// parse and sanity check security algorithm list
|
||||
std::vector<uint8_t> cap_list;
|
||||
srsran::string_parse_list(algorithm_string, ',', cap_list);
|
||||
if (cap_list.empty()) {
|
||||
logger.error("Empty security list. Select at least one security algorithm.");
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
for (std::vector<uint8_t>::const_iterator it = cap_list.begin(); it != cap_list.end(); ++it) {
|
||||
if (*it < 4) {
|
||||
algorithm_caps[*it] = true;
|
||||
} else {
|
||||
logger.error("EEA/EIA/5G-EA/5G-IA %d is not a valid algorithm.", *it);
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
}
|
||||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* Security
|
||||
******************************************************************************/
|
||||
|
||||
void nas_base::integrity_generate(uint8_t* key_128,
|
||||
uint32_t count,
|
||||
uint8_t direction,
|
||||
uint8_t* msg,
|
||||
uint32_t msg_len,
|
||||
uint8_t* mac)
|
||||
{
|
||||
switch (ctxt.integ_algo) {
|
||||
case INTEGRITY_ALGORITHM_ID_EIA0:
|
||||
break;
|
||||
case INTEGRITY_ALGORITHM_ID_128_EIA1:
|
||||
security_128_eia1(key_128,
|
||||
count,
|
||||
0, // Bearer always 0 for NAS
|
||||
direction,
|
||||
msg,
|
||||
msg_len,
|
||||
mac);
|
||||
break;
|
||||
case INTEGRITY_ALGORITHM_ID_128_EIA2:
|
||||
security_128_eia2(key_128,
|
||||
count,
|
||||
0, // Bearer always 0 for NAS
|
||||
direction,
|
||||
msg,
|
||||
msg_len,
|
||||
mac);
|
||||
break;
|
||||
case INTEGRITY_ALGORITHM_ID_128_EIA3:
|
||||
security_128_eia3(key_128,
|
||||
count,
|
||||
0, // Bearer always 0 for NAS
|
||||
direction,
|
||||
msg,
|
||||
msg_len,
|
||||
mac);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// This function depends to a valid k_nas_int.
|
||||
// This key is generated in the security mode command.
|
||||
bool nas_base::integrity_check(byte_buffer_t* pdu)
|
||||
{
|
||||
if (pdu == nullptr) {
|
||||
logger.error("Invalid PDU");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pdu->N_bytes > 5) {
|
||||
uint8_t exp_mac[4] = {0};
|
||||
uint8_t* mac = &pdu->msg[1];
|
||||
|
||||
// generate expected MAC
|
||||
uint32_t count_est = (ctxt.rx_count & 0x00FFFF00u) | pdu->msg[5];
|
||||
integrity_generate(
|
||||
&k_nas_int[16], count_est, SECURITY_DIRECTION_DOWNLINK, &pdu->msg[5], pdu->N_bytes - 5, &exp_mac[0]);
|
||||
|
||||
// Check if expected mac equals the sent mac
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (exp_mac[i] != mac[i]) {
|
||||
logger.warning("Integrity check failure. Local: count=%d, [%02x %02x %02x %02x], "
|
||||
"Received: count=%d, [%02x %02x %02x %02x]",
|
||||
count_est,
|
||||
exp_mac[0],
|
||||
exp_mac[1],
|
||||
exp_mac[2],
|
||||
exp_mac[3],
|
||||
pdu->msg[5],
|
||||
mac[0],
|
||||
mac[1],
|
||||
mac[2],
|
||||
mac[3]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
logger.info("Integrity check ok. Local: count=%d, Received: count=%d [%02x %02x %02x %02x]",
|
||||
count_est,
|
||||
pdu->msg[5],
|
||||
mac[0],
|
||||
mac[1],
|
||||
mac[2],
|
||||
mac[3]);
|
||||
|
||||
// Updated local count (according to TS 24.301 Sec. 4.4.3.3)
|
||||
if (count_est != ctxt.rx_count) {
|
||||
logger.info("Update local count to estimated count %d", count_est);
|
||||
ctxt.rx_count = count_est;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
logger.error("Invalid integrity check PDU size (%d)", pdu->N_bytes);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void nas_base::cipher_encrypt(byte_buffer_t* pdu)
|
||||
{
|
||||
byte_buffer_t pdu_tmp;
|
||||
|
||||
if (ctxt.cipher_algo != CIPHERING_ALGORITHM_ID_EEA0) {
|
||||
logger.debug("Encrypting PDU. count=%d", ctxt.tx_count);
|
||||
}
|
||||
|
||||
switch (ctxt.cipher_algo) {
|
||||
case CIPHERING_ALGORITHM_ID_EEA0:
|
||||
break;
|
||||
case CIPHERING_ALGORITHM_ID_128_EEA1:
|
||||
security_128_eea1(&k_nas_enc[16],
|
||||
ctxt.tx_count,
|
||||
0, // Bearer always 0 for NAS
|
||||
SECURITY_DIRECTION_UPLINK,
|
||||
&pdu->msg[6],
|
||||
pdu->N_bytes - 6,
|
||||
&pdu_tmp.msg[6]);
|
||||
memcpy(&pdu->msg[6], &pdu_tmp.msg[6], pdu->N_bytes - 6);
|
||||
break;
|
||||
case CIPHERING_ALGORITHM_ID_128_EEA2:
|
||||
security_128_eea2(&k_nas_enc[16],
|
||||
ctxt.tx_count,
|
||||
0, // Bearer always 0 for NAS
|
||||
SECURITY_DIRECTION_UPLINK,
|
||||
&pdu->msg[6],
|
||||
pdu->N_bytes - 6,
|
||||
&pdu_tmp.msg[6]);
|
||||
memcpy(&pdu->msg[6], &pdu_tmp.msg[6], pdu->N_bytes - 6);
|
||||
break;
|
||||
case CIPHERING_ALGORITHM_ID_128_EEA3:
|
||||
security_128_eea3(&k_nas_enc[16],
|
||||
ctxt.tx_count,
|
||||
0, // Bearer always 0 for NAS
|
||||
SECURITY_DIRECTION_UPLINK,
|
||||
&pdu->msg[6],
|
||||
pdu->N_bytes - 6,
|
||||
&pdu_tmp.msg[6]);
|
||||
memcpy(&pdu->msg[6], &pdu_tmp.msg[6], pdu->N_bytes - 6);
|
||||
break;
|
||||
default:
|
||||
logger.error("Ciphering algorithm not known");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void nas_base::cipher_decrypt(byte_buffer_t* pdu)
|
||||
{
|
||||
byte_buffer_t tmp_pdu;
|
||||
|
||||
uint32_t count_est = (ctxt.rx_count & 0x00FFFF00u) | pdu->msg[5];
|
||||
if (ctxt.cipher_algo != CIPHERING_ALGORITHM_ID_EEA0) {
|
||||
logger.debug("Decrypting PDU. Local: count=%d, Received: count=%d", ctxt.rx_count, count_est);
|
||||
}
|
||||
|
||||
switch (ctxt.cipher_algo) {
|
||||
case CIPHERING_ALGORITHM_ID_EEA0:
|
||||
break;
|
||||
case CIPHERING_ALGORITHM_ID_128_EEA1:
|
||||
security_128_eea1(&k_nas_enc[16],
|
||||
count_est,
|
||||
0, // Bearer always 0 for NAS
|
||||
SECURITY_DIRECTION_DOWNLINK,
|
||||
&pdu->msg[6],
|
||||
pdu->N_bytes - 6,
|
||||
&tmp_pdu.msg[6]);
|
||||
memcpy(&pdu->msg[6], &tmp_pdu.msg[6], pdu->N_bytes - 6);
|
||||
break;
|
||||
case CIPHERING_ALGORITHM_ID_128_EEA2:
|
||||
security_128_eea2(&k_nas_enc[16],
|
||||
count_est,
|
||||
0, // Bearer always 0 for NAS
|
||||
SECURITY_DIRECTION_DOWNLINK,
|
||||
&pdu->msg[6],
|
||||
pdu->N_bytes - 6,
|
||||
&tmp_pdu.msg[6]);
|
||||
logger.debug(tmp_pdu.msg, pdu->N_bytes, "Decrypted");
|
||||
memcpy(&pdu->msg[6], &tmp_pdu.msg[6], pdu->N_bytes - 6);
|
||||
break;
|
||||
case CIPHERING_ALGORITHM_ID_128_EEA3:
|
||||
security_128_eea3(&k_nas_enc[16],
|
||||
count_est,
|
||||
0, // Bearer always 0 for NAS
|
||||
SECURITY_DIRECTION_DOWNLINK,
|
||||
&pdu->msg[6],
|
||||
pdu->N_bytes - 6,
|
||||
&tmp_pdu.msg[6]);
|
||||
logger.debug(tmp_pdu.msg, pdu->N_bytes, "Decrypted");
|
||||
memcpy(&pdu->msg[6], &tmp_pdu.msg[6], pdu->N_bytes - 6);
|
||||
break;
|
||||
default:
|
||||
logger.error("Ciphering algorithms not known");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace srsue
|
Loading…
Reference in New Issue