From 3fe6dad32391e37babd0a3fc1495e5cb2c7be630 Mon Sep 17 00:00:00 2001 From: Andre Puschmann Date: Wed, 4 Apr 2018 11:53:36 +0200 Subject: [PATCH 01/24] adding hard SIM card support using PCSC --- CMakeLists.txt | 10 + cmake/modules/FindPCSCLite.cmake | 42 + lib/include/srslte/interfaces/ue_interfaces.h | 10 +- srsue/hdr/ue.h | 2 +- srsue/hdr/upper/nas.h | 2 + srsue/hdr/upper/pcsc_usim.h | 270 ++++ srsue/hdr/upper/usim.h | 59 +- srsue/hdr/upper/usim_base.h | 110 ++ srsue/src/main.cc | 3 +- srsue/src/ue.cc | 18 +- srsue/src/upper/CMakeLists.txt | 14 +- srsue/src/upper/nas.cc | 94 +- srsue/src/upper/pcsc_usim.cc | 1294 +++++++++++++++++ srsue/src/upper/usim.cc | 64 +- srsue/src/upper/usim_base.cc | 59 + srsue/test/upper/CMakeLists.txt | 5 + srsue/test/upper/nas_test.cc | 7 +- srsue/test/upper/pcsc_usim_test.cc | 61 + srsue/test/upper/usim_test.cc | 6 +- srsue/ue.conf.example | 6 +- 20 files changed, 2029 insertions(+), 107 deletions(-) create mode 100644 cmake/modules/FindPCSCLite.cmake create mode 100644 srsue/hdr/upper/pcsc_usim.h create mode 100644 srsue/hdr/upper/usim_base.h create mode 100644 srsue/src/upper/pcsc_usim.cc create mode 100644 srsue/src/upper/usim_base.cc create mode 100644 srsue/test/upper/pcsc_usim_test.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index df9cf4f32..e30c18d9d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -131,6 +131,16 @@ else(POLARSSL_FOUND) endif (MBEDTLS_FOUND) endif(POLARSSL_FOUND) +# Hard-SIM support +find_package(PCSCLite) +if (PCSCLITE_FOUND) + message(STATUS "Building with PCSC support.") + add_definitions(-DHAVE_PCSC) + set(HAVE_PCSC TRUE) + include_directories(${PCSCLITE_INCLUDE_DIR}) + #link_directories(${PCSCLITE_LIBRARIES}) +endif (PCSCLITE_FOUND) + # UHD find_package(UHD) if(UHD_FOUND) diff --git a/cmake/modules/FindPCSCLite.cmake b/cmake/modules/FindPCSCLite.cmake new file mode 100644 index 000000000..49f9b7722 --- /dev/null +++ b/cmake/modules/FindPCSCLite.cmake @@ -0,0 +1,42 @@ +# - Find PCSC-Lite +# Find the native PCSC-Lite includes and library +# +# PCSCLITE_INCLUDE_DIR - where to find winscard.h, wintypes.h, etc. +# PCSCLITE_LIBRARIES - List of libraries when using PCSC-Lite. +# PCSCLITE_FOUND - True if PCSC-Lite found. + +FIND_PACKAGE(PkgConfig) +PKG_CHECK_MODULES(PC_PCSCLITE libpcsclite) + +IF(NOT PCSCLITE_FOUND) + +FIND_PATH(PCSCLITE_INCLUDE_DIR winscard.h + HINTS + /usr/include/PCSC + ${PC_PCSCLITE_INCLUDEDIR} + ${PC_PCSCLITE_INCLUDE_DIRS} + ${PC_PCSCLITE_INCLUDE_DIRS}/PCSC + ) + +FIND_LIBRARY(PCSCLITE_LIBRARY NAMES pcsclite libpcsclite PCSC + HINTS + ${PC_PCSCLITE_LIBDIR} + ${PC_PCSCLITE_LIBRARY_DIRS} + ) + +# handle the QUIETLY and REQUIRED arguments and set PCSCLITE_FOUND to TRUE if +# all listed variables are TRUE +INCLUDE(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(PCSCLITE DEFAULT_MSG PCSCLITE_LIBRARY PCSCLITE_INCLUDE_DIR) + +IF(PCSCLITE_FOUND) + SET(PCSCLITE_LIBRARIES ${PCSCLITE_LIBRARY}) +ELSE(PCSCLITE_FOUND) + SET(PCSCLITE_LIBRARIES ) +ENDIF(PCSCLITE_FOUND) + +message(STATUS "PCSC LIBRARIES: " ${PCSCLITE_LIBRARY}) +message(STATUS "PCSC INCLUDE DIRS: " ${PCSCLITE_INCLUDE_DIR}) + +MARK_AS_ADVANCED( PCSCLITE_LIBRARY PCSCLITE_INCLUDE_DIR ) +ENDIF(NOT PCSCLITE_FOUND) \ No newline at end of file diff --git a/lib/include/srslte/interfaces/ue_interfaces.h b/lib/include/srslte/interfaces/ue_interfaces.h index f0384a532..0c2ff806e 100644 --- a/lib/include/srslte/interfaces/ue_interfaces.h +++ b/lib/include/srslte/interfaces/ue_interfaces.h @@ -43,6 +43,12 @@ namespace srsue { +typedef enum { + AUTH_OK, + AUTH_FAILED, + AUTH_SYNCH_FAILURE +} auth_result_t; + // UE interface class ue_interface { @@ -57,12 +63,12 @@ public: 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, + virtual auth_result_t generate_authentication_response(uint8_t *rand, uint8_t *autn_enb, uint16_t mcc, uint16_t mnc, - bool *net_valid, uint8_t *res, + int *res_len, uint8_t *k_asme) = 0; virtual void generate_nas_keys(uint8_t *k_asme, uint8_t *k_nas_enc, diff --git a/srsue/hdr/ue.h b/srsue/hdr/ue.h index a305df931..5a1ba31e7 100644 --- a/srsue/hdr/ue.h +++ b/srsue/hdr/ue.h @@ -98,7 +98,7 @@ private: srsue::rrc rrc; srsue::nas nas; srsue::gw gw; - srsue::usim usim; + srsue::usim_base* usim; srslte::logger_stdout logger_stdout; srslte::logger_file logger_file; diff --git a/srsue/hdr/upper/nas.h b/srsue/hdr/upper/nas.h index ee9838dd4..219707f18 100644 --- a/srsue/hdr/upper/nas.h +++ b/srsue/hdr/upper/nas.h @@ -172,6 +172,8 @@ private: // Senders void send_identity_response(); void send_esm_information_response(); + void send_authentication_response(const uint8_t* res, const size_t res_len); + void send_authentication_failure(const uint8_t cause, const uint8_t* auth_fail_param); void gen_pdn_connectivity_request(LIBLTE_BYTE_MSG_STRUCT *msg); void send_security_mode_reject(uint8_t cause); diff --git a/srsue/hdr/upper/pcsc_usim.h b/srsue/hdr/upper/pcsc_usim.h new file mode 100644 index 000000000..e5892b35e --- /dev/null +++ b/srsue/hdr/upper/pcsc_usim.h @@ -0,0 +1,270 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsUE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSUE_PCSC_USIM_H +#define SRSUE_PCSC_USIM_H + +#include +#include "srslte/common/log.h" +#include "srslte/common/common.h" +#include "srslte/interfaces/ue_interfaces.h" +#include "srslte/common/security.h" +#include "srsue/hdr/upper/usim.h" +#include + +namespace srsue { + +#define AKA_RAND_LEN 16 +#define AKA_AUTN_LEN 16 +#define AKA_AUTS_LEN 14 +#define RES_MAX_LEN 16 +#define MAC_LEN 8 +#define IK_LEN 16 +#define CK_LEN 16 +#define AK_LEN 6 +#define SQN_LEN 6 + +#define KEY_LEN 32 + +typedef enum { + SCARD_GSM_SIM, + SCARD_USIM +} sim_types_t; + +static inline uint16_t to_uint16(const uint8_t *a) +{ + return (a[0] << 8) | a[1]; +} + +class pcsc_usim + :public usim_base +{ +public: + pcsc_usim(); + ~pcsc_usim(); + void init(usim_args_t *args, srslte::log *usim_log_); + void stop(); + + // NAS interface + 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); + + 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); + + 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(uint8_t *k_asme, + uint32_t count_ul, + uint8_t *k_rrc_enc, + uint8_t *k_rrc_int, + uint8_t *k_up_enc, + uint8_t *k_up_int, + srslte::CIPHERING_ALGORITHM_ID_ENUM cipher_algo, + srslte::INTEGRITY_ALGORITHM_ID_ENUM integ_algo); + + void generate_as_keys_ho(uint32_t pci, + uint32_t earfcn, + int ncc, + uint8_t *k_rrc_enc, + uint8_t *k_rrc_int, + uint8_t *k_up_enc, + uint8_t *k_up_int, + srslte::CIPHERING_ALGORITHM_ID_ENUM cipher_algo, + srslte::INTEGRITY_ALGORITHM_ID_ENUM integ_algo); + + +private: + srslte::log *log; + + // User data + uint8_t amf[2]; // 3GPP 33.102 v10.0.0 Annex H + uint8_t op[16]; + uint64_t imsi; + uint64_t imei; + uint8_t k[16]; + + std::string imsi_str; + std::string imei_str; + + uint32_t mnc_length; + + // Security variables + uint8_t rand[AKA_RAND_LEN]; + uint8_t ck[CK_LEN]; + uint8_t ik[IK_LEN]; + uint8_t ak[AK_LEN]; + uint8_t mac[MAC_LEN]; + uint8_t autn[AKA_AUTN_LEN]; + uint8_t k_asme[KEY_LEN]; + uint8_t nh[KEY_LEN]; + uint8_t k_enb[KEY_LEN]; + uint8_t k_enb_star[KEY_LEN]; + uint8_t auts[AKA_AUTS_LEN]; + + uint32_t current_ncc; + + bool initiated; + + // Smartcard sub-class which is a port of the PC/SC smartcard implementation + // of WPA Supplicant written by Jouni Malinen and licensed under BSD + // Source: https://w1.fi/cvs.html + class scard { + public: + scard() : log(NULL) {}; + ~scard() {}; + + int init(usim_args_t *args, srslte::log *log_); + void deinit(); + + int select_file(unsigned short file_id,unsigned char *buf, size_t *buf_len); + int _select_file(unsigned short file_id, unsigned char *buf, size_t *buf_len, sim_types_t sim_type, unsigned char *aid, size_t aidlen); + + long transmit(unsigned char *_send, size_t send_len, unsigned char *_recv, size_t *recv_len); + + int get_aid(unsigned char *aid, size_t maxlen); + int get_record_len(unsigned char recnum, unsigned char mode); + int read_record(unsigned char *data, size_t len, unsigned char recnum, unsigned char mode); + int get_imsi(char *imsi, size_t *len); + int parse_fsp_templ(unsigned char *buf, size_t buf_len, int *ps_do, int *file_len); + int read_file(unsigned char *data, size_t len); + int get_mnc_len(); + int umts_auth(const unsigned char *_rand, + const unsigned char *autn, + unsigned char *res, int *res_len, + unsigned char *ik, unsigned char *ck, unsigned char *auts); + int pin_needed(unsigned char *hdr, size_t hlen); + int verify_pin(const char *pin); + int get_pin_retry_counter(); + + private: + /* See ETSI GSM 11.11 and ETSI TS 102 221 for details. + * SIM commands: + * Command APDU: CLA INS P1 P2 P3 Data + * CLA (class of instruction): A0 for GSM, 00 for USIM + * INS (instruction) + * P1 P2 P3 (parameters, P3 = length of Data) + * Response APDU: Data SW1 SW2 + * SW1 SW2 (Status words) + * Commands (INS P1 P2 P3): + * SELECT: A4 00 00 02 + * GET RESPONSE: C0 00 00 + * RUN GSM ALG: 88 00 00 00 + * RUN UMTS ALG: 88 00 81 data: 0x10 | RAND | 0x10 | AUTN + * P1 = ID of alg in card + * P2 = ID of secret key + * READ BINARY: B0 + * READ RECORD: B2 + * P2 (mode) = '02' (next record), '03' (previous record), + * '04' (absolute mode) + * VERIFY CHV: 20 00 08 + * CHANGE CHV: 24 00 10 + * DISABLE CHV: 26 00 01 08 + * ENABLE CHV: 28 00 01 08 + * UNBLOCK CHV: 2C 00 <00=CHV1, 02=CHV2> 10 + * SLEEP: FA 00 00 00 + */ + + /* GSM SIM commands */ + #define SIM_CMD_SELECT 0xa0, 0xa4, 0x00, 0x00, 0x02 + #define SIM_CMD_RUN_GSM_ALG 0xa0, 0x88, 0x00, 0x00, 0x10 + #define SIM_CMD_GET_RESPONSE 0xa0, 0xc0, 0x00, 0x00 + #define SIM_CMD_READ_BIN 0xa0, 0xb0, 0x00, 0x00 + #define SIM_CMD_READ_RECORD 0xa0, 0xb2, 0x00, 0x00 + #define SIM_CMD_VERIFY_CHV1 0xa0, 0x20, 0x00, 0x01, 0x08 + + /* USIM commands */ + #define USIM_CLA 0x00 + #define USIM_CMD_RUN_UMTS_ALG 0x00, 0x88, 0x00, 0x81, 0x22 + #define USIM_CMD_GET_RESPONSE 0x00, 0xc0, 0x00, 0x00 + + #define SIM_RECORD_MODE_ABSOLUTE 0x04 + + #define USIM_FSP_TEMPL_TAG 0x62 + + #define USIM_TLV_FILE_DESC 0x82 + #define USIM_TLV_FILE_ID 0x83 + #define USIM_TLV_DF_NAME 0x84 + #define USIM_TLV_PROPR_INFO 0xA5 + #define USIM_TLV_LIFE_CYCLE_STATUS 0x8A + #define USIM_TLV_FILE_SIZE 0x80 + #define USIM_TLV_TOTAL_FILE_SIZE 0x81 + #define USIM_TLV_PIN_STATUS_TEMPLATE 0xC6 + #define USIM_TLV_SHORT_FILE_ID 0x88 + #define USIM_TLV_SECURITY_ATTR_8B 0x8B + #define USIM_TLV_SECURITY_ATTR_8C 0x8C + #define USIM_TLV_SECURITY_ATTR_AB 0xAB + + #define USIM_PS_DO_TAG 0x90 + + /* GSM files + * File type in first octet: + * 3F = Master File + * 7F = Dedicated File + * 2F = Elementary File under the Master File + * 6F = Elementary File under a Dedicated File + */ + #define SCARD_FILE_MF 0x3F00 + #define SCARD_FILE_GSM_DF 0x7F20 + #define SCARD_FILE_UMTS_DF 0x7F50 + #define SCARD_FILE_GSM_EF_IMSI 0x6F07 + #define SCARD_FILE_GSM_EF_AD 0x6FAD + #define SCARD_FILE_EF_DIR 0x2F00 + #define SCARD_FILE_EF_ICCID 0x2FE2 + #define SCARD_FILE_EF_CK 0x6FE1 + #define SCARD_FILE_EF_IK 0x6FE2 + + #define SCARD_CHV1_OFFSET 13 + #define SCARD_CHV1_FLAG 0x80 + + SCARDCONTEXT scard_context; + SCARDHANDLE scard_handle; + long unsigned scard_protocol; + sim_types_t sim_type; + bool pin1_needed; + srslte::log *log; + }; + + scard sc; +}; + +} // namespace srsue + +#endif // SRSUE_PCSC_USIM_H diff --git a/srsue/hdr/upper/usim.h b/srsue/hdr/upper/usim.h index a5f703781..8e47f2456 100644 --- a/srsue/hdr/upper/usim.h +++ b/srsue/hdr/upper/usim.h @@ -28,6 +28,7 @@ #define SRSUE_USIM_H #include +#include "usim_base.h" #include "srslte/common/log.h" #include "srslte/common/common.h" #include "srslte/interfaces/ue_interfaces.h" @@ -35,22 +36,8 @@ namespace srsue { -typedef enum{ - auth_algo_milenage = 0, - auth_algo_xor, -}auth_algo_t; - -typedef struct{ - std::string algo; - std::string op; - std::string imsi; - std::string imei; - std::string k; -}usim_args_t; - class usim - :public usim_interface_nas - ,public usim_interface_rrc + :public usim_base { public: usim(); @@ -65,13 +52,13 @@ public: 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 *k_asme); + 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); void generate_nas_keys(uint8_t *k_asme, uint8_t *k_nas_enc, @@ -101,20 +88,20 @@ public: private: - void gen_auth_res_milenage( uint8_t *rand, - uint8_t *autn_enb, - uint16_t mcc, - uint16_t mnc, - bool *net_valid, - 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 *k_asme); + auth_result_t gen_auth_res_milenage(uint8_t *rand, + uint8_t *autn_enb, + uint16_t mcc, + uint16_t mnc, + uint8_t *res, + int *res_len, + uint8_t *k_asme); + auth_result_t gen_auth_res_xor(uint8_t *rand, + uint8_t *autn_enb, + uint16_t mcc, + uint16_t mnc, + uint8_t *res, + int *res_len, + uint8_t *k_asme); void str_to_hex(std::string str, uint8_t *hex); srslte::log *usim_log; diff --git a/srsue/hdr/upper/usim_base.h b/srsue/hdr/upper/usim_base.h new file mode 100644 index 000000000..125c31771 --- /dev/null +++ b/srsue/hdr/upper/usim_base.h @@ -0,0 +1,110 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsUE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSUE_USIM_BASE_H +#define SRSUE_USIM_BASE_H + +#include +#include "srslte/common/log.h" +#include "srslte/common/common.h" +#include "srslte/interfaces/ue_interfaces.h" +#include "srslte/common/security.h" + +namespace srsue { + +typedef enum{ + auth_algo_milenage = 0, + auth_algo_xor, +}auth_algo_t; + +typedef struct{ + std::string mode; + std::string algo; + std::string op; + std::string imsi; + std::string imei; + std::string k; + std::string pin; +}usim_args_t; + +class usim_base + :public usim_interface_nas + ,public usim_interface_rrc +{ +public: + usim_base(); + virtual ~usim_base(); + static usim_base* get_instance(usim_args_t *args, srslte::log *usim_log_); + + virtual void init(usim_args_t *args, srslte::log *usim_log_) = 0; + virtual void stop() = 0; + + // NAS interface + 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 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; + + 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; + + // RRC interface + 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, + uint8_t *k_up_int, + srslte::CIPHERING_ALGORITHM_ID_ENUM cipher_algo, + srslte::INTEGRITY_ALGORITHM_ID_ENUM integ_algo) = 0; + + virtual void generate_as_keys_ho(uint32_t pci, + uint32_t earfcn, + int ncc, + uint8_t *k_rrc_enc, + uint8_t *k_rrc_int, + uint8_t *k_up_enc, + uint8_t *k_up_int, + srslte::CIPHERING_ALGORITHM_ID_ENUM cipher_algo, + srslte::INTEGRITY_ALGORITHM_ID_ENUM integ_algo) = 0; +}; + +} // namespace srsue + +#endif // SRSUE_USIM_BASE_H diff --git a/srsue/src/main.cc b/srsue/src/main.cc index e0487f60d..fd7d0e161 100644 --- a/srsue/src/main.cc +++ b/srsue/src/main.cc @@ -124,12 +124,13 @@ void parse_args(all_args_t *args, int argc, char *argv[]) { ("log.filename", bpo::value(&args->log.filename)->default_value("/tmp/ue.log"), "Log filename") ("log.file_max_size", bpo::value(&args->log.file_max_size)->default_value(-1), "Maximum file size (in kilobytes). When passed, multiple files are created. Default -1 (single file)") + ("usim.mode", bpo::value(&args->usim.mode)->default_value("soft"), "USIM mode (soft or pcsc)") ("usim.algo", bpo::value(&args->usim.algo), "USIM authentication algorithm") ("usim.op", bpo::value(&args->usim.op), "USIM operator variant") ("usim.imsi", bpo::value(&args->usim.imsi), "USIM IMSI") ("usim.imei", bpo::value(&args->usim.imei), "USIM IMEI") ("usim.k", bpo::value(&args->usim.k), "USIM K") - + ("usim.pin", bpo::value(&args->usim.pin), "PIN in case real SIM card is used") /* Expert section */ ("expert.ip_netmask", diff --git a/srsue/src/ue.cc b/srsue/src/ue.cc index f98912680..650b22af9 100644 --- a/srsue/src/ue.cc +++ b/srsue/src/ue.cc @@ -47,11 +47,13 @@ ue::~ue() for (uint32_t i = 0; i < phy_log.size(); i++) { delete(phy_log[i]); } + if (usim) { + delete usim; + } } -bool ue::init(all_args_t *args_) -{ - args = args_; +bool ue::init(all_args_t *args_) { + args = args_; if (!args->log.filename.compare("stdout")) { logger = &logger_stdout; @@ -195,15 +197,15 @@ bool ue::init(all_args_t *args_) mac.init(&phy, &rlc, &rrc, &mac_log); rlc.init(&pdcp, &rrc, this, &rlc_log, &mac, 0 /* RB_ID_SRB0 */); pdcp.init(&rlc, &rrc, &gw, &pdcp_log, 0 /* RB_ID_SRB0 */, SECURITY_DIRECTION_UPLINK); - - usim.init(&args->usim, &usim_log); + usim = usim_base::get_instance(&args->usim, &usim_log); + usim->init(&args->usim, &usim_log); srslte_nas_config_t nas_cfg(1, args->apn); /* RB_ID_SRB1 */ - nas.init(&usim, &rrc, &gw, &nas_log, nas_cfg); + nas.init(usim, &rrc, &gw, &nas_log, nas_cfg); gw.init(&pdcp, &nas, &gw_log, 3 /* RB_ID_DRB1 */); gw.set_netmask(args->expert.ip_netmask); - rrc.init(&phy, &mac, &rlc, &pdcp, &nas, &usim, &mac, &rrc_log); + rrc.init(&phy, &mac, &rlc, &pdcp, &nas, usim, &mac, &rrc_log); // Get current band from provided EARFCN args->rrc.supported_bands[0] = srslte_band_get_band(args->rf.dl_earfcn); @@ -244,7 +246,7 @@ void ue::stop() { if(started) { - usim.stop(); + usim->stop(); nas.stop(); rrc.stop(); diff --git a/srsue/src/upper/CMakeLists.txt b/srsue/src/upper/CMakeLists.txt index 43e6acf4c..766893bbd 100644 --- a/srsue/src/upper/CMakeLists.txt +++ b/srsue/src/upper/CMakeLists.txt @@ -18,6 +18,16 @@ # and at http://www.gnu.org/licenses/. # -file(GLOB SOURCES "*.cc") +set(SOURCES gw.cc nas.cc rrc.cc usim_base.cc usim.cc) + +if(HAVE_PCSC) + list(APPEND SOURCES "pcsc_usim.cc") +endif(HAVE_PCSC) + add_library(srsue_upper STATIC ${SOURCES}) -install(TARGETS srsue_upper DESTINATION ${LIBRARY_DIR}) + +if(HAVE_PCSC) + target_link_libraries(srsue_upper ${PCSCLITE_LIBRARY}) +endif(HAVE_PCSC) + +install(TARGETS srsue_upper DESTINATION ${LIBRARY_DIR}) \ No newline at end of file diff --git a/srsue/src/upper/nas.cc b/srsue/src/upper/nas.cc index 067e6bfab..867d73ffb 100644 --- a/srsue/src/upper/nas.cc +++ b/srsue/src/upper/nas.cc @@ -695,14 +695,12 @@ void nas::parse_attach_reject(uint32_t lcid, byte_buffer_t *pdu) { void nas::parse_authentication_request(uint32_t lcid, byte_buffer_t *pdu) { LIBLTE_MME_AUTHENTICATION_REQUEST_MSG_STRUCT auth_req; bzero(&auth_req, sizeof(LIBLTE_MME_AUTHENTICATION_REQUEST_MSG_STRUCT)); - LIBLTE_MME_AUTHENTICATION_RESPONSE_MSG_STRUCT auth_res; - bzero(&auth_res, sizeof(LIBLTE_MME_AUTHENTICATION_RESPONSE_MSG_STRUCT)); 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 - pdu->reset(); + // Deallocate PDU after parsing + pool->deallocate(pdu); // Generate authentication response using RAND, AUTN & KSI-ASME uint16 mcc, mnc; @@ -711,11 +709,12 @@ void nas::parse_authentication_request(uint32_t lcid, byte_buffer_t *pdu) { nas_log->info("MCC=%d, MNC=%d\n", mcc, mnc); - bool net_valid; uint8_t res[16]; - 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()); + int res_len = 0; + nas_log->debug_hex(auth_req.rand, 16, "Authentication request RAND\n"); + nas_log->debug_hex(auth_req.autn, 16, "Authentication request AUTN\n"); + auth_result_t auth_result = usim->generate_authentication_response(auth_req.rand, auth_req.autn, mcc, mnc, + res, &res_len, ctxt.k_asme); if(LIBLTE_MME_TYPE_OF_SECURITY_CONTEXT_FLAG_NATIVE == auth_req.nas_ksi.tsc_flag) { ctxt.ksi = auth_req.nas_ksi.nas_ksi; } else { @@ -723,23 +722,17 @@ void nas::parse_authentication_request(uint32_t lcid, byte_buffer_t *pdu) { nas_log->console("Warning: NAS mapped security context not currently supported\n"); } - if (net_valid) { + if (auth_result == AUTH_OK) { nas_log->info("Network authentication successful\n"); - for (int i = 0; i < 8; i++) { - auth_res.res[i] = res[i]; - } - liblte_mme_pack_authentication_response_msg(&auth_res, (LIBLTE_BYTE_MSG_STRUCT *) pdu); - - nas_log->info("Sending Authentication Response\n"); - // Write NAS pcap - if (pcap != NULL) { - pcap->write_nas(pdu->msg, pdu->N_bytes); - } - rrc->write_sdu(lcid, pdu); + send_authentication_response(res, res_len); + nas_log->info("Generated k_asme=%s\n", hex_to_string(ctxt.k_asme, 32).c_str()); + } else if (auth_result == AUTH_SYNCH_FAILURE) { + nas_log->error("Network authentication synchronization failure.\n"); + send_authentication_failure(LIBLTE_MME_EMM_CAUSE_SYNCH_FAILURE, res); } else { nas_log->warning("Network authentication failure\n"); nas_log->console("Warning: Network authentication failure\n"); - pool->deallocate(pdu); + send_authentication_failure(LIBLTE_MME_EMM_CAUSE_MAC_FAILURE, NULL); } } @@ -1065,7 +1058,13 @@ void nas::gen_pdn_connectivity_request(LIBLTE_BYTE_MSG_STRUCT *msg) { strncpy(apn.apn, cfg.apn.c_str(), LIBLTE_STRING_LEN); pdn_con_req.apn = apn; } - pdn_con_req.protocol_cnfg_opts_present = false; + + // Request DNS Server + pdn_con_req.protocol_cnfg_opts_present = true; + pdn_con_req.protocol_cnfg_opts.opt[0].id = LIBLTE_MME_ADDITIONAL_PARAMETERS_UL_P_CSCF_IPV4_ADDRESS_REQUEST; + pdn_con_req.protocol_cnfg_opts.opt[1].id = LIBLTE_MME_ADDITIONAL_PARAMETERS_UL_DNS_SERVER_IPV4_ADDRESS_REQUEST; + pdn_con_req.protocol_cnfg_opts.N_opts = 2; + pdn_con_req.device_properties_present = false; // Pack the message @@ -1089,6 +1088,57 @@ void nas::send_security_mode_reject(uint8_t cause) { rrc->write_sdu(cfg.lcid, msg); } + +void nas::send_authentication_response(const uint8_t* res, const size_t res_len) { + byte_buffer_t *msg = pool_allocate; + if (!msg) { + nas_log->error("Fatal Error: Couldn't allocate PDU in send_authentication_response().\n"); + return; + } + + LIBLTE_MME_AUTHENTICATION_RESPONSE_MSG_STRUCT auth_res; + bzero(&auth_res, sizeof(LIBLTE_MME_AUTHENTICATION_RESPONSE_MSG_STRUCT)); + + for (uint32_t i = 0; i < res_len; i++) { + auth_res.res[i] = res[i]; + } + auth_res.res_len = res_len; + liblte_mme_pack_authentication_response_msg(&auth_res, (LIBLTE_BYTE_MSG_STRUCT *)msg); + + if(pcap != NULL) { + pcap->write_nas(msg->msg, msg->N_bytes); + } + nas_log->info("Sending Authentication Response\n"); + rrc->write_sdu(cfg.lcid, msg); +} + + +void nas::send_authentication_failure(const uint8_t cause, const uint8_t* auth_fail_param) { + byte_buffer_t *msg = pool_allocate; + if (!msg) { + nas_log->error("Fatal Error: Couldn't allocate PDU in send_authentication_failure().\n"); + return; + } + + LIBLTE_MME_AUTHENTICATION_FAILURE_MSG_STRUCT auth_failure; + auth_failure.emm_cause = cause; + if (auth_fail_param) { + memcpy(auth_failure.auth_fail_param, auth_fail_param, 14); + nas_log->debug_hex(auth_failure.auth_fail_param, 14, "auth_failure.auth_fail_param\n"); + auth_failure.auth_fail_param_present = true; + } else { + auth_failure.auth_fail_param_present = false; + } + + liblte_mme_pack_authentication_failure_msg(&auth_failure, (LIBLTE_BYTE_MSG_STRUCT *)msg); + if(pcap != NULL) { + pcap->write_nas(msg->msg, msg->N_bytes); + } + nas_log->info("Sending authentication failure.\n"); + rrc->write_sdu(cfg.lcid, msg); +} + + void nas::send_identity_response() {} void nas::send_esm_information_response() {} diff --git a/srsue/src/upper/pcsc_usim.cc b/srsue/src/upper/pcsc_usim.cc new file mode 100644 index 000000000..e3573059f --- /dev/null +++ b/srsue/src/upper/pcsc_usim.cc @@ -0,0 +1,1294 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsUE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + + +#include +#include +#include "srslte/common/bcd_helpers.h" + +using namespace srslte; + +namespace srsue{ + +pcsc_usim::pcsc_usim() : initiated(false) +{ + bzero(ck, CK_LEN); + bzero(ik, IK_LEN); + bzero(auts, IK_LEN); +} + +pcsc_usim::~pcsc_usim() +{ + sc.deinit(); +} + +void pcsc_usim::init(usim_args_t *args, srslte::log *log_) +{ + log = log_; + + if (sc.init(args, log_) == SRSLTE_SUCCESS) { + initiated = true; + } + + // Read IMSI from SIM card + char tmp[100]; + size_t tmp_len; + if (sc.get_imsi(tmp, &tmp_len)) { + log->error("Error reading IMSI from SIM\n"); + } + imsi_str = tmp; + + // Check extracted IMSI and convert + const char *imsi_c = imsi_str.c_str(); + if(15 == args->imsi.length()) { + imsi = 0; + for(int i = 0; i < 15; i++) + { + imsi *= 10; + imsi += imsi_c[i] - '0'; + } + } else { + log->error("Invalid length for ISMI: %zu should be %d\n", imsi_str.length(), 15); + log->console("Invalid length for IMSI: %zu should be %d\n", imsi_str.length(), 15); + } + + // Check IMEI + const char *imei_c = args->imei.c_str(); + if(15 == args->imei.length()) { + imei = 0; + for(int i = 0; i < 15; i++) + { + imei *= 10; + imei += imei_c[i] - '0'; + } + } else { + log->error("Invalid length for IMEI: %zu should be %d\n", args->imei.length(), 15); + log->console("Invalid length for IMEI: %zu should be %d\n", args->imei.length(), 15); + } + + // Get MNC length + mnc_length = sc.get_mnc_len(); + log->debug("MNC length %d\n", mnc_length); +} + +void pcsc_usim::stop() +{} + + +/******************************************************************************* + NAS interface +*******************************************************************************/ + +std::string pcsc_usim::get_imsi_str() +{ + return imsi_str; +} +std::string pcsc_usim::get_imei_str() +{ + return imei_str; +} + +bool pcsc_usim::get_imsi_vec(uint8_t* imsi_, uint32_t n) +{ + if (!initiated) { + fprintf(stderr, "USIM not initiated!\n"); + return false; + } + + if(NULL == imsi_ || n < 15) { + log->error("Invalid parameters to get_imsi_vec"); + return false; + } + + uint64_t temp = imsi; + for(int i=14;i>=0;i--) { + imsi_[i] = temp % 10; + temp /= 10; + } + return true; +} + +bool pcsc_usim::get_imei_vec(uint8_t* imei_, uint32_t n) +{ + if (!initiated) { + fprintf(stderr, "USIM not initiated!\n"); + return false; + } + + if(NULL == imei_ || n < 15) { + log->error("Invalid parameters to get_imei_vec"); + return false; + } + + uint64 temp = imei; + for(int i=14;i>=0;i--) + { + imei_[i] = temp % 10; + temp /= 10; + } + return true; +} + +bool pcsc_usim::get_home_plmn_id(LIBLTE_RRC_PLMN_IDENTITY_STRUCT *home_plmn_id) +{ + if (!initiated) { + fprintf(stderr, "USIM not initiated!\n"); + return false; + } + + uint8_t imsi_vec[15]; + get_imsi_vec(imsi_vec, 15); + + std::ostringstream mcc_str, mnc_str; + + int mcc_len = 3; + for (int i=0;imcc); + string_to_mnc(mnc_str.str(), &home_plmn_id->mnc); + + log->info("Read Home PLMN Id=%s\n", + plmn_id_to_string(*home_plmn_id).c_str()); + + return true; +} + +auth_result_t pcsc_usim::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) +{ + auth_result_t ret = AUTH_FAILED; + if (!initiated) { + fprintf(stderr, "USIM not initiated!\n"); + return ret; + } + + // Use RAND and AUTN to compute RES, CK, IK using SIM card + switch (sc.umts_auth(rand, autn_enb, res, res_len, ik, ck, auts)) { + case 0: + log->info("SCARD: USIM authentication successful.\n"); + break; + case -1: + log->error("SCARD: Failure during USIM UMTS authentication\n"); + return ret; + case -2: + log->info("SCARD: USIM synchronization failure, AUTS generated\n"); + log->debug_hex(auts, AKA_AUTS_LEN, "AUTS\n"); + memcpy(res, auts, AKA_AUTS_LEN); + *res_len = AKA_AUTS_LEN; + return AUTH_SYNCH_FAILURE; + default: + log->warning("SCARD: Unknown USIM failure.\n"); + return ret; + } + + // FIXME: Extract ak and seq from auts + memset(ak, 0x00, AK_LEN); + + // Extract sqn from autn + uint8_t sqn[SQN_LEN]; + for(int i=0;i<6;i++) { + sqn[i] = autn_enb[i] ^ ak[i]; + } + + // Generate K_asme + log->debug_hex(ck, CK_LEN, "CK:\n"); + log->debug_hex(ik, IK_LEN, "IK:\n"); + log->debug_hex(ak, AK_LEN, "AK:\n"); + log->debug_hex(sqn, SQN_LEN, "SQN:\n"); + log->debug("mcc=%d, mnc=%d\n", mcc, mnc); + security_generate_k_asme( ck, + ik, + ak, + sqn, + mcc, + mnc, + k_asme); + log->debug_hex(k_asme, KEY_LEN, "K_ASME:\n"); + + ret = AUTH_OK; + + return ret; +} + +void pcsc_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) +{ + if (!initiated) { + fprintf(stderr, "USIM not initiated!\n"); + return; + } + + // Generate K_nas_enc and K_nas_int + security_generate_k_nas( k_asme, + cipher_algo, + integ_algo, + k_nas_enc, + k_nas_int); +} + +/******************************************************************************* + RRC interface +*******************************************************************************/ + +void pcsc_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, + uint8_t *k_up_int, + CIPHERING_ALGORITHM_ID_ENUM cipher_algo, + INTEGRITY_ALGORITHM_ID_ENUM integ_algo) +{ + if (!initiated) { + fprintf(stderr, "USIM not initiated!\n"); + return; + } + + // Generate K_enb + security_generate_k_enb( k_asme, + count_ul, + k_enb); + + memcpy(this->k_asme, k_asme, 32); + + // Generate K_rrc_enc and K_rrc_int + security_generate_k_rrc( k_enb, + cipher_algo, + integ_algo, + k_rrc_enc, + k_rrc_int); + + // Generate K_up_enc and K_up_int + security_generate_k_up( k_enb, + cipher_algo, + integ_algo, + k_up_enc, + k_up_int); + + current_ncc = 0; +} + +void pcsc_usim::generate_as_keys_ho(uint32_t pci, + uint32_t earfcn, + int ncc, + uint8_t *k_rrc_enc, + uint8_t *k_rrc_int, + uint8_t *k_up_enc, + uint8_t *k_up_int, + CIPHERING_ALGORITHM_ID_ENUM cipher_algo, + INTEGRITY_ALGORITHM_ID_ENUM integ_algo) +{ + if (!initiated) { + fprintf(stderr, "USIM not initiated!\n"); + return; + } + + uint8_t *enb_star_key = k_enb; + + if (ncc < 0) { + ncc = current_ncc; + } + + // Generate successive NH + while(current_ncc != (uint32_t) ncc) { + uint8_t *sync = NULL; + if (current_ncc) { + sync = nh; + } else { + sync = k_enb; + } + // Generate NH + security_generate_nh(k_asme, + sync, + nh); + + current_ncc++; + if (current_ncc == 7) { + current_ncc = 0; + } + enb_star_key = nh; + } + + // Generate K_enb + security_generate_k_enb_star( enb_star_key, + pci, + earfcn, + k_enb_star); + + // K_enb becomes K_enb* + memcpy(k_enb, k_enb_star, 32); + + // Generate K_rrc_enc and K_rrc_int + security_generate_k_rrc( k_enb, + cipher_algo, + integ_algo, + k_rrc_enc, + k_rrc_int); + + // Generate K_up_enc and K_up_int + security_generate_k_up( k_enb, + cipher_algo, + integ_algo, + k_up_enc, + k_up_int); +} + +/******************************************************************************* + Helpers +*******************************************************************************/ + + +/********************************* + * PC/SC class + ********************************/ + +// return 0 if initialization was successfull, -1 otherwies +int pcsc_usim::scard::init(usim_args_t *args, srslte::log *log_) +{ + int ret_value = SRSLTE_ERROR; + int pos = 0; // SC reader + //int transaction = 1; + size_t blen; + log = log_; + + long ret; + ret = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &scard_context); + if (ret != SCARD_S_SUCCESS) { + log->error("SCardEstablishContext(): %s\n", pcsc_stringify_error(ret)); + return ret_value; + } + + unsigned long len; + ret = SCardListReaders(scard_context, NULL, NULL, &len); + if (ret != SCARD_S_SUCCESS) { + log->error("SCardListReaders(): %s\n", pcsc_stringify_error(ret)); + return ret_value; + } + + char *readers = (char *)malloc(len); + if (readers == NULL) { + log->error("Malloc failed\n"); + return ret_value; + } + + ret = SCardListReaders(scard_context, NULL, readers, &len); + if (ret != SCARD_S_SUCCESS) { + log->error("%s\n", pcsc_stringify_error(ret)); + goto clean_exit; + } + if (len < 3) { + log->info("No smart card readers available.\n"); + return ret_value; + } + + log->info("%s\n", readers); + + // TODO: Implement reader selection + // (readers is a list of available readers. The last entry is terminated with double null) + pos = 0; // select first reader + + // Connect to reader + ret = SCardConnect(scard_context, &readers[pos], SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, &scard_handle, &scard_protocol); + if (ret != SCARD_S_SUCCESS) { + if (ret == (long)SCARD_E_NO_SMARTCARD) { + log->error("No smart card inserted.\n"); + } else { + log->error("%s\n", pcsc_stringify_error(ret)); + } + } + + free(readers); + readers = NULL; + + log->info("Card=0x%x active_protocol=%lu (%s)\n", + (unsigned int)scard_handle, (unsigned long)scard_protocol, scard_protocol == SCARD_PROTOCOL_T0 ? "T0" : "T1"); + + ret = SCardBeginTransaction(scard_handle); + if (ret != SCARD_S_SUCCESS) { + log->error("%s\n", pcsc_stringify_error(ret)); + goto clean_exit; + } + + // Verify USIM support + unsigned char buf[100]; + blen = sizeof(buf); + if (_select_file(SCARD_FILE_MF, buf, &blen, SCARD_USIM, NULL, 0)) { + log->info("USIM is not supported. Trying to use GSM SIM"); + sim_type = SCARD_GSM_SIM; + } else { + log->info("USIM is supported\n"); + sim_type = SCARD_USIM; + } + + if (sim_type == SCARD_GSM_SIM) { + blen = sizeof(buf); + if (select_file(SCARD_FILE_MF, buf, &blen)) { + log->debug("SCARD: Failed to read MF\n"); + goto clean_exit; + } + + blen = sizeof(buf); + if (select_file(SCARD_FILE_GSM_DF, buf, &blen)) { + log->debug("SCARD: Failed to read GSM DF\n"); + goto clean_exit; + } + } else { + unsigned char aid[32]; + int aid_len; + + aid_len = get_aid(aid, sizeof(aid)); + if (aid_len < 0) { + log->debug("SCARD: Failed to find AID for 3G USIM app - try to use standard 3G RID\n"); + memcpy(aid, "\xa0\x00\x00\x00\x87", 5); + aid_len = 5; + } + + log->debug_hex(aid, aid_len, "SCARD: 3G USIM AID\n"); + + /* Select based on AID = 3G RID from EF_DIR. This is usually + * starting with A0 00 00 00 87. */ + blen = sizeof(buf); + if (_select_file(0, buf, &blen, sim_type, aid, aid_len)) { + log->error("SCARD: Failed to read 3G USIM app\n"); + log->error_hex(aid, aid_len, "SCARD: 3G USIM AID\n"); + goto clean_exit; + } + } + + // Verify whether CHV1 (PIN1) is needed to access the card. + ret = pin_needed(buf, blen); + if (ret < 0) { + log->debug("SCARD: Failed to determine whether PIN is needed\n"); + goto clean_exit; + } + if (ret) { + log->debug("PIN1 needed for SIM access (retry counter=%d)\n", get_pin_retry_counter()); + pin1_needed = true; + } else { + pin1_needed = false; + } + + // Set pin + if (pin1_needed) { + // verify PIN + ret = verify_pin(args->pin.c_str()); + if (ret != SCARD_S_SUCCESS) { + log->debug("SCARD: Could not verify PIN\n"); + goto clean_exit; + } + } + + ret = SCardEndTransaction(scard_handle, SCARD_LEAVE_CARD); + if (ret != SCARD_S_SUCCESS) { + log->debug("SCARD: Could not end transaction: 0x%x\n", (unsigned int) ret); + goto clean_exit; + } + + ret_value = SRSLTE_SUCCESS; + +clean_exit: + if (readers) { + free(readers); + } + + return ret_value; +} + + +int pcsc_usim::scard::_select_file(unsigned short file_id, unsigned char *buf, size_t *buf_len, sim_types_t sim_type, unsigned char *aid, size_t aidlen) +{ + long ret; + unsigned char resp[3]; + unsigned char cmd[50] = { SIM_CMD_SELECT }; + int cmdlen; + unsigned char get_resp[5] = { SIM_CMD_GET_RESPONSE }; + size_t len, rlen; + + if (sim_type == SCARD_USIM) { + cmd[0] = USIM_CLA; + cmd[3] = 0x04; + get_resp[0] = USIM_CLA; + } + + log->debug("SCARD: select file %04x\n", file_id); + if (aid) { + log->debug_hex(aid, aidlen, "SCARD: select file by AID"); + if (5 + aidlen > sizeof(cmd)) + return -1; + cmd[2] = 0x04; /* Select by AID */ + cmd[4] = aidlen; /* len */ + memcpy(cmd + 5, aid, aidlen); + cmdlen = 5 + aidlen; + } else { + cmd[5] = file_id >> 8; + cmd[6] = file_id & 0xff; + cmdlen = 7; + } + len = sizeof(resp); + ret = transmit(cmd, cmdlen, resp, &len); + if (ret != SCARD_S_SUCCESS) { + log->error("SCARD: SCardTransmit failed %s\n", pcsc_stringify_error(ret)); + return -1; + } + + if (len != 2) { + log->error("SCARD: unexpected resp len %d (expected 2)\n", (int)len); + return -1; + } + + if (resp[0] == 0x98 && resp[1] == 0x04) { + /* Security status not satisfied (PIN_WLAN) */ + log->warning("SCARD: Security status not satisfied.\n"); + return -1; + } + + if (resp[0] == 0x6e) { + log->debug("SCARD: used CLA not supported.\n"); + return -1; + } + + if (resp[0] != 0x6c && resp[0] != 0x9f && resp[0] != 0x61) { + log->warning("SCARD: unexpected response 0x%02x (expected 0x61, 0x6c, or 0x9f)\n", resp[0]); + return -1; + } + + /* Normal ending of command; resp[1] bytes available */ + get_resp[4] = resp[1]; + log->debug("SCARD: trying to get response (%d bytes)\n", resp[1]); + + rlen = *buf_len; + ret = transmit(get_resp, sizeof(get_resp), buf, &rlen); + if (ret == SCARD_S_SUCCESS) { + *buf_len = resp[1] < rlen ? resp[1] : rlen; + return 0; + } + + log->warning("SCARD: SCardTransmit err=0x%lx\n", ret); + return -1; +} + + +int pcsc_usim::scard::select_file(unsigned short file_id,unsigned char *buf, size_t *buf_len) +{ + return _select_file(file_id, buf, buf_len, sim_type, NULL, 0); +} + + +long pcsc_usim::scard::transmit(unsigned char *_send, size_t send_len, unsigned char *_recv, size_t *recv_len) +{ + long ret; + unsigned long rlen; + + log->debug_hex(_send, send_len, "SCARD: scard_transmit: send\n"); + rlen = *recv_len; + ret = SCardTransmit(scard_handle, + scard_protocol == SCARD_PROTOCOL_T1 ? + SCARD_PCI_T1 : SCARD_PCI_T0, + _send, (unsigned long) send_len, + NULL, _recv, &rlen); + *recv_len = rlen; + if (ret == SCARD_S_SUCCESS) { + log->debug_hex(_recv, rlen, "SCARD: SCardTransmit: recv\n"); + } else { + log->error("SCARD: SCardTransmit failed %s\n", pcsc_stringify_error(ret)); + } + return ret; +} + +int pcsc_usim::scard::pin_needed(unsigned char *hdr, size_t hlen) +{ + if (sim_type == SCARD_GSM_SIM) { + if (hlen > SCARD_CHV1_OFFSET && !(hdr[SCARD_CHV1_OFFSET] & SCARD_CHV1_FLAG)) + return 1; + return 0; + } + + if (sim_type == SCARD_USIM) { + int ps_do; + if (parse_fsp_templ(hdr, hlen, &ps_do, NULL)) + return -1; + /* TODO: there could be more than one PS_DO entry because of + * multiple PINs in key reference.. */ + if (ps_do > 0 && (ps_do & 0x80)) + return 1; + return 0; + } + + return -1; +} + + + +int pcsc_usim::scard::get_pin_retry_counter() +{ + long ret; + unsigned char resp[3]; + unsigned char cmd[5] = { SIM_CMD_VERIFY_CHV1 }; + size_t len; + uint16_t val; + + log->info("SCARD: fetching PIN retry counter\n"); + + if (sim_type == SCARD_USIM) + cmd[0] = USIM_CLA; + cmd[4] = 0; /* Empty data */ + + len = sizeof(resp); + ret = transmit(cmd, sizeof(cmd), resp, &len); + if (ret != SCARD_S_SUCCESS) + return -2; + + if (len != 2) { + log->error("SCARD: failed to fetch PIN retry counter\n"); + return -1; + } + + val = to_uint16(resp); + if (val == 0x63c0 || val == 0x6983) { + log->debug("SCARD: PIN has been blocked\n"); + return 0; + } + + if (val >= 0x63c0 && val <= 0x63cf) + return val & 0x000f; + + log->info("SCARD: Unexpected PIN retry counter response value 0x%x\n", val); + return 0; +} + + + +int pcsc_usim::scard::get_aid(unsigned char *aid, size_t maxlen) +{ + int rlen, rec; + struct efdir { + unsigned char appl_template_tag; /* 0x61 */ + unsigned char appl_template_len; + unsigned char appl_id_tag; /* 0x4f */ + unsigned char aid_len; + unsigned char rid[5]; + unsigned char appl_code[2]; /* 0x1002 for 3G USIM */ + } *efdir; + unsigned char buf[127], *aid_pos; + size_t blen; + unsigned int aid_len = 0; + + efdir = (struct efdir *) buf; + aid_pos = &buf[4]; + blen = sizeof(buf); + if (select_file(SCARD_FILE_EF_DIR, buf, &blen)) { + log->debug("SCARD: Failed to read EF_DIR\n"); + return -1; + } + log->debug_hex(buf, blen, "SCARD: EF_DIR select\n"); + + for (rec = 1; rec < 10; rec++) { + rlen = get_record_len(rec, SIM_RECORD_MODE_ABSOLUTE); + if (rlen < 0) { + log->debug("SCARD: Failed to get EF_DIR record length\n"); + return -1; + } + blen = sizeof(buf); + if (rlen > (int) blen) { + log->debug("SCARD: Too long EF_DIR record\n"); + return -1; + } + if (read_record(buf, rlen, rec, SIM_RECORD_MODE_ABSOLUTE) < 0) { + log->debug("SCARD: Failed to read EF_DIR record %d", rec); + return -1; + } + log->debug_hex(buf, rlen, "SCARD: EF_DIR record\n"); + + if (efdir->appl_template_tag != 0x61) { + log->debug("SCARD: Unexpected application template tag 0x%x", efdir->appl_template_tag); + continue; + } + + if (efdir->appl_template_len > rlen - 2) { + log->debug("SCARD: Too long application template (len=%d rlen=%d)", efdir->appl_template_len, rlen); + continue; + } + + if (efdir->appl_id_tag != 0x4f) { + log->debug("SCARD: Unexpected application identifier tag 0x%x", efdir->appl_id_tag); + continue; + } + + aid_len = efdir->aid_len; + if (aid_len < 1 || aid_len > 16) { + log->debug("SCARD: Invalid AID length %u\n", aid_len); + continue; + } + + log->debug_hex(aid_pos, aid_len, "SCARD: AID from EF_DIR record\n"); + + if (efdir->appl_code[0] == 0x10 && efdir->appl_code[1] == 0x02) { + log->debug("SCARD: 3G USIM app found from EF_DIR record %d\n", rec); + break; + } + } + + if (rec >= 10) { + log->debug("SCARD: 3G USIM app not found from EF_DIR records\n"); + return -1; + } + + if (aid_len > maxlen) { + log->debug("SCARD: Too long AID\n"); + return -1; + } + + memcpy(aid, aid_pos, aid_len); + + return aid_len; +} + +int pcsc_usim::scard::get_record_len(unsigned char recnum, unsigned char mode) +{ + unsigned char buf[255]; + unsigned char cmd[5] = { SIM_CMD_READ_RECORD /* , len */ }; + size_t blen; + long ret; + + if (sim_type == SCARD_USIM) + cmd[0] = USIM_CLA; + cmd[2] = recnum; + cmd[3] = mode; + cmd[4] = sizeof(buf); + + blen = sizeof(buf); + ret = transmit(cmd, sizeof(cmd), buf, &blen); + if (ret != SCARD_S_SUCCESS) { + log->debug("SCARD: failed to determine file length for record %d\n", recnum); + return -1; + } + + log->debug_hex(buf, blen, "SCARD: file length determination response\n"); + + if (blen < 2 || (buf[0] != 0x6c && buf[0] != 0x67)) { + log->error("SCARD: unexpected response to file length determination\n"); + return -1; + } + + return buf[1]; +} + + +int pcsc_usim::scard::read_record(unsigned char *data, size_t len, unsigned char recnum, unsigned char mode) +{ + unsigned char cmd[5] = { SIM_CMD_READ_RECORD /* , len */ }; + size_t blen = len + 3; + unsigned char *buf; + long ret; + + if (sim_type == SCARD_USIM) + cmd[0] = USIM_CLA; + cmd[2] = recnum; + cmd[3] = mode; + cmd[4] = len; + + buf = (unsigned char*)malloc(blen); + if (buf == NULL) + return -1; + + ret = transmit(cmd, sizeof(cmd), buf, &blen); + if (ret != SCARD_S_SUCCESS) { + free(buf); + return -2; + } + if (blen != len + 2) { + log->debug("SCARD: record read returned unexpected length %ld (expected %ld)\n", (long) blen, (long) len + 2); + free(buf); + return -3; + } + + if (buf[len] != 0x90 || buf[len + 1] != 0x00) { + log->debug("SCARD: record read returned unexpected status %02x %02x (expected 90 00)\n", buf[len], buf[len + 1]); + free(buf); + return -4; + } + + memcpy(data, buf, len); + free(buf); + + return 0; +} + + +/** + * scard_get_imsi - Read IMSI from SIM/USIM card + * @scard: Pointer to private data from scard_init() + * @imsi: Buffer for IMSI + * @len: Length of imsi buffer; set to IMSI length on success + * Returns: 0 on success, -1 if IMSI file cannot be selected, -2 if IMSI file + * selection returns invalid result code, -3 if parsing FSP template file fails + * (USIM only), -4 if IMSI does not fit in the provided imsi buffer (len is set + * to needed length), -5 if reading IMSI file fails. + * + * This function can be used to read IMSI from the SIM/USIM card. If the IMSI + * file is PIN protected, scard_set_pin() must have been used to set the + * correct PIN code before calling scard_get_imsi(). + */ +int pcsc_usim::scard::get_imsi(char *imsi, size_t *len) +{ + unsigned char buf[100]; + size_t blen, imsilen, i; + char *pos; + + log->debug("SCARD: reading IMSI from (GSM) EF-IMSI\n"); + blen = sizeof(buf); + if (select_file(SCARD_FILE_GSM_EF_IMSI, buf, &blen)) + return -1; + if (blen < 4) { + log->warning("SCARD: too short (GSM) EF-IMSI header (len=%ld)\n", (long) blen); + return -2; + } + + if (sim_type == SCARD_GSM_SIM) { + blen = to_uint16(&buf[2]); + } else { + int file_size; + if (parse_fsp_templ(buf, blen, NULL, &file_size)) + return -3; + blen = file_size; + } + if (blen < 2 || blen > sizeof(buf)) { + log->debug("SCARD: invalid IMSI file length=%ld\n", (long) blen); + return -3; + } + + imsilen = (blen - 2) * 2 + 1; + log->debug("SCARD: IMSI file length=%ld imsilen=%ld\n", (long) blen, (long) imsilen); + if (blen < 2 || imsilen > *len) { + *len = imsilen; + return -4; + } + + if (read_file(buf, blen)) + return -5; + + pos = imsi; + *pos++ = '0' + (buf[1] >> 4 & 0x0f); + for (i = 2; i < blen; i++) { + unsigned char digit; + + digit = buf[i] & 0x0f; + if (digit < 10) + *pos++ = '0' + digit; + else + imsilen--; + + digit = buf[i] >> 4 & 0x0f; + if (digit < 10) + *pos++ = '0' + digit; + else + imsilen--; + } + *len = imsilen; + + return 0; +} + +int pcsc_usim::scard::read_file(unsigned char *data, size_t len) +{ + unsigned char cmd[5] = { SIM_CMD_READ_BIN /* , len */ }; + size_t blen = len + 3; + unsigned char *buf; + long ret; + + cmd[4] = len; + + buf = (unsigned char*)malloc(blen); + if (buf == NULL) + return -1; + + if (sim_type == SCARD_USIM) + cmd[0] = USIM_CLA; + ret = transmit(cmd, sizeof(cmd), buf, &blen); + if (ret != SCARD_S_SUCCESS) { + free(buf); + return -2; + } + if (blen != len + 2) { + log->error("SCARD: file read returned unexpected length %ld (expected %ld)\n", (long) blen, (long) len + 2); + free(buf); + return -3; + } + + if (buf[len] != 0x90 || buf[len + 1] != 0x00) { + log->error("SCARD: file read returned unexpected status %02x %02x (expected 90 00)\n", buf[len], buf[len + 1]); + free(buf); + return -4; + } + + memcpy(data, buf, len); + free(buf); + + return 0; +} + + +int pcsc_usim::scard::parse_fsp_templ(unsigned char *buf, size_t buf_len, int *ps_do, int *file_len) +{ + unsigned char *pos, *end; + + if (ps_do) + *ps_do = -1; + if (file_len) + *file_len = -1; + + pos = buf; + end = pos + buf_len; + if (*pos != USIM_FSP_TEMPL_TAG) { + log->error("SCARD: file header did not start with FSP template tag\n"); + return -1; + } + pos++; + if (pos >= end) + return -1; + if (pos[0] < end - pos) + end = pos + 1 + pos[0]; + pos++; + log->debug_hex(pos, end - pos, "SCARD: file header FSP template\n"); + + while (end - pos >= 2) { + unsigned char type, len; + + type = pos[0]; + len = pos[1]; + log->debug("SCARD: file header TLV 0x%02x len=%d\n", type, len); + pos += 2; + + if (len > (unsigned int) (end - pos)) + break; + + switch (type) { + case USIM_TLV_FILE_DESC: + log->debug_hex(pos, len, "SCARD: File Descriptor TLV\n"); + break; + case USIM_TLV_FILE_ID: + log->debug_hex(pos, len, "SCARD: File Identifier TLV\n"); + break; + case USIM_TLV_DF_NAME: + log->debug_hex(pos, len, "SCARD: DF name (AID) TLV\n"); + break; + case USIM_TLV_PROPR_INFO: + log->debug_hex(pos, len,"SCARD: Proprietary information TLV\n"); + break; + case USIM_TLV_LIFE_CYCLE_STATUS: + log->debug_hex(pos, len, "SCARD: Life Cycle Status Integer TLV\n"); + break; + case USIM_TLV_FILE_SIZE: + log->debug_hex(pos, len, "SCARD: File size TLV\n"); + if ((len == 1 || len == 2) && file_len) { + if (len == 1) { + *file_len = (int) pos[0]; + } else { + *file_len = to_uint16(pos); + } + log->debug("SCARD: file_size=%d\n", *file_len); + } + break; + case USIM_TLV_TOTAL_FILE_SIZE: + log->debug_hex(pos, len, "SCARD: Total file size TLV\n"); + break; + case USIM_TLV_PIN_STATUS_TEMPLATE: + log->debug_hex(pos, len, "SCARD: PIN Status Template DO TLV\n"); + if (len >= 2 && pos[0] == USIM_PS_DO_TAG && + pos[1] >= 1 && ps_do) { + log->debug("SCARD: PS_DO=0x%02x\n", pos[2]); + *ps_do = (int) pos[2]; + } + break; + case USIM_TLV_SHORT_FILE_ID: + log->debug_hex(pos, len, "SCARD: Short File Identifier (SFI) TLV\n"); + break; + case USIM_TLV_SECURITY_ATTR_8B: + case USIM_TLV_SECURITY_ATTR_8C: + case USIM_TLV_SECURITY_ATTR_AB: + log->debug_hex(pos, len, "SCARD: Security attribute TLV\n"); + break; + default: + log->debug_hex(pos, len,"SCARD: Unrecognized TLV\n"); + break; + } + + pos += len; + + if (pos == end) + return 0; + } + return -1; +} + + + +/** + * scard_deinit - Deinitialize SIM/USIM connection + * @scard: Pointer to private data from scard_init() + * + * This function closes the SIM/USIM connect opened with scard_init(). + */ +void pcsc_usim::scard::deinit() +{ + long ret; + + log->debug("SCARD: deinitializing smart card interface\n"); + + ret = SCardDisconnect(scard_handle, SCARD_UNPOWER_CARD); + if (ret != SCARD_S_SUCCESS) { + log->debug("SCARD: Failed to disconnect smart card (err=%ld)\n", ret); + } + + ret = SCardReleaseContext(scard_context); + if (ret != SCARD_S_SUCCESS) { + log->debug("Failed to release smart card context (err=%ld)\n", ret); + } +} + + + +/** + * scard_get_mnc_len - Read length of MNC in the IMSI from SIM/USIM card + * @scard: Pointer to private data from scard_init() + * Returns: length (>0) on success, -1 if administrative data file cannot be + * selected, -2 if administrative data file selection returns invalid result + * code, -3 if parsing FSP template file fails (USIM only), -4 if length of + * the file is unexpected, -5 if reading file fails, -6 if MNC length is not + * in range (i.e. 2 or 3), -7 if MNC length is not available. + * + */ +int pcsc_usim::scard::get_mnc_len() +{ + unsigned char buf[100]; + size_t blen; + int file_size; + + log->debug("SCARD: reading MNC len from (GSM) EF-AD\n"); + blen = sizeof(buf); + if (select_file(SCARD_FILE_GSM_EF_AD, buf, &blen)) + return -1; + if (blen < 4) { + log->debug("SCARD: too short (GSM) EF-AD header (len=%ld)\n", (long) blen); + return -2; + } + + if (sim_type == SCARD_GSM_SIM) { + file_size = to_uint16(&buf[2]); + } else { + if (parse_fsp_templ(buf, blen, NULL, &file_size)) + return -3; + } + if (file_size == 3) { + log->debug("SCARD: MNC length not available\n"); + return -7; + } + if (file_size < 4 || file_size > (int) sizeof(buf)) { + log->debug("SCARD: invalid file length=%ld\n", (long) file_size); + return -4; + } + + if (read_file(buf, file_size)) + return -5; + buf[3] = buf[3] & 0x0f; /* upper nibble reserved for future use */ + if (buf[3] < 2 || buf[3] > 3) { + log->debug("SCARD: invalid MNC length=%ld\n", (long) buf[3]); + return -6; + } + log->debug("SCARD: MNC length=%ld\n", (long) buf[3]); + return buf[3]; +} + + +/** + * scard_umts_auth - Run UMTS authentication command on USIM card + * @scard: Pointer to private data from scard_init() + * @_rand: 16-byte RAND value from HLR/AuC + * @autn: 16-byte AUTN value from HLR/AuC + * @res: 16-byte buffer for RES + * @res_len: Variable that will be set to RES length + * @ik: 16-byte buffer for IK + * @ck: 16-byte buffer for CK + * @auts: 14-byte buffer for AUTS + * Returns: 0 on success, -1 on failure, or -2 if USIM reports synchronization + * failure + * + * This function performs AKA authentication using USIM card and the provided + * RAND and AUTN values from HLR/AuC. If authentication command can be + * completed successfully, RES, IK, and CK values will be written into provided + * buffers and res_len is set to length of received RES value. If USIM reports + * synchronization failure, the received AUTS value will be written into auts + * buffer. In this case, RES, IK, and CK are not valid. + */ +int pcsc_usim::scard::umts_auth(const unsigned char *_rand, + const unsigned char *autn, + unsigned char *res, int *res_len, + unsigned char *ik, unsigned char *ck, unsigned char *auts) +{ + unsigned char cmd[5 + 1 + AKA_RAND_LEN + 1 + AKA_AUTN_LEN] = { USIM_CMD_RUN_UMTS_ALG }; + unsigned char get_resp[5] = { USIM_CMD_GET_RESPONSE }; + unsigned char resp[3], buf[64], *pos, *end; + size_t len; + long ret; + + + if (sim_type == SCARD_GSM_SIM) { + log->debug("SCARD: Non-USIM card - cannot do UMTS auth\n"); + return -1; + } + + log->debug_hex(_rand, AKA_RAND_LEN, "SCARD: UMTS auth - RAND\n"); + log->debug_hex(autn, AKA_AUTN_LEN, "SCARD: UMTS auth - AUTN\n"); + cmd[5] = AKA_RAND_LEN; + memcpy(cmd + 6, _rand, AKA_RAND_LEN); + cmd[6 + AKA_RAND_LEN] = AKA_AUTN_LEN; + memcpy(cmd + 6 + AKA_RAND_LEN + 1, autn, AKA_AUTN_LEN); + + len = sizeof(resp); + ret = transmit(cmd, sizeof(cmd), resp, &len); + if (ret != SCARD_S_SUCCESS) + return -1; + + if (len <= sizeof(resp)) + log->debug_hex(resp, len, "SCARD: UMTS alg response\n"); + + if (len == 2 && resp[0] == 0x98 && resp[1] == 0x62) { + // Authentication error, application specific + log->warning("SCARD: UMTS auth failed - MAC != XMAC\n"); + return -1; + } else if (len != 2 || resp[0] != 0x61) { + log->warning("SCARD: unexpected response for UMTS auth request (len=%ld resp=%02x %02x)\n", + (long) len, resp[0], resp[1]); + return -1; + } + get_resp[4] = resp[1]; + + len = sizeof(buf); + ret = transmit(get_resp, sizeof(get_resp), buf, &len); + if (ret != SCARD_S_SUCCESS || len > sizeof(buf)) + return -1; + + log->debug_hex(buf, len, "SCARD: UMTS get response result\n"); + if (len >= 2 + AKA_AUTS_LEN && buf[0] == 0xdc && buf[1] == AKA_AUTS_LEN) { + log->debug("SCARD: UMTS Synchronization-Failure\n"); + memcpy(auts, buf + 2, AKA_AUTS_LEN); + log->debug_hex(auts, AKA_AUTS_LEN, "SCARD: AUTS\n"); + *res_len = AKA_AUTS_LEN; + return -2; + } else if (len >= 6 + IK_LEN + CK_LEN && buf[0] == 0xdb) { + pos = buf + 1; + end = buf + len; + + /* RES */ + if (pos[0] > RES_MAX_LEN || pos[0] > end - pos) { + log->debug("SCARD: Invalid RES\n"); + return -1; + } + *res_len = *pos++; + memcpy(res, pos, *res_len); + pos += *res_len; + log->debug_hex(res, *res_len, "SCARD: RES\n"); + + /* CK */ + if (pos[0] != CK_LEN || CK_LEN > end - pos) { + log->debug("SCARD: Invalid CK\n"); + return -1; + } + pos++; + memcpy(ck, pos, CK_LEN); + pos += CK_LEN; + log->debug_hex(ck, CK_LEN, "SCARD: CK\n"); + + /* IK */ + if (pos[0] != IK_LEN || IK_LEN > end - pos) { + log->debug("SCARD: Invalid IK\n"); + return -1; + } + pos++; + memcpy(ik, pos, IK_LEN); + pos += IK_LEN; + log->debug_hex(ik, IK_LEN, "SCARD: IK\n"); + + if (end > pos) { + log->debug_hex(pos, end - pos, "SCARD: Ignore extra data in end\n"); + } + + return 0; + } + + log->debug("SCARD: Unrecognized response\n"); + return -1; +} + + +int pcsc_usim::scard::verify_pin(const char *pin) +{ + long ret; + unsigned char resp[3]; + unsigned char cmd[5 + 8] = { SIM_CMD_VERIFY_CHV1 }; + size_t len; + + log->debug("SCARD: verifying PIN\n"); + + if (pin == NULL || strlen(pin) > 8) + return -1; + + if (sim_type == SCARD_USIM) + cmd[0] = USIM_CLA; + memcpy(cmd + 5, pin, strlen(pin)); + memset(cmd + 5 + strlen(pin), 0xff, 8 - strlen(pin)); + + len = sizeof(resp); + ret = transmit(cmd, sizeof(cmd), resp, &len); + if (ret != SCARD_S_SUCCESS) + return -2; + + if (len != 2 || resp[0] != 0x90 || resp[1] != 0x00) { + log->debug("SCARD: PIN verification failed\n"); + return -1; + } + + log->debug("SCARD: PIN verified successfully\n"); + return SCARD_S_SUCCESS; +} + + +} // namespace srsue diff --git a/srsue/src/upper/usim.cc b/srsue/src/upper/usim.cc index 3e0e086e5..fcbd24361 100644 --- a/srsue/src/upper/usim.cc +++ b/srsue/src/upper/usim.cc @@ -189,18 +189,18 @@ bool usim::get_home_plmn_id(LIBLTE_RRC_PLMN_IDENTITY_STRUCT *home_plmn_id) return true; } -void usim::generate_authentication_response(uint8_t *rand, - uint8_t *autn_enb, - uint16_t mcc, - uint16_t mnc, - bool *net_valid, - uint8_t *res, - uint8_t *k_asme) +auth_result_t usim::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) { if(auth_algo_xor == auth_algo) { - gen_auth_res_xor(rand, autn_enb, mcc, mnc, net_valid, res, k_asme); + return gen_auth_res_xor(rand, autn_enb, mcc, mnc, res, res_len, k_asme); } else { - gen_auth_res_milenage(rand, autn_enb, mcc, mnc, net_valid, res, k_asme); + return gen_auth_res_milenage(rand, autn_enb, mcc, mnc, res, res_len, k_asme); } } @@ -322,19 +322,18 @@ void usim::generate_as_keys_ho(uint32_t pci, Helpers *******************************************************************************/ -void usim::gen_auth_res_milenage( uint8_t *rand, - uint8_t *autn_enb, - uint16_t mcc, - uint16_t mnc, - bool *net_valid, - uint8_t *res, - uint8_t *k_asme) +auth_result_t usim::gen_auth_res_milenage(uint8_t *rand, + uint8_t *autn_enb, + uint16_t mcc, + uint16_t mnc, + uint8_t *res, + int *res_len, + uint8_t *k_asme) { + auth_result_t result = AUTH_OK; uint32_t i; uint8_t sqn[6]; - *net_valid = true; - // Use RAND and K to compute RES, CK, IK and AK security_milenage_f2345( k, op, @@ -344,6 +343,8 @@ void usim::gen_auth_res_milenage( uint8_t *rand, ik, ak); + *res_len = 8; + // Extract sqn from autn for(i=0;i<6;i++) { @@ -382,7 +383,7 @@ void usim::gen_auth_res_milenage( uint8_t *rand, { if(autn[i] != autn_enb[i]) { - *net_valid = false; + result = AUTH_FAILED; } } @@ -394,24 +395,25 @@ void usim::gen_auth_res_milenage( uint8_t *rand, mcc, mnc, k_asme); + + return result; } // 3GPP TS 34.108 version 10.0.0 Section 8 -void usim::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 *k_asme) +auth_result_t usim::gen_auth_res_xor(uint8_t *rand, + uint8_t *autn_enb, + uint16_t mcc, + uint16_t mnc, + uint8_t *res, + int *res_len, + uint8_t *k_asme) { + auth_result_t result = AUTH_OK; uint32_t i; uint8_t sqn[6]; uint8_t xdout[16]; uint8_t cdout[8]; - *net_valid = true; - // Use RAND and K to compute RES, CK, IK and AK for(i=0; i<16; i++) { xdout[i] = k[i]^rand[i]; @@ -425,6 +427,8 @@ void usim::gen_auth_res_xor(uint8_t *rand, ak[i] = xdout[i+3]; } + *res_len = 8; + // Extract sqn from autn for(i=0;i<6;i++) { sqn[i] = autn_enb[i] ^ ak[i]; @@ -466,7 +470,7 @@ void usim::gen_auth_res_xor(uint8_t *rand, { if(autn[i] != autn_enb[i]) { - *net_valid = false; + result = AUTH_FAILED; } } @@ -478,6 +482,8 @@ void usim::gen_auth_res_xor(uint8_t *rand, mcc, mnc, k_asme); + + return result; } void usim::str_to_hex(std::string str, uint8_t *hex) diff --git a/srsue/src/upper/usim_base.cc b/srsue/src/upper/usim_base.cc new file mode 100644 index 000000000..d6672cf3b --- /dev/null +++ b/srsue/src/upper/usim_base.cc @@ -0,0 +1,59 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsUE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include +#include +#ifdef HAVE_PCSC +#include +#endif + +namespace srsue{ + +usim_base* usim_base::get_instance(usim_args_t *args, srslte::log *usim_log_) +{ + usim_base* instance = NULL; + if (args->mode == "soft") { + instance = new usim(); + } +#if HAVE_PCSC + else if (args->mode == "pcsc") { + instance = new pcsc_usim(); + } +#endif + else { + // default to soft USIM + instance = new usim(); + } + return(instance); +} + +usim_base::usim_base() { +} + +usim_base::~usim_base() { +} + +} // namespace srsue diff --git a/srsue/test/upper/CMakeLists.txt b/srsue/test/upper/CMakeLists.txt index 00fb54576..2d04dc9ed 100644 --- a/srsue/test/upper/CMakeLists.txt +++ b/srsue/test/upper/CMakeLists.txt @@ -22,6 +22,11 @@ add_executable(usim_test usim_test.cc) target_link_libraries(usim_test srsue_upper srslte_upper srslte_phy) add_test(usim_test usim_test) +if(HAVE_PCSC) +add_executable(pcsc_usim_test pcsc_usim_test.cc) +target_link_libraries(pcsc_usim_test srsue_upper srslte_upper srslte_phy) +endif(HAVE_PCSC) + add_executable(rrc_reconfig_test rrc_reconfig_test.cc) target_link_libraries(rrc_reconfig_test srsue_upper srslte_upper srslte_phy) add_test(rrc_reconfig_test rrc_reconfig_test) diff --git a/srsue/test/upper/nas_test.cc b/srsue/test/upper/nas_test.cc index 91b9c1d24..432b85e40 100644 --- a/srsue/test/upper/nas_test.cc +++ b/srsue/test/upper/nas_test.cc @@ -26,7 +26,9 @@ #include #include +#include #include "srsue/hdr/upper/usim.h" +#include "srsue/hdr/upper/pcsc_usim.h" #include "srsue/hdr/upper/nas.h" #include "srslte/upper/rlc.h" #include "srsue/hdr/upper/rrc.h" @@ -195,13 +197,16 @@ int mme_attach_request_test() nas_log.set_level(srslte::LOG_LEVEL_DEBUG); rrc_log.set_level(srslte::LOG_LEVEL_DEBUG); + usim_log.set_level(srslte::LOG_LEVEL_DEBUG); nas_log.set_hex_limit(100000); rrc_log.set_hex_limit(100000); + usim_log.set_hex_limit(100000); rrc_dummy rrc_dummy; gw_dummy gw; - srsue::usim usim; + srsue::pcsc_usim usim; usim_args_t args; + args.mode = "pcsc"; args.algo = "xor"; args.imei = "353490069873319"; args.imsi = "001010123456789"; diff --git a/srsue/test/upper/pcsc_usim_test.cc b/srsue/test/upper/pcsc_usim_test.cc new file mode 100644 index 000000000..12dfcd3c1 --- /dev/null +++ b/srsue/test/upper/pcsc_usim_test.cc @@ -0,0 +1,61 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsUE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include +#include "srsue/hdr/upper/pcsc_usim.h" +#include "srslte/common/log_filter.h" +#include +#include + +using namespace srsue; +using namespace std; + + +uint8_t rand_enb[] = {0xbc, 0x4c, 0xb0, 0x27, 0xb3, 0x4b, 0x7f, 0x51, 0x21, 0x5e, 0x56, 0x5f, 0x67, 0x3f, 0xde, 0x4f}; +uint8_t autn_enb[] = {0x5a, 0x17, 0x77, 0x3c, 0x62, 0x57, 0x90, 0x01, 0xcf, 0x47, 0xf7, 0x6d, 0xb3, 0xa0, 0x19, 0x46}; + +int main(int argc, char **argv) +{ + srslte::log_filter usim_log("USIM"); + usim_log.set_level(srslte::LOG_LEVEL_DEBUG); + usim_log.set_hex_limit(100000); + uint8_t res[16]; + int res_len; + uint8_t k_asme[32]; + uint16 mcc = 0; + uint16 mnc = 0; + + usim_args_t args; + args.pin = "6129"; + args.imei = "353490069873319"; + + srsue::pcsc_usim usim; + usim.init(&args, &usim_log); + + std::string imsi = usim.get_imsi_str(); + cout << "IMSI: " << imsi << endl; + auth_result_t result = usim.generate_authentication_response(rand_enb, autn_enb, mcc, mnc, res, &res_len, k_asme); +} diff --git a/srsue/test/upper/usim_test.cc b/srsue/test/upper/usim_test.cc index 225bee36d..97a3eef6f 100644 --- a/srsue/test/upper/usim_test.cc +++ b/srsue/test/upper/usim_test.cc @@ -70,18 +70,18 @@ int main(int argc, char **argv) srslte::log_filter usim_log("USIM"); bool net_valid; uint8_t res[16]; + int res_len; uint8_t k_asme[32]; usim_args_t args; args.algo = "milenage"; - args.imei = "35609204079301"; + args.imei = "356092040793011"; args.imsi = "208930000000001"; args.k = "8BAF473F2F8FD09487CCCBD7097C6862"; args.op = "11111111111111111111111111111111"; srsue::usim usim; usim.init(&args, &usim_log); - usim.generate_authentication_response(rand_enb, autn_enb, mcc, mnc, &net_valid, res, k_asme); - assert(net_valid == true); + assert(usim.generate_authentication_response(rand_enb, autn_enb, mcc, mnc, res, &res_len, k_asme) == AUTH_OK); } diff --git a/srsue/ue.conf.example b/srsue/ue.conf.example index b2cd36ffe..11fbdc0e6 100644 --- a/srsue/ue.conf.example +++ b/srsue/ue.conf.example @@ -86,20 +86,22 @@ file_max_size = -1 ##################################################################### # USIM configuration # +# mode: USIM mode (soft/pcsc) # algo: Authentication algorithm (xor/milenage) # op: 128-bit Operator Variant Algorithm Configuration Field (hex) -# amf: 16-bit Authentication Management Field (hex) # k: 128-bit subscriber key (hex) # imsi: 15 digit International Mobile Subscriber Identity # imei: 15 digit International Mobile Station Equipment Identity +# pin: PIN in case real SIM card is used ##################################################################### [usim] +mode = soft algo = xor op = 63BFA50EE6523365FF14C1F45F88737D k = 00112233445566778899aabbccddeeff imsi = 001010123456789 imei = 353490069873319 - +#pin = 1234 ##################################################################### # RRC configuration From 455d4491c5fc9c6c0f2bf809be4676b18a3bc60c Mon Sep 17 00:00:00 2001 From: Andre Puschmann Date: Mon, 9 Apr 2018 15:32:38 +0200 Subject: [PATCH 02/24] make RES length variable when packing auth response --- lib/include/srslte/asn1/liblte_mme.h | 1 + lib/src/asn1/liblte_mme.cc | 9 +++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/include/srslte/asn1/liblte_mme.h b/lib/include/srslte/asn1/liblte_mme.h index 4a168bca2..75f6f353b 100644 --- a/lib/include/srslte/asn1/liblte_mme.h +++ b/lib/include/srslte/asn1/liblte_mme.h @@ -2810,6 +2810,7 @@ LIBLTE_ERROR_ENUM liblte_mme_unpack_authentication_request_msg(LIBLTE_BYTE_MSG_S // Structs typedef struct{ uint8 res[16]; + int res_len; }LIBLTE_MME_AUTHENTICATION_RESPONSE_MSG_STRUCT; // Functions LIBLTE_ERROR_ENUM liblte_mme_pack_authentication_response_msg(LIBLTE_MME_AUTHENTICATION_RESPONSE_MSG_STRUCT *auth_resp, diff --git a/lib/src/asn1/liblte_mme.cc b/lib/src/asn1/liblte_mme.cc index 0425e8a35..846d697b6 100644 --- a/lib/src/asn1/liblte_mme.cc +++ b/lib/src/asn1/liblte_mme.cc @@ -1027,6 +1027,7 @@ LIBLTE_ERROR_ENUM liblte_mme_unpack_authentication_parameter_rand_ie(uint8 **ie_ Document Reference: 24.301 v10.2.0 Section 9.9.3.4 *********************************************************************/ LIBLTE_ERROR_ENUM liblte_mme_pack_authentication_response_parameter_ie(uint8 *res, + int res_len, uint8 **ie_ptr) { LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; @@ -1035,12 +1036,12 @@ LIBLTE_ERROR_ENUM liblte_mme_pack_authentication_response_parameter_ie(uint8 *r if(res != NULL && ie_ptr != NULL) { - (*ie_ptr)[0] = 8; - for(i=0; i<8; i++) + (*ie_ptr)[0] = res_len; + for(i=0; ires, &msg_ptr); + liblte_mme_pack_authentication_response_parameter_ie(auth_resp->res, auth_resp->res_len, &msg_ptr); // Fill in the number of bytes used msg->N_bytes = msg_ptr - msg->msg; From 9f710516966c2d4a3e66c5ae523f8869942ce400 Mon Sep 17 00:00:00 2001 From: Andre Puschmann Date: Mon, 9 Apr 2018 19:28:06 +0200 Subject: [PATCH 03/24] fix parsing of attach accept PDN address to find protocol options --- lib/src/asn1/liblte_mme.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/asn1/liblte_mme.cc b/lib/src/asn1/liblte_mme.cc index 846d697b6..57dda12fd 100644 --- a/lib/src/asn1/liblte_mme.cc +++ b/lib/src/asn1/liblte_mme.cc @@ -4417,7 +4417,7 @@ LIBLTE_ERROR_ENUM liblte_mme_unpack_pdn_address_ie(uint8 pdn_addr->addr[i] = (*ie_ptr)[2+i]; } } - *ie_ptr += (*ie_ptr)[0]; + *ie_ptr += (*ie_ptr)[0] + 1; err = LIBLTE_SUCCESS; } From 3b70c70eee4df708fb2e524401429ccc4b7b14c9 Mon Sep 17 00:00:00 2001 From: Andre Puschmann Date: Mon, 9 Apr 2018 19:46:22 +0200 Subject: [PATCH 04/24] add DNS option parsing --- srsue/src/upper/nas.cc | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/srsue/src/upper/nas.cc b/srsue/src/upper/nas.cc index 867d73ffb..2da2f4b43 100644 --- a/srsue/src/upper/nas.cc +++ b/srsue/src/upper/nas.cc @@ -622,6 +622,24 @@ void nas::parse_attach_accept(uint32_t lcid, byte_buffer_t *pdu) { transaction_id = act_def_eps_bearer_context_req.proc_transaction_id; } + // Search for DNS entry in protocol config options + if (act_def_eps_bearer_context_req.protocol_cnfg_opts_present) { + for (uint32_t i = 0; i < act_def_eps_bearer_context_req.protocol_cnfg_opts.N_opts; i++) { + if (act_def_eps_bearer_context_req.protocol_cnfg_opts.opt[i].id == LIBLTE_MME_ADDITIONAL_PARAMETERS_DL_DNS_SERVER_IPV4_ADDRESS) { + uint32_t dns_addr = 0; + dns_addr |= act_def_eps_bearer_context_req.protocol_cnfg_opts.opt[i].contents[0] << 24; + dns_addr |= act_def_eps_bearer_context_req.protocol_cnfg_opts.opt[i].contents[1] << 16; + dns_addr |= act_def_eps_bearer_context_req.protocol_cnfg_opts.opt[i].contents[2] << 8; + dns_addr |= act_def_eps_bearer_context_req.protocol_cnfg_opts.opt[i].contents[3]; + nas_log->info("DNS: %u.%u.%u.%u\n", + act_def_eps_bearer_context_req.protocol_cnfg_opts.opt[i].contents[0], + act_def_eps_bearer_context_req.protocol_cnfg_opts.opt[i].contents[1], + act_def_eps_bearer_context_req.protocol_cnfg_opts.opt[i].contents[2], + act_def_eps_bearer_context_req.protocol_cnfg_opts.opt[i].contents[3]); + } + } + } + //FIXME: Handle the following parameters // act_def_eps_bearer_context_req.eps_qos.qci // act_def_eps_bearer_context_req.eps_qos.br_present From 0a6a72832a5e9d6fa99c107d6d587508b780e685 Mon Sep 17 00:00:00 2001 From: Andre Puschmann Date: Thu, 12 Apr 2018 11:29:09 +0200 Subject: [PATCH 05/24] add return value to USIM init and exit UE if reader wasn't found --- srsue/hdr/upper/pcsc_usim.h | 2 +- srsue/hdr/upper/usim.h | 2 +- srsue/hdr/upper/usim_base.h | 2 +- srsue/src/ue.cc | 10 ++++++++-- srsue/src/upper/pcsc_usim.cc | 10 +++++++--- srsue/src/upper/usim.cc | 4 +++- 6 files changed, 21 insertions(+), 9 deletions(-) diff --git a/srsue/hdr/upper/pcsc_usim.h b/srsue/hdr/upper/pcsc_usim.h index e5892b35e..877a33983 100644 --- a/srsue/hdr/upper/pcsc_usim.h +++ b/srsue/hdr/upper/pcsc_usim.h @@ -65,7 +65,7 @@ class pcsc_usim public: pcsc_usim(); ~pcsc_usim(); - void init(usim_args_t *args, srslte::log *usim_log_); + int init(usim_args_t *args, srslte::log *usim_log_); void stop(); // NAS interface diff --git a/srsue/hdr/upper/usim.h b/srsue/hdr/upper/usim.h index 8e47f2456..a95d0cf8a 100644 --- a/srsue/hdr/upper/usim.h +++ b/srsue/hdr/upper/usim.h @@ -41,7 +41,7 @@ class usim { public: usim(); - void init(usim_args_t *args, srslte::log *usim_log_); + int init(usim_args_t *args, srslte::log *usim_log_); void stop(); // NAS interface diff --git a/srsue/hdr/upper/usim_base.h b/srsue/hdr/upper/usim_base.h index 125c31771..e8583bd46 100644 --- a/srsue/hdr/upper/usim_base.h +++ b/srsue/hdr/upper/usim_base.h @@ -59,7 +59,7 @@ public: virtual ~usim_base(); static usim_base* get_instance(usim_args_t *args, srslte::log *usim_log_); - virtual void init(usim_args_t *args, srslte::log *usim_log_) = 0; + virtual int init(usim_args_t *args, srslte::log *usim_log_) = 0; virtual void stop() = 0; // NAS interface diff --git a/srsue/src/ue.cc b/srsue/src/ue.cc index 650b22af9..ea73c3f13 100644 --- a/srsue/src/ue.cc +++ b/srsue/src/ue.cc @@ -133,6 +133,13 @@ bool ue::init(all_args_t *args_) { // Init layers + // Init USIM first to allow early exit in case reader couldn't be found + usim = usim_base::get_instance(&args->usim, &usim_log); + if (usim->init(&args->usim, &usim_log)) { + usim_log.console("Failed to initialize USIM.\n"); + return false; + } + // PHY inits in background, start before radio args->expert.phy.nof_rx_ant = args->rf.nof_rx_ant; phy.init(&radio, &mac, &rrc, phy_log, &args->expert.phy); @@ -197,8 +204,7 @@ bool ue::init(all_args_t *args_) { mac.init(&phy, &rlc, &rrc, &mac_log); rlc.init(&pdcp, &rrc, this, &rlc_log, &mac, 0 /* RB_ID_SRB0 */); pdcp.init(&rlc, &rrc, &gw, &pdcp_log, 0 /* RB_ID_SRB0 */, SECURITY_DIRECTION_UPLINK); - usim = usim_base::get_instance(&args->usim, &usim_log); - usim->init(&args->usim, &usim_log); + srslte_nas_config_t nas_cfg(1, args->apn); /* RB_ID_SRB1 */ nas.init(usim, &rrc, &gw, &nas_log, nas_cfg); gw.init(&pdcp, &nas, &gw_log, 3 /* RB_ID_DRB1 */); diff --git a/srsue/src/upper/pcsc_usim.cc b/srsue/src/upper/pcsc_usim.cc index e3573059f..86eb6f2c3 100644 --- a/srsue/src/upper/pcsc_usim.cc +++ b/srsue/src/upper/pcsc_usim.cc @@ -45,12 +45,12 @@ pcsc_usim::~pcsc_usim() sc.deinit(); } -void pcsc_usim::init(usim_args_t *args, srslte::log *log_) +int pcsc_usim::init(usim_args_t *args, srslte::log *log_) { log = log_; - if (sc.init(args, log_) == SRSLTE_SUCCESS) { - initiated = true; + if (sc.init(args, log_) != SRSLTE_SUCCESS) { + return SRSLTE_ERROR; } // Read IMSI from SIM card @@ -92,6 +92,10 @@ void pcsc_usim::init(usim_args_t *args, srslte::log *log_) // Get MNC length mnc_length = sc.get_mnc_len(); log->debug("MNC length %d\n", mnc_length); + + initiated = true; + + return SRSLTE_SUCCESS; } void pcsc_usim::stop() diff --git a/srsue/src/upper/usim.cc b/srsue/src/upper/usim.cc index fcbd24361..644f955fa 100644 --- a/srsue/src/upper/usim.cc +++ b/srsue/src/upper/usim.cc @@ -36,7 +36,7 @@ namespace srsue{ usim::usim() : initiated(false) {} -void usim::init(usim_args_t *args, srslte::log *usim_log_) +int usim::init(usim_args_t *args, srslte::log *usim_log_) { usim_log = usim_log_; imsi_str = args->imsi; @@ -89,6 +89,8 @@ void usim::init(usim_args_t *args, srslte::log *usim_log_) auth_algo = auth_algo_xor; } initiated = true; + + return SRSLTE_SUCCESS; } void usim::stop() From d7d6bd1a7873e4d3cb84737637261c07c5de7215 Mon Sep 17 00:00:00 2001 From: Andre Puschmann Date: Mon, 16 Apr 2018 13:36:00 +0200 Subject: [PATCH 06/24] fix IMSI reading from SIM --- srsue/src/upper/pcsc_usim.cc | 31 ++++++++++++++++++++---------- srsue/test/upper/pcsc_usim_test.cc | 7 ++++++- 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/srsue/src/upper/pcsc_usim.cc b/srsue/src/upper/pcsc_usim.cc index 86eb6f2c3..7bbaf0f01 100644 --- a/srsue/src/upper/pcsc_usim.cc +++ b/srsue/src/upper/pcsc_usim.cc @@ -47,23 +47,25 @@ pcsc_usim::~pcsc_usim() int pcsc_usim::init(usim_args_t *args, srslte::log *log_) { + int ret = SRSLTE_ERROR; log = log_; if (sc.init(args, log_) != SRSLTE_SUCCESS) { - return SRSLTE_ERROR; + return ret; } // Read IMSI from SIM card - char tmp[100]; - size_t tmp_len; + char tmp[15]; + size_t tmp_len = 15; // set to max IMSI length if (sc.get_imsi(tmp, &tmp_len)) { - log->error("Error reading IMSI from SIM\n"); + log->error("Error reading IMSI from SIM.\n"); + return ret; } imsi_str = tmp; // Check extracted IMSI and convert - const char *imsi_c = imsi_str.c_str(); - if(15 == args->imsi.length()) { + if(15 == imsi_str.length()) { + const char *imsi_c = imsi_str.c_str(); imsi = 0; for(int i = 0; i < 15; i++) { @@ -71,13 +73,14 @@ int pcsc_usim::init(usim_args_t *args, srslte::log *log_) imsi += imsi_c[i] - '0'; } } else { - log->error("Invalid length for ISMI: %zu should be %d\n", imsi_str.length(), 15); + log->error("Invalid length for IMSI: %zu should be %d\n", imsi_str.length(), 15); log->console("Invalid length for IMSI: %zu should be %d\n", imsi_str.length(), 15); + return ret; } // Check IMEI - const char *imei_c = args->imei.c_str(); if(15 == args->imei.length()) { + const char *imei_c = args->imei.c_str(); imei = 0; for(int i = 0; i < 15; i++) { @@ -87,6 +90,7 @@ int pcsc_usim::init(usim_args_t *args, srslte::log *log_) } else { log->error("Invalid length for IMEI: %zu should be %d\n", args->imei.length(), 15); log->console("Invalid length for IMEI: %zu should be %d\n", args->imei.length(), 15); + return ret; } // Get MNC length @@ -94,8 +98,9 @@ int pcsc_usim::init(usim_args_t *args, srslte::log *log_) log->debug("MNC length %d\n", mnc_length); initiated = true; + ret = SRSLTE_SUCCESS; - return SRSLTE_SUCCESS; + return ret; } void pcsc_usim::stop() @@ -415,7 +420,7 @@ int pcsc_usim::scard::init(usim_args_t *args, srslte::log *log_) ret = SCardListReaders(scard_context, NULL, readers, &len); if (ret != SCARD_S_SUCCESS) { - log->error("%s\n", pcsc_stringify_error(ret)); + log->error("SCardListReaders() 2: %s\n", pcsc_stringify_error(ret)); goto clean_exit; } if (len < 3) { @@ -510,6 +515,12 @@ int pcsc_usim::scard::init(usim_args_t *args, srslte::log *log_) pin1_needed = false; } + // stop before pin retry counter reaches zero + if (pin1_needed && get_pin_retry_counter() <= 1) { + log->error("PIN1 needed for SIM access (retry counter=%d), emergency stop.\n", get_pin_retry_counter()); + goto clean_exit; + } + // Set pin if (pin1_needed) { // verify PIN diff --git a/srsue/test/upper/pcsc_usim_test.cc b/srsue/test/upper/pcsc_usim_test.cc index 12dfcd3c1..b28b5e749 100644 --- a/srsue/test/upper/pcsc_usim_test.cc +++ b/srsue/test/upper/pcsc_usim_test.cc @@ -53,9 +53,14 @@ int main(int argc, char **argv) args.imei = "353490069873319"; srsue::pcsc_usim usim; - usim.init(&args, &usim_log); + if (usim.init(&args, &usim_log)) { + printf("Error initializing PC/SC USIM.\n"); + return SRSLTE_ERROR; + }; std::string imsi = usim.get_imsi_str(); cout << "IMSI: " << imsi << endl; auth_result_t result = usim.generate_authentication_response(rand_enb, autn_enb, mcc, mnc, res, &res_len, k_asme); + + return SRSLTE_SUCCESS; } From ff42fa099181a7ff3871ee2bd844047b45880645 Mon Sep 17 00:00:00 2001 From: Andre Puschmann Date: Mon, 16 Apr 2018 13:37:42 +0200 Subject: [PATCH 07/24] stop time alignment timer before setting new one (if any) this caused an issue where time-alignment timer was set in sib2 but then set to infinity for the UE during dedicated MAC configuration --- srsue/src/mac/mac.cc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/srsue/src/mac/mac.cc b/srsue/src/mac/mac.cc index 14f895257..03b57bc8f 100644 --- a/srsue/src/mac/mac.cc +++ b/srsue/src/mac/mac.cc @@ -320,6 +320,11 @@ void mac::harq_recv(uint32_t tti, bool ack, mac_interface_phy::tb_action_ul_t* a void mac::setup_timers() { + // stop currently running time alignment timer + if (timers.get(timer_alignment)->is_running()) { + timers.get(timer_alignment)->stop(); + } + int value = liblte_rrc_time_alignment_timer_num[config.main.time_alignment_timer]; if (value > 0) { timers.get(timer_alignment)->set(this, value); From 4dc2951d5935514304d1cb90529d2a5f9c802c09 Mon Sep 17 00:00:00 2001 From: Andre Puschmann Date: Tue, 17 Apr 2018 15:24:47 +0200 Subject: [PATCH 08/24] perform initial attach with ESM info transfer --- lib/include/srslte/asn1/liblte_mme.h | 2 + lib/src/asn1/liblte_mme.cc | 27 ++++++ srsue/hdr/upper/nas.h | 3 +- srsue/src/upper/nas.cc | 131 +++++++++++++++++++++++---- srsue/src/upper/rrc.cc | 2 +- srsue/test/upper/nas_test.cc | 68 +++++++++++++- 6 files changed, 212 insertions(+), 21 deletions(-) diff --git a/lib/include/srslte/asn1/liblte_mme.h b/lib/include/srslte/asn1/liblte_mme.h index 75f6f353b..93b3b903f 100644 --- a/lib/include/srslte/asn1/liblte_mme.h +++ b/lib/include/srslte/asn1/liblte_mme.h @@ -3792,6 +3792,8 @@ typedef struct{ }LIBLTE_MME_ESM_INFORMATION_RESPONSE_MSG_STRUCT; // Functions LIBLTE_ERROR_ENUM liblte_mme_pack_esm_information_response_msg(LIBLTE_MME_ESM_INFORMATION_RESPONSE_MSG_STRUCT *esm_info_resp, + uint8 sec_hdr_type, + uint32 count, LIBLTE_BYTE_MSG_STRUCT *msg); LIBLTE_ERROR_ENUM liblte_mme_unpack_esm_information_response_msg(LIBLTE_BYTE_MSG_STRUCT *msg, LIBLTE_MME_ESM_INFORMATION_RESPONSE_MSG_STRUCT *esm_info_resp); diff --git a/lib/src/asn1/liblte_mme.cc b/lib/src/asn1/liblte_mme.cc index 57dda12fd..e6ac256bd 100644 --- a/lib/src/asn1/liblte_mme.cc +++ b/lib/src/asn1/liblte_mme.cc @@ -10041,10 +10041,19 @@ LIBLTE_ERROR_ENUM liblte_mme_unpack_esm_information_request_msg(LIBLTE_BYTE_MSG_ { LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; uint8 *msg_ptr = msg->msg; + uint8 sec_hdr_type; if(msg != NULL && esm_info_req != NULL) { + // Security Header Type + sec_hdr_type = (msg->msg[0] & 0xF0) >> 4; + if(LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS == sec_hdr_type) { + msg_ptr++; + } else{ + msg_ptr += 6; + } + // EPS Bearer ID esm_info_req->eps_bearer_id = (*msg_ptr >> 4); msg_ptr++; @@ -10072,6 +10081,8 @@ LIBLTE_ERROR_ENUM liblte_mme_unpack_esm_information_request_msg(LIBLTE_BYTE_MSG_ Document Reference: 24.301 v10.2.0 Section 8.3.14 *********************************************************************/ LIBLTE_ERROR_ENUM liblte_mme_pack_esm_information_response_msg(LIBLTE_MME_ESM_INFORMATION_RESPONSE_MSG_STRUCT *esm_info_resp, + uint8 sec_hdr_type, + uint32 count, LIBLTE_BYTE_MSG_STRUCT *msg) { LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; @@ -10080,6 +10091,20 @@ LIBLTE_ERROR_ENUM liblte_mme_pack_esm_information_response_msg(LIBLTE_MME_ESM_IN if(esm_info_resp != 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 EPS Bearer ID *msg_ptr = (esm_info_resp->eps_bearer_id << 4) | (LIBLTE_MME_PD_EPS_SESSION_MANAGEMENT); msg_ptr++; @@ -10116,6 +10141,8 @@ LIBLTE_ERROR_ENUM liblte_mme_pack_esm_information_response_msg(LIBLTE_MME_ESM_IN return(err); } + + LIBLTE_ERROR_ENUM srslte_mme_unpack_esm_information_response_msg(LIBLTE_BYTE_MSG_STRUCT *msg, LIBLTE_MME_ESM_INFORMATION_RESPONSE_MSG_STRUCT *esm_info_resp) { diff --git a/srsue/hdr/upper/nas.h b/srsue/hdr/upper/nas.h index 219707f18..7879d50f3 100644 --- a/srsue/hdr/upper/nas.h +++ b/srsue/hdr/upper/nas.h @@ -171,7 +171,8 @@ private: // Senders void send_identity_response(); - void send_esm_information_response(); + void send_service_request(); + void send_esm_information_response(const uint8 proc_transaction_id); void send_authentication_response(const uint8_t* res, const size_t res_len); void send_authentication_failure(const uint8_t cause, const uint8_t* auth_fail_param); void gen_pdn_connectivity_request(LIBLTE_BYTE_MSG_STRUCT *msg); diff --git a/srsue/src/upper/nas.cc b/srsue/src/upper/nas.cc index 2da2f4b43..4eede23f3 100644 --- a/srsue/src/upper/nas.cc +++ b/srsue/src/upper/nas.cc @@ -30,6 +30,7 @@ #include #include #include +#include #include "srslte/asn1/liblte_rrc.h" #include "srsue/hdr/upper/nas.h" #include "srslte/common/security.h" @@ -51,7 +52,10 @@ nas::nas() ctxt.tx_count = 0; ctxt.cipher_algo = CIPHERING_ALGORITHM_ID_EEA0; ctxt.integ_algo = INTEGRITY_ALGORITHM_ID_EIA0; +<<<<<<< HEAD plmn_is_selected = false; +======= +>>>>>>> perform initial attach with ESM info transfer } void nas::init(usim_interface_nas *usim_, @@ -353,7 +357,13 @@ void nas::write_pdu(uint32_t lcid, byte_buffer_t *pdu) { } uint32_t nas::get_ul_count() { - return ctxt.tx_count; + // UL count for RRC key derivation depends on ESM information transfer procedure + if (cfg.apn.empty()) { + // No ESM info transfer has been sent + return ctxt.tx_count - 1; + } else { + return ctxt.tx_count - 2; + } } bool nas::get_k_asme(uint8_t *k_asme_, uint32_t n) { @@ -483,7 +493,11 @@ void nas::cipher_encrypt(byte_buffer_t *pdu) memcpy(&pdu->msg[6], &pdu_tmp.msg[6], pdu->N_bytes-6); break; default: +<<<<<<< HEAD nas_log->error("Ciphering algorithm not known\n"); +======= + nas_log->error("Ciphering algorithmus not known\n"); +>>>>>>> perform initial attach with ESM info transfer break; } } @@ -517,7 +531,7 @@ void nas::cipher_decrypt(byte_buffer_t *pdu) memcpy(&pdu->msg[6], &tmp_pdu.msg[6], pdu->N_bytes-6); break; default: - nas_log->error("Ciphering algorithmus not known"); + nas_log->error("Ciphering algorithmus not known\n"); break; } } @@ -921,8 +935,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"); + LIBLTE_MME_ESM_INFORMATION_REQUEST_MSG_STRUCT esm_info_req; + liblte_mme_unpack_esm_information_request_msg((LIBLTE_BYTE_MSG_STRUCT *)pdu, &esm_info_req); + + nas_log->info("ESM information request received for beaser=%d, transaction_id=%d\n", esm_info_req.eps_bearer_id, esm_info_req.proc_transaction_id); + ctxt.rx_count++; pool->deallocate(pdu); + + // send response + send_esm_information_response(esm_info_req.proc_transaction_id); } void nas::parse_emm_information(uint32_t lcid, byte_buffer_t *pdu) { @@ -1065,24 +1086,18 @@ void nas::gen_pdn_connectivity_request(LIBLTE_BYTE_MSG_STRUCT *msg) { pdn_con_req.proc_transaction_id = 0x01; // First transaction ID pdn_con_req.pdn_type = LIBLTE_MME_PDN_TYPE_IPV4; pdn_con_req.request_type = LIBLTE_MME_REQUEST_TYPE_INITIAL_REQUEST; + pdn_con_req.apn_present = false; // Set the optional flags - pdn_con_req.esm_info_transfer_flag_present = false; //FIXME: Check if this is needed if (cfg.apn == "") { - pdn_con_req.apn_present = false; + pdn_con_req.esm_info_transfer_flag_present = false; } else { - pdn_con_req.apn_present = true; - LIBLTE_MME_ACCESS_POINT_NAME_STRUCT apn = {0}; - strncpy(apn.apn, cfg.apn.c_str(), LIBLTE_STRING_LEN); - pdn_con_req.apn = apn; + // request ESM info transfer is APN is specified + pdn_con_req.esm_info_transfer_flag_present = true; + pdn_con_req.esm_info_transfer_flag = LIBLTE_MME_ESM_INFO_TRANSFER_FLAG_REQUIRED; } - // Request DNS Server - pdn_con_req.protocol_cnfg_opts_present = true; - pdn_con_req.protocol_cnfg_opts.opt[0].id = LIBLTE_MME_ADDITIONAL_PARAMETERS_UL_P_CSCF_IPV4_ADDRESS_REQUEST; - pdn_con_req.protocol_cnfg_opts.opt[1].id = LIBLTE_MME_ADDITIONAL_PARAMETERS_UL_DNS_SERVER_IPV4_ADDRESS_REQUEST; - pdn_con_req.protocol_cnfg_opts.N_opts = 2; - + pdn_con_req.protocol_cnfg_opts_present = false; pdn_con_req.device_properties_present = false; // Pack the message @@ -1159,7 +1174,91 @@ void nas::send_authentication_failure(const uint8_t cause, const uint8_t* auth_f void nas::send_identity_response() {} -void nas::send_esm_information_response() {} +void nas::send_service_request() { + byte_buffer_t *msg = pool_allocate; + if (!msg) { + nas_log->error("Fatal Error: Couldn't allocate PDU in send_service_request().\n"); + return; + } + + // 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] = (ctxt.ksi & 0x07) << 5; + msg->msg[1] |= ctxt.tx_count & 0x1F; + msg->N_bytes++; + + uint8_t mac[4]; + integrity_generate(&k_nas_int[16], + ctxt.tx_count, + SECURITY_DIRECTION_UPLINK, + &msg->msg[0], + 2, + &mac[0]); + // Set the short MAC + msg->msg[2] = mac[2]; + msg->N_bytes++; + msg->msg[3] = mac[3]; + msg->N_bytes++; + + if(pcap != NULL) { + pcap->write_nas(msg->msg, 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(const uint8 proc_transaction_id) { + LIBLTE_MME_ESM_INFORMATION_RESPONSE_MSG_STRUCT esm_info_resp; + esm_info_resp.proc_transaction_id = proc_transaction_id; + esm_info_resp.eps_bearer_id = 0; // respone shall always have no bearer assigned + + if (cfg.apn == "") { + esm_info_resp.apn_present = false; + } else { + esm_info_resp.apn_present = true; + esm_info_resp.apn.apn = cfg.apn; + } + esm_info_resp.protocol_cnfg_opts_present = false; + + byte_buffer_t *pdu = pool_allocate; + if (!pdu) { + nas_log->error("Fatal Error: Couldn't allocate PDU in send_attach_request().\n"); + return; + } + + if (liblte_mme_pack_esm_information_response_msg(&esm_info_resp, + LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY_AND_CIPHERED, + ctxt.tx_count, + (LIBLTE_BYTE_MSG_STRUCT *)pdu)) { + nas_log->error("Error packing ESM information response.\n"); + return; + } + + if(pcap != NULL) { + pcap->write_nas(pdu->msg, pdu->N_bytes); + } + + cipher_encrypt(pdu); + if (pdu->N_bytes > 5) { + integrity_generate(&k_nas_int[16], + ctxt.tx_count, + SECURITY_DIRECTION_UPLINK, + &pdu->msg[5], + pdu->N_bytes - 5, + &pdu->msg[1]); + } else { + nas_log->error("Invalid PDU size %d\n", pdu->N_bytes); + return; + } + + nas_log->info_hex(pdu->msg, pdu->N_bytes, "Sending ESM information response\n"); + rrc->write_sdu(cfg.lcid, pdu); + + ctxt.tx_count++; +} /******************************************************************************* diff --git a/srsue/src/upper/rrc.cc b/srsue/src/upper/rrc.cc index eebbd38b8..24cca3ec7 100644 --- a/srsue/src/upper/rrc.cc +++ b/srsue/src/upper/rrc.cc @@ -1947,7 +1947,7 @@ void rrc::parse_dl_dcch(uint32_t lcid, byte_buffer_t *pdu) { // Generate AS security keys 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); + usim->generate_as_keys(k_asme, nas->get_ul_count(), k_rrc_enc, k_rrc_int, k_up_enc, k_up_int, cipher_algo, integ_algo); rrc_log->debug_hex(k_rrc_enc, 32, "RRC encryption key - k_rrc_enc"); rrc_log->debug_hex(k_rrc_int, 32, "RRC integrity key - k_rrc_int"); rrc_log->debug_hex(k_up_enc, 32, "UP encryption key - k_up_enc"); diff --git a/srsue/test/upper/nas_test.cc b/srsue/test/upper/nas_test.cc index 432b85e40..9a3054c6e 100644 --- a/srsue/test/upper/nas_test.cc +++ b/srsue/test/upper/nas_test.cc @@ -28,7 +28,6 @@ #include #include #include "srsue/hdr/upper/usim.h" -#include "srsue/hdr/upper/pcsc_usim.h" #include "srsue/hdr/upper/nas.h" #include "srslte/upper/rlc.h" #include "srsue/hdr/upper/rrc.h" @@ -61,6 +60,8 @@ uint8_t attach_accept_pdu[] = { 0x27, 0x0f, 0x4f, 0xb3, 0xef, 0x01, 0x07, 0x42, 0x80, 0x50, 0x0b, 0xf6, 0x00, 0xf1, 0x10, 0x80, 0x01, 0x01, 0x35, 0x16, 0x6d, 0xbc, 0x64, 0x01, 0x00 }; +uint8_t esm_info_req_pdu[] = { 0x27, 0x1d, 0xbf, 0x7e, 0x05, 0x01, 0x02, 0x5a, 0xd9 }; + uint16 mcc = 61441; uint16 mnc = 65281; @@ -204,9 +205,9 @@ int mme_attach_request_test() rrc_dummy rrc_dummy; gw_dummy gw; - srsue::pcsc_usim usim; + srsue::usim usim; usim_args_t args; - args.mode = "pcsc"; + args.mode = "soft"; args.algo = "xor"; args.imei = "353490069873319"; args.imsi = "001010123456789"; @@ -215,6 +216,7 @@ int mme_attach_request_test() usim.init(&args, &usim_log); srslte_nas_config_t nas_cfg; + nas_cfg.apn = "test123"; srsue::nas nas; nas.init(&usim, &rrc_dummy, &gw, &nas_log, nas_cfg); @@ -239,6 +241,61 @@ int mme_attach_request_test() } + +int esm_info_request_test() +{ + int ret = SRSLTE_ERROR; + srslte::log_filter nas_log("NAS"); + srslte::log_filter rrc_log("RRC"); + srslte::log_filter mac_log("MAC"); + srslte::log_filter usim_log("USIM"); + + nas_log.set_level(srslte::LOG_LEVEL_DEBUG); + rrc_log.set_level(srslte::LOG_LEVEL_DEBUG); + nas_log.set_hex_limit(100000); + rrc_log.set_hex_limit(100000); + + rrc_dummy rrc_dummy; + gw_dummy gw; + + usim_args_t args; + args.algo = "xor"; + args.imei = "353490069873319"; + args.imsi = "001010123456789"; + args.k = "00112233445566778899aabbccddeeff"; + args.op = "63BFA50EE6523365FF14C1F45F88737D"; + + // init USIM + srsue::usim usim; + bool net_valid; + uint8_t res[16]; + usim.init(&args, &usim_log); + + srslte::byte_buffer_pool *pool; + pool = byte_buffer_pool::get_instance(); + + srsue::nas nas; + srslte_nas_config_t cfg; + cfg.apn = "srslte"; + nas.init(&usim, &rrc_dummy, &gw, &nas_log, cfg); + + // push ESM info request PDU to NAS to generate response + byte_buffer_t* tmp = pool->allocate(); + memcpy(tmp->msg, esm_info_req_pdu, sizeof(esm_info_req_pdu)); + tmp->N_bytes = sizeof(esm_info_req_pdu); + nas.write_pdu(LCID, tmp); + + // check length of generated NAS SDU + if (rrc_dummy.get_last_sdu_len() > 3) { + ret = SRSLTE_SUCCESS; + } + + pool->cleanup(); + + return ret; +} + + int main(int argc, char **argv) { if (security_command_test()) { @@ -251,5 +308,10 @@ int main(int argc, char **argv) return -1; } + if (esm_info_request_test()) { + printf("ESM info request test failed.\n"); + return -1; + } + return 0; } From 258785e7deee288d30bf6b760fca472479b6324a Mon Sep 17 00:00:00 2001 From: Andre Puschmann Date: Thu, 19 Apr 2018 10:07:10 +0200 Subject: [PATCH 09/24] add log to s1ap --- srsepc/src/mme/s1ap_nas_transport.cc | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/srsepc/src/mme/s1ap_nas_transport.cc b/srsepc/src/mme/s1ap_nas_transport.cc index b6220f33a..4487f3c9a 100644 --- a/srsepc/src/mme/s1ap_nas_transport.cc +++ b/srsepc/src/mme/s1ap_nas_transport.cc @@ -1068,7 +1068,7 @@ s1ap_nas_transport::handle_nas_security_mode_complete(srslte::byte_buffer_t *nas { pack_esm_information_request(reply_buffer, emm_ctx, ecm_ctx); m_s1ap_log->console("Sending ESM information request\n"); - m_s1ap_log->info("Sending ESM information request\n"); + m_s1ap_log->info_hex(reply_buffer->msg, reply_buffer->N_bytes, "Sending ESM information request\n"); *reply_flag = true; } else @@ -1698,6 +1698,8 @@ s1ap_nas_transport::pack_esm_information_request(srslte::byte_buffer_t *reply_ms return false; } + m_s1ap_log->debug_hex(nas_buffer->msg, nas_buffer->N_bytes, "ESM info request after packing: "); + uint8_t mac[4]; srslte::security_128_eia1 (&ue_emm_ctx->security_ctxt.k_nas_int[16], ue_emm_ctx->security_ctxt.dl_nas_count, @@ -1709,6 +1711,9 @@ s1ap_nas_transport::pack_esm_information_request(srslte::byte_buffer_t *reply_ms ); memcpy(&nas_buffer->msg[1],mac,4); + + m_s1ap_log->debug_hex(nas_buffer->msg, nas_buffer->N_bytes, "ESM info request after security&integrity: "); + //Copy NAS PDU to Downlink NAS Trasport message buffer memcpy(dw_nas->NAS_PDU.buffer, nas_buffer->msg, nas_buffer->N_bytes); dw_nas->NAS_PDU.n_octets = nas_buffer->N_bytes; From 4fe5258a23ea887ca1199dbad56b9a274ce049af Mon Sep 17 00:00:00 2001 From: Andre Puschmann Date: Thu, 19 Apr 2018 18:19:38 +0200 Subject: [PATCH 10/24] fix packing/unpacking of esm info request msg --- lib/src/asn1/liblte_mme.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/src/asn1/liblte_mme.cc b/lib/src/asn1/liblte_mme.cc index e6ac256bd..fd8e120f8 100644 --- a/lib/src/asn1/liblte_mme.cc +++ b/lib/src/asn1/liblte_mme.cc @@ -9962,7 +9962,7 @@ LIBLTE_ERROR_ENUM srslte_mme_pack_esm_information_request_msg(LIBLTE_MME_ESM_INF 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_SESSION_MANAGEMENT); + *msg_ptr = (sec_hdr_type << 4) | (LIBLTE_MME_PD_EPS_MOBILITY_MANAGEMENT); msg_ptr++; // MAC will be filled in later @@ -10160,7 +10160,7 @@ LIBLTE_ERROR_ENUM srslte_mme_unpack_esm_information_response_msg(LIBLTE_BYTE_MSG { msg_ptr++; }else{ - msg_ptr += 7; + msg_ptr += 6; } // EPS Bearer ID esm_info_resp->eps_bearer_id = (*msg_ptr >> 4); From ab2c1cd10f366e3c0f7ee6c6d78d63b7b67d7ca7 Mon Sep 17 00:00:00 2001 From: Andre Puschmann Date: Thu, 19 Apr 2018 18:21:27 +0200 Subject: [PATCH 11/24] fix typos in s1ap in EPC --- srsepc/src/mme/s1ap_ctx_mngmt_proc.cc | 4 ++-- srsepc/src/mme/s1ap_nas_transport.cc | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/srsepc/src/mme/s1ap_ctx_mngmt_proc.cc b/srsepc/src/mme/s1ap_ctx_mngmt_proc.cc index a509b8a70..2ba49ae01 100644 --- a/srsepc/src/mme/s1ap_ctx_mngmt_proc.cc +++ b/srsepc/src/mme/s1ap_ctx_mngmt_proc.cc @@ -283,8 +283,8 @@ s1ap_ctx_mngmt_proc::handle_ue_context_release_request(LIBLTE_S1AP_MESSAGE_UECON if (ecm_ctx->state == ECM_STATE_CONNECTED) { //There are active E-RABs, send release access mearers request - m_s1ap_log->console("There are active E-RABs, send release access mearers request"); - m_s1ap_log->info("There are active E-RABs, send release access mearers request"); + m_s1ap_log->console("There are active E-RABs, send release access mearers request\n"); + m_s1ap_log->info("There are active E-RABs, send release access mearers request\n"); //The handle_release_access_bearers_response function will make sure to mark E-RABS DEACTIVATED //It will release the UEs downstream S1-u and keep the upstream S1-U connection active. diff --git a/srsepc/src/mme/s1ap_nas_transport.cc b/srsepc/src/mme/s1ap_nas_transport.cc index 4487f3c9a..c918de3f3 100644 --- a/srsepc/src/mme/s1ap_nas_transport.cc +++ b/srsepc/src/mme/s1ap_nas_transport.cc @@ -284,7 +284,7 @@ s1ap_nas_transport::handle_uplink_nas_transport(LIBLTE_S1AP_MESSAGE_UPLINKNASTRA } else if(sec_hdr_type == LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY || sec_hdr_type == LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY_AND_CIPHERED) { - //Integrity protected NAS message, possibly chiphered. + //Integrity protected NAS message, possibly ciphered. emm_ctx->security_ctxt.ul_nas_count++; mac_valid = integrity_check(emm_ctx,nas_msg); if(!mac_valid){ @@ -1145,8 +1145,8 @@ s1ap_nas_transport::handle_esm_information_response(srslte::byte_buffer_t *nas_m m_s1ap_log->info("ESM Info: EPS bearer id %d\n",esm_info_resp.eps_bearer_id); if(esm_info_resp.apn_present) { - m_s1ap_log->info("ESM Info: APN %d\n",esm_info_resp.eps_bearer_id); - m_s1ap_log->console("ESM Info: APN %d\n",esm_info_resp.eps_bearer_id); + m_s1ap_log->info("ESM Info: APN %s\n",esm_info_resp.apn.apn.c_str()); + m_s1ap_log->console("ESM Info: APN %s\n",esm_info_resp.apn.apn.c_str()); } if(esm_info_resp.protocol_cnfg_opts_present) { @@ -1683,10 +1683,10 @@ s1ap_nas_transport::pack_esm_information_request(srslte::byte_buffer_t *reply_ms dw_nas->SubscriberProfileIDforRFP_present=false; LIBLTE_MME_ESM_INFORMATION_REQUEST_MSG_STRUCT esm_info_req; - esm_info_req.eps_bearer_id=0; + esm_info_req.eps_bearer_id = 0; esm_info_req.proc_transaction_id = ue_emm_ctx->procedure_transaction_id; - uint8_t sec_hdr_type=2; + uint8_t sec_hdr_type = LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY_AND_CIPHERED; ue_emm_ctx->security_ctxt.dl_nas_count++; From 0f6695b9cd7b5609d23db14063d8632d8ee827a7 Mon Sep 17 00:00:00 2001 From: Andre Puschmann Date: Wed, 25 Apr 2018 16:40:29 +0200 Subject: [PATCH 12/24] fixing uninitialized vars --- srsue/src/upper/nas.cc | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/srsue/src/upper/nas.cc b/srsue/src/upper/nas.cc index 4eede23f3..4a7a65997 100644 --- a/srsue/src/upper/nas.cc +++ b/srsue/src/upper/nas.cc @@ -978,7 +978,12 @@ void nas::gen_attach_request(byte_buffer_t *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.ue_network_cap.ucs2_present = false; attach_req.ms_network_cap_present = false; // A/Gb mode (2G) or Iu mode (3G) + attach_req.ue_network_cap.lpp_present = false; + attach_req.ue_network_cap.lcs_present = false; + attach_req.ue_network_cap.onexsrvcc_present = false; + attach_req.ue_network_cap.nf_present = false; attach_req.old_p_tmsi_signature_present = false; attach_req.additional_guti_present = false; attach_req.last_visited_registered_tai_present = false; @@ -1025,6 +1030,8 @@ void nas::gen_attach_request(byte_buffer_t *msg) { } } else { attach_req.eps_mobile_id.type_of_id = LIBLTE_MME_EPS_MOBILE_ID_TYPE_IMSI; + attach_req.nas_ksi.tsc_flag = LIBLTE_MME_TYPE_OF_SECURITY_CONTEXT_FLAG_NATIVE; + attach_req.nas_ksi.nas_ksi = 0; usim->get_imsi_vec(attach_req.eps_mobile_id.imsi, 15); nas_log->info("Requesting IMSI attach (IMSI=%s)\n", usim->get_imsi_str().c_str()); liblte_mme_pack_attach_request_msg(&attach_req, (LIBLTE_BYTE_MSG_STRUCT *) msg); From 5ac08b62e94b658bf021fad84455fa935efb3639 Mon Sep 17 00:00:00 2001 From: Andre Puschmann Date: Tue, 1 May 2018 15:18:35 +0200 Subject: [PATCH 13/24] remove usage of std::string for APN --- srsepc/src/mme/s1ap_nas_transport.cc | 4 ++-- srsue/src/upper/nas.cc | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/srsepc/src/mme/s1ap_nas_transport.cc b/srsepc/src/mme/s1ap_nas_transport.cc index c918de3f3..6bd287f58 100644 --- a/srsepc/src/mme/s1ap_nas_transport.cc +++ b/srsepc/src/mme/s1ap_nas_transport.cc @@ -1145,8 +1145,8 @@ s1ap_nas_transport::handle_esm_information_response(srslte::byte_buffer_t *nas_m m_s1ap_log->info("ESM Info: EPS bearer id %d\n",esm_info_resp.eps_bearer_id); if(esm_info_resp.apn_present) { - m_s1ap_log->info("ESM Info: APN %s\n",esm_info_resp.apn.apn.c_str()); - m_s1ap_log->console("ESM Info: APN %s\n",esm_info_resp.apn.apn.c_str()); + m_s1ap_log->info("ESM Info: APN %s\n",esm_info_resp.apn.apn); + m_s1ap_log->console("ESM Info: APN %s\n",esm_info_resp.apn.apn); } if(esm_info_resp.protocol_cnfg_opts_present) { diff --git a/srsue/src/upper/nas.cc b/srsue/src/upper/nas.cc index 4a7a65997..2b76ef84c 100644 --- a/srsue/src/upper/nas.cc +++ b/srsue/src/upper/nas.cc @@ -1226,7 +1226,9 @@ void nas::send_esm_information_response(const uint8 proc_transaction_id) { esm_info_resp.apn_present = false; } else { esm_info_resp.apn_present = true; - esm_info_resp.apn.apn = cfg.apn; + int len = std::min((int)cfg.apn.length(), LIBLTE_STRING_LEN); + strncpy(esm_info_resp.apn.apn, cfg.apn.c_str(), len); + esm_info_resp.apn.apn[len - 1] = '\0'; } esm_info_resp.protocol_cnfg_opts_present = false; From ca8adde0c621e685f8eaf2b92447449ece9388f5 Mon Sep 17 00:00:00 2001 From: Andre Puschmann Date: Tue, 1 May 2018 15:19:14 +0200 Subject: [PATCH 14/24] cleanup rebase --- srsue/src/upper/nas.cc | 7 ------- 1 file changed, 7 deletions(-) diff --git a/srsue/src/upper/nas.cc b/srsue/src/upper/nas.cc index 2b76ef84c..7a73ab5a6 100644 --- a/srsue/src/upper/nas.cc +++ b/srsue/src/upper/nas.cc @@ -52,10 +52,7 @@ nas::nas() ctxt.tx_count = 0; ctxt.cipher_algo = CIPHERING_ALGORITHM_ID_EEA0; ctxt.integ_algo = INTEGRITY_ALGORITHM_ID_EIA0; -<<<<<<< HEAD plmn_is_selected = false; -======= ->>>>>>> perform initial attach with ESM info transfer } void nas::init(usim_interface_nas *usim_, @@ -493,11 +490,7 @@ void nas::cipher_encrypt(byte_buffer_t *pdu) memcpy(&pdu->msg[6], &pdu_tmp.msg[6], pdu->N_bytes-6); break; default: -<<<<<<< HEAD nas_log->error("Ciphering algorithm not known\n"); -======= - nas_log->error("Ciphering algorithmus not known\n"); ->>>>>>> perform initial attach with ESM info transfer break; } } From 939ab9a0c8fbd416c3762cd3f090c30d19d3f11b Mon Sep 17 00:00:00 2001 From: Andre Puschmann Date: Tue, 1 May 2018 15:29:56 +0200 Subject: [PATCH 15/24] add license info for scard class --- LICENSE | 38 +++++++++++++++++++++++++++++++++++++- README.md | 1 + 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 2def0e883..4ac29ae7d 100644 --- a/LICENSE +++ b/LICENSE @@ -658,4 +658,40 @@ specific requirements. You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU AGPL, see -. \ No newline at end of file +. + + +The license terms used for the scard class (in pcsc_usim) derived from wpa_supplicant +------------------------------------------------------------------------------------- + +Modified BSD license (no advertisement clause): + +Copyright (c) 2002-2017, Jouni Malinen and contributors +All Rights Reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name(s) of the above-listed copyright holder(s) nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/README.md b/README.md index a8f186a62..4a68f520a 100644 --- a/README.md +++ b/README.md @@ -108,6 +108,7 @@ Note that depending on your flavor and version of Linux, the actual package name * Optional requirements: * srsgui: https://github.com/srslte/srsgui - for real-time plotting. + * libpcsclite-dev https://pcsclite.apdu.fr/ - for accessing smart card readers * RF front-end driver: * UHD: https://github.com/EttusResearch/uhd From 2001bb1c491eeeb82bd531bcbd43242b46ce33e9 Mon Sep 17 00:00:00 2001 From: Andre Puschmann Date: Tue, 1 May 2018 15:36:40 +0200 Subject: [PATCH 16/24] remove unneeded debug --- srsepc/src/mme/s1ap_nas_transport.cc | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/srsepc/src/mme/s1ap_nas_transport.cc b/srsepc/src/mme/s1ap_nas_transport.cc index 6bd287f58..f4114603a 100644 --- a/srsepc/src/mme/s1ap_nas_transport.cc +++ b/srsepc/src/mme/s1ap_nas_transport.cc @@ -1698,8 +1698,6 @@ s1ap_nas_transport::pack_esm_information_request(srslte::byte_buffer_t *reply_ms return false; } - m_s1ap_log->debug_hex(nas_buffer->msg, nas_buffer->N_bytes, "ESM info request after packing: "); - uint8_t mac[4]; srslte::security_128_eia1 (&ue_emm_ctx->security_ctxt.k_nas_int[16], ue_emm_ctx->security_ctxt.dl_nas_count, @@ -1711,9 +1709,7 @@ s1ap_nas_transport::pack_esm_information_request(srslte::byte_buffer_t *reply_ms ); memcpy(&nas_buffer->msg[1],mac,4); - - m_s1ap_log->debug_hex(nas_buffer->msg, nas_buffer->N_bytes, "ESM info request after security&integrity: "); - + //Copy NAS PDU to Downlink NAS Trasport message buffer memcpy(dw_nas->NAS_PDU.buffer, nas_buffer->msg, nas_buffer->N_bytes); dw_nas->NAS_PDU.n_octets = nas_buffer->N_bytes; From d0457211ad484e2f20418346e5c1108042d00137 Mon Sep 17 00:00:00 2001 From: Andre Puschmann Date: Wed, 2 May 2018 16:49:16 +0200 Subject: [PATCH 17/24] adding support for CHAP auth --- lib/include/srslte/common/interfaces_common.h | 8 +- lib/include/srslte/common/security.h | 5 ++ lib/src/common/security.cc | 20 +++++ srsue/hdr/ue_base.h | 4 +- srsue/hdr/upper/nas.h | 2 + srsue/src/main.cc | 4 +- srsue/src/ue.cc | 2 +- srsue/src/upper/nas.cc | 85 ++++++++++++++++++- srsue/test/upper/nas_test.cc | 2 + 9 files changed, 126 insertions(+), 6 deletions(-) diff --git a/lib/include/srslte/common/interfaces_common.h b/lib/include/srslte/common/interfaces_common.h index 50eb024b5..5051cacbe 100644 --- a/lib/include/srslte/common/interfaces_common.h +++ b/lib/include/srslte/common/interfaces_common.h @@ -38,13 +38,17 @@ namespace srslte { class srslte_nas_config_t { public: - srslte_nas_config_t(uint32_t lcid_ = 0, std::string apn_ = "") + srslte_nas_config_t(uint32_t lcid_ = 0, std::string apn_ = "", std::string user_ = "", std::string pass_ = "") :lcid(lcid_), - apn(apn_) + apn(apn_), + user(user_), + pass(pass_) {} uint32_t lcid; std::string apn; + std::string user; + std::string pass; }; diff --git a/lib/include/srslte/common/security.h b/lib/include/srslte/common/security.h index f88d48a17..61b46d640 100644 --- a/lib/include/srslte/common/security.h +++ b/lib/include/srslte/common/security.h @@ -123,6 +123,11 @@ uint8_t security_128_eia2( uint8_t *key, uint32_t msg_len, uint8_t *mac); +uint8_t security_md5(const uint8_t *input, + size_t len, + uint8_t *output); + + /****************************************************************************** * Encryption / Decryption *****************************************************************************/ diff --git a/lib/src/common/security.cc b/lib/src/common/security.cc index b10b5beab..63cd478c0 100644 --- a/lib/src/common/security.cc +++ b/lib/src/common/security.cc @@ -29,6 +29,13 @@ #include "srslte/common/liblte_security.h" #include "srslte/common/snow_3g.h" +#ifdef HAVE_MBEDTLS +#include "mbedtls/md5.h" +#endif +#ifdef HAVE_POLARSSL +#include "polarssl/md5.h" +#endif + namespace srslte { /****************************************************************************** @@ -166,6 +173,19 @@ uint8_t security_128_eia2( uint8_t *key, mac); } +uint8_t security_md5(const uint8_t *input, size_t len, uint8_t *output) +{ + memset(output, 0x00, 16); +#ifdef HAVE_MBEDTLS + mbedtls_md5(input, len, output); +#endif // HAVE_MBEDTLS +#ifdef HAVE_POLARSSL + md5(input, len, output); +#endif + return SRSLTE_SUCCESS; +} + + /****************************************************************************** * Encryption / Decryption *****************************************************************************/ diff --git a/srsue/hdr/ue_base.h b/srsue/hdr/ue_base.h index 63bb19533..41abd15e0 100644 --- a/srsue/hdr/ue_base.h +++ b/srsue/hdr/ue_base.h @@ -128,7 +128,9 @@ typedef struct { usim_args_t usim; rrc_args_t rrc; std::string ue_category_str; - std::string apn; + std::string apn_name; + std::string apn_user; + std::string apn_pass; expert_args_t expert; }all_args_t; diff --git a/srsue/hdr/upper/nas.h b/srsue/hdr/upper/nas.h index 7879d50f3..e84b4002a 100644 --- a/srsue/hdr/upper/nas.h +++ b/srsue/hdr/upper/nas.h @@ -127,6 +127,8 @@ private: uint32_t ip_addr; uint8_t eps_bearer_id; + uint8_t chap_id; + uint8_t transaction_id; // Security diff --git a/srsue/src/main.cc b/srsue/src/main.cc index fd7d0e161..10759321e 100644 --- a/srsue/src/main.cc +++ b/srsue/src/main.cc @@ -83,7 +83,9 @@ void parse_args(all_args_t *args, int argc, char *argv[]) { "UECapabilityInformation message. Default 0xe6041c00") ("rrc.ue_category", bpo::value(&args->ue_category_str)->default_value("4"), "UE Category (1 to 5)") - ("nas.apn", bpo::value(&args->apn)->default_value(""), "Set Access Point Name (APN) for data services") + ("nas.apn", bpo::value(&args->apn_name)->default_value(""), "Set Access Point Name (APN) for data services") + ("nas.user", bpo::value(&args->apn_user)->default_value(""), "Username for CHAP authentication") + ("nas.pass", bpo::value(&args->apn_pass)->default_value(""), "Password for CHAP authentication") ("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/ue.cc b/srsue/src/ue.cc index ea73c3f13..53dcf0528 100644 --- a/srsue/src/ue.cc +++ b/srsue/src/ue.cc @@ -205,7 +205,7 @@ bool ue::init(all_args_t *args_) { rlc.init(&pdcp, &rrc, this, &rlc_log, &mac, 0 /* RB_ID_SRB0 */); pdcp.init(&rlc, &rrc, &gw, &pdcp_log, 0 /* RB_ID_SRB0 */, SECURITY_DIRECTION_UPLINK); - srslte_nas_config_t nas_cfg(1, args->apn); /* RB_ID_SRB1 */ + srslte_nas_config_t nas_cfg(1, args->apn_name, args->apn_user, args->apn_pass); /* RB_ID_SRB1 */ nas.init(usim, &rrc, &gw, &nas_log, nas_cfg); gw.init(&pdcp, &nas, &gw_log, 3 /* RB_ID_DRB1 */); diff --git a/srsue/src/upper/nas.cc b/srsue/src/upper/nas.cc index 7a73ab5a6..af24d70e6 100644 --- a/srsue/src/upper/nas.cc +++ b/srsue/src/upper/nas.cc @@ -53,6 +53,7 @@ nas::nas() ctxt.cipher_algo = CIPHERING_ALGORITHM_ID_EEA0; ctxt.integ_algo = INTEGRITY_ALGORITHM_ID_EIA0; plmn_is_selected = false; + chap_id = 0; } void nas::init(usim_interface_nas *usim_, @@ -1218,12 +1219,93 @@ void nas::send_esm_information_response(const uint8 proc_transaction_id) { if (cfg.apn == "") { esm_info_resp.apn_present = false; } else { + nas_log->debug("Including APN %s in ESM info response\n", cfg.apn.c_str()); esm_info_resp.apn_present = true; int len = std::min((int)cfg.apn.length(), LIBLTE_STRING_LEN); strncpy(esm_info_resp.apn.apn, cfg.apn.c_str(), len); esm_info_resp.apn.apn[len - 1] = '\0'; } - esm_info_resp.protocol_cnfg_opts_present = false; + + + if (cfg.user != "" && cfg.user.length() < LIBLTE_STRING_LEN && + cfg.pass != "" && cfg.pass.length() < LIBLTE_STRING_LEN) { + + nas_log->debug("Including CHAP authentication for user %s in ESM info response\n", cfg.user.c_str()); + + // Generate CHAP challenge + uint16_t len = 1 /* CHAP code */ + + 1 /* ID */ + + 2 /* complete length */ + + 1 /* data value size */ + + 16 /* data value */ + + cfg.user.length(); + + uint8_t challenge[len]; + challenge[0] = 0x01; // challenge code + challenge[1] = chap_id; // ID + + challenge[2] = (len >> 8) & 0xff; + challenge[3] = len & 0xff; + challenge[4] = 16; + + uint8_t chal_val[16] = { 0xed, 0x0b, 0x26, 0x26, 0xed, 0x0b, 0x26, 0x26, + 0xed, 0x0b, 0x26, 0x26, 0xed, 0x0b, 0x26, 0x26 }; + for (int i = 0; i < 16; i++) { + challenge[5 + i] = chal_val[i]; + } + + // add user as name field + for (size_t i = 0; i < cfg.user.length(); i++) { + const char *name = cfg.user.c_str(); + challenge[21 + i] = name[i]; + } + + // Generate response + uint8_t response[len]; + response[0] = 0x02; // response code + response[1] = chap_id; + response[2] = (len >> 8) & 0xff; + response[3] = len & 0xff; + response[4] = 16; + + // Generate response value + uint16_t resp_val_len = 16 /* MD5 len */ + + 1 /* ID */ + + cfg.pass.length(); + uint8_t resp_val[resp_val_len]; + resp_val[0] = chap_id; + + // add secret + for (size_t i = 0; i < cfg.pass.length(); i++) { + const char* pass = cfg.pass.c_str(); + resp_val[1 + i] = pass[i]; + } + + // copy original challenge behind secret + memcpy(&resp_val[1+cfg.pass.length()], chal_val, 16); + + // Compute MD5 of resp_val and add to response + security_md5(resp_val, resp_val_len, &response[5]); + + // add user as name field again + for (size_t i = 0; i < cfg.user.length(); i++) { + const char *name = cfg.user.c_str(); + response[21 + i] = name[i]; + } + + // Add challenge and resposne to ESM info response + esm_info_resp.protocol_cnfg_opts_present = true; + esm_info_resp.protocol_cnfg_opts.opt[0].id = LIBLTE_MME_CONFIGURATION_PROTOCOL_OPTIONS_CHAP; + memcpy(esm_info_resp.protocol_cnfg_opts.opt[0].contents, challenge, sizeof(challenge)); + esm_info_resp.protocol_cnfg_opts.opt[0].len = sizeof(challenge); + + esm_info_resp.protocol_cnfg_opts.opt[1].id = LIBLTE_MME_CONFIGURATION_PROTOCOL_OPTIONS_CHAP; + memcpy(esm_info_resp.protocol_cnfg_opts.opt[1].contents, response, sizeof(response)); + esm_info_resp.protocol_cnfg_opts.opt[1].len = sizeof(response); + esm_info_resp.protocol_cnfg_opts.N_opts = 2; + } else { + esm_info_resp.protocol_cnfg_opts_present = false; + } byte_buffer_t *pdu = pool_allocate; if (!pdu) { @@ -1260,6 +1342,7 @@ void nas::send_esm_information_response(const uint8 proc_transaction_id) { rrc->write_sdu(cfg.lcid, pdu); ctxt.tx_count++; + chap_id++; } diff --git a/srsue/test/upper/nas_test.cc b/srsue/test/upper/nas_test.cc index 9a3054c6e..589e24da9 100644 --- a/srsue/test/upper/nas_test.cc +++ b/srsue/test/upper/nas_test.cc @@ -277,6 +277,8 @@ int esm_info_request_test() srsue::nas nas; srslte_nas_config_t cfg; cfg.apn = "srslte"; + cfg.user = "srsuser"; + cfg.pass = "srspass"; nas.init(&usim, &rrc_dummy, &gw, &nas_log, cfg); // push ESM info request PDU to NAS to generate response From 47c09fc034f90161905ea5dd8a844d832fb60058 Mon Sep 17 00:00:00 2001 From: Andre Puschmann Date: Wed, 2 May 2018 17:18:58 +0200 Subject: [PATCH 18/24] disable console prints when receiving paging --- srsue/src/upper/rrc.cc | 4 ---- 1 file changed, 4 deletions(-) diff --git a/srsue/src/upper/rrc.cc b/srsue/src/upper/rrc.cc index 24cca3ec7..65b3a3372 100644 --- a/srsue/src/upper/rrc.cc +++ b/srsue/src/upper/rrc.cc @@ -1721,7 +1721,6 @@ void rrc::process_pcch(byte_buffer_t *pdu) { if (pdu->N_bytes > 0 && pdu->N_bytes < SRSLTE_MAX_BUFFER_SIZE_BITS) { rrc_log->info_hex(pdu->msg, pdu->N_bytes, "PCCH message received %d bytes\n", pdu->N_bytes); rrc_log->info("PCCH message Stack latency: %ld us\n", pdu->get_latency_us()); - rrc_log->console("PCCH message received %d bytes\n", pdu->N_bytes); LIBLTE_RRC_PCCH_MSG_STRUCT pcch_msg; ZERO_OBJECT(pcch_msg); @@ -1744,9 +1743,6 @@ void rrc::process_pcch(byte_buffer_t *pdu) { rrc_log->info("Received paging (%d/%d) for UE %x:%x\n", i + 1, pcch_msg.paging_record_list_size, pcch_msg.paging_record_list[i].ue_identity.s_tmsi.mmec, pcch_msg.paging_record_list[i].ue_identity.s_tmsi.m_tmsi); - rrc_log->console("Received paging (%d/%d) for UE %x:%x\n", i + 1, pcch_msg.paging_record_list_size, - pcch_msg.paging_record_list[i].ue_identity.s_tmsi.mmec, - pcch_msg.paging_record_list[i].ue_identity.s_tmsi.m_tmsi); if (ueIdentity.mmec == s_tmsi_paged->mmec && ueIdentity.m_tmsi == s_tmsi_paged->m_tmsi) { if (RRC_STATE_IDLE == state) { rrc_log->info("S-TMSI match in paging message\n"); From 6add9ed4b43cfcde5e9d81d578ff2c4ec5936c66 Mon Sep 17 00:00:00 2001 From: Andre Puschmann Date: Fri, 4 May 2018 14:31:18 +0200 Subject: [PATCH 19/24] generate random challenge during CHAP auth --- srsue/src/upper/nas.cc | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/srsue/src/upper/nas.cc b/srsue/src/upper/nas.cc index af24d70e6..6a6af6cd6 100644 --- a/srsue/src/upper/nas.cc +++ b/srsue/src/upper/nas.cc @@ -84,6 +84,10 @@ void nas::init(usim_interface_nas *usim_, have_guti = true; have_ctxt = true; } + + // set seed for rand (used in CHAP auth) + srand(time(NULL)); + running = true; } @@ -1240,18 +1244,16 @@ void nas::send_esm_information_response(const uint8 proc_transaction_id) { 16 /* data value */ + cfg.user.length(); - uint8_t challenge[len]; + uint8_t challenge[len] = {}; challenge[0] = 0x01; // challenge code challenge[1] = chap_id; // ID - challenge[2] = (len >> 8) & 0xff; challenge[3] = len & 0xff; challenge[4] = 16; - uint8_t chal_val[16] = { 0xed, 0x0b, 0x26, 0x26, 0xed, 0x0b, 0x26, 0x26, - 0xed, 0x0b, 0x26, 0x26, 0xed, 0x0b, 0x26, 0x26 }; + // Append random challenge value for (int i = 0; i < 16; i++) { - challenge[5 + i] = chal_val[i]; + challenge[5 + i] = rand() & 0xFF; } // add user as name field @@ -1261,7 +1263,7 @@ void nas::send_esm_information_response(const uint8 proc_transaction_id) { } // Generate response - uint8_t response[len]; + uint8_t response[len] = {}; response[0] = 0x02; // response code response[1] = chap_id; response[2] = (len >> 8) & 0xff; @@ -1282,6 +1284,7 @@ void nas::send_esm_information_response(const uint8 proc_transaction_id) { } // copy original challenge behind secret + uint8_t *chal_val = &challenge[5]; memcpy(&resp_val[1+cfg.pass.length()], chal_val, 16); // Compute MD5 of resp_val and add to response From cfd387e7b0cb8537d0099f1450173362d8baae24 Mon Sep 17 00:00:00 2001 From: Andre Puschmann Date: Sat, 5 May 2018 20:38:22 +0200 Subject: [PATCH 20/24] fix null-termination when sending APN in ESM info response --- srsue/src/upper/nas.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/srsue/src/upper/nas.cc b/srsue/src/upper/nas.cc index 6a6af6cd6..c4f7a4584 100644 --- a/srsue/src/upper/nas.cc +++ b/srsue/src/upper/nas.cc @@ -1225,9 +1225,9 @@ void nas::send_esm_information_response(const uint8 proc_transaction_id) { } else { nas_log->debug("Including APN %s in ESM info response\n", cfg.apn.c_str()); esm_info_resp.apn_present = true; - int len = std::min((int)cfg.apn.length(), LIBLTE_STRING_LEN); + int len = std::min((int)cfg.apn.length(), LIBLTE_STRING_LEN-1); strncpy(esm_info_resp.apn.apn, cfg.apn.c_str(), len); - esm_info_resp.apn.apn[len - 1] = '\0'; + esm_info_resp.apn.apn[len] = '\0'; } From 0c94974da652a5d7a2281dba0e828bf146909f8a Mon Sep 17 00:00:00 2001 From: Andre Puschmann Date: Sat, 5 May 2018 20:39:58 +0200 Subject: [PATCH 21/24] add compile-time flag to turn off pin check --- srsue/src/upper/pcsc_usim.cc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/srsue/src/upper/pcsc_usim.cc b/srsue/src/upper/pcsc_usim.cc index 7bbaf0f01..26b7f8a1a 100644 --- a/srsue/src/upper/pcsc_usim.cc +++ b/srsue/src/upper/pcsc_usim.cc @@ -29,6 +29,8 @@ #include #include "srslte/common/bcd_helpers.h" +#define CHECK_SIM_PIN 1 + using namespace srslte; namespace srsue{ @@ -502,6 +504,7 @@ int pcsc_usim::scard::init(usim_args_t *args, srslte::log *log_) } } +#if CHECK_SIM_PIN // Verify whether CHV1 (PIN1) is needed to access the card. ret = pin_needed(buf, blen); if (ret < 0) { @@ -530,6 +533,9 @@ int pcsc_usim::scard::init(usim_args_t *args, srslte::log *log_) goto clean_exit; } } +#else + pin1_needed = false; +#endif ret = SCardEndTransaction(scard_handle, SCARD_LEAVE_CARD); if (ret != SCARD_S_SUCCESS) { From a30ad8952c79ced1820e0f660febeb277f3fd4b3 Mon Sep 17 00:00:00 2001 From: Andre Puschmann Date: Mon, 14 May 2018 15:02:32 +0200 Subject: [PATCH 22/24] fix indent and simplify auth request packing --- lib/include/srslte/asn1/liblte_mme.h | 4 +- lib/include/srslte/common/security.h | 4 +- lib/src/asn1/liblte_mme.cc | 7 ++- srsue/hdr/upper/pcsc_usim.h | 86 ++++++++++++++-------------- srsue/test/upper/CMakeLists.txt | 4 +- srsue/test/upper/nas_test.cc | 2 +- 6 files changed, 54 insertions(+), 53 deletions(-) diff --git a/lib/include/srslte/asn1/liblte_mme.h b/lib/include/srslte/asn1/liblte_mme.h index 93b3b903f..3d9640806 100644 --- a/lib/include/srslte/asn1/liblte_mme.h +++ b/lib/include/srslte/asn1/liblte_mme.h @@ -3792,8 +3792,8 @@ typedef struct{ }LIBLTE_MME_ESM_INFORMATION_RESPONSE_MSG_STRUCT; // Functions LIBLTE_ERROR_ENUM liblte_mme_pack_esm_information_response_msg(LIBLTE_MME_ESM_INFORMATION_RESPONSE_MSG_STRUCT *esm_info_resp, - uint8 sec_hdr_type, - uint32 count, + uint8 sec_hdr_type, + uint32 count, LIBLTE_BYTE_MSG_STRUCT *msg); LIBLTE_ERROR_ENUM liblte_mme_unpack_esm_information_response_msg(LIBLTE_BYTE_MSG_STRUCT *msg, LIBLTE_MME_ESM_INFORMATION_RESPONSE_MSG_STRUCT *esm_info_resp); diff --git a/lib/include/srslte/common/security.h b/lib/include/srslte/common/security.h index 61b46d640..5dc8a6305 100644 --- a/lib/include/srslte/common/security.h +++ b/lib/include/srslte/common/security.h @@ -124,8 +124,8 @@ uint8_t security_128_eia2( uint8_t *key, uint8_t *mac); uint8_t security_md5(const uint8_t *input, - size_t len, - uint8_t *output); + size_t len, + uint8_t *output); /****************************************************************************** diff --git a/lib/src/asn1/liblte_mme.cc b/lib/src/asn1/liblte_mme.cc index fd8e120f8..189f9138a 100644 --- a/lib/src/asn1/liblte_mme.cc +++ b/lib/src/asn1/liblte_mme.cc @@ -1027,7 +1027,7 @@ LIBLTE_ERROR_ENUM liblte_mme_unpack_authentication_parameter_rand_ie(uint8 **ie_ Document Reference: 24.301 v10.2.0 Section 9.9.3.4 *********************************************************************/ LIBLTE_ERROR_ENUM liblte_mme_pack_authentication_response_parameter_ie(uint8 *res, - int res_len, + int res_len, uint8 **ie_ptr) { LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; @@ -1037,11 +1037,12 @@ LIBLTE_ERROR_ENUM liblte_mme_pack_authentication_response_parameter_ie(uint8 *r ie_ptr != NULL) { (*ie_ptr)[0] = res_len; + *ie_ptr += 1; for(i=0; i #include -#include +#include "srsue/hdr/upper/usim_base.h" #include "srsue/hdr/upper/usim.h" #include "srsue/hdr/upper/nas.h" #include "srslte/upper/rlc.h" From 009c090623530fbae41cd44cfbb8cc17da05fae2 Mon Sep 17 00:00:00 2001 From: Andre Puschmann Date: Tue, 15 May 2018 11:11:57 +0200 Subject: [PATCH 23/24] extend cmake for finding libpcsclite --- cmake/modules/FindPCSCLite.cmake | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/cmake/modules/FindPCSCLite.cmake b/cmake/modules/FindPCSCLite.cmake index 49f9b7722..e335a7f41 100644 --- a/cmake/modules/FindPCSCLite.cmake +++ b/cmake/modules/FindPCSCLite.cmake @@ -10,19 +10,25 @@ PKG_CHECK_MODULES(PC_PCSCLITE libpcsclite) IF(NOT PCSCLITE_FOUND) -FIND_PATH(PCSCLITE_INCLUDE_DIR winscard.h - HINTS - /usr/include/PCSC - ${PC_PCSCLITE_INCLUDEDIR} - ${PC_PCSCLITE_INCLUDE_DIRS} - ${PC_PCSCLITE_INCLUDE_DIRS}/PCSC - ) - +FIND_PATH(PCSCLITE_INCLUDE_DIR + NAMES winscard.h + HINTS /usr/include/PCSC + /usr/local/include/PCSC + ${PC_PCSCLITE_INCLUDEDIR} + ${PC_PCSCLITE_INCLUDE_DIRS} + ${PC_PCSCLITE_INCLUDE_DIRS}/PCSC + ${CMAKE_INSTALL_PREFIX}/include +) FIND_LIBRARY(PCSCLITE_LIBRARY NAMES pcsclite libpcsclite PCSC - HINTS - ${PC_PCSCLITE_LIBDIR} - ${PC_PCSCLITE_LIBRARY_DIRS} - ) + HINTS ${PC_PCSCLITE_LIBDIR} + ${PC_PCSCLITE_LIBRARY_DIRS} + ${CMAKE_INSTALL_PREFIX}/lib + ${CMAKE_INSTALL_PREFIX}/lib64 + PATHS /usr/local/lib + /usr/local/lib64 + /usr/lib + /usr/lib64 +) # handle the QUIETLY and REQUIRED arguments and set PCSCLITE_FOUND to TRUE if # all listed variables are TRUE From e0e219fdce6715e2e333f1f068693b41af7f8ce1 Mon Sep 17 00:00:00 2001 From: Andre Puschmann Date: Tue, 15 May 2018 12:59:22 +0200 Subject: [PATCH 24/24] reduce default loglevel in RLC stress tests --- lib/test/upper/CMakeLists.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/test/upper/CMakeLists.txt b/lib/test/upper/CMakeLists.txt index 07d0a777f..1770c0f60 100644 --- a/lib/test/upper/CMakeLists.txt +++ b/lib/test/upper/CMakeLists.txt @@ -32,9 +32,9 @@ add_test(rlc_am_test rlc_am_test) add_executable(rlc_stress_test rlc_stress_test.cc) target_link_libraries(rlc_stress_test srslte_upper srslte_phy srslte_common ${Boost_LIBRARIES}) -add_test(rlc_am_stress_test rlc_stress_test --mode=AM) -add_test(rlc_um_stress_test rlc_stress_test --mode=UM) -add_test(rlc_tm_stress_test rlc_stress_test --mode=TM --opp_sdu_ratio=1.0) +add_test(rlc_am_stress_test rlc_stress_test --mode=AM --loglevel 1) +add_test(rlc_um_stress_test rlc_stress_test --mode=UM --loglevel 1) +add_test(rlc_tm_stress_test rlc_stress_test --mode=TM --loglevel 1 --opp_sdu_ratio=1.0) add_executable(rlc_um_data_test rlc_um_data_test.cc) target_link_libraries(rlc_um_data_test srslte_upper srslte_phy srslte_common)