Extend UICC configuration parsing to provide: routing_indicator, protection_scheme, home_network_public_key, and home_network_public_key_id. Use the protection_scheme value to decide what SUCI Profile Scheme to apply during SUCI generation. Add support for Profile Scheme A which provides ECIES-based encryption using Curve25519 and X9.63 KDF as outlined in TS 33.501 Section C.3.4.1 Profile A. When a configuration file specifies an unsupported Profile Scheme, the NAS layer triggers a fatal error. This occurs either because Profile Scheme B is unimplemented or the build uses OpenSSL < 3.0, which lacks Curve25519 and X9.63 KDF support, ensuring users are informed of the incompatibility.
150 lines
8.6 KiB
C
150 lines
8.6 KiB
C
/*
|
|
* SPDX-License-Identifier: LicenseRef-CSSL-1.0
|
|
*/
|
|
#include <ctype.h>
|
|
|
|
#include "common/platform_constants.h"
|
|
#include <openair3/UICC/usim_interface.h>
|
|
#include <openair3/NAS/COMMON/milenage.h>
|
|
extern uint16_t NB_UE_INST;
|
|
|
|
#define IMEISV_STR_MAX_LENGTH 16
|
|
#define UICC_CONFIG_HELP_OPTIONS " list of comma separated options to interface a simulated (real UICC to be developped). Available options: \n"\
|
|
" imsi: user imsi\n"\
|
|
" key: cyphering key\n"\
|
|
" opc: cyphering OPc\n"\
|
|
" imiesv: string with IMEISV value\n"
|
|
#define DEFAULT_HOME_NETWORK_PUBLIC_KEY "5a8d38864820197c3394b92613b20b91633cbd897119273bf8e4a6f4eec0a650"
|
|
/*-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
|
|
/* configuration parameters for the rfsimulator device */
|
|
/* optname helpstr paramflags XXXptr defXXXval type numelt */
|
|
/*-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
|
|
#define UICC_PARAMS_DESC { \
|
|
{"imsi", "USIM IMSI\n", 0, .strptr=&uicc->imsiStr, .defstrval="2089900007487", TYPE_STRING, 0}, \
|
|
{"nmc_size", "number of digits in NMC", 0, .iptr=&uicc->nmc_size, .defintval=2, TYPE_INT, 0}, \
|
|
{"key", "USIM Ki\n", 0, .strptr=&uicc->keyStr, .defstrval="fec86ba6eb707ed08905757b1bb44b8f", TYPE_STRING, 0}, \
|
|
{"opc", "USIM OPc\n", 0, .strptr=&uicc->opcStr, .defstrval="c42449363bbad02b66d16bc975d77cc1", TYPE_STRING, 0}, \
|
|
{"amf", "USIM amf\n", 0, .strptr=&uicc->amfStr, .defstrval="8000", TYPE_STRING, 0}, \
|
|
{"sqn", "USIM sqn\n", 0, .strptr=&uicc->sqnStr, .defstrval="000000", TYPE_STRING, 0}, \
|
|
{"dnn", "UE dnn (apn)\n", 0, .strptr=&uicc->dnnStr, .defstrval="oai", TYPE_STRING, 0}, \
|
|
{"nssai_sst", "UE nssai\n", 0, .iptr=&uicc->nssai_sst, .defintval=1, TYPE_INT, 0}, \
|
|
{"nssai_sd", "UE nssai\n", 0, .iptr=&uicc->nssai_sd, .defintval=0xffffff, TYPE_INT, 0}, \
|
|
{"imeisv", "IMEISV\n", 0, .strptr=&uicc->imeisvStr, .defstrval="6754567890123413", TYPE_STRING, 0}, \
|
|
{"routing_indicator", "Routing Indicator\n", 0, .strptr = &uicc->routing_indicatorStr, .defstrval="0000", TYPE_STRING, 0}, \
|
|
{"protection_scheme", "SUCI Profile Scheme\n", 0, .iptr = &uicc->protection_scheme, .defintval=0, TYPE_INT, 0}, \
|
|
{"home_network_public_key_id", "Home Network Public Key ID\n", 0, .iptr=&uicc->home_network_public_key_id, .defintval=1, TYPE_INT, 0}, \
|
|
{"home_network_public_key", "Home Network Public Key\n", 0, .strptr=&uicc->home_network_public_keyStr, .defstrval=DEFAULT_HOME_NETWORK_PUBLIC_KEY, TYPE_STRING, 0}, \
|
|
};
|
|
|
|
static uicc_t** uiccArray=NULL;
|
|
|
|
const char *const hexTable = "0123456789abcdef";
|
|
static inline uint8_t mkDigit(unsigned char in) {
|
|
for (int i=0; i<16; i++)
|
|
if (tolower(in)==hexTable[i])
|
|
return i;
|
|
LOG_E(SIM,"Impossible hexa input: %c\n",in);
|
|
return 0;
|
|
}
|
|
|
|
static inline void to_hex(char *in, uint8_t *out, int size) {
|
|
for (size_t i=0; in[i]!=0 && i < size*2 ; i+=2) {
|
|
*out++=(mkDigit(in[i]) << 4) | mkDigit(in[i+1]);
|
|
}
|
|
}
|
|
|
|
uicc_t *init_uicc(char *sectionName) {
|
|
uicc_t *uicc=(uicc_t *)calloc(sizeof(uicc_t),1);
|
|
paramdef_t uicc_params[] = UICC_PARAMS_DESC;
|
|
// here we call usim simulation, but calling actual usim is quite simple
|
|
// the code is in open-cells.com => program_uicc open source
|
|
// we can read the IMSI from the USIM
|
|
// key, OPc, sqn, amf don't need to be read from the true USIM
|
|
int ret = config_get(config_get_if(), uicc_params, sizeofArray(uicc_params), sectionName);
|
|
AssertFatal(ret >= 0, "configuration couldn't be performed for uicc name: %s", sectionName);
|
|
LOG_I(SIM, "UICC simulation: IMSI=%s, IMEISV=%s, Ki=%s, OPc=%s\n", uicc->imsiStr, uicc->imeisvStr, uicc->keyStr, uicc->opcStr);
|
|
to_hex(uicc->keyStr,uicc->key, sizeof(uicc->key) );
|
|
to_hex(uicc->opcStr,uicc->opc, sizeof(uicc->opc) );
|
|
to_hex(uicc->sqnStr,uicc->sqn, sizeof(uicc->sqn) );
|
|
to_hex(uicc->amfStr,uicc->amf, sizeof(uicc->amf) );
|
|
to_hex(uicc->home_network_public_keyStr, uicc->home_network_public_key, sizeof(uicc->home_network_public_key));
|
|
return uicc;
|
|
}
|
|
|
|
void uicc_milenage_generate(uint8_t *autn, uicc_t *uicc) {
|
|
// here we call usim simulation, but calling actual usim is quite simple
|
|
// the code is in open-cells.com => program_uicc open source
|
|
milenage_generate(uicc->opc, uicc->amf, uicc->key,
|
|
uicc->sqn, uicc->rand, autn, uicc->ik, uicc->ck, uicc->milenage_res);
|
|
log_dump(SIM,uicc->opc,sizeof(uicc->opc), LOG_DUMP_CHAR,"opc:");
|
|
log_dump(SIM,uicc->amf,sizeof(uicc->amf), LOG_DUMP_CHAR,"amf:");
|
|
log_dump(SIM,uicc->key,sizeof(uicc->key), LOG_DUMP_CHAR,"key:");
|
|
log_dump(SIM,uicc->sqn,sizeof(uicc->sqn), LOG_DUMP_CHAR,"sqn:");
|
|
log_dump(SIM,uicc->rand,sizeof(uicc->rand), LOG_DUMP_CHAR,"rand:");
|
|
log_dump(SIM,uicc->ik,sizeof(uicc->ik), LOG_DUMP_CHAR,"milenage output ik:");
|
|
log_dump(SIM,uicc->ck,sizeof(uicc->ck), LOG_DUMP_CHAR,"milenage output ck:");
|
|
log_dump(SIM,uicc->milenage_res,sizeof(uicc->milenage_res), LOG_DUMP_CHAR,"milenage output res:");
|
|
log_dump(SIM,autn,sizeof(autn), LOG_DUMP_CHAR,"milenage output autn:");
|
|
}
|
|
|
|
uicc_t * checkUicc(int Mod_id) {
|
|
AssertFatal(Mod_id < NB_UE_INST, "Mod_id must be less than NB_UE_INST. Mod_id:%d NB_UE_INST:%d", Mod_id,NB_UE_INST);
|
|
if(uiccArray==NULL){
|
|
uiccArray=(uicc_t **)calloc(1,sizeof(uicc_t*)*NB_UE_INST);
|
|
}
|
|
if (!uiccArray[Mod_id]) {
|
|
char uiccName[64];
|
|
sprintf(uiccName,"uicc%d", Mod_id);
|
|
uiccArray[Mod_id]=(void*)init_uicc(uiccName);
|
|
uicc_t *uicc = uiccArray[Mod_id];
|
|
uicc->n_pdu_sessions = get_pdu_session_configs(uiccName, uicc->pdu_sessions, sizeof(uicc->pdu_sessions));
|
|
if (uicc->n_pdu_sessions > 0) {
|
|
LOG_I(SIM, "overriding potential %s.dnn/nssai_sst/nssai_sd from %s.pdu_sessions\n", uiccName, uiccName);
|
|
} else if (uicc->n_pdu_sessions < 0) {
|
|
// we could not load from "new" pdu_sessions list, so read from "legacy"
|
|
// config
|
|
LOG_W(SIM,
|
|
"loading PDU session parameters from legacy UICC configuration. Please update to use %s.pdu_sessions instead to make this warning disappear\n",
|
|
uiccName);
|
|
sleep(3);
|
|
uicc->n_pdu_sessions = 1;
|
|
pdu_session_config_t *pdu = &uicc->pdu_sessions[0];
|
|
AssertFatal(uicc->dnnStr != NULL, "no default DNN, cannot create\n");
|
|
pdu->id = DEFAULT_NOS1_PDU_ID; // any default would do
|
|
pdu->dnn = strdup(uicc->dnnStr);
|
|
pdu->nssai.sst = uicc->nssai_sst;
|
|
pdu->nssai.sd = uicc->nssai_sd;
|
|
pdu->type = 1; /* = PDU_SESSION_TYPE_IPV4 */
|
|
} // else: uicc->n_pdu_sessions == 0 => don't request a PDU session
|
|
for (int i = 0; i < uicc->n_pdu_sessions; ++i) {
|
|
const pdu_session_config_t *pdu = &uicc->pdu_sessions[i];
|
|
LOG_I(SIM,
|
|
"will request PDU session ID %d, type %d, DNN %s, NSSAI %d.%6x\n",
|
|
pdu->id,
|
|
pdu->type,
|
|
pdu->dnn,
|
|
pdu->nssai.sst,
|
|
pdu->nssai.sd);
|
|
}
|
|
}
|
|
return (uicc_t*) uiccArray[Mod_id];
|
|
}
|
|
|
|
uint8_t getImeisvDigit(const uicc_t *uicc, uint8_t i)
|
|
{
|
|
uint8_t r = 0;
|
|
uint8_t l = strlen(uicc->imeisvStr);
|
|
if (l > IMEISV_STR_MAX_LENGTH) {
|
|
l=IMEISV_STR_MAX_LENGTH;
|
|
}
|
|
if((uicc->imeisvStr!=NULL) && (i<l))
|
|
{
|
|
char c = uicc->imeisvStr[i];
|
|
if (isdigit(c))
|
|
{
|
|
r=c-'0';
|
|
}
|
|
}
|
|
return (0x0f & r);
|
|
}
|