diff --git a/lib/include/srslte/asn1/liblte_mme.h b/lib/include/srslte/asn1/liblte_mme.h index 15799f7ca..977f309ce 100644 --- a/lib/include/srslte/asn1/liblte_mme.h +++ b/lib/include/srslte/asn1/liblte_mme.h @@ -2721,6 +2721,10 @@ typedef struct{ // Functions LIBLTE_ERROR_ENUM liblte_mme_pack_attach_request_msg(LIBLTE_MME_ATTACH_REQUEST_MSG_STRUCT *attach_req, LIBLTE_BYTE_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_mme_pack_attach_request_msg(LIBLTE_MME_ATTACH_REQUEST_MSG_STRUCT *attach_req, + uint8 sec_hdr_type, + uint32 count, + LIBLTE_BYTE_MSG_STRUCT *msg); LIBLTE_ERROR_ENUM liblte_mme_unpack_attach_request_msg(LIBLTE_BYTE_MSG_STRUCT *msg, LIBLTE_MME_ATTACH_REQUEST_MSG_STRUCT *attach_req); diff --git a/lib/include/srslte/interfaces/ue_interfaces.h b/lib/include/srslte/interfaces/ue_interfaces.h index 88dfd285c..f083c89e2 100644 --- a/lib/include/srslte/interfaces/ue_interfaces.h +++ b/lib/include/srslte/interfaces/ue_interfaces.h @@ -52,16 +52,20 @@ class ue_interface class usim_interface_nas { public: - virtual void get_imsi_vec(uint8_t* imsi_, uint32_t n) = 0; - virtual void get_imei_vec(uint8_t* imei_, uint32_t n) = 0; - virtual int get_home_plmn_id(LIBLTE_RRC_PLMN_IDENTITY_STRUCT *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(LIBLTE_RRC_PLMN_IDENTITY_STRUCT *home_plmn_id) = 0; virtual void generate_authentication_response(uint8_t *rand, uint8_t *autn_enb, uint16_t mcc, uint16_t mnc, bool *net_valid, - uint8_t *res) = 0; - virtual void generate_nas_keys(uint8_t *k_nas_enc, + uint8_t *res, + uint8_t *k_asme) = 0; + virtual void generate_nas_keys(uint8_t *k_asme, + uint8_t *k_nas_enc, uint8_t *k_nas_int, srslte::CIPHERING_ALGORITHM_ID_ENUM cipher_algo, srslte::INTEGRITY_ALGORITHM_ID_ENUM integ_algo) = 0; @@ -71,7 +75,8 @@ public: class usim_interface_rrc { public: - virtual void generate_as_keys(uint32_t count_ul, + virtual void generate_as_keys(uint8_t *k_asme, + uint32_t count_ul, uint8_t *k_rrc_enc, uint8_t *k_rrc_int, uint8_t *k_up_enc, @@ -104,6 +109,7 @@ public: virtual void write_pdu(uint32_t lcid, srslte::byte_buffer_t *pdu) = 0; virtual uint32_t get_ul_count() = 0; virtual bool get_s_tmsi(LIBLTE_RRC_S_TMSI_STRUCT *s_tmsi) = 0; + virtual bool get_k_asme(uint8_t *k_asme_, uint32_t n) = 0; virtual void plmn_found(LIBLTE_RRC_PLMN_IDENTITY_STRUCT plmn_id, uint16_t tracking_area_code) = 0; virtual void plmn_search_end() = 0; }; diff --git a/lib/src/asn1/liblte_mme.cc b/lib/src/asn1/liblte_mme.cc index 4447b001f..215ffd838 100644 --- a/lib/src/asn1/liblte_mme.cc +++ b/lib/src/asn1/liblte_mme.cc @@ -1411,9 +1411,9 @@ LIBLTE_ERROR_ENUM liblte_mme_pack_eps_mobile_id_ie(LIBLTE_MME_EPS_MOBILE_ID_STRU **ie_ptr = (((eps_mobile_id->guti.mnc/10) % 10) << 4) | ((eps_mobile_id->guti.mnc/100) % 10); *ie_ptr += 1; } - **ie_ptr = (eps_mobile_id->guti.mme_group_id >> 8) & 0x0F; + **ie_ptr = (eps_mobile_id->guti.mme_group_id >> 8) & 0xFF; *ie_ptr += 1; - **ie_ptr = eps_mobile_id->guti.mme_group_id & 0x0F; + **ie_ptr = eps_mobile_id->guti.mme_group_id & 0xFF; *ie_ptr += 1; **ie_ptr = eps_mobile_id->guti.mme_code; *ie_ptr += 1; @@ -5518,6 +5518,14 @@ LIBLTE_ERROR_ENUM liblte_mme_unpack_attach_reject_msg(LIBLTE_BYTE_MSG_STRUCT *********************************************************************/ LIBLTE_ERROR_ENUM liblte_mme_pack_attach_request_msg(LIBLTE_MME_ATTACH_REQUEST_MSG_STRUCT *attach_req, LIBLTE_BYTE_MSG_STRUCT *msg) +{ + return liblte_mme_pack_attach_request_msg(attach_req, LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS, 0, msg); +} + +LIBLTE_ERROR_ENUM liblte_mme_pack_attach_request_msg(LIBLTE_MME_ATTACH_REQUEST_MSG_STRUCT *attach_req, + uint8 sec_hdr_type, + uint32 count, + LIBLTE_BYTE_MSG_STRUCT *msg) { LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; uint8 *msg_ptr = msg->msg; @@ -5525,6 +5533,20 @@ LIBLTE_ERROR_ENUM liblte_mme_pack_attach_request_msg(LIBLTE_MME_ATTACH_REQUEST_M if(attach_req != NULL && msg != NULL) { + if(LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS != sec_hdr_type) + { + // Protocol Discriminator and Security Header Type + *msg_ptr = (sec_hdr_type << 4) | (LIBLTE_MME_PD_EPS_MOBILITY_MANAGEMENT); + msg_ptr++; + + // MAC will be filled in later + msg_ptr += 4; + + // Sequence Number + *msg_ptr = count & 0xFF; + msg_ptr++; + } + // Protocol Discriminator and Security Header Type *msg_ptr = (LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS << 4) | (LIBLTE_MME_PD_EPS_MOBILITY_MANAGEMENT); msg_ptr++; diff --git a/srsue/hdr/upper/nas.h b/srsue/hdr/upper/nas.h index e743165fb..d79eddea8 100644 --- a/srsue/hdr/upper/nas.h +++ b/srsue/hdr/upper/nas.h @@ -57,6 +57,9 @@ static const char emm_state_text[EMM_STATE_N_ITEMS][100] = {"NULL", "DEREGISTERED INITIATED", "TRACKING AREA UPDATE INITIATED"}; +static const bool eia_caps[8] = {false, true, true, false, false, false, false, false}; +static const bool eea_caps[8] = {true, false, false, false, false, false, false, false}; + typedef enum { PLMN_NOT_SELECTED = 0, PLMN_SELECTED @@ -80,16 +83,12 @@ public: // RRC interface void notify_connection_setup(); - void write_pdu(uint32_t lcid, byte_buffer_t *pdu); - uint32_t get_ul_count(); - bool is_attached(); bool is_attaching(); - bool get_s_tmsi(LIBLTE_RRC_S_TMSI_STRUCT *s_tmsi); - + bool get_k_asme(uint8_t *k_asme_, uint32_t n); void plmn_found(LIBLTE_RRC_PLMN_IDENTITY_STRUCT plmn_id, uint16_t tracking_area_code); void plmn_search_end(); @@ -115,72 +114,73 @@ private: std::vector known_plmns; - // Save short MAC + LIBLTE_MME_EMM_INFORMATION_MSG_STRUCT emm_info; // Identifiers LIBLTE_MME_EPS_MOBILE_ID_GUTI_STRUCT guti; - bool is_guti_set; + bool have_guti; + + // Security context + struct nas_sec_ctxt{ + uint8_t ksi; + uint8_t k_asme[32]; + uint32_t tx_count; + uint32_t rx_count; + srslte::CIPHERING_ALGORITHM_ID_ENUM cipher_algo; + srslte::INTEGRITY_ALGORITHM_ID_ENUM integ_algo; + }; + + bool have_ctxt; + nas_sec_ctxt ctxt; uint32_t ip_addr; uint8_t eps_bearer_id; uint8_t transaction_id; - // NAS counters - incremented for each security-protected message recvd/sent - uint32_t count_ul; - uint32_t count_dl; - // Security - uint8_t ksi; uint8_t k_nas_enc[32]; uint8_t k_nas_int[32]; - srslte::CIPHERING_ALGORITHM_ID_ENUM cipher_algo; - srslte::INTEGRITY_ALGORITHM_ID_ENUM integ_algo; - - void integrity_generate(uint8_t *key_128, + void integrity_generate(uint8_t integ_algo, + uint8_t *key_128, uint32_t count, uint8_t rb_id, uint8_t direction, uint8_t *msg, uint32_t msg_len, uint8_t *mac); - void integrity_check(); - void cipher_encrypt(); - void cipher_decrypt(); + bool check_cap_replay(LIBLTE_MME_UE_SECURITY_CAPABILITIES_STRUCT *caps); // Parsers void parse_attach_accept(uint32_t lcid, byte_buffer_t *pdu); - void parse_attach_reject(uint32_t lcid, byte_buffer_t *pdu); - void parse_authentication_request(uint32_t lcid, byte_buffer_t *pdu); - void parse_authentication_reject(uint32_t lcid, byte_buffer_t *pdu); - void parse_identity_request(uint32_t lcid, byte_buffer_t *pdu); - void parse_security_mode_command(uint32_t lcid, byte_buffer_t *pdu); - void parse_service_reject(uint32_t lcid, byte_buffer_t *pdu); - void parse_esm_information_request(uint32_t lcid, byte_buffer_t *pdu); - void parse_emm_information(uint32_t lcid, byte_buffer_t *pdu); // Senders void send_attach_request(); - void send_identity_response(); - void send_service_request(); - void send_esm_information_response(); - void gen_pdn_connectivity_request(LIBLTE_BYTE_MSG_STRUCT *msg); + void send_security_mode_reject(uint8_t cause); + + // guti persistence file + bool read_guti_file(LIBLTE_MME_EPS_MOBILE_ID_GUTI_STRUCT *guti); + bool write_guti_file(LIBLTE_MME_EPS_MOBILE_ID_GUTI_STRUCT guti); + + // security context persistence file + bool read_ctxt_file(nas_sec_ctxt *ctxt); + bool write_ctxt_file(nas_sec_ctxt ctxt); }; } // namespace srsue diff --git a/srsue/hdr/upper/rrc.h b/srsue/hdr/upper/rrc.h index 37ab941cd..d561b0f1c 100644 --- a/srsue/hdr/upper/rrc.h +++ b/srsue/hdr/upper/rrc.h @@ -40,8 +40,6 @@ #include typedef struct { - bool stmsi_attach; - LIBLTE_RRC_S_TMSI_STRUCT stmsi_value; uint32_t ue_category; uint32_t feature_group; uint8_t supported_bands[LIBLTE_RRC_BAND_N_ITEMS]; @@ -198,21 +196,16 @@ private: // MAC interface void release_pucch_srs(); - void ra_problem(); // GW interface bool is_connected(); - bool have_drb(); // PDCP interface void write_pdu(uint32_t lcid, byte_buffer_t *pdu); - void write_pdu_bcch_bch(byte_buffer_t *pdu); - void write_pdu_bcch_dlsch(byte_buffer_t *pdu); - void write_pdu_pcch(byte_buffer_t *pdu); // Radio bearers @@ -280,11 +273,6 @@ private: void set_mac_default(); void set_rrc_default(); void set_bearers(); - - // s-tmsi persistent file - bool read_stimsi_file(LIBLTE_RRC_S_TMSI_STRUCT *s_tmsi); - bool write_stimsi_file(LIBLTE_RRC_S_TMSI_STRUCT s_tmsi); - }; } // namespace srsue diff --git a/srsue/hdr/upper/usim.h b/srsue/hdr/upper/usim.h index fea15ba68..73acd5594 100644 --- a/srsue/hdr/upper/usim.h +++ b/srsue/hdr/upper/usim.h @@ -1,4 +1,4 @@ -/** +/** * * \section COPYRIGHT * @@ -59,24 +59,30 @@ public: void stop(); // NAS interface - void get_imsi_vec(uint8_t* imsi_, uint32_t n); - void get_imei_vec(uint8_t* imei_, uint32_t n); - int get_home_plmn_id(LIBLTE_RRC_PLMN_IDENTITY_STRUCT *home_plmn_id); + std::string get_imsi_str(); + std::string get_imei_str(); + + bool get_imsi_vec(uint8_t* imsi_, uint32_t n); + bool get_imei_vec(uint8_t* imei_, uint32_t n); + bool get_home_plmn_id(LIBLTE_RRC_PLMN_IDENTITY_STRUCT *home_plmn_id); void generate_authentication_response(uint8_t *rand, uint8_t *autn_enb, uint16_t mcc, uint16_t mnc, bool *net_valid, - uint8_t *res); + uint8_t *res, + uint8_t *k_asme); - void generate_nas_keys(uint8_t *k_nas_enc, + void generate_nas_keys(uint8_t *k_asme, + uint8_t *k_nas_enc, uint8_t *k_nas_int, srslte::CIPHERING_ALGORITHM_ID_ENUM cipher_algo, srslte::INTEGRITY_ALGORITHM_ID_ENUM integ_algo); // RRC interface - void generate_as_keys(uint32_t count_ul, + void generate_as_keys(uint8_t *k_asme, + uint32_t count_ul, uint8_t *k_rrc_enc, uint8_t *k_rrc_int, uint8_t *k_up_enc, @@ -91,13 +97,15 @@ private: uint16_t mcc, uint16_t mnc, bool *net_valid, - uint8_t *res); + uint8_t *res, + uint8_t *k_asme); void gen_auth_res_xor( uint8_t *rand, uint8_t *autn_enb, uint16_t mcc, uint16_t mnc, bool *net_valid, - uint8_t *res); + uint8_t *res, + uint8_t *k_asme); void str_to_hex(std::string str, uint8_t *hex); srslte::log *usim_log; @@ -110,6 +118,9 @@ private: uint64_t imei; uint8_t k[16]; + std::string imsi_str; + std::string imei_str; + // Security variables uint8_t rand[16]; uint8_t ck[16]; @@ -117,7 +128,6 @@ private: uint8_t ak[6]; uint8_t mac[8]; uint8_t autn[16]; - uint8_t k_asme[32]; uint8_t k_enb[32]; bool initiated; diff --git a/srsue/src/main.cc b/srsue/src/main.cc index 59d6360c7..b9717d93e 100644 --- a/srsue/src/main.cc +++ b/srsue/src/main.cc @@ -77,17 +77,12 @@ void parse_args(all_args_t *args, int argc, char *argv[]) { "Transmission time advance") ("rf.burst_preamble_us", bpo::value(&args->rf.burst_preamble)->default_value("auto"), "Transmission time advance") - ("rrc.stmsi_attach", bpo::value(&args->rrc.stmsi_attach)->default_value(false),"If enabled, always tries first an S-TMSI attach using the\n" - "S-TMSI value stored in .stimsi file generated in previous run") - ("rrc.mmec_value", bpo::value(&args->rrc.stmsi_value.mmec)->default_value(0), "If defined (non-zero), overwrites the value stored in .stimsi file") - ("rrc.mtmsi_value", bpo::value(&args->rrc.stmsi_value.m_tmsi)->default_value(0), "If defined (non-zero), overwrites the value stored in .stimsi file") - ("rrc.feature_group", bpo::value(&args->rrc.feature_group)->default_value(0xe6041c00), "Hex value of the featureGroupIndicators field in the" "UECapabilityInformation message. Default 0xe6041c00") ("rrc.ue_category", bpo::value(&args->ue_category_str)->default_value("4"), "UE Category (1 to 5)") - ("pcap.enable", bpo::value(&args->pcap.enable)->default_value(false), + ("pcap.enable", bpo::value(&args->pcap.enable)->default_value(false), "Enable MAC packet captures for wireshark") ("pcap.filename", bpo::value(&args->pcap.filename)->default_value("ue.pcap"), "MAC layer capture filename") diff --git a/srsue/src/upper/nas.cc b/srsue/src/upper/nas.cc index d8bd40c6e..2b01efcec 100644 --- a/srsue/src/upper/nas.cc +++ b/srsue/src/upper/nas.cc @@ -25,6 +25,11 @@ */ +#include +#include +#include +#include +#include #include "srslte/asn1/liblte_rrc.h" #include "upper/nas.h" #include "srslte/common/bcd_helpers.h" @@ -33,9 +38,78 @@ using namespace srslte; namespace srsue { +/********************************************************************* + * Conversion helpers + ********************************************************************/ +std::string hex_to_string(uint8_t *hex, int size) +{ + std::stringstream ss; + + ss << std::hex << std::setfill('0'); + for(int i=0; i(hex[i]); + } + return ss.str(); +} + +bool string_to_hex(std::string hex_str, uint8_t *hex, uint32_t len) +{ + static const char* const lut = "0123456789abcdef"; + uint32_t str_len = hex_str.length(); + if(str_len & 1) { + return false; // uneven hex_str length + } + if(str_len > len*2) { + return false; // not enough space in hex buffer + } + + for(uint32_t i=0; ifull_net_name_present) { + ss << info->full_net_name.name; + } + if(info->short_net_name_present) { + ss << " (" << info->short_net_name.name << ")"; + } + if(info->utc_and_local_time_zone_present) { + ss << " " << (int)info->utc_and_local_time_zone.day; + ss << "/" << (int)info->utc_and_local_time_zone.month; + ss << "/" << (int)info->utc_and_local_time_zone.year; + ss << " " << (int)info->utc_and_local_time_zone.hour; + ss << ":" << (int)info->utc_and_local_time_zone.minute; + ss << ":" << (int)info->utc_and_local_time_zone.second; + ss << " TZ:" << (int)info->utc_and_local_time_zone.tz; + } + return ss.str(); +} + + nas::nas() - : state(EMM_STATE_DEREGISTERED), plmn_selection(PLMN_SELECTED), is_guti_set(false), ip_addr(0), eps_bearer_id(0), - count_ul(0), count_dl(0) {} + : state(EMM_STATE_DEREGISTERED), plmn_selection(PLMN_SELECTED), have_guti(false), ip_addr(0), eps_bearer_id(0) +{ + ctxt.rx_count = 0; + ctxt.tx_count = 0; +} void nas::init(usim_interface_nas *usim_, rrc_interface_nas *rrc_, @@ -51,15 +125,26 @@ void nas::init(usim_interface_nas *usim_, state = EMM_STATE_DEREGISTERED; plmn_selection = PLMN_NOT_SELECTED; - if (usim->get_home_plmn_id(&home_plmn)) { + if (!usim->get_home_plmn_id(&home_plmn)) { nas_log->error("Getting Home PLMN Id from USIM. Defaulting to 001-01\n"); home_plmn.mcc = 61441; // This is 001 home_plmn.mnc = 65281; // This is 01 } cfg = cfg_; + + if((have_guti = read_guti_file(&guti))) { + if((have_ctxt = read_ctxt_file(&ctxt))) { + usim->generate_nas_keys(ctxt.k_asme, k_nas_enc, k_nas_int, + ctxt.cipher_algo, ctxt.integ_algo); + nas_log->debug_hex(k_nas_enc, 32, "NAS encryption key - k_nas_enc"); + nas_log->debug_hex(k_nas_int, 32, "NAS integrity key - k_nas_int"); + } + } } -void nas::stop() {} +void nas::stop() { + write_ctxt_file(ctxt); +} emm_state_t nas::get_state() { return state; @@ -207,11 +292,11 @@ void nas::write_pdu(uint32_t lcid, byte_buffer_t *pdu) { } uint32_t nas::get_ul_count() { - return count_ul; + return ctxt.tx_count; } bool nas::get_s_tmsi(LIBLTE_RRC_S_TMSI_STRUCT *s_tmsi) { - if (is_guti_set) { + if (have_guti) { s_tmsi->mmec = guti.mme_code; s_tmsi->m_tmsi = guti.m_tmsi; return true; @@ -220,11 +305,26 @@ bool nas::get_s_tmsi(LIBLTE_RRC_S_TMSI_STRUCT *s_tmsi) { } } +bool nas::get_k_asme(uint8_t *k_asme_, uint32_t n) { + if(!have_ctxt) { + nas_log->error("K_asme requested before security context established\n"); + return false; + } + if(NULL == k_asme_ || n < 32) { + nas_log->error("Invalid parameters to get_k_asme"); + return false; + } + + memcpy(k_asme_, ctxt.k_asme, 32); + return true; +} + /******************************************************************************* Security *******************************************************************************/ -void nas::integrity_generate(uint8_t *key_128, +void nas::integrity_generate(uint8_t integ_algo, + uint8_t *key_128, uint32_t count, uint8_t rb_id, uint8_t direction, @@ -269,6 +369,16 @@ void nas::cipher_decrypt() { } +bool nas::check_cap_replay(LIBLTE_MME_UE_SECURITY_CAPABILITIES_STRUCT *caps) +{ + for(uint32_t i=0; i<8; i++) { + if(caps->eea[i] != eea_caps[i] || caps->eia[i] != eia_caps[i]) { + return false; + } + } + return true; +} + /******************************************************************************* Parsers @@ -289,8 +399,8 @@ void nas::parse_attach_accept(uint32_t lcid, byte_buffer_t *pdu) { //FIXME: Handle tai_list if (attach_accept.guti_present) { memcpy(&guti, &attach_accept.guti.guti, sizeof(LIBLTE_MME_EPS_MOBILE_ID_GUTI_STRUCT)); - is_guti_set = true; - // TODO: log message to console + have_guti = true; + write_guti_file(guti); } if (attach_accept.lai_present) { } @@ -358,10 +468,9 @@ void nas::parse_attach_accept(uint32_t lcid, byte_buffer_t *pdu) { state = EMM_STATE_REGISTERED; current_plmn = selecting_plmn; - count_dl++; + ctxt.rx_count++; // Send EPS bearer context accept and attach complete - count_ul++; act_def_eps_bearer_context_accept.eps_bearer_id = eps_bearer_id; act_def_eps_bearer_context_accept.proc_transaction_id = transaction_id; act_def_eps_bearer_context_accept.protocol_cnfg_opts_present = false; @@ -369,10 +478,11 @@ void nas::parse_attach_accept(uint32_t lcid, byte_buffer_t *pdu) { &attach_complete.esm_msg); liblte_mme_pack_attach_complete_msg(&attach_complete, LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY_AND_CIPHERED, - count_ul, + ctxt.tx_count, (LIBLTE_BYTE_MSG_STRUCT *) pdu); - integrity_generate(&k_nas_int[16], - count_ul, + integrity_generate(ctxt.integ_algo, + &k_nas_int[16], + ctxt.tx_count, lcid - 1, SECURITY_DIRECTION_UPLINK, &pdu->msg[5], @@ -384,6 +494,7 @@ void nas::parse_attach_accept(uint32_t lcid, byte_buffer_t *pdu) { nas_log->info("Sending Attach Complete\n"); rrc->write_sdu(lcid, pdu); + ctxt.tx_count++; } else { nas_log->info("Not handling attach type %u\n", attach_accept.eps_attach_result); @@ -407,7 +518,7 @@ void nas::parse_authentication_request(uint32_t lcid, byte_buffer_t *pdu) { LIBLTE_MME_AUTHENTICATION_REQUEST_MSG_STRUCT auth_req; LIBLTE_MME_AUTHENTICATION_RESPONSE_MSG_STRUCT auth_res; - nas_log->info("Received Authentication Request\n");; + nas_log->info("Received Authentication Request\n"); liblte_mme_unpack_authentication_request_msg((LIBLTE_BYTE_MSG_STRUCT *) pdu, &auth_req); // Reuse the pdu for the response message @@ -422,7 +533,15 @@ void nas::parse_authentication_request(uint32_t lcid, byte_buffer_t *pdu) { bool net_valid; uint8_t res[16]; - usim->generate_authentication_response(auth_req.rand, auth_req.autn, mcc, mnc, &net_valid, res); + usim->generate_authentication_response(auth_req.rand, auth_req.autn, mcc, mnc, + &net_valid, res, ctxt.k_asme); + nas_log->info("Generated k_asme=%s\n", hex_to_string(ctxt.k_asme, 32).c_str()); + if(LIBLTE_MME_TYPE_OF_SECURITY_CONTEXT_FLAG_NATIVE == auth_req.nas_ksi.tsc_flag) { + ctxt.ksi = auth_req.nas_ksi.nas_ksi; + } else { + nas_log->error("NAS mapped security context not currently supported\n"); + nas_log->console("Warning: NAS mapped security context not currently supported\n"); + } if (net_valid) { nas_log->info("Network authentication successful\n"); @@ -438,9 +557,6 @@ void nas::parse_authentication_request(uint32_t lcid, byte_buffer_t *pdu) { nas_log->console("Warning: Network authentication failure\n"); pool->deallocate(pdu); } - - // Reset DL counter (as per 24.301 5.4.3.2) - count_dl = 0; } void nas::parse_authentication_reject(uint32_t lcid, byte_buffer_t *pdu) { @@ -451,108 +567,154 @@ void nas::parse_authentication_reject(uint32_t lcid, byte_buffer_t *pdu) { } void nas::parse_identity_request(uint32_t lcid, byte_buffer_t *pdu) { - nas_log->error("TODO:parse_identity_request\n"); + LIBLTE_MME_ID_REQUEST_MSG_STRUCT id_req; + LIBLTE_MME_ID_RESPONSE_MSG_STRUCT id_resp; + + liblte_mme_unpack_identity_request_msg((LIBLTE_BYTE_MSG_STRUCT *) pdu, &id_req); + nas_log->info("Received Identity Request. ID type: %d\n", id_req.id_type); + + switch(id_req.id_type) { + case LIBLTE_MME_MOBILE_ID_TYPE_IMSI: + id_resp.mobile_id.type_of_id = LIBLTE_MME_MOBILE_ID_TYPE_IMSI; + usim->get_imsi_vec(id_resp.mobile_id.imsi, 15); + break; + case LIBLTE_MME_MOBILE_ID_TYPE_IMEI: + id_resp.mobile_id.type_of_id = LIBLTE_MME_MOBILE_ID_TYPE_IMEI; + usim->get_imei_vec(id_resp.mobile_id.imei, 15); + break; + default: + nas_log->error("Unhandled ID type: %d\n"); + pool->deallocate(pdu); + return; + } + + pdu->reset(); + liblte_mme_pack_identity_response_msg(&id_resp, (LIBLTE_BYTE_MSG_STRUCT *) pdu); + rrc->write_sdu(lcid, pdu); } -void nas::parse_security_mode_command(uint32_t lcid, byte_buffer_t *pdu) { - bool success; +void nas::parse_security_mode_command(uint32_t lcid, byte_buffer_t *pdu) +{ LIBLTE_MME_SECURITY_MODE_COMMAND_MSG_STRUCT sec_mode_cmd; LIBLTE_MME_SECURITY_MODE_COMPLETE_MSG_STRUCT sec_mode_comp; - LIBLTE_MME_SECURITY_MODE_REJECT_MSG_STRUCT sec_mode_rej; - nas_log->info("Received Security Mode Command\n"); liblte_mme_unpack_security_mode_command_msg((LIBLTE_BYTE_MSG_STRUCT *) pdu, &sec_mode_cmd); + nas_log->info("Received Security Mode Command ksi: %d, eea: %s, eia: %s\n", + sec_mode_cmd.nas_ksi.nas_ksi, + ciphering_algorithm_id_text[sec_mode_cmd.selected_nas_sec_algs.type_of_eea], + integrity_algorithm_id_text[sec_mode_cmd.selected_nas_sec_algs.type_of_eia]); - ksi = sec_mode_cmd.nas_ksi.nas_ksi; - cipher_algo = (CIPHERING_ALGORITHM_ID_ENUM) sec_mode_cmd.selected_nas_sec_algs.type_of_eea; - integ_algo = (INTEGRITY_ALGORITHM_ID_ENUM) sec_mode_cmd.selected_nas_sec_algs.type_of_eia; - // FIXME: Handle nonce_ue, nonce_mme - // FIXME: Currently only handling ciphering EEA0 (null) and integrity EIA1,EIA2 - // FIXME: Use selected_nas_sec_algs to choose correct algos + if(sec_mode_cmd.nas_ksi.tsc_flag != LIBLTE_MME_TYPE_OF_SECURITY_CONTEXT_FLAG_NATIVE) { + nas_log->error("Mapped security context not supported\n"); + pool->deallocate(pdu); + return; + } - nas_log->debug("Security details: ksi=%d, eea=%s, eia=%s\n", - ksi, ciphering_algorithm_id_text[cipher_algo], integrity_algorithm_id_text[integ_algo]); + if(sec_mode_cmd.nas_ksi.nas_ksi != ctxt.ksi) + { + nas_log->warning("Sending Security Mode Reject due to key set ID mismatch\n"); + send_security_mode_reject(LIBLTE_MME_EMM_CAUSE_SECURITY_MODE_REJECTED_UNSPECIFIED); + pool->deallocate(pdu); + return; + } + // MME is setting up security context + // TODO: check nonce (not sent by Amari) - if (CIPHERING_ALGORITHM_ID_EEA0 != cipher_algo || - (INTEGRITY_ALGORITHM_ID_128_EIA2 != integ_algo && - INTEGRITY_ALGORITHM_ID_128_EIA1 != integ_algo) || - sec_mode_cmd.nas_ksi.tsc_flag != LIBLTE_MME_TYPE_OF_SECURITY_CONTEXT_FLAG_NATIVE) { - sec_mode_rej.emm_cause = LIBLTE_MME_EMM_CAUSE_UE_SECURITY_CAPABILITIES_MISMATCH; + // Check capabilities replay + if(!check_cap_replay(&sec_mode_cmd.ue_security_cap)) { nas_log->warning("Sending Security Mode Reject due to security capabilities mismatch\n"); - nas_log->console("Unsupported Security Mode Command settings: use ciphering algorithm EEA0 and integrity check EIA1 or EIA2.\n"); - success = false; + send_security_mode_reject(LIBLTE_MME_EMM_CAUSE_UE_SECURITY_CAPABILITIES_MISMATCH); + pool->deallocate(pdu); + return; + } + + // Reset counterd (as per 24.301 5.4.3.2) + ctxt.rx_count = 0; + ctxt.tx_count = 0; + + ctxt.cipher_algo = (CIPHERING_ALGORITHM_ID_ENUM) sec_mode_cmd.selected_nas_sec_algs.type_of_eea; + ctxt.integ_algo = (INTEGRITY_ALGORITHM_ID_ENUM) sec_mode_cmd.selected_nas_sec_algs.type_of_eia; + + // Check capabilities + if(!eea_caps[ctxt.cipher_algo] || !eia_caps[ctxt.integ_algo]) { + nas_log->warning("Sending Security Mode Reject due to security capabilities mismatch\n"); + send_security_mode_reject(LIBLTE_MME_EMM_CAUSE_UE_SECURITY_CAPABILITIES_MISMATCH); + pool->deallocate(pdu); + return; + } + + // Generate NAS keys + usim->generate_nas_keys(ctxt.k_asme, k_nas_enc, k_nas_int, ctxt.cipher_algo, ctxt.integ_algo); + nas_log->debug_hex(k_nas_enc, 32, "NAS encryption key - k_nas_enc"); + nas_log->debug_hex(k_nas_int, 32, "NAS integrity key - k_nas_int"); + + nas_log->debug("Generating integrity check. integ_algo:%d, count_dl:%d, lcid:%d\n", + ctxt.integ_algo, ctxt.rx_count, lcid); + + // Check incoming MAC + uint8_t *inMAC = &pdu->msg[1]; + uint8_t genMAC[4]; + integrity_generate(ctxt.integ_algo, + &k_nas_int[16], + ctxt.rx_count, + lcid - 1, + SECURITY_DIRECTION_DOWNLINK, + &pdu->msg[5], + pdu->N_bytes - 5, + genMAC); + + nas_log->info_hex(inMAC, 4, "Incoming PDU MAC:"); + nas_log->info_hex(genMAC, 4, "Generated PDU MAC:"); + + ctxt.rx_count++; + + bool match = true; + for (int i = 0; i < 4; i++) { + if (inMAC[i] != genMAC[i]) { + match = false; + } + } + if(!match) { + nas_log->warning("Sending Security Mode Reject due to integrity check failure\n"); + send_security_mode_reject(LIBLTE_MME_EMM_CAUSE_SECURITY_MODE_REJECTED_UNSPECIFIED); + pool->deallocate(pdu); + return; + } + + // Take security context into use + have_ctxt = true; + + if (sec_mode_cmd.imeisv_req_present && LIBLTE_MME_IMEISV_REQUESTED == sec_mode_cmd.imeisv_req) { + sec_mode_comp.imeisv_present = true; + sec_mode_comp.imeisv.type_of_id = LIBLTE_MME_MOBILE_ID_TYPE_IMEISV; + usim->get_imei_vec(sec_mode_comp.imeisv.imeisv, 15); + sec_mode_comp.imeisv.imeisv[14] = 5; + sec_mode_comp.imeisv.imeisv[15] = 3; } else { - // Generate NAS encryption key and integrity protection key - usim->generate_nas_keys(k_nas_enc, k_nas_int, cipher_algo, integ_algo); - nas_log->debug_hex(k_nas_enc, 32, "NAS encryption key - k_nas_enc"); - nas_log->debug_hex(k_nas_int, 32, "NAS integrity key - k_nas_int"); - - // Check incoming MAC - uint8_t *inMAC = &pdu->msg[1]; - uint8_t genMAC[4]; - integrity_generate(&k_nas_int[16], - count_dl, - lcid - 1, - SECURITY_DIRECTION_DOWNLINK, - &pdu->msg[5], - pdu->N_bytes - 5, - genMAC); - - nas_log->info_hex(inMAC, 4, "Incoming PDU MAC:"); - nas_log->info_hex(genMAC, 4, "Generated PDU MAC:"); - - bool match = true; - for (int i = 0; i < 4; i++) { - if (inMAC[i] != genMAC[i]) { - match = false; - } - } - if (!match) { - sec_mode_rej.emm_cause = LIBLTE_MME_EMM_CAUSE_SECURITY_MODE_REJECTED_UNSPECIFIED; - nas_log->warning("Sending Security Mode Reject due to integrity check failure\n"); - success = false; - } else { - - if (sec_mode_cmd.imeisv_req_present && LIBLTE_MME_IMEISV_REQUESTED == sec_mode_cmd.imeisv_req) { - sec_mode_comp.imeisv_present = true; - sec_mode_comp.imeisv.type_of_id = LIBLTE_MME_MOBILE_ID_TYPE_IMEISV; - usim->get_imei_vec(sec_mode_comp.imeisv.imeisv, 15); - sec_mode_comp.imeisv.imeisv[14] = 5; - sec_mode_comp.imeisv.imeisv[15] = 3; - } else { - sec_mode_comp.imeisv_present = false; - } - - // Reuse pdu for response - pdu->reset(); - liblte_mme_pack_security_mode_complete_msg(&sec_mode_comp, - LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY_AND_CIPHERED, - count_ul, - (LIBLTE_BYTE_MSG_STRUCT *) pdu); - integrity_generate(&k_nas_int[16], - count_ul, - lcid - 1, - SECURITY_DIRECTION_UPLINK, - &pdu->msg[5], - pdu->N_bytes - 5, - &pdu->msg[1]); - nas_log->info("Sending Security Mode Complete nas_count_ul=%d, RB=%s\n", - count_ul, - rrc->get_rb_name(lcid).c_str()); - success = true; - } + sec_mode_comp.imeisv_present = false; } - count_dl++; - - if (!success) { - // Reuse pdu for response - pdu->reset(); - liblte_mme_pack_security_mode_reject_msg(&sec_mode_rej, (LIBLTE_BYTE_MSG_STRUCT *) pdu); - } - - rrc->write_sdu(lcid, pdu); + // Send response + byte_buffer_t *sdu = pool_allocate; + liblte_mme_pack_security_mode_complete_msg(&sec_mode_comp, + LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY_AND_CIPHERED, + ctxt.tx_count, + (LIBLTE_BYTE_MSG_STRUCT *) sdu); + integrity_generate(ctxt.integ_algo, + &k_nas_int[16], + ctxt.tx_count, + lcid - 1, + SECURITY_DIRECTION_UPLINK, + &sdu->msg[5], + sdu->N_bytes - 5, + &sdu->msg[1]); + nas_log->info("Sending Security Mode Complete nas_current_ctxt.tx_count=%d, RB=%s\n", + ctxt.tx_count, + rrc->get_rb_name(lcid).c_str()); + rrc->write_sdu(lcid, sdu); + ctxt.tx_count++; + pool->deallocate(pdu); } void nas::parse_service_reject(uint32_t lcid, byte_buffer_t *pdu) { @@ -561,10 +723,15 @@ void nas::parse_service_reject(uint32_t lcid, byte_buffer_t *pdu) { void nas::parse_esm_information_request(uint32_t lcid, byte_buffer_t *pdu) { nas_log->error("TODO:parse_esm_information_request\n"); + } void nas::parse_emm_information(uint32_t lcid, byte_buffer_t *pdu) { - nas_log->error("TODO:parse_emm_information\n"); + liblte_mme_unpack_emm_information_msg((LIBLTE_BYTE_MSG_STRUCT *) pdu, &emm_info); + std::string str = emm_info_str(&emm_info); + nas_log->info("Received EMM Information: %s\n", str.c_str()); + nas_log->console("%s\n", str.c_str()); + ctxt.rx_count++; } /******************************************************************************* @@ -579,25 +746,13 @@ void nas::send_attach_request() { attach_req.eps_attach_type = LIBLTE_MME_EPS_ATTACH_TYPE_EPS_ATTACH; for (i = 0; i < 8; i++) { - attach_req.ue_network_cap.eea[i] = false; - attach_req.ue_network_cap.eia[i] = false; + attach_req.ue_network_cap.eea[i] = eea_caps[i]; + attach_req.ue_network_cap.eia[i] = eia_caps[i]; } - attach_req.ue_network_cap.eea[0] = true; // EEA0 supported - attach_req.ue_network_cap.eia[0] = true; // EIA0 supported - attach_req.ue_network_cap.eia[1] = true; // EIA1 supported - attach_req.ue_network_cap.eia[2] = true; // EIA2 supported - - attach_req.ue_network_cap.uea_present = false; // UMTS encryption algos - attach_req.ue_network_cap.uia_present = false; // UMTS integrity algos - - attach_req.ms_network_cap_present = false; // A/Gb mode (2G) or Iu mode (3G) - - attach_req.eps_mobile_id.type_of_id = LIBLTE_MME_EPS_MOBILE_ID_TYPE_IMSI; - usim->get_imsi_vec(attach_req.eps_mobile_id.imsi, 15); - - // ESM message (PDN connectivity request) for first default bearer - gen_pdn_connectivity_request(&attach_req.esm_msg); + attach_req.ue_network_cap.uea_present = false; // UMTS encryption algos + attach_req.ue_network_cap.uia_present = false; // UMTS integrity algos + attach_req.ms_network_cap_present = false; // A/Gb mode (2G) or Iu mode (3G) attach_req.old_p_tmsi_signature_present = false; attach_req.additional_guti_present = false; attach_req.last_visited_registered_tai_present = false; @@ -613,11 +768,47 @@ void nas::send_attach_request() { attach_req.device_properties_present = false; attach_req.old_guti_type_present = false; - // Pack the message - liblte_mme_pack_attach_request_msg(&attach_req, (LIBLTE_BYTE_MSG_STRUCT *) msg); + // ESM message (PDN connectivity request) for first default bearer + gen_pdn_connectivity_request(&attach_req.esm_msg); + + // GUTI or IMSI attach + if(have_guti && have_ctxt) { + attach_req.eps_mobile_id.type_of_id = LIBLTE_MME_EPS_MOBILE_ID_TYPE_GUTI; + memcpy(&attach_req.eps_mobile_id.guti, &guti, sizeof(LIBLTE_MME_EPS_MOBILE_ID_GUTI_STRUCT)); + attach_req.old_guti_type = LIBLTE_MME_GUTI_TYPE_NATIVE; + attach_req.old_guti_type_present = true; + attach_req.nas_ksi.tsc_flag = LIBLTE_MME_TYPE_OF_SECURITY_CONTEXT_FLAG_NATIVE; + attach_req.nas_ksi.nas_ksi = ctxt.ksi; + nas_log->info("Requesting GUTI attach. " + "m_tmsi: %x, mcc: %x, mnc: %x, mme_group_id: %x, mme_code: %x\n", + guti.m_tmsi, guti.mcc, guti.mnc, guti.mme_group_id, guti.mme_code); + liblte_mme_pack_attach_request_msg(&attach_req, + LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY, + ctxt.tx_count, + (LIBLTE_BYTE_MSG_STRUCT *) msg); + + // Add MAC + integrity_generate(ctxt.integ_algo, + &k_nas_int[16], + ctxt.tx_count, + cfg.lcid-1, + SECURITY_DIRECTION_UPLINK, + &msg->msg[5], + msg->N_bytes - 5, + &msg->msg[1]); + } else { + attach_req.eps_mobile_id.type_of_id = LIBLTE_MME_EPS_MOBILE_ID_TYPE_IMSI; + usim->get_imsi_vec(attach_req.eps_mobile_id.imsi, 15); + nas_log->info("Requesting IMSI attach. imsi: %s\n", usim->get_imsi_str()); + liblte_mme_pack_attach_request_msg(&attach_req, (LIBLTE_BYTE_MSG_STRUCT *) msg); + } nas_log->info("Sending attach request\n"); rrc->write_sdu(cfg.lcid, msg); + + if(have_ctxt) { + ctxt.tx_count++; + } } void nas::gen_pdn_connectivity_request(LIBLTE_BYTE_MSG_STRUCT *msg) { @@ -641,27 +832,36 @@ void nas::gen_pdn_connectivity_request(LIBLTE_BYTE_MSG_STRUCT *msg) { liblte_mme_pack_pdn_connectivity_request_msg(&pdn_con_req, msg); } +void nas::send_security_mode_reject(uint8_t cause) { + byte_buffer_t *msg = pool_allocate; + + LIBLTE_MME_SECURITY_MODE_REJECT_MSG_STRUCT sec_mode_rej; + sec_mode_rej.emm_cause = cause; + liblte_mme_pack_security_mode_reject_msg(&sec_mode_rej, (LIBLTE_BYTE_MSG_STRUCT *) msg); + rrc->write_sdu(cfg.lcid, msg); +} + void nas::send_identity_response() {} void nas::send_service_request() { byte_buffer_t *msg = pool_allocate; - count_ul++; // Pack the service request message directly msg->msg[0] = (LIBLTE_MME_SECURITY_HDR_TYPE_SERVICE_REQUEST << 4) | (LIBLTE_MME_PD_EPS_MOBILITY_MANAGEMENT); msg->N_bytes++; - msg->msg[1] = (ksi & 0x07) << 5; - msg->msg[1] |= count_ul & 0x1F; + msg->msg[1] = (ctxt.ksi & 0x07) << 5; + msg->msg[1] |= ctxt.tx_count & 0x1F; msg->N_bytes++; uint8_t mac[4]; - integrity_generate(&k_nas_int[16], - count_ul, - cfg.lcid-1, - SECURITY_DIRECTION_UPLINK, - &msg->msg[0], - 2, - &mac[0]); + integrity_generate(ctxt.integ_algo, + &k_nas_int[16], + ctxt.tx_count, + cfg.lcid-1, + SECURITY_DIRECTION_UPLINK, + &msg->msg[0], + 2, + &mac[0]); // Set the short MAC msg->msg[2] = mac[2]; msg->N_bytes++; @@ -669,8 +869,239 @@ void nas::send_service_request() { msg->N_bytes++; nas_log->info("Sending service request\n"); rrc->write_sdu(cfg.lcid, msg); + ctxt.tx_count++; } void nas::send_esm_information_response() {} +bool nas::read_guti_file(LIBLTE_MME_EPS_MOBILE_ID_GUTI_STRUCT *guti) +{ + std::ifstream file; + std::string line; + if (!guti) { + return false; + } + + const char *m_tmsi_str = "m_tmsi="; + size_t m_tmsi_str_len = strlen(m_tmsi_str); + const char *mcc_str = "mcc="; + size_t mcc_str_len = strlen(mcc_str); + const char *mnc_str = "mnc="; + size_t mnc_str_len = strlen(mnc_str); + const char *mme_group_id_str = "mme_group_id="; + size_t mme_group_id_str_len = strlen(mme_group_id_str); + const char *mme_code_str = "mme_code="; + size_t mme_code_str_len = strlen(mme_code_str); + + file.open(".guti", std::ios::in); + if (file.is_open()) { + bool read_ok = true; + if (std::getline(file, line)) { + if (!line.substr(0,m_tmsi_str_len).compare(m_tmsi_str)) { + guti->m_tmsi = atoi(line.substr(m_tmsi_str_len).c_str()); + } else { + read_ok = false; + } + } else { + read_ok = false; + } + if (std::getline(file, line)) { + if (!line.substr(0,mcc_str_len).compare(mcc_str)) { + guti->mcc = atoi(line.substr(mcc_str_len).c_str()); + } else { + read_ok = false; + } + } else { + read_ok = false; + } + if (std::getline(file, line)) { + if (!line.substr(0,mnc_str_len).compare(mnc_str)) { + guti->mnc = atoi(line.substr(mnc_str_len).c_str()); + } else { + read_ok = false; + } + } else { + read_ok = false; + } + if (std::getline(file, line)) { + if (!line.substr(0,mme_group_id_str_len).compare(mme_group_id_str)) { + guti->mme_group_id = atoi(line.substr(mme_group_id_str_len).c_str()); + } else { + read_ok = false; + } + } else { + read_ok = false; + } + if (std::getline(file, line)) { + if (!line.substr(0,mme_code_str_len).compare(mme_code_str)) { + guti->mme_code = atoi(line.substr(mme_code_str_len).c_str()); + } else { + read_ok = false; + } + } else { + read_ok = false; + } + file.close(); + if (read_ok) { + nas_log->info("Read GUTI from file .guti. " + "m_tmsi: %x, mcc: %x, mnc: %x, mme_group_id: %x, mme_code: %x\n", + guti->m_tmsi, guti->mcc, guti->mnc, guti->mme_group_id, guti->mme_code); + return true; + } else { + nas_log->error("Invalid GUTI file format\n"); + return false; + } + } else { + return false; + } +} + +bool nas::write_guti_file(LIBLTE_MME_EPS_MOBILE_ID_GUTI_STRUCT guti) { + std::ofstream file; + if (!have_guti) { + return false; + } + file.open(".guti", std::ios::out | std::ios::trunc); + if (file.is_open()) { + file << "m_tmsi=" << (int) guti.m_tmsi << std::endl; + file << "mcc=" << (int) guti.mcc << std::endl; + file << "mnc=" << (int) guti.mnc << std::endl; + file << "mme_group_id=" << (int) guti.mme_group_id << std::endl; + file << "mme_code=" << (int) guti.mme_code << std::endl; + nas_log->info("Saved GUTI to file .guti. " + "m_tmsi: %x, mcc: %x, mnc: %x, mme_group_id: %x, mme_code: %x\n", + guti.m_tmsi, guti.mcc, guti.mnc, guti.mme_group_id, guti.mme_code); + file.close(); + return true; + } else { + return false; + } +} + +bool nas::read_ctxt_file(nas_sec_ctxt *ctxt) +{ + std::ifstream file; + std::string line; + if (!ctxt) { + return false; + } + + const char *ksi_str = "ksi="; + size_t ksi_str_len = strlen(ksi_str); + const char *k_asme_str = "k_asme="; + size_t k_asme_str_len = strlen(k_asme_str); + const char *tx_count_str = "tx_count="; + size_t tx_count_str_len = strlen(tx_count_str); + const char *rx_count_str = "rx_count="; + size_t rx_count_str_len = strlen(rx_count_str); + const char *int_alg_str = "int_alg="; + size_t int_alg_str_len = strlen(int_alg_str); + const char *enc_alg_str = "enc_alg="; + size_t enc_alg_str_len = strlen(enc_alg_str); + + file.open(".ctxt", std::ios::in); + if (file.is_open()) { + bool read_ok = true; + if (std::getline(file, line)) { + if (!line.substr(0,ksi_str_len).compare(ksi_str)) { + ctxt->ksi = atoi(line.substr(ksi_str_len).c_str()); + } else { + read_ok = false; + } + } else { + read_ok = false; + } + if (std::getline(file, line)) { + if (!line.substr(0,k_asme_str_len).compare(k_asme_str)) { + std::string tmp = line.substr(k_asme_str_len); + if(!string_to_hex(tmp, ctxt->k_asme, 32)) { + read_ok = false; + } + } else { + read_ok = false; + } + } else { + read_ok = false; + } + if (std::getline(file, line)) { + if (!line.substr(0,tx_count_str_len).compare(tx_count_str)) { + ctxt->tx_count = atoi(line.substr(tx_count_str_len).c_str()); + } else { + read_ok = false; + } + } else { + read_ok = false; + } + if (std::getline(file, line)) { + if (!line.substr(0,rx_count_str_len).compare(rx_count_str)) { + ctxt->rx_count = atoi(line.substr(rx_count_str_len).c_str()); + } else { + read_ok = false; + } + } else { + read_ok = false; + } + if (std::getline(file, line)) { + if (!line.substr(0,int_alg_str_len).compare(int_alg_str)) { + ctxt->integ_algo = (srslte::INTEGRITY_ALGORITHM_ID_ENUM)atoi(line.substr(int_alg_str_len).c_str()); + } else { + read_ok = false; + } + } else { + read_ok = false; + } + if (std::getline(file, line)) { + if (!line.substr(0,enc_alg_str_len).compare(enc_alg_str)) { + ctxt->cipher_algo = (srslte::CIPHERING_ALGORITHM_ID_ENUM)atoi(line.substr(enc_alg_str_len).c_str()); + } else { + read_ok = false; + } + } else { + read_ok = false; + } + file.close(); + if (read_ok) { + nas_log->info("Read security ctxt from file .ctxt. " + "ksi: %x, k_asme: %s, " + "tx_count: %x, rx_count: %x, " + "int_alg: %d, enc_alg: %d\n", + ctxt->ksi, hex_to_string(ctxt->k_asme,32).c_str(), + ctxt->tx_count, ctxt->rx_count, + ctxt->integ_algo, ctxt->cipher_algo); + return true; + } else { + nas_log->error("Invalid security ctxt file format\n"); + return false; + } + } else { + return false; + } +} + +bool nas::write_ctxt_file(nas_sec_ctxt ctxt) +{ + std::ofstream file; + file.open(".ctxt", std::ios::out | std::ios::trunc); + if (file.is_open()) { + file << "ksi=" << (int) ctxt.ksi << std::endl; + file << "k_asme=" << hex_to_string(ctxt.k_asme, 32) << std::endl; + file << "tx_count=" << (int) ctxt.tx_count << std::endl; + file << "rx_count=" << (int) ctxt.rx_count << std::endl; + file << "int_alg=" << (int) ctxt.integ_algo << std::endl; + file << "enc_alg=" << (int) ctxt.cipher_algo << std::endl; + nas_log->info("Saved security ctxt to file .ctxt. " + "ksi: %x, k_asme: %s, " + "tx_count: %x, rx_count: %x, " + "int_alg: %d, enc_alg: %d\n", + ctxt.ksi, hex_to_string(ctxt.k_asme,32).c_str(), + ctxt.tx_count, ctxt.rx_count, + ctxt.integ_algo, ctxt.cipher_algo); + file.close(); + return true; + } else { + return false; + } +} + + } // namespace srsue diff --git a/srsue/src/upper/rrc.cc b/srsue/src/upper/rrc.cc index b33332f1d..665761a04 100644 --- a/srsue/src/upper/rrc.cc +++ b/srsue/src/upper/rrc.cc @@ -27,8 +27,9 @@ #include #include -#include #include +#include +#include #include "upper/rrc.h" #include "srslte/asn1/liblte_rrc.h" #include "srslte/common/security.h" @@ -99,7 +100,6 @@ void rrc::init(phy_interface_rrc *phy_, args.supported_bands[0] = 7; args.nof_supported_bands = 1; args.feature_group = 0xe6041c00; - args.stmsi_attach = false; t301 = mac_timers->timer_get_unique_id(); t310 = mac_timers->timer_get_unique_id(); @@ -117,6 +117,9 @@ void rrc::init(phy_interface_rrc *phy_, set_rrc_default(); set_phy_default(); set_mac_default(); + + // set seed for rand (used in attach) + srand(time(NULL)); } void rrc::stop() { @@ -592,66 +595,6 @@ void rrc::timer_expired(uint32_t timeout_id) { * *******************************************************************************/ -bool rrc::read_stimsi_file(LIBLTE_RRC_S_TMSI_STRUCT *s_tmsi) { - std::ifstream file; - std::string line; - if (!s_tmsi) { - return false; - } - - const char *mmec_str = "mmec="; - size_t mmec_str_len = strlen(mmec_str); - const char *mtmsi_str = "mtmsi="; - size_t mtmsi_str_len = strlen(mtmsi_str); - - file.open(".stmsi", std::ios::in); - if (file.is_open()) { - bool read_ok = true; - if (std::getline(file, line)) { - if (!line.substr(0,mmec_str_len).compare(mmec_str)) { - s_tmsi->mmec = atoi(line.substr(5).c_str()); - } else { - read_ok = false; - } - } else { - read_ok = false; - } - if (std::getline(file, line)) { - if (!line.substr(0,mtmsi_str_len).compare(mtmsi_str)) { - s_tmsi->m_tmsi = atoi(line.substr(mtmsi_str_len).c_str()); - } else { - read_ok = false; - } - } else { - read_ok = false; - } - file.close(); - if (read_ok) { - rrc_log->info("Read S-TMSI value: %x:%x\n", s_tmsi->mmec, s_tmsi->m_tmsi); - return true; - } else { - rrc_log->error("Invalid s-tmsi persistent file format\n"); - return false; - } - } else { - return false; - } -} - -bool rrc::write_stimsi_file(LIBLTE_RRC_S_TMSI_STRUCT s_tmsi) { - std::ofstream file; - file.open(".stmsi", std::ios::out | std::ios::trunc); - if (file.is_open()) { - file << "mmec=" << (int) s_tmsi.mmec << std::endl; - file << "mtmsi=" << (int) s_tmsi.m_tmsi << std::endl; - rrc_log->info("Saved S-TMSI in persistent file: %x:%x\n", s_tmsi.mmec, s_tmsi.m_tmsi); - file.close(); - return true; - } else { - return false; - } -} - void rrc::send_con_request() { rrc_log->debug("Preparing RRC Connection Request\n"); LIBLTE_RRC_UL_CCCH_MSG_STRUCT ul_ccch_msg; @@ -659,30 +602,13 @@ void rrc::send_con_request() { // Prepare ConnectionRequest packet ul_ccch_msg.msg_type = LIBLTE_RRC_UL_CCCH_MSG_TYPE_RRC_CON_REQ; - bool valid_stmsi = false; - if (nas->get_s_tmsi(&s_tmsi) || (args.stmsi_attach && !first_stimsi_attempt)) { + if (nas->get_s_tmsi(&s_tmsi)) { ul_ccch_msg.msg.rrc_con_req.ue_id_type = LIBLTE_RRC_CON_REQ_UE_ID_TYPE_S_TMSI; - if (nas->get_s_tmsi(&s_tmsi)) { - ul_ccch_msg.msg.rrc_con_req.ue_id.s_tmsi = s_tmsi; - valid_stmsi = true; - } else { - first_stimsi_attempt = true; - if (args.stmsi_value.m_tmsi) { - ul_ccch_msg.msg.rrc_con_req.ue_id.s_tmsi = args.stmsi_value; - valid_stmsi = true; - } else { - if (!read_stimsi_file(&ul_ccch_msg.msg.rrc_con_req.ue_id.s_tmsi)) { - rrc_log->warning("Could not read S-TMSI from persistent file. Trying normal attach.\n"); - } else { - valid_stmsi = true; - } - } - } - } - if (!valid_stmsi) { + ul_ccch_msg.msg.rrc_con_req.ue_id.s_tmsi = s_tmsi; + } else { ul_ccch_msg.msg.rrc_con_req.ue_id_type = LIBLTE_RRC_CON_REQ_UE_ID_TYPE_RANDOM_VALUE; - ul_ccch_msg.msg.rrc_con_req.ue_id.random = 1000; + ul_ccch_msg.msg.rrc_con_req.ue_id.random = rand() % 2^40; } ul_ccch_msg.msg.rrc_con_req.cause = LIBLTE_RRC_CON_REQ_EST_CAUSE_MO_SIGNALLING; @@ -956,14 +882,6 @@ void rrc::handle_rrc_con_reconfig(uint32_t lcid, LIBLTE_RRC_CONNECTION_RECONFIGU nas_sdu->N_bytes = reconfig->ded_info_nas_list[i].N_bytes; nas->write_pdu(lcid, nas_sdu); } - - // Get S-TMSI from NAS and store it in persistent file - LIBLTE_RRC_S_TMSI_STRUCT s_tmsi; - if (nas->get_s_tmsi(&s_tmsi)) { - if (!write_stimsi_file(s_tmsi)) { - rrc_log->warning("Could not store S-TMSI in persistent file\n"); - } - } } /* Actions upon reception of RRCConnectionRelease 5.3.8.3 */ @@ -1247,7 +1165,9 @@ void rrc::parse_dl_dcch(uint32_t lcid, byte_buffer_t *pdu) { integ_algo = (INTEGRITY_ALGORITHM_ID_ENUM) dl_dcch_msg.msg.security_mode_cmd.sec_algs.int_alg; // Configure PDCP for security - usim->generate_as_keys(nas->get_ul_count(), k_rrc_enc, k_rrc_int, k_up_enc, k_up_int, cipher_algo, integ_algo); + uint8_t k_asme[32]; + nas->get_k_asme(k_asme, 32); + usim->generate_as_keys(k_asme, nas->get_ul_count()-1, k_rrc_enc, k_rrc_int, k_up_enc, k_up_int, cipher_algo, integ_algo); pdcp->config_security(lcid, k_rrc_enc, k_rrc_int, cipher_algo, integ_algo); send_security_mode_complete(lcid, pdu); break; diff --git a/srsue/src/upper/usim.cc b/srsue/src/upper/usim.cc index e1d2c204f..7ae53988b 100644 --- a/srsue/src/upper/usim.cc +++ b/srsue/src/upper/usim.cc @@ -39,9 +39,11 @@ usim::usim() : initiated(false) void usim::init(usim_args_t *args, srslte::log *usim_log_) { usim_log = usim_log_; + imsi_str = args->imsi; + imei_str = args->imei; - const char *imsi_str = args->imsi.c_str(); - const char *imei_str = args->imei.c_str(); + const char *imsi_c = args->imsi.c_str(); + const char *imei_c = args->imei.c_str(); uint32_t i; if(32 == args->op.length()) { @@ -63,7 +65,7 @@ void usim::init(usim_args_t *args, srslte::log *usim_log_) for(i=0; i<15; i++) { imsi *= 10; - imsi += imsi_str[i] - '0'; + imsi += imsi_c[i] - '0'; } } else { usim_log->error("Invalid length for ISMI: %d should be %d", args->imsi.length(), 15); @@ -75,7 +77,7 @@ void usim::init(usim_args_t *args, srslte::log *usim_log_) for(i=0; i<15; i++) { imei *= 10; - imei += imei_str[i] - '0'; + imei += imei_c[i] - '0'; } } else { usim_log->error("Invalid length for IMEI: %d should be %d", args->imei.length(), 15); @@ -103,16 +105,25 @@ void usim::stop() NAS interface *******************************************************************************/ -void usim::get_imsi_vec(uint8_t* imsi_, uint32_t n) +std::string usim::get_imsi_str() +{ + return imsi_str; +} +std::string usim::get_imei_str() +{ + return imei_str; +} + +bool usim::get_imsi_vec(uint8_t* imsi_, uint32_t n) { if (!initiated) { fprintf(stderr, "USIM not initiated!\n"); - return; + return false; } if(NULL == imsi_ || n < 15) { usim_log->error("Invalid parameters to get_imsi_vec"); - return; + return false; } uint64_t temp = imsi; @@ -120,18 +131,19 @@ void usim::get_imsi_vec(uint8_t* imsi_, uint32_t n) imsi_[i] = temp % 10; temp /= 10; } + return true; } -void usim::get_imei_vec(uint8_t* imei_, uint32_t n) +bool usim::get_imei_vec(uint8_t* imei_, uint32_t n) { if (!initiated) { fprintf(stderr, "USIM not initiated!\n"); - return; + return false; } if(NULL == imei_ || n < 15) { usim_log->error("Invalid parameters to get_imei_vec"); - return; + return false; } uint64 temp = imei; @@ -140,13 +152,14 @@ void usim::get_imei_vec(uint8_t* imei_, uint32_t n) imei_[i] = temp % 10; temp /= 10; } + return true; } -int usim::get_home_plmn_id(LIBLTE_RRC_PLMN_IDENTITY_STRUCT *home_plmn_id) +bool usim::get_home_plmn_id(LIBLTE_RRC_PLMN_IDENTITY_STRUCT *home_plmn_id) { if (!initiated) { fprintf(stderr, "USIM not initiated!\n"); - return -1; + return false; } int mcc_len = 3; @@ -180,7 +193,7 @@ int usim::get_home_plmn_id(LIBLTE_RRC_PLMN_IDENTITY_STRUCT *home_plmn_id) usim_log->info("Read Home PLMN Id=%s\n", plmn_id_to_string(*home_plmn_id).c_str()); - return 0; + return true; } void usim::generate_authentication_response(uint8_t *rand, @@ -188,16 +201,18 @@ void usim::generate_authentication_response(uint8_t *rand, uint16_t mcc, uint16_t mnc, bool *net_valid, - uint8_t *res) + uint8_t *res, + uint8_t *k_asme) { if(auth_algo_xor == auth_algo) { - gen_auth_res_xor(rand, autn_enb, mcc, mnc, net_valid, res); + gen_auth_res_xor(rand, autn_enb, mcc, mnc, net_valid, res, k_asme); } else { - gen_auth_res_milenage(rand, autn_enb, mcc, mnc, net_valid, res); + gen_auth_res_milenage(rand, autn_enb, mcc, mnc, net_valid, res, k_asme); } } -void usim::generate_nas_keys(uint8_t *k_nas_enc, +void usim::generate_nas_keys(uint8_t *k_asme, + uint8_t *k_nas_enc, uint8_t *k_nas_int, CIPHERING_ALGORITHM_ID_ENUM cipher_algo, INTEGRITY_ALGORITHM_ID_ENUM integ_algo) @@ -214,7 +229,8 @@ void usim::generate_nas_keys(uint8_t *k_nas_enc, RRC interface *******************************************************************************/ -void usim::generate_as_keys(uint32_t count_ul, +void usim::generate_as_keys(uint8_t *k_asme, + uint32_t count_ul, uint8_t *k_rrc_enc, uint8_t *k_rrc_int, uint8_t *k_up_enc, @@ -251,7 +267,8 @@ void usim::gen_auth_res_milenage( uint8_t *rand, uint16_t mcc, uint16_t mnc, bool *net_valid, - uint8_t *res) + uint8_t *res, + uint8_t *k_asme) { uint32_t i; uint8_t sqn[6]; @@ -320,7 +337,8 @@ void usim::gen_auth_res_xor(uint8_t *rand, uint16_t mcc, uint16_t mnc, bool *net_valid, - uint8_t *res) + uint8_t *res, + uint8_t *k_asme) { uint32_t i; uint8_t sqn[6];