Commit abbd670e authored by Aaron van Diepen's avatar Aaron van Diepen

Derive Serving Network Name from SIB1 PLMN

Extract MCC and MNC from the SIB1 PLMN identity and store them in the
UE NAS context to propogate them to NAS security procedures.

Previously, the Serving Network Name (SNN) used during authentication
was constructed from the IMSI, implicitly assuming the UE was attached
to its home network. In roaming scenarios, this produced an incorrect
SNN, causing authentication and key derivation failures.

The NAS key derivation functions (transferRES, derive_kausf, derive_kseaf)
now build the SNN using the MCC/MNC broadcast by the serving network in SIB1,
ensuring that authentication uses the correct serving PLMN during roaming.
parent fdb6b44b
...@@ -486,6 +486,30 @@ static void nr_rrc_process_sib1(NR_UE_RRC_INST_t *rrc, NR_UE_RRC_SI_INFO *SI_inf ...@@ -486,6 +486,30 @@ static void nr_rrc_process_sib1(NR_UE_RRC_INST_t *rrc, NR_UE_RRC_SI_INFO *SI_inf
if(g_log->log_component[NR_RRC].level >= OAILOG_DEBUG) if(g_log->log_component[NR_RRC].level >= OAILOG_DEBUG)
xer_fprint(stdout, &asn_DEF_NR_SIB1, (const void *) sib1); xer_fprint(stdout, &asn_DEF_NR_SIB1, (const void *) sib1);
LOG_A(NR_RRC, "SIB1 decoded\n"); LOG_A(NR_RRC, "SIB1 decoded\n");
plmn_id_t *plmn_id = malloc_or_fail(sizeof(plmn_id_t));
/* selected_plmn_identity is one-indexed */
AssertFatal(rrc->selected_plmn_identity > 0, "No PLMN selected");
/* only one PLMN info block is currently supported*/
NR_PLMN_Identity_t *plmn = sib1->cellAccessRelatedInfo.plmn_IdentityInfoList.list.array[0]
->plmn_IdentityList.list.array[rrc->selected_plmn_identity - 1];
/* Convert MCC */
plmn_id->mcc = (*plmn->mcc->list.array[0]) * 100 + (*plmn->mcc->list.array[1]) * 10 + (*plmn->mcc->list.array[2]);
plmn_id->mnc_digit_length = plmn->mnc.list.count;
/* Convert MNC (2 or 3 digits) */
if (plmn->mnc.list.count == 3) {
plmn_id->mnc = (*plmn->mnc.list.array[0]) * 100 + (*plmn->mnc.list.array[1]) * 10 + (*plmn->mnc.list.array[2]);
} else {
plmn_id->mnc = (*plmn->mnc.list.array[0]) * 10 + (*plmn->mnc.list.array[1]);
}
nr_ue_nas_t *nas = get_ue_nas_info(rrc->ue_id);
nas->sn_id = plmn_id;
nr_timer_start(&SI_info->sib1_timer); nr_timer_start(&SI_info->sib1_timer);
SI_info->sib1_validity = true; SI_info->sib1_validity = true;
if (rrc->nrRrcState == RRC_STATE_IDLE_NR) { if (rrc->nrRrcState == RRC_STATE_IDLE_NR) {
......
...@@ -101,26 +101,13 @@ static fgmm_msg_header_t set_mm_header(fgs_nas_msg_t type, Security_header_t sec ...@@ -101,26 +101,13 @@ static fgmm_msg_header_t set_mm_header(fgs_nas_msg_t type, Security_header_t sec
return mm_header; return mm_header;
} }
static void servingNetworkName(uint8_t *msg, char *imsiStr, int mnc_size) static void servingNetworkName(uint8_t *msg, plmn_id_t *plmn_id)
{ {
// SNN-network-identifier in TS 24.501 // SNN-network-identifier in TS 24.501
// TS 24.501: If the MNC of the serving PLMN has two digits, then a zero is added at the beginning. // TS 24.501: If the MNC of the serving PLMN has two digits, then a zero is added at the beginning.
// MNC
char mnc[4];
if (mnc_size == 2) {
snprintf(mnc, sizeof(mnc), "0%c%c", imsiStr[3], imsiStr[4]);
} else {
snprintf(mnc, sizeof(mnc), "%c%c%c", imsiStr[3], imsiStr[4], imsiStr[5]);
}
// MCC
char mcc[4];
snprintf(mcc, sizeof(mcc), "%c%c%c", imsiStr[0], imsiStr[1], imsiStr[2]);
int size = 64; int size = 64;
snprintf((char *)msg, size, "5G:mnc%3s.mcc%3s.3gppnetwork.org", mnc, mcc); snprintf((char *)msg, size, "5G:mnc%03d.mcc%03d.3gppnetwork.org", plmn_id->mnc, plmn_id->mcc);
} }
static const char *print_info(uint8_t id, const text_info_t *array, uint8_t array_size) static const char *print_info(uint8_t id, const text_info_t *array, uint8_t array_size)
...@@ -505,11 +492,11 @@ static int fill_imeisv(FGSMobileIdentity *mi, const uicc_t *uicc) ...@@ -505,11 +492,11 @@ static int fill_imeisv(FGSMobileIdentity *mi, const uicc_t *uicc)
return 19; return 19;
} }
void transferRES(uint8_t ck[16], uint8_t ik[16], uint8_t *input, uint8_t rand[16], uint8_t *output, uicc_t *uicc) void transferRES(uint8_t ck[16], uint8_t ik[16], uint8_t *input, uint8_t rand[16], uint8_t *output, plmn_id_t *plmn_id)
{ {
uint8_t S[100] = {0}; uint8_t S[100] = {0};
S[0] = 0x6B; S[0] = 0x6B;
servingNetworkName(S + 1, uicc->imsiStr, uicc->nmc_size); servingNetworkName(S + 1, plmn_id);
int netNamesize = strlen((char *)S + 1); int netNamesize = strlen((char *)S + 1);
S[1 + netNamesize] = (netNamesize & 0xff00) >> 8; S[1 + netNamesize] = (netNamesize & 0xff00) >> 8;
S[2 + netNamesize] = (netNamesize & 0x00ff); S[2 + netNamesize] = (netNamesize & 0x00ff);
...@@ -548,7 +535,7 @@ void transferRES(uint8_t ck[16], uint8_t ik[16], uint8_t *input, uint8_t rand[16 ...@@ -548,7 +535,7 @@ void transferRES(uint8_t ck[16], uint8_t ik[16], uint8_t *input, uint8_t rand[16
memcpy(output, out + 16, 16); memcpy(output, out + 16, 16);
} }
void derive_kausf(uint8_t ck[16], uint8_t ik[16], uint8_t sqn[6], uint8_t kausf[32], uicc_t *uicc) void derive_kausf(uint8_t ck[16], uint8_t ik[16], uint8_t sqn[6], uint8_t kausf[32], plmn_id_t *plmn_id)
{ {
uint8_t S[100] = {0}; uint8_t S[100] = {0};
uint8_t key[32] = {0}; uint8_t key[32] = {0};
...@@ -556,7 +543,7 @@ void derive_kausf(uint8_t ck[16], uint8_t ik[16], uint8_t sqn[6], uint8_t kausf[ ...@@ -556,7 +543,7 @@ void derive_kausf(uint8_t ck[16], uint8_t ik[16], uint8_t sqn[6], uint8_t kausf[
memcpy(&key[0], ck, 16); memcpy(&key[0], ck, 16);
memcpy(&key[16], ik, 16); // KEY memcpy(&key[16], ik, 16); // KEY
S[0] = 0x6A; S[0] = 0x6A;
servingNetworkName(S + 1, uicc->imsiStr, uicc->nmc_size); servingNetworkName(S + 1, plmn_id);
int netNamesize = strlen((char *)S + 1); int netNamesize = strlen((char *)S + 1);
S[1 + netNamesize] = (uint8_t)((netNamesize & 0xff00) >> 8); S[1 + netNamesize] = (uint8_t)((netNamesize & 0xff00) >> 8);
S[2 + netNamesize] = (uint8_t)(netNamesize & 0x00ff); S[2 + netNamesize] = (uint8_t)(netNamesize & 0x00ff);
...@@ -570,11 +557,11 @@ void derive_kausf(uint8_t ck[16], uint8_t ik[16], uint8_t sqn[6], uint8_t kausf[ ...@@ -570,11 +557,11 @@ void derive_kausf(uint8_t ck[16], uint8_t ik[16], uint8_t sqn[6], uint8_t kausf[
kdf(key, data, 32, kausf); kdf(key, data, 32, kausf);
} }
void derive_kseaf(uint8_t kausf[32], uint8_t kseaf[32], uicc_t *uicc) void derive_kseaf(uint8_t kausf[32], uint8_t kseaf[32], plmn_id_t *plmn_id)
{ {
uint8_t S[100] = {0}; uint8_t S[100] = {0};
S[0] = 0x6C; // FC S[0] = 0x6C; // FC
servingNetworkName(S + 1, uicc->imsiStr, uicc->nmc_size); servingNetworkName(S + 1, plmn_id);
int netNamesize = strlen((char *)S + 1); int netNamesize = strlen((char *)S + 1);
S[1 + netNamesize] = (uint8_t)((netNamesize & 0xff00) >> 8); S[1 + netNamesize] = (uint8_t)((netNamesize & 0xff00) >> 8);
S[2 + netNamesize] = (uint8_t)(netNamesize & 0x00ff); S[2 + netNamesize] = (uint8_t)(netNamesize & 0x00ff);
...@@ -641,14 +628,14 @@ static void derive_ue_keys(uint8_t *buf, nr_ue_nas_t *nas) ...@@ -641,14 +628,14 @@ static void derive_ue_keys(uint8_t *buf, nr_ue_nas_t *nas)
uint8_t ck[16], ik[16]; uint8_t ck[16], ik[16];
f2345(nas->uicc->key, rand, resTemp, ck, ik, ak, nas->uicc->opc); f2345(nas->uicc->key, rand, resTemp, ck, ik, ak, nas->uicc->opc);
transferRES(ck, ik, resTemp, rand, output, nas->uicc); transferRES(ck, ik, resTemp, rand, output, nas->sn_id);
for (int index = 0; index < 6; index++) { for (int index = 0; index < 6; index++) {
sqn[index] = buf[26 + index]; sqn[index] = buf[26 + index];
} }
derive_kausf(ck, ik, sqn, kausf, nas->uicc); derive_kausf(ck, ik, sqn, kausf, nas->sn_id);
derive_kseaf(kausf, kseaf, nas->uicc); derive_kseaf(kausf, kseaf, nas->sn_id);
derive_kamf(kseaf, kamf, 0x0000, nas->uicc); derive_kamf(kseaf, kamf, 0x0000, nas->uicc);
derive_kgnb(kamf, nas->security.nas_count_ul, kgnb); derive_kgnb(kamf, nas->security.nas_count_ul, kgnb);
......
...@@ -102,6 +102,7 @@ typedef struct { ...@@ -102,6 +102,7 @@ typedef struct {
int t3446; int t3446;
/* NAS Key Set Identifier associated to the security context */ /* NAS Key Set Identifier associated to the security context */
uint8_t *ksi; uint8_t *ksi;
plmn_id_t *sn_id;
} nr_ue_nas_t; } nr_ue_nas_t;
nr_ue_nas_t *get_ue_nas_info(module_id_t module_id); nr_ue_nas_t *get_ue_nas_info(module_id_t module_id);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment