From 3fe6dad32391e37babd0a3fc1495e5cb2c7be630 Mon Sep 17 00:00:00 2001 From: Andre Puschmann Date: Wed, 4 Apr 2018 11:53:36 +0200 Subject: [PATCH] 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