From 505225d84508f58c9d214ad07858defc95bddcd5 Mon Sep 17 00:00:00 2001 From: David Rupprecht Date: Wed, 7 Jul 2021 16:48:27 +0200 Subject: [PATCH] Added ue nas base class with security functions --- srsue/hdr/stack/upper/nas.h | 30 +--- srsue/hdr/stack/upper/nas_base.h | 67 ++++++++ srsue/src/stack/upper/CMakeLists.txt | 2 +- srsue/src/stack/upper/nas.cc | 235 ++------------------------ srsue/src/stack/upper/nas_base.cc | 238 +++++++++++++++++++++++++++ 5 files changed, 322 insertions(+), 250 deletions(-) create mode 100644 srsue/hdr/stack/upper/nas_base.h create mode 100644 srsue/src/stack/upper/nas_base.cc diff --git a/srsue/hdr/stack/upper/nas.h b/srsue/hdr/stack/upper/nas.h index eaac627e6..2294096f4 100644 --- a/srsue/hdr/stack/upper/nas.h +++ b/srsue/hdr/stack/upper/nas.h @@ -13,6 +13,7 @@ #ifndef SRSUE_NAS_H #define SRSUE_NAS_H +#include "nas_base.h" #include "srsran/asn1/liblte_mme.h" #include "srsran/common/buffer_pool.h" #include "srsran/common/common.h" @@ -34,7 +35,7 @@ class usim_interface_nas; class gw_interface_nas; class rrc_interface_nas; -class nas : public nas_interface_rrc, public srsran::timer_callback +class nas : public nas_interface_rrc, public srsran::timer_callback, public nas_base { public: explicit nas(srsran::task_sched_handle task_sched_); @@ -71,11 +72,7 @@ public: // timer callback void timer_expired(uint32_t timeout_id) override; - // PCAP - void start_pcap(srsran::nas_pcap* pcap_) { pcap = pcap_; } - private: - srslog::basic_logger& logger; rrc_interface_nas* rrc = nullptr; usim_interface_nas* usim = nullptr; gw_interface_nas* gw = nullptr; @@ -92,18 +89,6 @@ private: std::vector known_plmns; - // 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; - }; - typedef enum { DEFAULT_EPS_BEARER = 0, DEDICATED_EPS_BEARER } eps_bearer_type_t; typedef struct { @@ -118,7 +103,6 @@ private: bool have_guti = false; bool have_ctxt = false; - nas_sec_ctxt ctxt = {}; bool auth_request = false; uint8_t current_sec_hdr = LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS; @@ -155,23 +139,13 @@ private: // Security bool eia_caps[8] = {}; bool eea_caps[8] = {}; - uint8_t k_nas_enc[32] = {}; - uint8_t k_nas_int[32] = {}; // Airplane mode simulation typedef enum { DISABLED = 0, ENABLED } airplane_mode_state_t; airplane_mode_state_t airplane_mode_state = {}; srsran::timer_handler::unique_timer airplane_mode_sim_timer; - // PCAP - srsran::nas_pcap* pcap = nullptr; - // 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); int apply_security_config(srsran::unique_byte_buffer_t& pdu, uint8_t sec_hdr_type); void reset_security_context(); void set_k_enb_count(uint32_t count); diff --git a/srsue/hdr/stack/upper/nas_base.h b/srsue/hdr/stack/upper/nas_base.h new file mode 100644 index 000000000..225278146 --- /dev/null +++ b/srsue/hdr/stack/upper/nas_base.h @@ -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 diff --git a/srsue/src/stack/upper/CMakeLists.txt b/srsue/src/stack/upper/CMakeLists.txt index 3038c610c..4d9f1e09f 100644 --- a/srsue/src/stack/upper/CMakeLists.txt +++ b/srsue/src/stack/upper/CMakeLists.txt @@ -8,7 +8,7 @@ add_subdirectory(test) -set(SOURCES nas.cc nas_emm_state.cc nas_idle_procedures.cc gw.cc usim_base.cc usim.cc tft_packet_filter.cc) +set(SOURCES nas.cc nas_emm_state.cc nas_idle_procedures.cc gw.cc usim_base.cc usim.cc tft_packet_filter.cc nas_base.cc) if(HAVE_PCSC) list(APPEND SOURCES "pcsc_usim.cc") diff --git a/srsue/src/stack/upper/nas.cc b/srsue/src/stack/upper/nas.cc index 1003da37a..7a228a209 100644 --- a/srsue/src/stack/upper/nas.cc +++ b/srsue/src/stack/upper/nas.cc @@ -35,6 +35,7 @@ namespace srsue { ********************************************************************/ nas::nas(srsran::task_sched_handle task_sched_) : + nas_base("NAS"), plmn_searcher(this), task_sched(task_sched_), t3402(task_sched_.get_unique_timer()), @@ -42,8 +43,7 @@ nas::nas(srsran::task_sched_handle task_sched_) : t3411(task_sched_.get_unique_timer()), t3421(task_sched_.get_unique_timer()), reattach_timer(task_sched_.get_unique_timer()), - airplane_mode_sim_timer(task_sched_.get_unique_timer()), - logger(srslog::fetch_basic_logger("NAS")) + airplane_mode_sim_timer(task_sched_.get_unique_timer()) {} int nas::init(usim_interface_nas* usim_, rrc_interface_nas* rrc_, gw_interface_nas* gw_, const nas_args_t& cfg_) @@ -58,30 +58,21 @@ int nas::init(usim_interface_nas* usim_, rrc_interface_nas* rrc_, gw_interface_n } // parse and sanity check EIA list - std::vector cap_list; - srsran::string_parse_list(cfg_.eia, ',', cap_list); - if (cap_list.empty()) { - logger.error("Empty EIA list. Select at least one EIA algorithm."); - } - for (std::vector::const_iterator it = cap_list.begin(); it != cap_list.end(); ++it) { - if (*it != 0 && *it < 4) { - eia_caps[*it] = true; - } else { - logger.error("EIA%d is not a valid EIA algorithm.", *it); - } + if (parse_security_algorithm_list(cfg_.eia, eia_caps) != SRSRAN_SUCCESS) { + logger.warning("Failed to parse integrity protection algorithm list: Defaulting to EIA1-128, EIA2-128, EIA3-128"); + eia_caps[0] = false; + eia_caps[1] = true; + eia_caps[2] = true; + eia_caps[3] = true; } // parse and sanity check EEA list - srsran::string_parse_list(cfg_.eea, ',', cap_list); - if (cap_list.empty()) { - logger.error("Empty EEA list. Select at least one EEA algorithm."); - } - for (std::vector::const_iterator it = cap_list.begin(); it != cap_list.end(); ++it) { - if (*it < 4) { - eea_caps[*it] = true; - } else { - logger.error("EEA%d is not a valid EEA algorithm.", *it); - } + if (parse_security_algorithm_list(cfg_.eea, eea_caps) != SRSRAN_SUCCESS) { + logger.warning("Failed to parse encryption algorithm list: Defaulting to EEA0, EEA1-128, EEA2-128, EEA3-128"); + eea_caps[0] = true; + eea_caps[1] = true; + eea_caps[2] = true; + eea_caps[3] = true; } cfg = cfg_; @@ -689,204 +680,6 @@ void nas::select_plmn() } } -/******************************************************************************* - * Security - ******************************************************************************/ - -void nas::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::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::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::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; - } -} bool nas::check_cap_replay(LIBLTE_MME_UE_SECURITY_CAPABILITIES_STRUCT* caps) { diff --git a/srsue/src/stack/upper/nas_base.cc b/srsue/src/stack/upper/nas_base.cc new file mode 100644 index 000000000..b4a01f76a --- /dev/null +++ b/srsue/src/stack/upper/nas_base.cc @@ -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 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::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 \ No newline at end of file