open5gs/lib/crypt/ogs-kdf.c
Pau Espin Pedrol 5a31af36e0 [MME] Gn: Introduce initial support for 4G->2G cell reselection
In an Inter-RAT setup a UE could perform a RAU coming from a 4G network.
In that case the UE/MS is unknown to the SGSN and it should request the
SGSN context (MM, PDP) from the MME. This is done through the following
GTPv1C message exchange on the Gn interface of SGSN and MME:
SGSN -> MME: SGSN Context Request
SGSN <- MME: SGSN Context Response
SGSN -> MME: SGSN Context Acknowledge

This commit doesn't aim to be a complete implementation of the mentioned
procedure, since it's quite a complex one, with lots of fields and logic
required. This so far only implements in general the minimally
successful case by filling as much as possible the required set of
fields.
This will allow for a base onto which do incremental improvements and
fixes while testing against UEs and SGSNs (such as osmo-sgsn, which
doesn't yet support this procedure but will potentially earn it soon).

This commit doesn't implement the reverse direction, aka UE issuing cell
reselection 2G->4G. Initial support for this scenario will hopefully be
added soon as a follow-up patch, similar to this one.

Related: https://osmocom.org/issues/6294
2023-12-21 22:11:49 +09:00

483 lines
14 KiB
C

/*
* Copyright (C) 2019,2020 by Sukchan Lee <acetcom@gmail.com>
*
* This file is part of Open5GS.
*
* This program 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.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "ogs-crypt.h"
#define MAX_NUM_OF_KDF_PARAM 16
#define FC_FOR_CK_PRIME_IK_PRIME_DERIVATION 0x20
#define FC_FOR_5GS_ALGORITHM_KEY_DERIVATION 0x69
#define FC_FOR_KAUSF_DERIVATION 0x6A
#define FC_FOR_RES_STAR_XRES_STAR_DERIVATION 0x6B
#define FC_FOR_KSEAF_DERIVATION 0x6C
#define FC_FOR_KAMF_DERIVATION 0x6D
#define FC_FOR_KGNB_KN3IWF_DERIVATION 0x6E
#define FC_FOR_NH_GNB_DERIVATION 0x6F
#define FC_FOR_KASME 0x10
#define FC_FOR_KENB_DERIVATION 0x11
#define FC_FOR_NH_ENB_DERIVATION 0x12
#define FC_FOR_EPS_ALGORITHM_KEY_DERIVATION 0x15
#define FC_FOR_CK_IK_DERIVATION_HANDOVER 0x16
#define FC_FOR_NAS_TOKEN_DERIVATION 0x17
#define FC_FOR_CK_IK_DERIVATION_IDLE_MOBILITY 0x1B
typedef struct kdf_param_s {
const uint8_t *buf;
uint16_t len;
} kdf_param_t[MAX_NUM_OF_KDF_PARAM];
/* KDF function : TS.33220 cluase B.2.0 */
static void ogs_kdf_common(const uint8_t *key, uint32_t key_size,
uint8_t fc, kdf_param_t param, uint8_t *output)
{
int i = 0, pos;
uint8_t *s = NULL;
ogs_assert(key);
ogs_assert(key_size);
ogs_assert(fc);
ogs_assert(param[0].buf);
ogs_assert(param[0].len);
ogs_assert(output);
pos = 1; /* FC Value */
/* Calculate buffer length */
for (i = 0; i < MAX_NUM_OF_KDF_PARAM && param[i].buf && param[i].len; i++) {
pos += (param[i].len + 2);
}
s = ogs_calloc(1, pos);
ogs_assert(s);
/* Copy buffer from param */
pos = 0;
s[pos++] = fc;
for (i = 0; i < MAX_NUM_OF_KDF_PARAM && param[i].buf && param[i].len; i++) {
uint16_t len;
memcpy(&s[pos], param[i].buf, param[i].len);
pos += param[i].len;
len = htobe16(param[i].len);
memcpy(&s[pos], &len, sizeof(len));
pos += 2;
}
ogs_hmac_sha256(key, key_size, s, pos, output, OGS_SHA256_DIGEST_SIZE);
ogs_free(s);
}
/* TS33.501 Annex A.2 : Kausf derviation function */
void ogs_kdf_kausf(
uint8_t *ck, uint8_t *ik,
char *serving_network_name, uint8_t *autn,
uint8_t *kausf)
{
kdf_param_t param;
uint8_t key[OGS_KEY_LEN*2];
ogs_assert(ck);
ogs_assert(ik);
ogs_assert(serving_network_name);
ogs_assert(autn);
ogs_assert(kausf);
memcpy(key, ck, OGS_KEY_LEN);
memcpy(key+OGS_KEY_LEN, ik, OGS_KEY_LEN);
memset(param, 0, sizeof(param));
param[0].buf = (uint8_t *)serving_network_name;
param[0].len = strlen(serving_network_name);
param[1].buf = autn;
param[1].len = OGS_SQN_XOR_AK_LEN;
ogs_kdf_common(key, OGS_KEY_LEN*2,
FC_FOR_KAUSF_DERIVATION, param, kausf);
}
/* TS33.501 Annex A.4 : RES* and XRES* derivation function */
void ogs_kdf_xres_star(
uint8_t *ck, uint8_t *ik,
char *serving_network_name, uint8_t *rand,
uint8_t *xres, size_t xres_len,
uint8_t *xres_star)
{
kdf_param_t param;
uint8_t key[OGS_KEY_LEN*2];
uint8_t output[OGS_SHA256_DIGEST_SIZE];
ogs_assert(ck);
ogs_assert(ik);
ogs_assert(serving_network_name);
ogs_assert(rand);
ogs_assert(xres);
ogs_assert(xres_len);
memcpy(key, ck, OGS_KEY_LEN);
memcpy(key+OGS_KEY_LEN, ik, OGS_KEY_LEN);
memset(param, 0, sizeof(param));
param[0].buf = (uint8_t *)serving_network_name;
param[0].len = strlen(serving_network_name);
param[1].buf = rand;
param[1].len = OGS_RAND_LEN;
param[2].buf = xres;
param[2].len = xres_len;
ogs_kdf_common(key, OGS_KEY_LEN*2,
FC_FOR_RES_STAR_XRES_STAR_DERIVATION, param, output);
memcpy(xres_star, output+OGS_KEY_LEN, OGS_KEY_LEN);
}
/* TS33.501 Annex A.5 : HRES* and HXRES* derivation function */
void ogs_kdf_hxres_star(uint8_t *rand, uint8_t *xres_star, uint8_t *hxres_star)
{
uint8_t message[OGS_RAND_LEN + OGS_KEY_LEN];
uint8_t output[OGS_SHA256_DIGEST_SIZE];
ogs_assert(rand);
ogs_assert(xres_star);
ogs_assert(hxres_star);
memcpy(message, rand, OGS_RAND_LEN);
memcpy(message+OGS_RAND_LEN, xres_star, OGS_KEY_LEN);
ogs_sha256(message, OGS_RAND_LEN+OGS_KEY_LEN, output);
memcpy(hxres_star, output+OGS_KEY_LEN, OGS_KEY_LEN);
}
/* TS33.501 Annex A.6 : Kseaf derivation function */
void ogs_kdf_kseaf(char *serving_network_name, const uint8_t *kausf, uint8_t *kseaf)
{
kdf_param_t param;
ogs_assert(serving_network_name);
ogs_assert(kausf);
ogs_assert(kseaf);
memset(param, 0, sizeof(param));
param[0].buf = (uint8_t *)serving_network_name;
param[0].len = strlen(serving_network_name);
ogs_kdf_common(kausf, OGS_SHA256_DIGEST_SIZE,
FC_FOR_KSEAF_DERIVATION, param, kseaf);
}
/* TS33.501 Annex A.7 : Kamf derivation function */
void ogs_kdf_kamf(const char *supi, const uint8_t *abba, uint8_t abba_len,
const uint8_t *kseaf, uint8_t *kamf)
{
kdf_param_t param;
char *val;
ogs_assert(supi);
ogs_assert(abba);
ogs_assert(abba_len);
ogs_assert(kseaf);
ogs_assert(kamf);
val = ogs_id_get_value(supi);
memset(param, 0, sizeof(param));
param[0].buf = (const uint8_t*) val;
ogs_assert(param[0].buf);
param[0].len = strlen(val);
param[1].buf = abba;
param[1].len = abba_len;
ogs_kdf_common(kseaf, OGS_SHA256_DIGEST_SIZE,
FC_FOR_KAMF_DERIVATION, param, kamf);
ogs_free(val);
}
/* TS33.501 Annex A.8 : Algorithm key derivation functions */
void ogs_kdf_nas_5gs(uint8_t algorithm_type_distinguishers,
uint8_t algorithm_identity, const uint8_t *kamf, uint8_t *knas)
{
kdf_param_t param;
uint8_t output[OGS_SHA256_DIGEST_SIZE];
ogs_assert(kamf);
ogs_assert(knas);
memset(param, 0, sizeof(param));
param[0].buf = &algorithm_type_distinguishers;
param[0].len = 1;
param[1].buf = &algorithm_identity;
param[1].len = 1;
ogs_kdf_common(kamf, OGS_SHA256_DIGEST_SIZE,
FC_FOR_5GS_ALGORITHM_KEY_DERIVATION, param, output);
memcpy(knas, output+16, 16);
}
/* TS33.501 Annex A.9 KgNB and Kn3iwf derivation function */
void ogs_kdf_kgnb_and_kn3iwf(const uint8_t *kamf, uint32_t ul_count,
uint8_t access_type_distinguisher, uint8_t *kgnb)
{
kdf_param_t param;
ogs_assert(kamf);
ogs_assert(kgnb);
memset(param, 0, sizeof(param));
ul_count = htobe32(ul_count);
param[0].buf = (uint8_t *)&ul_count;
param[0].len = 4;
param[1].buf = &access_type_distinguisher;
param[1].len = 1;
ogs_kdf_common(kamf, OGS_SHA256_DIGEST_SIZE,
FC_FOR_KGNB_KN3IWF_DERIVATION, param, kgnb);
}
/* TS33.501 Annex A.10 NH derivation function */
void ogs_kdf_nh_gnb(const uint8_t *kamf, uint8_t *sync_input, uint8_t *kgnb)
{
kdf_param_t param;
ogs_assert(kamf);
ogs_assert(kgnb);
memset(param, 0, sizeof(param));
param[0].buf = sync_input;
param[0].len = OGS_SHA256_DIGEST_SIZE;
ogs_kdf_common(kamf, OGS_SHA256_DIGEST_SIZE,
FC_FOR_NH_GNB_DERIVATION, param, kgnb);
}
/*
* TS33.501 Annex C.3.4.1 Profile A
* TS33.501 Annex C.3.4.2 Profile B
* ANSI-X9.63-KDF
*/
void ogs_kdf_ansi_x963(
const uint8_t *z, size_t z_len, const uint8_t *info, size_t info_len,
uint8_t *ek, uint8_t *icb, uint8_t *mk)
{
uint8_t input[ECC_BYTES+4+ECC_BYTES+1];
uint8_t output[OGS_KEY_LEN+OGS_IVEC_LEN+OGS_SHA256_DIGEST_SIZE];
uint32_t counter = 0;
size_t counter_len = sizeof(counter);
ogs_assert(z);
ogs_assert(info);
ogs_assert(ek);
ogs_assert(icb);
ogs_assert(mk);
ogs_assert((z_len+counter_len+info_len) <= (ECC_BYTES+4+ECC_BYTES+1));
memcpy(input, z, z_len);
counter = htobe32(1);
memcpy(input+z_len, &counter, counter_len);
memcpy(input+z_len+counter_len, info, info_len);
ogs_sha256(input, z_len+counter_len+info_len, output);
memcpy(ek, output, OGS_KEY_LEN);
memcpy(icb, output+OGS_KEY_LEN, OGS_IVEC_LEN);
counter = htobe32(2);
memcpy(input+z_len, &counter, counter_len);
ogs_sha256(input, z_len+counter_len+info_len, mk);
}
/* TS33.401 Annex A.2 KASME derivation function */
void ogs_auc_kasme(const uint8_t *ck, const uint8_t *ik,
const uint8_t *plmn_id, const uint8_t *sqn, const uint8_t *ak,
uint8_t *kasme)
{
kdf_param_t param;
int i;
uint8_t key[OGS_KEY_LEN*2];
uint8_t sqn_xor_ak[OGS_SQN_XOR_AK_LEN];
ogs_assert(ck);
ogs_assert(ik);
ogs_assert(plmn_id);
ogs_assert(sqn);
ogs_assert(ak);
memcpy(key, ck, OGS_KEY_LEN);
memcpy(key + OGS_KEY_LEN, ik, OGS_KEY_LEN);
memset(param, 0, sizeof(param));
param[0].buf = (uint8_t *)plmn_id;
param[0].len = OGS_PLMN_ID_LEN;
for (i = 0; i < 6; i++)
sqn_xor_ak[i] = sqn[i] ^ ak[i];
param[1].buf = sqn_xor_ak;
param[1].len = OGS_SQN_XOR_AK_LEN;
ogs_kdf_common(key, OGS_SHA256_DIGEST_SIZE, FC_FOR_KASME, param, kasme);
}
/* TS33.401 Annex A.3 KeNB derivation function */
void ogs_kdf_kenb(const uint8_t *kasme, uint32_t ul_count, uint8_t *kenb)
{
kdf_param_t param;
memset(param, 0, sizeof(param));
ul_count = htobe32(ul_count);
param[0].buf = (uint8_t *)&ul_count;
param[0].len = 4;
ogs_kdf_common(kasme, OGS_SHA256_DIGEST_SIZE,
FC_FOR_KENB_DERIVATION, param, kenb);
}
/* TS33.401 Annex A.4 NH derivation function */
void ogs_kdf_nh_enb(const uint8_t *kasme, const uint8_t *sync_input, uint8_t *kenb)
{
kdf_param_t param;
memset(param, 0, sizeof(param));
param[0].buf = sync_input;
param[0].len = OGS_SHA256_DIGEST_SIZE;
ogs_kdf_common(kasme, OGS_SHA256_DIGEST_SIZE,
FC_FOR_NH_ENB_DERIVATION, param, kenb);
}
/* TS33.401 Annex A.7 Algorithm key derivation functions */
void ogs_kdf_nas_eps(uint8_t algorithm_type_distinguishers,
uint8_t algorithm_identity, const uint8_t *kasme, uint8_t *knas)
{
kdf_param_t param;
uint8_t output[OGS_SHA256_DIGEST_SIZE];
memset(param, 0, sizeof(param));
param[0].buf = &algorithm_type_distinguishers;
param[0].len = 1;
param[1].buf = &algorithm_identity;
param[1].len = 1;
ogs_kdf_common(kasme, OGS_SHA256_DIGEST_SIZE,
FC_FOR_EPS_ALGORITHM_KEY_DERIVATION, param, output);
memcpy(knas, output+16, 16);
}
/* TS33.401 Annex A.8: KASME to CK', IK' derivation at handover */
void ogs_kdf_ck_ik_handover(
uint32_t dl_count, const uint8_t *kasme, uint8_t *ck, uint8_t *ik)
{
kdf_param_t param;
uint8_t output[OGS_SHA256_DIGEST_SIZE];
memset(param, 0, sizeof(param));
param[0].buf = (uint8_t *)&dl_count;
param[0].len = 4;
ogs_kdf_common(kasme, OGS_SHA256_DIGEST_SIZE,
FC_FOR_CK_IK_DERIVATION_HANDOVER, param, output);
memcpy(ck, output, 16);
memcpy(ik, output+16, 16);
}
/* TS33.401 Annex A.9: NAS token derivation for inter-RAT mobility */
void ogs_kdf_nas_token(
uint32_t ul_count, const uint8_t *kasme, uint8_t *nas_token)
{
kdf_param_t param;
uint8_t output[OGS_SHA256_DIGEST_SIZE];
memset(param, 0, sizeof(param));
param[0].buf = (uint8_t *)&ul_count;
param[0].len = 4;
ogs_kdf_common(kasme, OGS_SHA256_DIGEST_SIZE,
FC_FOR_NAS_TOKEN_DERIVATION, param, output);
memcpy(nas_token, output, 2);
}
/* TS33.401 Annex A.13: KASME to CK', IK' derivation at idle mobility */
void ogs_kdf_ck_ik_idle_mobility(
uint32_t ul_count, const uint8_t *kasme, uint8_t *ck, uint8_t *ik)
{
kdf_param_t param;
uint8_t output[OGS_SHA256_DIGEST_SIZE];
memset(param, 0, sizeof(param));
param[0].buf = (uint8_t *)&ul_count;
param[0].len = 4;
ogs_kdf_common(kasme, OGS_SHA256_DIGEST_SIZE,
FC_FOR_CK_IK_DERIVATION_IDLE_MOBILITY, param, output);
memcpy(ck, output, 16);
memcpy(ik, output+16, 16);
}
/*
* TS33.401 Annex I Hash Functions
* Use the KDF given in TS33.220
*/
void ogs_kdf_hash_mme(const uint8_t *message, uint8_t message_len, uint8_t *hash_mme)
{
uint8_t key[32];
uint8_t output[OGS_SHA256_DIGEST_SIZE];
ogs_assert(message);
ogs_assert(message_len);
ogs_assert(hash_mme);
memset(key, 0, 32);
ogs_hmac_sha256(key, 32, message, message_len,
output, OGS_SHA256_DIGEST_SIZE);
memcpy(hash_mme, output+24, OGS_HASH_MME_LEN);
}
/*
* TS33.102
* 6.3.3 Authentication and key agreement
* Re-use and re-transmission of (RAND, AUTN)
*/
void ogs_auc_sqn(
const uint8_t *opc, const uint8_t *k,
const uint8_t *rand, const uint8_t *conc_sqn_ms,
uint8_t *sqn_ms, uint8_t *mac_s)
{
int i;
uint8_t ak[OGS_AK_LEN];
/*
* The AMF used to calculate MAC-S assumes a dummy value of
* all zeros so that it does not need to be transmitted in the clear
* in the re-synch message.
*/
uint8_t amf[2] = { 0, 0 };
ogs_assert(opc);
ogs_assert(k);
ogs_assert(rand);
ogs_assert(conc_sqn_ms);
milenage_f2345(opc, k, rand, NULL, NULL, NULL, NULL, ak);
for (i = 0; i < OGS_SQN_LEN; i++)
sqn_ms[i] = ak[i] ^ conc_sqn_ms[i];
milenage_f1(opc, k, rand, sqn_ms, amf, NULL, mac_s);
}