ue,rrc_nr,nas_5g: Introduce NR AS Key derivation and update context handling in 5G NAS

This commit is contained in:
Bedran Karakoc 2022-01-24 17:27:37 +01:00 committed by Andre Puschmann
parent cabdd05cb8
commit efa13290a9
11 changed files with 118 additions and 28 deletions

View File

@ -97,6 +97,7 @@ struct k_enb_context_t {
};
struct k_gnb_context_t {
as_key_t k_gnb;
as_key_t sk_gnb;
};
@ -180,6 +181,8 @@ uint8_t security_generate_k_amf(const uint8_t* k_seaf,
uint8_t security_generate_k_seaf(const uint8_t* k_ausf, const char* serving_network_name, uint8_t* k_seaf);
uint8_t security_generate_k_gnb(const as_key_t& k_amf, const uint32_t nas_count, as_key_t& k_gnb);
uint8_t security_generate_k_enb(const uint8_t* k_asme, const uint32_t nas_count, uint8_t* k_enb);
uint8_t security_generate_k_nb_star_common(uint8_t fc,

View File

@ -58,8 +58,9 @@ public:
class nas_5g_interface_rrc_nr
{
public:
virtual int write_pdu(srsran::unique_byte_buffer_t pdu) = 0;
virtual int get_k_amf(srsran::as_key_t& k_amf) = 0;
virtual int write_pdu(srsran::unique_byte_buffer_t pdu) = 0;
virtual int get_k_amf(srsran::as_key_t& k_amf) = 0;
virtual uint32_t get_ul_nas_count() = 0;
};
class nas_5g_interface_procedures

View File

@ -24,24 +24,24 @@ enum auth_result_t { AUTH_OK, AUTH_FAILED, AUTH_SYNCH_FAILURE };
class usim_interface_nas
{
public:
virtual std::string get_imsi_str() = 0;
virtual std::string get_imei_str() = 0;
virtual bool get_imsi_vec(uint8_t* imsi_, uint32_t n) = 0;
virtual bool get_imei_vec(uint8_t* imei_, uint32_t n) = 0;
virtual bool get_home_plmn_id(srsran::plmn_id_t* home_plmn_id) = 0;
virtual std::string get_imsi_str() = 0;
virtual std::string get_imei_str() = 0;
virtual bool get_imsi_vec(uint8_t* imsi_, uint32_t n) = 0;
virtual bool get_imei_vec(uint8_t* imei_, uint32_t n) = 0;
virtual bool get_home_plmn_id(srsran::plmn_id_t* home_plmn_id) = 0;
// Get the home mcc as bytes array
virtual bool get_home_mcc_bytes(uint8_t* mcc_, uint32_t n) = 0;
virtual bool get_home_mcc_bytes(uint8_t* mcc_, uint32_t n) = 0;
// Get the home mnc as byte array
virtual bool get_home_mnc_bytes(uint8_t* mnc_, uint32_t n) = 0;
virtual bool get_home_mnc_bytes(uint8_t* mnc_, uint32_t n) = 0;
// Get the home msin in bytes array encoded as bcd
virtual bool get_home_msin_bcd(uint8_t* msin_, uint32_t n) = 0;
virtual bool get_home_msin_bcd(uint8_t* msin_, uint32_t n) = 0;
virtual auth_result_t generate_authentication_response(uint8_t* rand,
uint8_t* autn_enb,
uint16_t mcc,
uint16_t mnc,
uint8_t* res,
int* res_len,
uint8_t* k_asme) = 0;
uint8_t* k_asme) = 0;
virtual auth_result_t generate_authentication_response_5g(uint8_t* rand,
uint8_t* autn_enb,
@ -77,8 +77,10 @@ public:
class usim_interface_rrc_nr
{
public:
virtual bool generate_nr_context(uint16_t sk_counter, srsran::as_security_config_t* sec_cfg) = 0;
virtual bool update_nr_context(srsran::as_security_config_t* sec_cfg) = 0;
virtual void
generate_nr_as_keys(srsran::as_key_t& k_amf, uint32_t count_ul, srsran::as_security_config_t* sec_cfg) = 0;
virtual bool generate_nr_context(uint16_t sk_counter, srsran::as_security_config_t* sec_cfg) = 0;
virtual bool update_nr_context(srsran::as_security_config_t* sec_cfg) = 0;
};
} // namespace srsue

View File

@ -15,7 +15,6 @@
#include "srsran/common/s3g.h"
#include "srsran/common/ssl.h"
#include "srsran/config.h"
#include <arpa/inet.h>
#ifdef HAVE_MBEDTLS
@ -195,6 +194,32 @@ uint8_t security_generate_k_amf(const uint8_t* k_seaf,
return SRSRAN_SUCCESS;
}
uint8_t security_generate_k_gnb(const as_key_t& k_amf, const uint32_t nas_count_, as_key_t& k_gnb)
{
if (k_amf.empty()) {
log_error("Invalid inputs");
return SRSRAN_ERROR;
}
// NAS Count
std::vector<uint8_t> nas_count;
nas_count.resize(4);
nas_count[0] = (nas_count_ >> 24) & 0xFF;
nas_count[1] = (nas_count_ >> 16) & 0xFF;
nas_count[2] = (nas_count_ >> 8) & 0xFF;
nas_count[3] = nas_count_ & 0xFF;
// Access Type Distinguisher 3GPP access = 0x01 (TS 33501 Annex A.9)
std::vector<uint8_t> access_type_distinguisher = {1};
if (kdf_common(FC_5G_KGNB_KN3IWF_DERIVATION, k_amf, nas_count, access_type_distinguisher, k_gnb.data()) !=
SRSRAN_SUCCESS) {
log_error("Failed to run kdf_common");
return SRSRAN_ERROR;
}
return SRSRAN_SUCCESS;
}
uint8_t security_generate_k_enb(const uint8_t* k_asme, const uint32_t nas_count_, uint8_t* k_enb)
{
if (k_asme == NULL || k_enb == NULL) {

View File

@ -57,8 +57,9 @@ public:
void run_tti();
// Stack+RRC interface
bool is_registered();
int get_k_amf(srsran::as_key_t& k_amf);
bool is_registered();
int get_k_amf(srsran::as_key_t& k_amf);
uint32_t get_ul_nas_count();
int write_pdu(srsran::unique_byte_buffer_t pdu);
@ -94,6 +95,8 @@ private:
bool ia5g_caps[8] = {};
bool ea5g_caps[8] = {};
void set_k_gnb_count(uint32_t count);
// TS 23.003 Sec. 6.2.2 IMEISV's last two octets are Software Version Number (SVN)
// which identifies the software version number of the mobile equipment
const uint8_t ue_svn_oct1 = 0x5;

View File

@ -60,6 +60,7 @@ protected:
struct nas_5g_sec_ctxt {
uint8_t ksi;
uint8_t k_amf[32];
uint32_t k_gnb_count;
};
nas_sec_base_ctxt ctxt_base = {};
@ -70,7 +71,7 @@ protected:
// Security
void
integrity_generate(uint8_t* key_128, uint32_t count, uint8_t direction, uint8_t* msg, uint32_t msg_len, uint8_t* mac);
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);

View File

@ -97,6 +97,7 @@ public:
void restore_keys_from_failed_ho(srsran::as_security_config_t* as_ctx) final;
// NR RRC interface
void generate_nr_as_keys(srsran::as_key_t& k_amf, uint32_t count_ul, srsran::as_security_config_t* sec_cfg) final;
bool generate_nr_context(uint16_t sk_counter, srsran::as_security_config_t* sec_cfg) final;
bool update_nr_context(srsran::as_security_config_t* sec_cfg) final;
@ -140,6 +141,8 @@ protected:
uint8_t k_enb_initial[KEY_LEN] = {};
uint8_t auts[AKA_AUTS_LEN] = {};
srsran::as_key_t k_gnb_initial = {};
// Current K_eNB context (K_eNB, NH and NCC)
srsran::k_enb_context_t k_enb_ctx = {};
srsran::k_gnb_context_t k_gnb_ctx = {};

View File

@ -2114,12 +2114,11 @@ void rrc_nr::handle_security_mode_command(const asn1::rrc_nr::security_mode_cmd_
// Security helper used by Security Mode Command and Mobility handling routines
void rrc_nr::generate_as_keys()
{
uint8_t k_asme[32] = {};
// FIXME: need to add
// nas->get_k_asme(k_asme, 32);
logger.debug(k_asme, 32, "UE K_asme");
// logger.debug("Generating K_enb with UL NAS COUNT: %d", nas->get_k_enb_count());
// usim->generate_as_keys(k_asme, nas->get_k_enb_count(), &sec_cfg);
as_key_t k_amf = {};
nas->get_k_amf(k_amf);
logger.debug(k_amf.data(), 32, "UE K_amf");
logger.debug("Generating K_gnb with UL NAS COUNT: %d", nas->get_ul_nas_count());
usim->generate_nr_as_keys(k_amf, nas->get_ul_nas_count(), &sec_cfg);
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");

View File

@ -109,12 +109,14 @@ class dummy_eutra : public rrc_eutra_interface_rrc_nr
class dummy_nas : public nas_5g_interface_rrc_nr
{
int write_pdu(srsran::unique_byte_buffer_t pdu) { return SRSRAN_SUCCESS; };
int get_k_amf(srsran::as_key_t& k_amf) { return SRSRAN_SUCCESS; };
int write_pdu(srsran::unique_byte_buffer_t pdu) { return SRSRAN_SUCCESS; };
int get_k_amf(srsran::as_key_t& k_amf) { return SRSRAN_SUCCESS; };
uint32_t get_ul_nas_count() { return SRSRAN_SUCCESS; };
};
class dummy_sim : public usim_interface_rrc_nr
{
void generate_nr_as_keys(srsran::as_key_t& k_amf, uint32_t count_ul, srsran::as_security_config_t* sec_cfg){};
bool generate_nr_context(uint16_t sk_counter, srsran::as_security_config_t* sec_cfg) { return true; }
bool update_nr_context(srsran::as_security_config_t* sec_cfg) { return true; }
};

View File

@ -293,6 +293,11 @@ int nas_5g::send_registration_request()
}
}
if (has_sec_ctxt) {
set_k_gnb_count(ctxt_base.tx_count);
ctxt_base.tx_count++;
}
state.set_registered_initiated();
return SRSRAN_SUCCESS;
@ -847,6 +852,7 @@ int nas_5g::handle_authentication_request(authentication_request_t& authenticati
return SRSRAN_ERROR;
}
initial_sec_command = true;
uint8_t res_star[16];
logger.info(authentication_request.authentication_parameter_rand.rand.data(),
@ -936,10 +942,8 @@ int nas_5g::handle_security_mode_command(security_mode_command_t& security_m
return SRSRAN_ERROR;
}
initial_sec_command = false; // TODO
if (initial_sec_command) {
ctxt_base.rx_count = 0;
set_k_gnb_count(0);
ctxt_base.tx_count = 0;
initial_sec_command = false;
}
@ -1121,6 +1125,16 @@ int nas_5g::get_k_amf(as_key_t& k_amf)
return SRSRAN_SUCCESS;
}
uint32_t nas_5g::get_ul_nas_count()
{
return ctxt_5g.k_gnb_count;
}
void nas_5g::set_k_gnb_count(uint32_t count)
{
ctxt_5g.k_gnb_count = count;
}
/*******************************************************************************
* Helpers
******************************************************************************/

View File

@ -222,6 +222,7 @@ void usim_base::generate_nas_keys(uint8_t* k_asme,
/*
* RRC Interface
*/
void usim_base::generate_as_keys(uint8_t* k_asme_, uint32_t count_ul, srsran::as_security_config_t* sec_cfg)
{
if (!initiated) {
@ -367,6 +368,42 @@ bool usim_base::generate_nas_keys_5g(uint8_t* k_amf,
* NR RRC Interface
*/
void usim_base::generate_nr_as_keys(srsran::as_key_t& k_amf, uint32_t count_ul, srsran::as_security_config_t* sec_cfg)
{
if (!initiated) {
logger.error("USIM not initiated!");
return;
}
logger.info("Generating NR AS Keys. NAS UL COUNT %d", count_ul);
logger.debug(k_amf.data(), 32, "K_amf");
// Generate K_gnb
srsran::security_generate_k_gnb(k_amf, count_ul, k_gnb_ctx.k_gnb);
logger.info(k_gnb_ctx.k_gnb.data(), 32, "K_gnb");
// Save initial k_gnb
k_gnb_initial = k_gnb_ctx.k_gnb;
security_generate_k_nr_rrc(k_gnb_ctx.k_gnb.data(),
sec_cfg->cipher_algo,
sec_cfg->integ_algo,
sec_cfg->k_rrc_enc.data(),
sec_cfg->k_rrc_int.data());
security_generate_k_nr_up(k_gnb_ctx.k_gnb.data(),
sec_cfg->cipher_algo,
sec_cfg->integ_algo,
sec_cfg->k_up_enc.data(),
sec_cfg->k_up_int.data());
logger.info(k_gnb_ctx.k_gnb.data(), 32, "Initial K_gnb");
logger.info(sec_cfg->k_rrc_int.data(), sec_cfg->k_rrc_int.size(), "NR K_RRC_int");
logger.info(sec_cfg->k_rrc_enc.data(), sec_cfg->k_rrc_enc.size(), "NR K_RRC_enc");
logger.info(sec_cfg->k_up_int.data(), sec_cfg->k_up_int.size(), "NR K_UP_int");
logger.info(sec_cfg->k_up_enc.data(), sec_cfg->k_up_enc.size(), "NR K_UP_enc");
}
bool usim_base::generate_nr_context(uint16_t sk_counter, srsran::as_security_config_t* sec_cfg)
{
if (!initiated) {