open5gs/lib/crypt/ogs-kdf.c

511 lines
14 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* 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_KASME_DERIVATION_IDLE_MOBILITY 0x19
#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.11 : KASME from CK, IK derivation during idle mode mobility */
void ogs_kdf_kasme_idle_mobility(
const uint8_t *ck, const uint8_t *ik,
uint32_t nonce_ue, uint32_t nonce_mme,
uint8_t *kasme)
{
kdf_param_t param;
uint8_t key[OGS_KEY_LEN*2];
ogs_assert(ck);
ogs_assert(ik);
ogs_assert(kasme);
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 *)&nonce_ue;
param[0].len = sizeof(nonce_ue);
param[1].buf = (uint8_t *)&nonce_mme;
param[1].len = sizeof(nonce_mme);
ogs_kdf_common(key, OGS_KEY_LEN*2,
FC_FOR_KASME_DERIVATION_IDLE_MOBILITY, param, kasme);
}
/* 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);
}