srsRAN/srsue/src/stack/upper/usim.cc

463 lines
13 KiB
C++
Raw Normal View History

2019-04-26 19:27:38 +00:00
/*
2020-03-13 11:12:52 +00:00
* Copyright 2013-2020 Software Radio Systems Limited
2017-05-18 10:52:29 +00:00
*
2019-04-26 19:27:38 +00:00
* This file is part of srsLTE.
2017-05-18 10:52:29 +00:00
*
2019-04-26 19:27:38 +00:00
* srsLTE is free software: you can redistribute it and/or modify
2017-05-18 10:52:29 +00:00
* 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.
*
2019-04-26 19:27:38 +00:00
* srsLTE is distributed in the hope that it will be useful,
2017-05-18 10:52:29 +00:00
* 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 "srsue/hdr/stack/upper/usim.h"
2017-10-07 19:58:08 +00:00
#include "srslte/common/bcd_helpers.h"
#include <sstream>
2017-05-18 10:52:29 +00:00
using namespace srslte;
namespace srsue {
2017-05-18 10:52:29 +00:00
usim::usim(srslte::log* log_) : usim_log(log_) {}
2017-05-18 10:52:29 +00:00
int usim::init(usim_args_t* args)
2017-05-18 10:52:29 +00:00
{
2017-11-23 18:42:48 +00:00
imsi_str = args->imsi;
imei_str = args->imei;
2017-05-18 10:52:29 +00:00
const char* imsi_c = args->imsi.c_str();
const char* imei_c = args->imei.c_str();
auth_algo = auth_algo_milenage;
if ("xor" == args->algo) {
auth_algo = auth_algo_xor;
}
2017-05-18 10:52:29 +00:00
if (32 == args->k.length()) {
str_to_hex(args->k, k);
2017-05-18 10:52:29 +00:00
} else {
usim_log->error("Invalid length for K: %zu should be %d\n", args->k.length(), 32);
srslte::out_stream("Invalid length for K: %zu should be %d\n", args->k.length(), 32);
}
if (auth_algo == auth_algo_milenage) {
if (args->using_op) {
if (32 == args->op.length()) {
str_to_hex(args->op, op);
compute_opc(k, op, opc);
} else {
usim_log->error("Invalid length for OP: %zu should be %d\n", args->op.length(), 32);
srslte::out_stream("Invalid length for OP: %zu should be %d\n", args->op.length(), 32);
}
} else {
if (32 == args->opc.length()) {
str_to_hex(args->opc, opc);
} else {
usim_log->error("Invalid length for OPc: %zu should be %d\n", args->opc.length(), 32);
srslte::out_stream("Invalid length for OPc: %zu should be %d\n", args->opc.length(), 32);
}
}
2017-05-18 10:52:29 +00:00
}
if (15 == args->imsi.length()) {
2017-05-18 10:52:29 +00:00
imsi = 0;
for (int i = 0; i < 15; i++) {
2017-05-18 10:52:29 +00:00
imsi *= 10;
2017-11-23 18:42:48 +00:00
imsi += imsi_c[i] - '0';
2017-05-18 10:52:29 +00:00
}
} else {
2019-02-15 12:45:19 +00:00
usim_log->error("Invalid length for IMSI: %zu should be %d\n", args->imsi.length(), 15);
srslte::out_stream("Invalid length for IMSI: %zu should be %d\n", args->imsi.length(), 15);
2017-05-18 10:52:29 +00:00
}
if (15 == args->imei.length()) {
2017-05-18 10:52:29 +00:00
imei = 0;
for (int i = 0; i < 15; i++) {
2017-05-18 10:52:29 +00:00
imei *= 10;
2017-11-23 18:42:48 +00:00
imei += imei_c[i] - '0';
2017-05-18 10:52:29 +00:00
}
} else {
2018-05-09 11:53:59 +00:00
usim_log->error("Invalid length for IMEI: %zu should be %d\n", args->imei.length(), 15);
srslte::out_stream("Invalid length for IMEI: %zu should be %d\n", args->imei.length(), 15);
2017-05-18 10:52:29 +00:00
}
2017-10-07 19:58:08 +00:00
initiated = true;
return SRSLTE_SUCCESS;
2017-05-18 10:52:29 +00:00
}
void usim::stop() {}
2017-05-18 10:52:29 +00:00
/*******************************************************************************
NAS interface
*******************************************************************************/
2017-11-23 18:42:48 +00:00
std::string usim::get_imsi_str()
{
return imsi_str;
}
std::string usim::get_imei_str()
{
return imei_str;
}
bool usim::get_imsi_vec(uint8_t* imsi_, uint32_t n)
2017-05-18 10:52:29 +00:00
{
if (!initiated) {
2019-04-23 08:53:11 +00:00
ERROR("USIM not initiated!\n");
2017-11-23 18:42:48 +00:00
return false;
2017-10-07 19:58:08 +00:00
}
if (NULL == imsi_ || n < 15) {
2018-05-09 11:53:59 +00:00
usim_log->error("Invalid parameters to get_imsi_vec\n");
2017-11-23 18:42:48 +00:00
return false;
2017-05-18 10:52:29 +00:00
}
uint64_t temp = imsi;
for (int i = 14; i >= 0; i--) {
2017-10-07 19:58:08 +00:00
imsi_[i] = temp % 10;
temp /= 10;
2017-05-18 10:52:29 +00:00
}
2017-11-23 18:42:48 +00:00
return true;
2017-05-18 10:52:29 +00:00
}
2017-11-23 18:42:48 +00:00
bool usim::get_imei_vec(uint8_t* imei_, uint32_t n)
2017-05-18 10:52:29 +00:00
{
if (!initiated) {
2019-04-23 08:53:11 +00:00
ERROR("USIM not initiated!\n");
2017-11-23 18:42:48 +00:00
return false;
2017-10-07 19:58:08 +00:00
}
if (NULL == imei_ || n < 15) {
2018-05-09 11:53:59 +00:00
usim_log->error("Invalid parameters to get_imei_vec\n");
2017-11-23 18:42:48 +00:00
return false;
2017-05-18 10:52:29 +00:00
}
uint64 temp = imei;
for (int i = 14; i >= 0; i--) {
2017-10-07 19:58:08 +00:00
imei_[i] = temp % 10;
temp /= 10;
}
2017-11-23 18:42:48 +00:00
return true;
2017-10-07 19:58:08 +00:00
}
bool usim::get_home_plmn_id(srslte::plmn_id_t* home_plmn_id)
2017-10-07 19:58:08 +00:00
{
if (!initiated) {
2019-04-23 08:53:11 +00:00
ERROR("USIM not initiated!\n");
2017-11-23 18:42:48 +00:00
return false;
2017-05-18 10:52:29 +00:00
}
2017-10-07 19:58:08 +00:00
2017-10-07 20:17:39 +00:00
int mcc_len = 3;
int mnc_len = 2;
2017-10-07 19:58:08 +00:00
uint8_t imsi_vec[15];
get_imsi_vec(imsi_vec, 15);
std::ostringstream mcc_str, mnc_str;
for (int i = 0; i < mcc_len; i++) {
mcc_str << (int)imsi_vec[i];
2017-10-07 19:58:08 +00:00
}
// US MCC uses 3 MNC digits
if (!mcc_str.str().compare("310") || !mcc_str.str().compare("311") || !mcc_str.str().compare("312") ||
!mcc_str.str().compare("313") || !mcc_str.str().compare("316")) {
2017-10-07 19:58:08 +00:00
mnc_len = 3;
}
for (int i = mcc_len; i < mcc_len + mnc_len; i++) {
mnc_str << (int)imsi_vec[i];
2017-10-07 19:58:08 +00:00
}
home_plmn_id->from_string(mcc_str.str() + mnc_str.str());
2017-10-07 19:58:08 +00:00
usim_log->info("Read Home PLMN Id=%s\n", home_plmn_id->to_string().c_str());
2017-10-07 19:58:08 +00:00
2017-11-23 18:42:48 +00:00
return true;
2017-05-18 10:52:29 +00:00
}
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_)
2017-05-18 10:52:29 +00:00
{
if (auth_algo_xor == auth_algo) {
return gen_auth_res_xor(rand, autn_enb, mcc, mnc, res, res_len, k_asme_);
2017-05-18 10:52:29 +00:00
} else {
return gen_auth_res_milenage(rand, autn_enb, mcc, mnc, res, res_len, k_asme_);
2017-05-18 10:52:29 +00:00
}
}
void usim::generate_nas_keys(uint8_t* k_asme_,
uint8_t* k_nas_enc,
uint8_t* k_nas_int,
2017-05-18 10:52:29 +00:00
CIPHERING_ALGORITHM_ID_ENUM cipher_algo,
INTEGRITY_ALGORITHM_ID_ENUM integ_algo)
{
// Generate K_nas_enc and K_nas_int
security_generate_k_nas(k_asme_, cipher_algo, integ_algo, k_nas_enc, k_nas_int);
2017-05-18 10:52:29 +00:00
}
/*******************************************************************************
RRC interface
*******************************************************************************/
void usim::generate_as_keys(uint8_t* k_asme_, uint32_t count_ul, srslte::as_security_config_t* sec_cfg)
2017-05-18 10:52:29 +00:00
{
// Generate K_enb
security_generate_k_enb(k_asme_, count_ul, k_enb);
2017-05-18 10:52:29 +00:00
memcpy(k_asme, k_asme_, 32);
2017-11-27 12:57:05 +00:00
// Save initial k_enb
memcpy(k_enb_initial, k_enb, 32);
2017-11-23 18:46:34 +00:00
// Generate K_rrc_enc and K_rrc_int
security_generate_k_rrc(
k_enb, sec_cfg->cipher_algo, sec_cfg->integ_algo, sec_cfg->k_rrc_enc.data(), sec_cfg->k_rrc_int.data());
2017-11-23 18:46:34 +00:00
// Generate K_up_enc and K_up_int
security_generate_k_up(
k_enb, sec_cfg->cipher_algo, sec_cfg->integ_algo, sec_cfg->k_up_enc.data(), sec_cfg->k_up_int.data());
2017-11-27 12:57:05 +00:00
2018-07-28 08:16:01 +00:00
current_ncc = 0;
is_first_ncc = true;
2017-11-23 18:46:34 +00:00
}
void usim::generate_as_keys_ho(uint32_t pci, uint32_t earfcn, int ncc, srslte::as_security_config_t* sec_cfg)
2017-11-23 18:46:34 +00:00
{
usim_log->info("Generating AS Keys HO. PCI 0x%02x, DL-EARFCN %d, NCC %d\n", pci, earfcn, ncc);
2020-09-10 13:54:25 +00:00
usim_log->info_hex(sec_cfg->k_rrc_enc.data(), sec_cfg->k_rrc_enc.size(), "Original HO K_RRC_enc");
usim_log->info_hex(sec_cfg->k_rrc_int.data(), sec_cfg->k_rrc_int.size(), "Original HO K_RRC_int");
uint8_t* enb_star_key = k_enb;
2017-11-27 12:57:05 +00:00
if (ncc < 0) {
ncc = current_ncc;
}
// Generate successive NH
while (current_ncc != (uint32_t)ncc) {
uint8_t* sync = NULL;
2018-07-28 08:16:01 +00:00
if (is_first_ncc) {
sync = k_enb_initial;
2018-07-28 08:16:01 +00:00
is_first_ncc = false;
} else {
sync = nh;
2017-11-27 12:57:05 +00:00
}
// Generate NH
security_generate_nh(k_asme, sync, nh);
2017-11-27 12:57:05 +00:00
current_ncc++;
2018-05-28 06:19:23 +00:00
if (current_ncc == 8) {
2017-11-27 12:57:05 +00:00
current_ncc = 0;
}
enb_star_key = nh;
}
2017-11-23 18:46:34 +00:00
// Generate K_enb
security_generate_k_enb_star(enb_star_key, pci, earfcn, k_enb_star);
2017-11-23 18:46:34 +00:00
2017-11-27 12:57:05 +00:00
// K_enb becomes K_enb*
2017-11-23 18:46:34 +00:00
memcpy(k_enb, k_enb_star, 32);
2017-05-18 10:52:29 +00:00
// Generate K_rrc_enc and K_rrc_int
security_generate_k_rrc(
k_enb, sec_cfg->cipher_algo, sec_cfg->integ_algo, sec_cfg->k_rrc_enc.data(), sec_cfg->k_rrc_int.data());
2017-05-18 10:52:29 +00:00
// Generate K_up_enc and K_up_int
security_generate_k_up(
k_enb, sec_cfg->cipher_algo, sec_cfg->integ_algo, sec_cfg->k_up_enc.data(), sec_cfg->k_up_int.data());
2020-09-10 13:54:25 +00:00
usim_log->info_hex(sec_cfg->k_rrc_enc.data(), sec_cfg->k_rrc_enc.size(), "HO K_RRC_enc");
usim_log->info_hex(sec_cfg->k_rrc_int.data(), sec_cfg->k_rrc_int.size(), "HO K_RRC_int");
2017-05-18 10:52:29 +00:00
}
void usim::store_keys_before_ho(const srslte::as_security_config_t& as_ctx)
{
usim_log->info("Storing AS Keys pre-handover. NCC=%d\n", current_ncc);
old_as_ctx = as_ctx;
old_ncc = current_ncc;
memcpy(old_k_enb, k_enb, 32);
return;
}
void usim::restore_keys_from_failed_ho(srslte::as_security_config_t* as_ctx)
{
usim_log->info("Restoring Keys from failed handover. NCC=%d\n", old_ncc);
*as_ctx = old_as_ctx;
current_ncc = old_ncc;
memcpy(k_enb, old_k_enb, 32);
return;
}
2017-05-18 10:52:29 +00:00
/*******************************************************************************
Helpers
*******************************************************************************/
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)
2017-05-18 10:52:29 +00:00
{
auth_result_t result = AUTH_OK;
uint32_t i;
uint8_t sqn[6];
2017-05-18 10:52:29 +00:00
// Use RAND and K to compute RES, CK, IK and AK
security_milenage_f2345(k, opc, rand, res, ck, ik, ak);
2017-05-18 10:52:29 +00:00
*res_len = 8;
2017-05-18 10:52:29 +00:00
// Extract sqn from autn
for (i = 0; i < 6; i++) {
2017-05-18 10:52:29 +00:00
sqn[i] = autn_enb[i] ^ ak[i];
}
// Extract AMF from autn
for (int i = 0; i < 2; i++) {
amf[i] = autn_enb[6 + i];
}
2017-05-18 10:52:29 +00:00
// Generate MAC
security_milenage_f1(k, opc, rand, sqn, amf, mac);
2017-05-18 10:52:29 +00:00
// Construct AUTN
for (i = 0; i < 6; i++) {
2017-05-18 10:52:29 +00:00
autn[i] = sqn[i] ^ ak[i];
}
for (i = 0; i < 2; i++) {
autn[6 + i] = amf[i];
2017-05-18 10:52:29 +00:00
}
for (i = 0; i < 8; i++) {
autn[8 + i] = mac[i];
2017-05-18 10:52:29 +00:00
}
// Compare AUTNs
for (i = 0; i < 16; i++) {
if (autn[i] != autn_enb[i]) {
result = AUTH_FAILED;
2017-05-18 10:52:29 +00:00
}
}
// Generate K_asme
security_generate_k_asme(ck, ik, ak, sqn, mcc, mnc, k_asme);
return result;
2017-05-18 10:52:29 +00:00
}
// 3GPP TS 34.108 version 10.0.0 Section 8
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_)
2017-05-18 10:52:29 +00:00
{
auth_result_t result = AUTH_OK;
uint8_t sqn[6];
uint8_t xdout[16];
uint8_t cdout[8];
2017-05-18 10:52:29 +00:00
// Use RAND and K to compute RES, CK, IK and AK
for (uint32_t i = 0; i < 16; i++) {
xdout[i] = k[i] ^ rand[i];
2017-05-18 10:52:29 +00:00
}
for (uint32_t i = 0; i < 16; i++) {
res[i] = xdout[i];
ck[i] = xdout[(i + 1) % 16];
ik[i] = xdout[(i + 2) % 16];
2017-05-18 10:52:29 +00:00
}
for (uint32_t i = 0; i < 6; i++) {
ak[i] = xdout[i + 3];
2017-05-18 10:52:29 +00:00
}
*res_len = 8;
2017-05-18 10:52:29 +00:00
// Extract sqn from autn
for (uint32_t i = 0; i < 6; i++) {
2017-05-18 10:52:29 +00:00
sqn[i] = autn_enb[i] ^ ak[i];
}
// Extract AMF from autn
for (uint32_t i = 0; i < 2; i++) {
amf[i] = autn_enb[6 + i];
}
2017-05-18 10:52:29 +00:00
// Generate cdout
for (uint32_t i = 0; i < 6; i++) {
2017-05-18 10:52:29 +00:00
cdout[i] = sqn[i];
}
for (uint32_t i = 0; i < 2; i++) {
cdout[6 + i] = amf[i];
2017-05-18 10:52:29 +00:00
}
// Generate MAC
for (uint32_t i = 0; i < 8; i++) {
2017-05-18 10:52:29 +00:00
mac[i] = xdout[i] ^ cdout[i];
}
// Construct AUTN
for (uint32_t i = 0; i < 6; i++) {
2017-05-18 10:52:29 +00:00
autn[i] = sqn[i] ^ ak[i];
}
for (uint32_t i = 0; i < 2; i++) {
autn[6 + i] = amf[i];
2017-05-18 10:52:29 +00:00
}
for (uint32_t i = 0; i < 8; i++) {
autn[8 + i] = mac[i];
2017-05-18 10:52:29 +00:00
}
// Compare AUTNs
for (uint32_t i = 0; i < 16; i++) {
if (autn[i] != autn_enb[i]) {
result = AUTH_FAILED;
2017-05-18 10:52:29 +00:00
}
}
// Generate K_asme
security_generate_k_asme(ck, ik, ak, sqn, mcc, mnc, k_asme_);
return result;
2017-05-18 10:52:29 +00:00
}
void usim::str_to_hex(std::string str, uint8_t* hex)
2017-05-18 10:52:29 +00:00
{
uint32_t i;
const char* h_str = str.c_str();
uint32_t len = str.length();
for (i = 0; i < len / 2; i++) {
if (h_str[i * 2 + 0] >= '0' && h_str[i * 2 + 0] <= '9') {
hex[i] = (h_str[i * 2 + 0] - '0') << 4;
} else if (h_str[i * 2 + 0] >= 'A' && h_str[i * 2 + 0] <= 'F') {
hex[i] = ((h_str[i * 2 + 0] - 'A') + 0xA) << 4;
} else {
hex[i] = ((h_str[i * 2 + 0] - 'a') + 0xA) << 4;
2017-05-18 10:52:29 +00:00
}
if (h_str[i * 2 + 1] >= '0' && h_str[i * 2 + 1] <= '9') {
hex[i] |= h_str[i * 2 + 1] - '0';
} else if (h_str[i * 2 + 1] >= 'A' && h_str[i * 2 + 1] <= 'F') {
hex[i] |= (h_str[i * 2 + 1] - 'A') + 0xA;
} else {
hex[i] |= (h_str[i * 2 + 1] - 'a') + 0xA;
2017-05-18 10:52:29 +00:00
}
}
}
} // namespace srsue