Added NR security context manager to RRC NR

This commit is contained in:
Pedro Alvarez 2021-11-17 15:43:58 +00:00
parent 37f8dba4ac
commit e294311034
7 changed files with 307 additions and 6 deletions

View File

@ -53,6 +53,9 @@ struct rrc_nr_cfg_t {
rrc_cell_list_nr_t cell_list;
bool is_standalone;
std::array<srsran::CIPHERING_ALGORITHM_ID_ENUM, srsran::CIPHERING_ALGORITHM_ID_N_ITEMS> eea_preference_list;
std::array<srsran::INTEGRITY_ALGORITHM_ID_ENUM, srsran::INTEGRITY_ALGORITHM_ID_N_ITEMS> eia_preference_list;
std::string log_name = "RRC-NR";
std::string log_level;
uint32_t log_hex_limit;

View File

@ -0,0 +1,74 @@
/**
*
* \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 SRSRAN_RRC_NR_SECURITY_CONTEXT_H
#define SRSRAN_RRC_NR_SECURITY_CONTEXT_H
#include "srsgnb/hdr/stack/rrc/rrc_nr_config.h"
#include "srsran/asn1/ngap.h"
#include "srsran/interfaces/gnb_interfaces.h"
#include "srsran/srslog/srslog.h"
namespace srsgnb {
class nr_security_context
{
public:
explicit nr_security_context(const srsenb::rrc_nr_cfg_t& cfg_) :
cfg(&cfg_), logger(srslog::fetch_basic_logger("RRC_NR"))
{}
explicit nr_security_context(const nr_security_context& other) : logger(srslog::fetch_basic_logger("RRC_NR"))
{
cfg = other.cfg;
k_gnb_present = other.k_gnb_present;
security_capabilities = other.security_capabilities;
std::copy(other.k_gnb, other.k_gnb + 32, k_gnb);
sec_cfg = other.sec_cfg;
ncc = other.ncc;
}
nr_security_context& operator=(const nr_security_context& other)
{
cfg = other.cfg;
k_gnb_present = other.k_gnb_present;
security_capabilities = other.security_capabilities;
std::copy(other.k_gnb, other.k_gnb + 32, k_gnb);
sec_cfg = other.sec_cfg;
ncc = other.ncc;
return *this;
}
bool set_security_capabilities(const asn1::ngap_nr::ue_security_cap_s& caps);
void set_security_key(const asn1::fixed_bitstring<256, false, true>& key);
void set_ncc(uint8_t ncc_) { ncc = ncc_; }
asn1::rrc_nr::security_algorithm_cfg_s get_security_algorithm_cfg() const;
const srsran::as_security_config_t& get_as_sec_cfg() const { return sec_cfg; }
uint8_t get_ncc() const { return ncc; }
bool is_as_sec_cfg_valid() const { return k_gnb_present; }
void regenerate_keys_handover(uint32_t new_pci, uint32_t new_dl_earfcn);
private:
void generate_as_keys();
srslog::basic_logger& logger;
const srsenb::rrc_nr_cfg_t* cfg = nullptr;
bool k_gnb_present = false;
asn1::ngap_nr::ue_security_cap_s security_capabilities = {};
uint8_t k_gnb[32] = {}; // Provided by MME
srsran::as_security_config_t sec_cfg = {};
uint8_t ncc = 0;
};
} // namespace srsgnb
#endif

View File

@ -14,6 +14,7 @@
#define SRSRAN_RRC_NR_UE_H
#include "rrc_nr.h"
#include "rrc_nr_security_context.h"
namespace srsenb {
@ -43,6 +44,11 @@ public:
void get_metrics(rrc_ue_metrics_t& ue_metrics) { ue_metrics = {}; /*TODO fill RRC metrics*/ };
// setters
void set_security_key(const asn1::fixed_bitstring<256, false, true>& key) { sec_ctx.set_security_key(key); }
void set_security_capabilities(const asn1::ngap_nr::ue_security_cap_s& caps)
{
sec_ctx.set_security_capabilities(caps);
}
void deactivate_bearers();
@ -170,6 +176,9 @@ private:
const uint32_t drb1_lcid = 4;
// Security helper
srsgnb::nr_security_context sec_ctx;
// SA specific variables
struct ctxt_t {
uint64_t setup_ue_id = -1;

View File

@ -10,10 +10,10 @@ set(SOURCES rrc_nr_config_utils.cc)
add_library(srsgnb_rrc_config_utils STATIC ${SOURCES})
target_link_libraries(srsgnb_rrc_config_utils srsran_phy)
set(SOURCES rrc_nr.cc rrc_nr_ue.cc cell_asn1_config.cc)
set(SOURCES rrc_nr.cc rrc_nr_ue.cc rrc_nr_security_context.cc cell_asn1_config.cc)
add_library(srsgnb_rrc STATIC ${SOURCES})
target_link_libraries(srsgnb_rrc srsgnb_rrc_config_utils)
include_directories(${PROJECT_SOURCE_DIR})
add_subdirectory(test)
add_subdirectory(test)

View File

@ -538,6 +538,15 @@ void rrc_nr::notify_pdcp_integrity_error(uint16_t rnti, uint32_t lcid) {}
int rrc_nr::ue_set_security_cfg_key(uint16_t rnti, const asn1::fixed_bitstring<256, false, true>& key)
{
logger.debug("Setting securtiy key for rnti=0x%x", rnti);
auto ue_it = users.find(rnti);
if (ue_it == users.end()) {
logger.error("Trying to set key for non-existing rnti=0x%x", rnti);
return SRSRAN_ERROR;
}
ue& u = *ue_it->second;
u.set_security_key(key);
return SRSRAN_SUCCESS;
}
int rrc_nr::ue_set_bitrates(uint16_t rnti, const asn1::ngap_nr::ue_aggregate_maximum_bit_rate_s& rates)
@ -550,6 +559,15 @@ int rrc_nr::set_aggregate_max_bitrate(uint16_t rnti, const asn1::ngap_nr::ue_agg
}
int rrc_nr::ue_set_security_cfg_capabilities(uint16_t rnti, const asn1::ngap_nr::ue_security_cap_s& caps)
{
logger.debug("Setting securtiy capabilites for rnti=0x%x", rnti);
auto ue_it = users.find(rnti);
if (ue_it == users.end()) {
logger.error("Trying to set security capabilities for non-existing rnti=0x%x", rnti);
return SRSRAN_ERROR;
}
ue& u = *ue_it->second;
u.set_security_capabilities(caps);
return SRSRAN_SUCCESS;
}
int rrc_nr::start_security_mode_procedure(uint16_t rnti)
@ -609,9 +627,9 @@ void rrc_nr::sgnb_addition_request(uint16_t eutra_rnti, const sgnb_addition_req_
uecfg.carriers[0].cc = 0;
uecfg.ue_bearers[0].direction = mac_lc_ch_cfg_t::BOTH;
srsran::phy_cfg_nr_default_t::reference_cfg_t ref_args{};
ref_args.duplex = cfg.cell_list[0].duplex_mode == SRSRAN_DUPLEX_MODE_TDD
? srsran::phy_cfg_nr_default_t::reference_cfg_t::R_DUPLEX_TDD_CUSTOM_6_4
: srsran::phy_cfg_nr_default_t::reference_cfg_t::R_DUPLEX_FDD;
ref_args.duplex = cfg.cell_list[0].duplex_mode == SRSRAN_DUPLEX_MODE_TDD
? srsran::phy_cfg_nr_default_t::reference_cfg_t::R_DUPLEX_TDD_CUSTOM_6_4
: srsran::phy_cfg_nr_default_t::reference_cfg_t::R_DUPLEX_FDD;
uecfg.phy_cfg = srsran::phy_cfg_nr_default_t{ref_args};
uecfg.phy_cfg.csi = {}; // disable CSI until RA is complete

View File

@ -0,0 +1,197 @@
/**
*
* \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 "srsgnb/hdr/stack/rrc/rrc_nr_security_context.h"
#include "srsran/asn1/obj_id_cmp_utils.h"
#include "srsran/asn1/rrc_utils.h"
namespace srsgnb {
asn1::rrc_nr::security_algorithm_cfg_s nr_security_context::get_security_algorithm_cfg() const
{
asn1::rrc_nr::security_algorithm_cfg_s ret;
// TODO: select these based on UE capabilities and preference order
ret.integrity_prot_algorithm = (asn1::rrc_nr::integrity_prot_algorithm_e::options)sec_cfg.integ_algo;
ret.ciphering_algorithm = (asn1::rrc_nr::ciphering_algorithm_e::options)sec_cfg.cipher_algo;
return ret;
}
bool nr_security_context::set_security_capabilities(const asn1::ngap_nr::ue_security_cap_s& caps)
{
security_capabilities = caps;
// Selects security algorithms (cipher_algo and integ_algo) based on capabilities and config preferences
// Each position in the bitmap represents an encryption algorithm:
// “all bits equal to 0” UE supports no other algorithm than EEA0,
// “first bit” 128-EEA1,
// “second bit” 128-EEA2,
// “third bit” 128-EEA3,
// other bits reserved for future use. Value 1 indicates support and value
// 0 indicates no support of the algorithm.
// Algorithms are defined in TS 33.401 [15].
// Note: information missing
bool enc_algo_found = false;
bool integ_algo_found = false;
for (auto& cipher_item : cfg->eea_preference_list) {
auto& v = security_capabilities.nrencryption_algorithms;
switch (cipher_item) {
case srsran::CIPHERING_ALGORITHM_ID_EEA0:
// “all bits equal to 0” UE supports no other algorithm than EEA0,
// specification does not cover the case in which EEA0 is supported with other algorithms
// just assume that EEA0 is always supported even this can not be explicity signaled by S1AP
sec_cfg.cipher_algo = srsran::CIPHERING_ALGORITHM_ID_EEA0;
enc_algo_found = true;
logger.info("Selected EEA0 as RRC encryption algorithm");
break;
case srsran::CIPHERING_ALGORITHM_ID_128_EEA1:
// “first bit” 128-EEA1,
if (v.get(v.length() - srsran::CIPHERING_ALGORITHM_ID_128_EEA1)) {
sec_cfg.cipher_algo = srsran::CIPHERING_ALGORITHM_ID_128_EEA1;
enc_algo_found = true;
logger.info("Selected EEA1 as RRC encryption algorithm");
break;
} else {
logger.info("Failed to selected EEA1 as RRC encryption algorithm, due to unsupported algorithm");
}
break;
case srsran::CIPHERING_ALGORITHM_ID_128_EEA2:
// “second bit” 128-EEA2,
if (v.get(v.length() - srsran::CIPHERING_ALGORITHM_ID_128_EEA2)) {
sec_cfg.cipher_algo = srsran::CIPHERING_ALGORITHM_ID_128_EEA2;
enc_algo_found = true;
logger.info("Selected EEA2 as RRC encryption algorithm");
break;
} else {
logger.info("Failed to selected EEA2 as RRC encryption algorithm, due to unsupported algorithm");
}
break;
case srsran::CIPHERING_ALGORITHM_ID_128_EEA3:
// “third bit” 128-EEA3,
if (v.get(v.length() - srsran::CIPHERING_ALGORITHM_ID_128_EEA3)) {
sec_cfg.cipher_algo = srsran::CIPHERING_ALGORITHM_ID_128_EEA3;
enc_algo_found = true;
logger.info("Selected EEA3 as RRC encryption algorithm");
break;
} else {
logger.info("Failed to selected EEA2 as RRC encryption algorithm, due to unsupported algorithm");
}
break;
default:
enc_algo_found = false;
break;
}
if (enc_algo_found) {
break;
}
}
for (auto& eia_enum : cfg->eia_preference_list) {
auto& v = security_capabilities.nrintegrity_protection_algorithms;
switch (eia_enum) {
case srsran::INTEGRITY_ALGORITHM_ID_EIA0:
// Null integrity is not supported
logger.info("Skipping EIA0 as RRC integrity algorithm. Null integrity is not supported.");
sec_cfg.integ_algo = srsran::INTEGRITY_ALGORITHM_ID_EIA0;
integ_algo_found = true;
break;
case srsran::INTEGRITY_ALGORITHM_ID_128_EIA1:
// “first bit” 128-EIA1,
if (v.get(v.length() - srsran::INTEGRITY_ALGORITHM_ID_128_EIA1)) {
sec_cfg.integ_algo = srsran::INTEGRITY_ALGORITHM_ID_128_EIA1;
integ_algo_found = true;
logger.info("Selected EIA1 as RRC integrity algorithm.");
} else {
logger.info("Failed to selected EIA1 as RRC encryption algorithm, due to unsupported algorithm");
}
break;
case srsran::INTEGRITY_ALGORITHM_ID_128_EIA2:
// “second bit” 128-EIA2,
if (v.get(v.length() - srsran::INTEGRITY_ALGORITHM_ID_128_EIA2)) {
sec_cfg.integ_algo = srsran::INTEGRITY_ALGORITHM_ID_128_EIA2;
integ_algo_found = true;
logger.info("Selected EIA2 as RRC integrity algorithm.");
} else {
logger.info("Failed to selected EIA2 as RRC encryption algorithm, due to unsupported algorithm");
}
break;
case srsran::INTEGRITY_ALGORITHM_ID_128_EIA3:
// “third bit” 128-EIA3,
if (v.get(v.length() - srsran::INTEGRITY_ALGORITHM_ID_128_EIA3)) {
sec_cfg.integ_algo = srsran::INTEGRITY_ALGORITHM_ID_128_EIA3;
integ_algo_found = true;
logger.info("Selected EIA3 as RRC integrity algorithm.");
} else {
logger.info("Failed to selected EIA3 as RRC encryption algorithm, due to unsupported algorithm");
}
break;
default:
integ_algo_found = false;
break;
}
if (integ_algo_found) {
break;
}
}
if (not integ_algo_found || not enc_algo_found) {
logger.error("Did not find a matching integrity or encryption algorithm with the UE");
return false;
}
return true;
}
void nr_security_context::set_security_key(const asn1::fixed_bitstring<256, false, true>& key)
{
k_gnb_present = true;
for (uint32_t i = 0; i < key.nof_octets(); ++i) {
k_gnb[i] = key.data()[key.nof_octets() - 1 - i];
}
logger.info(k_gnb, 32, "Key gNodeB (k_gnb)");
generate_as_keys();
}
void nr_security_context::generate_as_keys()
{
// Generate K_rrc_enc and K_rrc_int
srsran::security_generate_k_nr_rrc(
k_gnb, sec_cfg.cipher_algo, sec_cfg.integ_algo, sec_cfg.k_rrc_enc.data(), sec_cfg.k_rrc_int.data());
// Generate K_up_enc and K_up_int
security_generate_k_nr_up(
k_gnb, sec_cfg.cipher_algo, sec_cfg.integ_algo, sec_cfg.k_up_enc.data(), sec_cfg.k_up_int.data());
logger.info(k_gnb, 32, "K_gNB (k_gnb)");
logger.info(sec_cfg.k_rrc_enc.data(), 32, "RRC Encryption Key (k_rrc_enc)");
logger.info(sec_cfg.k_rrc_int.data(), 32, "RRC Integrity Key (k_rrc_int)");
logger.info(sec_cfg.k_up_enc.data(), 32, "UP Encryption Key (k_up_enc)");
logger.info(sec_cfg.k_up_int.data(), 32, "UP Encryption Key (k_up_enc)");
}
void nr_security_context::regenerate_keys_handover(uint32_t new_pci, uint32_t new_dl_earfcn)
{
logger.info("Regenerating KeNB with PCI=0x%02x, DL-EARFCN=%d", new_pci, new_dl_earfcn);
logger.info(k_gnb, 32, "Old K_eNB (k_enb)");
// Generate K_enb*
uint8_t k_gnb_star[32];
srsran::security_generate_k_enb_star(k_gnb, new_pci, new_dl_earfcn, k_gnb_star);
// K_enb becomes K_enb*
memcpy(k_gnb, k_gnb_star, 32);
generate_as_keys();
}
} // namespace srsgnb

View File

@ -27,7 +27,7 @@ Every function in UE class is called from a mutex environment thus does not
need extra protection.
*******************************************************************************/
rrc_nr::ue::ue(rrc_nr* parent_, uint16_t rnti_, const sched_nr_ue_cfg_t& uecfg_, bool start_msg3_timer) :
parent(parent_), logger(parent_->logger), rnti(rnti_), uecfg(uecfg_)
parent(parent_), logger(parent_->logger), rnti(rnti_), uecfg(uecfg_), sec_ctx(parent->cfg)
{
if (not parent->cfg.is_standalone) {
// Add the final PDCCH config in case of NSA