pespin submitted this change.
5gc: Implement NAS integrity check of DL messages
Change-Id: I118081af10f260513734550854c3a1751e32cbb4
---
M 5gc/C5G_Tests.ttcn
M 5gc/gen_links.sh
M 5gc/regen_makefile.sh
M deps/Makefile
M library/LTE_CryptoFunctions.ttcn
M library/NGAP_Emulation.ttcn
M library/NG_CryptoFunctionDefs.cc
M library/NG_CryptoFunctions.ttcn
M library/NG_NAS_Osmo_Templates.ttcn
M library/ng_crypto/key_derivation.c
M library/ng_crypto/key_derivation.h
11 files changed, 540 insertions(+), 38 deletions(-)
diff --git a/5gc/C5G_Tests.ttcn b/5gc/C5G_Tests.ttcn
index 9cf5ec3..a27b6cb 100644
--- a/5gc/C5G_Tests.ttcn
+++ b/5gc/C5G_Tests.ttcn
@@ -35,6 +35,7 @@
import from NAS_CommonTypeDefs all;
import from NAS_CommonTemplates all;
import from NG_NAS_Common all;
+import from NG_NAS_TypeDefs all;
import from NG_NAS_MsgContainers all;
import from NG_NAS_Templates all;
@@ -75,7 +76,21 @@
hexstring imsi,
octetstring usim_key,
octetstring usim_opc,
- charstring ue_ip
+ charstring ue_ip,
+ OCT32 kausf optional,
+ OCT32 kseaf optional,
+ OCT32 kamf optional
+}
+
+template (value) UeParams ts_UeParams(integer imsi_suffix) :=
+{
+ imsi := f_concat_pad(lengthof(mp_imsi), substr(mp_imsi, 0, lengthof(mp_imsi) - 6), imsi_suffix),
+ usim_key := mp_usim_key,
+ usim_opc := mp_usim_opc,
+ ue_ip := "192.168.123.50",
+ kausf := omit,
+ kseaf := omit,
+ kamf := omit
}
type component MTC_CT {
@@ -122,7 +137,7 @@
remote_sctp_port := mp_5gc_ngap_port,
local_ip := mp_local_ngap_ip,
local_sctp_port := mp_local_ngap_port + num,
- role := NGAP_NAS_ROLE_UE
+ role := NG_NAS_ROLE_UE
};
var PLMNIdentity plmn_id := f_enc_mcc_mnc(mp_mcc, mp_mnc);
var NGRANParams ngran_pars := {
@@ -147,15 +162,12 @@
vc_NGAP[num].start(NGAP_Emulation.main(ops, pars, id));
NGAP_UNIT[num].receive(NGAPEM_Event:{up_down:=NGAPEM_EVENT_UP});
}
+
friend function f_init_one_ue(inout UeParams uep, integer imsi_suffix) {
- uep := {
- imsi := f_concat_pad(lengthof(mp_imsi), substr(mp_imsi, 0, lengthof(mp_imsi) - 6), imsi_suffix),
- usim_key := mp_usim_key,
- usim_opc := mp_usim_opc,
- ue_ip := "192.168.123.50"
- }
+ uep := valueof(ts_UeParams(imsi_suffix));
}
+
friend function f_init_ngap(integer imsi_suffix := 0) runs on MTC_CT {
var integer i;
for (i := 0; i < NUM_NGRAN; i := i+1) {
@@ -294,6 +306,7 @@
var integer my_sqn := 0; /* TODO: move to a ConnHdlr state attribute? */
var OCT16 rand := bit2oct(rx_nas.authentication_Request.rand.randValue);
var OCT16 autn := bit2oct(rx_nas.authentication_Request.autn.aUTN);
+ var OCT2 abba := rx_nas.authentication_Request.abba.abbaValue;
var OCT16 ik;
var OCT16 ck;
var OCT8 res;
@@ -322,27 +335,53 @@
return;
}
- /* Derive (X)RES* from (X)RES, 3GPP TS 33.501 A.4 */
var octetstring ssn := f_NG_NAS_ServingNetworkName_OCT(f_imsi_plmn_id(), omit);
- var OCT16 res_star := f_kdf_xres_star(ssn, ck, ik, rand, res);
- log("Auth Response: RES=", res, ", SSN=", ssn, " / ", oct2char(ssn), ", RES*=", res_star);
+ g_pars.ue_pars.kausf := f_kdf_kausf(ck, ik, ssn, autn);
+ g_pars.ue_pars.kseaf := f_kdf_kseaf(g_pars.ue_pars.kausf, ssn);
+ g_pars.ue_pars.kamf := f_kdf_kamf(g_pars.ue_pars.kseaf, char2oct(hex2str(g_pars.ue_pars.imsi)), abba);
+ var NGAPEM_Config cfg := {
+ set_nas_keys := {
+ k_nas_int := f_kdf_ng_nas_int(g_pars.ue_pars.kamf, NG_NAS_ALG_IP_NIA1),
+ k_nas_enc := f_kdf_ng_nas_enc(g_pars.ue_pars.kamf, NG_NAS_ALG_ENC_NEA0)
+ }
+ };
+ NGAP.send(cfg);
+
+ /* Derive (X)RES* from (X)RES, 3GPP TS 33.501 A.4 */
+ var OCT16 res_star := f_kdf_xres_star(ssn, ck, ik, rand, res);
NGAP.send(cs_NG_AUTHENTICATION_RESPONSE(cs_AuthenticationResponseParameter(oct2bit(res_star))));
}
}
private altstep as_ngap_handle_sec_mode() runs on ConnHdlr {
+ var NGAPEM_Config cfg;
var NG_NAS_DL_Message_Type rx_nas;
+ /* Make sure network selected specific algos we requested: */
+ var template (present) NG_UE_SecurityCapability ue_sec_cap :=
+ cr_NG_UE_SecurityCapabilityLV(ngeaCap := '80'O, /* ONLY NEA0 (no encryption) for now */
+ ngiaCap := '40'O /* ONLY NIA1 supported */);
- [] NGAP.receive(cr_NG_SECURITY_PROTECTED_NAS_MESSAGE) -> value rx_nas {
- var NG_NAS_DL_Message_Type in_nas := dec_NG_NAS_DL_Message_Type(rx_nas.security_Protected_Nas_Message.plainNASMessage);
- log("PESPIN: Rx inner NAS: ", in_nas);
- /* TODO: apply below integrity and ciphering based on Security Mode Command */
+ [] NGAP.receive(cr_NG_SECURITY_MODE_COMMAND(p_UECap := ue_sec_cap)) -> value rx_nas {
+ log("Rx inner NAS: ", rx_nas);
+ /* Configure integrity protection: */
+ cfg := {
+ set_nas_alg_int := NG_NAS_ALG_IP_NIA1
+ };
+ NGAP.send(cfg);
+ /* Configure Ciphering: */
+ cfg := {
+ set_nas_alg_enc := NG_NAS_ALG_ENC_NEA0
+ };
+ NGAP.send(cfg);
+
+ /* TODO: Tx Security Mode Complete */
}
}
private function f_register() runs on ConnHdlr {
var template (value) UserLocationInformation p_ueLocInf;
+ var template (value) NG_UE_SecurityCapability ue_sec_cap;
var template (value) NGAP_PDU tx_pdu;
var template (value) NG_NAS_UL_Message_Type nas_ul_msg;
var NAS_PDU nas_pdu;
@@ -353,12 +392,14 @@
ts_ngran_NGAP_TAI(g_pars.ngran_pars[g_pars.c5g_idx])
));
+ ue_sec_cap := cs_NG_UE_SecurityCapabilityTLV(ngeaCap := '80'O, /* ONLY NEA0 (no encryption) for now */
+ ngiaCap := '40'O /* ONLY NIA1 supported */);
nas_ul_msg := cs_NG_REGISTRATION_REQUEST(cs_RegistrationType(tsc_NG_RegistrationInitial, '1'B),
g_pars.kset_id.nasKeySetId,
g_pars.kset_id.tsc,
cs_NG_MobileIdentitySUCI('0000'B /* Type IMSI */,
f_SUCI_IMSI()),
- p_UESecurityCap := cs_NG_UE_SecurityCapability);
+ p_UESecurityCap := ue_sec_cap);
nas_pdu := enc_NG_NAS_UL_Message_Type(valueof(nas_ul_msg));
tx_pdu := m_ngap_initMsg(m_n2_initialUeMessage(g_pars.c5g_idx,
nas_pdu, /* Registration request */
diff --git a/5gc/gen_links.sh b/5gc/gen_links.sh
index 9f67736..ee09250 100755
--- a/5gc/gen_links.sh
+++ b/5gc/gen_links.sh
@@ -61,6 +61,10 @@
FILES+="NGAP_EncDec.cc NGAP_Types.ttcn NGAP_Pixits.ttcn NGAP_Templates.ttcn "
gen_links $DIR $FILES
+DIR=../library/snow_3g
+FILES="snow-3g.c snow-3g.h Snow3G_FunctionDefs.cc Snow3G_Functions.ttcn "
+gen_links $DIR $FILES
+
DIR=../library/milenage
FILES="milenage.c milenage.h Milenage_FunctionDefs.cc Milenage_Functions.ttcn "
gen_links $DIR $FILES
diff --git a/5gc/regen_makefile.sh b/5gc/regen_makefile.sh
index 0d53583..be45468 100755
--- a/5gc/regen_makefile.sh
+++ b/5gc/regen_makefile.sh
@@ -14,6 +14,7 @@
NGAP_CodecPort_CtrlFunctDef.cc
NGAP_EncDec.cc
NG_CryptoFunctionDefs.cc
+ Snow3G_FunctionDefs.cc
TCCConversion.cc
TCCEncoding.cc
TCCInterface.cc
diff --git a/deps/Makefile b/deps/Makefile
index b7e06cb..e841e35 100644
--- a/deps/Makefile
+++ b/deps/Makefile
@@ -85,7 +85,7 @@
# Do not put references to branches here, except for local testing: this breaks the caching
# logic of docker containers, which only invalidate their cached ttcn3 dependencies if this
# file changed.
-nas_commit= f7921c4d88e204e09067f0975957d075be82e800
+nas_commit= 3d14f387cc0d6f09cbbe1cea4920ebcaf1660bcf
titan.Libraries.TCCUsefulFunctions_commit= R.35.B-6-gb3687da
titan.ProtocolEmulations.M3UA_commit= b58f92046e48a7b1ed531e243a2319ebca53bf4c
titan.ProtocolEmulations.SCCP_commit= 750a3e836831e58eae59d4757ef5d0c759f9ca5d
diff --git a/library/LTE_CryptoFunctions.ttcn b/library/LTE_CryptoFunctions.ttcn
index 57e0c4d..882861b 100644
--- a/library/LTE_CryptoFunctions.ttcn
+++ b/library/LTE_CryptoFunctions.ttcn
@@ -49,7 +49,7 @@
return '00000000'O;
}
case (NAS_ALG_IP_EIA1) {
- return f_snow_3g_f9(k_nas_int, seq_nr, bearer, is_downlink, data);
+ return f_snow_3g_f9(k_nas_int, seq_nr, bit2int(int2bit(bearer, 32) << 27), is_downlink, data);
}
case else {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str("Unsupported EIA: ", alg));
diff --git a/library/NGAP_Emulation.ttcn b/library/NGAP_Emulation.ttcn
index 12487ae..1dcae70 100644
--- a/library/NGAP_Emulation.ttcn
+++ b/library/NGAP_Emulation.ttcn
@@ -54,10 +54,8 @@
import from NG_NAS_MsgContainers all;
import from NG_NAS_Functions all;
-type enumerated NGAP_NAS_Role {
- NGAP_NAS_ROLE_UE, /* ATS implements/emulates UE */
- NGAP_NAS_ROLE_AMF /* ATS implements/emulates AMF */
-};
+import from NG_NAS_Osmo_Templates all;
+import from NG_CryptoFunctions all;
type component NGAP_ConnHdlr {
port NGAP_Conn_PT NGAP;
@@ -67,9 +65,22 @@
/* port between individual per-connection components and this dispatcher */
type port NGAP_Conn_PT message {
- inout NGAP_PDU, NG_NAS_UL_Message_Type, NG_NAS_DL_Message_Type;
+ inout NGAP_PDU, NG_NAS_UL_Message_Type, NG_NAS_DL_Message_Type, NGAPEM_Config;
} with { extension "internal" };
+type record NG_NAS_Keys {
+ OCT32 k_nas_int,
+ OCT32 k_nas_enc
+};
+type record NG_ResetNAScounts {
+/* empty */
+};
+type union NGAPEM_Config {
+ NG_NAS_Keys set_nas_keys,
+ NG_ResetNAScounts reset_nas_counts,
+ NG_NAS_ALG_INT set_nas_alg_int,
+ NG_NAS_ALG_ENC set_nas_alg_enc
+};
type enumerated NGAPEM_EventUpDown {
NGAPEM_EVENT_DOWN,
@@ -145,8 +156,8 @@
NGAP_ConnHdlr comp_ref, /* component handling this UE connection */
uint32_t ran_ue_ngap_id optional, /* eNB side NGAP ID */
uint40_t amf_ue_ngap_id optional, /* AMF side NGAP ID */
- UserLocationInformation uli optional
- //NAS_UE_State nus
+ UserLocationInformation uli optional,
+ NG_NAS_UE_State nus
};
type component NGAP_Emulation_CT {
@@ -191,7 +202,7 @@
IPL4asp_Types.PortNumber remote_sctp_port,
HostName local_ip,
IPL4asp_Types.PortNumber local_sctp_port,
- NGAP_NAS_Role role
+ NG_NAS_Role role
}
function tr_NGAP_RecvFrom_R(template NGAP_PDU msg)
@@ -329,7 +340,7 @@
NGapAssociationTable[i].amf_ue_ngap_id := omit;
NGapAssociationTable[i].ran_ue_ngap_id := omit;
NGapAssociationTable[i].uli := omit;
- //NGapAssociationTable[i].nus := valueof(t_NAS_UE_State(g_pars.role));
+ NGapAssociationTable[i].nus := valueof(t_NG_NAS_UE_State(g_pars.role));
NGapAssociationTable[i].comp_ref := null;
}
}
@@ -451,18 +462,45 @@
var integer procedureCode;
var NGAP_RecvFrom mrf;
var NGAP_PDU msg;
+ var NGAPEM_Config ngcfg;
var charstring vlr_name, amf_name;
var integer ai;
var octetstring kasme;
alt {
+ /* Configuration primitive from client */
+ [] NGAP_CLIENT.receive(NGAPEM_Config:{set_nas_keys:=?}) -> value ngcfg sender vc_conn {
+ var integer assoc_id := f_assoc_id_by_comp(vc_conn);
+ NGapAssociationTable[assoc_id].nus.k_nas_int := ngcfg.set_nas_keys.k_nas_int;
+ NGapAssociationTable[assoc_id].nus.k_nas_enc := ngcfg.set_nas_keys.k_nas_enc;
+ }
+ /* Configuration primitive from client */
+ [] NGAP_CLIENT.receive(NGAPEM_Config:{reset_nas_counts:=?}) -> value ngcfg sender vc_conn {
+ var integer assoc_id := f_assoc_id_by_comp(vc_conn);
+ NGapAssociationTable[assoc_id].nus.rx_count := 0;
+ NGapAssociationTable[assoc_id].nus.tx_count := 0;
+ }
+ /* Configuration primitive from client */
+ [] NGAP_CLIENT.receive(NGAPEM_Config:{set_nas_alg_int:=?}) -> value ngcfg sender vc_conn {
+ var integer assoc_id := f_assoc_id_by_comp(vc_conn);
+ NGapAssociationTable[assoc_id].nus.alg_int := ngcfg.set_nas_alg_int;
+ /* Mark ciphering even if using NIA0: */
+ NGapAssociationTable[assoc_id].nus.use_int := true;
+ }
+ /* Configuration primitive from client */
+ [] NGAP_CLIENT.receive(NGAPEM_Config:{set_nas_alg_enc:=?}) -> value ngcfg sender vc_conn {
+ var integer assoc_id := f_assoc_id_by_comp(vc_conn);
+ NGapAssociationTable[assoc_id].nus.alg_enc := ngcfg.set_nas_alg_enc;
+ /* Mark ciphering even if using NEA0: */
+ NGapAssociationTable[assoc_id].nus.use_enc := true;
+ }
/* NGAP from client: InitialUE */
[] NGAP_CLIENT.receive(mw_ngap_initMsg(mw_n2_initialUeMessage)) -> value msg sender vc_conn {
/* create a table entry about this connection */
ai := f_ngap_id_table_add(vc_conn, omit, valueof(f_NGAP_get_RAN_UE_NGAP_ID(msg)));
/* Store ULI so we can use it for generating UlNasTransport from NAS */
NGapAssociationTable[ai].uli := msg.initiatingMessage.value_.InitialUEMessage.protocolIEs[2].value_.userLocationInformation;
-// /* Pass message through */
+ /* Pass message through */
NGAP.send(t_NGAP_Send(g_ngap_conn_id, msg));
}
/* NAS from client: Wrap in NGAP Uplink NAS Transport */
@@ -513,11 +551,11 @@
vc_conn := NGapAssociationTable[assoc_id].comp_ref;
nas_enc := f_NGAP_get_NAS_PDU(mrf.msg);
if (isvalue(nas_enc)) {
- if (g_pars.role == NGAP_NAS_ROLE_UE) {
+ if (g_pars.role == NG_NAS_ROLE_UE) {
var NG_NAS_DL_Message_Type dl_nas := dec_NG_NAS_DL_Message_Type(valueof(nas_enc));
-// if (match(dl_nas, tr_NAS_EMM_SecurityProtected)) {
-// dl_nas := f_nas_try_decaps(NGapAssociationTable[assoc_id].nus, dl_nas);
-// }
+ if (match(dl_nas, cr_NG_SECURITY_PROTECTED_NAS_MESSAGE)) {
+ dl_nas := f_NG_NAS_try_decaps_dl(NGapAssociationTable[assoc_id].nus, dl_nas);
+ }
/* DL/UlNasTransport are not interesting, don't send them */
if (not match(mrf.msg, mw_ngap_initMsg(mw_n2_DownlinkNASTransport))) {
/* send raw NGAP */
@@ -594,7 +632,7 @@
}
/* client/conn_hdlr side function to use procedure port to create expect in emulation */
-function f_create_s1ap_expect(template (omit) AMF_UE_NGAP_ID amf_id,
+function f_create_ngap_expect(template (omit) AMF_UE_NGAP_ID amf_id,
template (omit) RAN_UE_NGAP_ID ran_id) runs on NGAP_ConnHdlr {
NGAP_PROC.call(NGAPEM_register:{amf_id, ran_id, self}) {
[] NGAP_PROC.getreply(NGAPEM_register:{?,?,?}) {};
diff --git a/library/NG_CryptoFunctionDefs.cc b/library/NG_CryptoFunctionDefs.cc
index e45e519..9211916 100644
--- a/library/NG_CryptoFunctionDefs.cc
+++ b/library/NG_CryptoFunctionDefs.cc
@@ -17,6 +17,22 @@
namespace NG__CryptoFunctions {
+/* 3GPP TS 33.501 Annex A.2 */
+OCTETSTRING f__kdf__kausf(const OCTETSTRING& ck, const OCTETSTRING& ik, const OCTETSTRING &ssn,
+ const OCTETSTRING& autn)
+{
+ TTCN_Buffer ttcn_buf_ck(ck);
+ TTCN_Buffer ttcn_buf_ik(ik);
+ TTCN_Buffer ttcn_buf_ssn(ssn);
+ TTCN_Buffer ttcn_buf_autn(autn);
+ uint8_t kausf[32];
+
+ kdf_kausf(ttcn_buf_ck.get_data(), ttcn_buf_ik.get_data(),
+ ttcn_buf_ssn.get_data(), ttcn_buf_ssn.get_len(),
+ ttcn_buf_autn.get_data(), kausf);
+ return OCTETSTRING(sizeof(kausf), kausf);
+}
+
/* 3GPP TS 33.501 A.4 RES* and XRES* derivation function */
OCTETSTRING f__kdf__xres__star(const OCTETSTRING &ssn, const OCTETSTRING& ck, const OCTETSTRING& ik,
const OCTETSTRING& rand, const OCTETSTRING& xres)
@@ -37,4 +53,44 @@
return OCTETSTRING(sizeof(xres_star), xres_star);
}
+/* 3GPP TS 33.501 Annex A.6 */
+OCTETSTRING f__kdf__kseaf(const OCTETSTRING& kausf, const OCTETSTRING &ssn)
+{
+ TTCN_Buffer ttcn_buf_kausf(kausf);
+ TTCN_Buffer ttcn_buf_ssn(ssn);
+ uint8_t kseaf[32];
+
+ kdf_kseaf(ttcn_buf_kausf.get_data(),
+ ttcn_buf_ssn.get_data(), ttcn_buf_ssn.get_len(), kseaf);
+ return OCTETSTRING(sizeof(kseaf), kseaf);
+}
+
+/* 3GPP TS 33.501 Annex A.7 */
+OCTETSTRING f__kdf__kamf(const OCTETSTRING& kseaf, const OCTETSTRING &supi, const OCTETSTRING &abba)
+{
+ TTCN_Buffer ttcn_buf_kseaf(kseaf);
+ TTCN_Buffer ttcn_buf_supi(supi);
+ TTCN_Buffer ttcn_buf_abba(abba);
+ uint8_t kamf[32];
+
+ kdf_kamf(ttcn_buf_kseaf.get_data(),
+ ttcn_buf_supi.get_data(), ttcn_buf_supi.get_len(),
+ ttcn_buf_abba.get_data(), kamf);
+ return OCTETSTRING(sizeof(kamf), kamf);
+}
+
+/* 3GPP TS 33.501 Annex A.8 */
+OCTETSTRING f__kdf__ng__nas__algo(const OCTETSTRING& kamf, const OCTETSTRING &algo_type, const OCTETSTRING &algo_id)
+{
+ TTCN_Buffer ttcn_buf_kamf(kamf);
+ TTCN_Buffer ttcn_buf_algo_type(algo_type);
+ TTCN_Buffer ttcn_buf_algo_id(algo_id);
+ uint8_t out[32];
+
+ kdf_ng_nas_algo(ttcn_buf_kamf.get_data(),
+ algo_type[0].get_octet(),
+ algo_id[0].get_octet(), out);
+ return OCTETSTRING(sizeof(out), out);
+}
+
}
diff --git a/library/NG_CryptoFunctions.ttcn b/library/NG_CryptoFunctions.ttcn
index fbbe57e..63755f2 100644
--- a/library/NG_CryptoFunctions.ttcn
+++ b/library/NG_CryptoFunctions.ttcn
@@ -15,13 +15,43 @@
import from General_Types all;
import from Misc_Helpers all;
+import from Snow3G_Functions all;
+
import from NAS_CommonTypeDefs all;
+import from NG_NAS_Common all;
+import from NG_NAS_TypeDefs all;
+import from NG_NAS_MsgContainers all;
+import from NG_NAS_Osmo_Templates all;
+import from NG_NAS_Functions all;
/*********************************************************************************
* low-level API (external C/C++ code)
*********************************************************************************/
-/* 3GPP TS 33.102 Figure 9, 3GPP TS 35.206 Annex 3 */
+/* 3GPP TS 33.501 Annex A.2 */
+external function f_kdf_kausf(in OCT16 ck, in OCT16 ik, octetstring ssn,
+ in OCT16 autn) return OCT32;
+
+/* 3GPP TS 33.501 Annex A.6 */
+external function f_kdf_kseaf(in OCT32 kausf, octetstring ssn) return OCT32;
+
+/* 3GPP TS 33.501 Annex A.7 */
+external function f_kdf_kamf(in OCT32 kseaf, octetstring p0, OCT2 abba) return OCT32;
+
+/* 3GPP TS 33.501 Annex A.8 */
+const OCT1 KDF_ALGO_TYPE_NAS_ENC := '01'O;
+const OCT1 KDF_ALGO_TYPE_NAS_INT := '02'O;
+external function f_kdf_ng_nas_algo(in OCT32 kamf, in OCT1 algo_type, in OCT1 algo_id) return OCT32;
+function f_kdf_ng_nas_enc(in OCT32 kamf, in NG_NAS_ALG_ENC algo_id) return OCT32
+{
+ return f_kdf_ng_nas_algo(kamf, KDF_ALGO_TYPE_NAS_ENC, int2oct(enum2int(algo_id), 1));
+}
+function f_kdf_ng_nas_int(in OCT32 kamf, in NG_NAS_ALG_INT algo_id) return OCT32
+{
+ return f_kdf_ng_nas_algo(kamf, KDF_ALGO_TYPE_NAS_INT, int2oct(enum2int(algo_id), 1));
+}
+
+/* 3GPP TS 33.102 Figure 9, 3GPP TS 35.206 Annex 3, 3GPP TS 33.501 Annex A.4 */
external function f_kdf_xres_star(octetstring ssn, OCT16 ck, OCT16 ik, OCT16 rand,
octetstring xres) return OCT16;
@@ -75,4 +105,165 @@
return char2oct(f_NG_NAS_ServingNetworkName(plmn_id, NID));
}
+function f_NG_NAS_mac_calc(NG_NAS_ALG_INT alg, OCT32 k_nas_int, integer seq_nr,
+ integer bearer, boolean is_downlink, octetstring data) return OCT4 {
+ select (alg) {
+ case (NG_NAS_ALG_IP_NIA0) {
+ return '00000000'O;
+ }
+ case (NG_NAS_ALG_IP_NIA1) {
+ return f_snow_3g_f9(substr(k_nas_int, 16, 16), seq_nr, bit2int(int2bit(bearer, 32) << 27), is_downlink, data);
+ }
+ case else {
+ Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str("Unsupported EIA: ", alg));
+ return '00000000'O; /* never reached */
+ }
+ }
+}
+
+/*********************************************************************************
+ * high-level API (full NAS encapsulation/decapsulation)
+ *********************************************************************************/
+
+ type enumerated NG_NAS_Role {
+ NG_NAS_ROLE_UE, /* ATS implements/emulates UE */
+ NG_NAS_ROLE_AMF /* ATS implements/emulates AMF */
+};
+
+type enumerated NG_NAS_ALG_INT {
+ NG_NAS_ALG_IP_NIA0, /* no integrity protection */
+ NG_NAS_ALG_IP_NIA1, /* SNOW-3G F9 based */
+ NG_NAS_ALG_IP_NIA2, /* AES based */
+ NG_NAS_ALG_IP_NIA3 /* ZUC */
+};
+type enumerated NG_NAS_ALG_ENC {
+ NG_NAS_ALG_ENC_NEA0, /* no encryption */
+ NG_NAS_ALG_ENC_NEA1, /* SNOW-3G F8 based */
+ NG_NAS_ALG_ENC_NEA2, /* AES based */
+ NG_NAS_ALG_ENC_NEA3 /* ZUC */
+};
+
+type record NG_NAS_UE_State {
+ NG_NAS_Role role, /* ATS implements UE or AMR role? */
+ NG_NAS_ALG_INT alg_int, /* NAS Integrity Protection Algorithm */
+ OCT32 k_nas_int, /* NAS Integrity Protection Key */
+ NG_NAS_ALG_ENC alg_enc, /* NAS Encryption Algorithm */
+ OCT32 k_nas_enc, /* NAS Encryption Key */
+ integer rx_count, /* frame counter (ATS rx side) */
+ integer tx_count, /* frame counter (ATS tx side) */
+ boolean new_ctx, /* Use "New EPS Security Context" when building next sec_hdr_t */
+ boolean use_int, /* Whether to use "Integrity" in sec_hdr_t */
+ boolean use_enc /* Whether to use "Ciphering" in sec_hdr_t */
+};
+
+template (value) NG_NAS_UE_State t_NG_NAS_UE_State(NG_NAS_Role role) := {
+ role := role,
+ alg_int := NG_NAS_ALG_IP_NIA0,
+ k_nas_int := '0000000000000000000000000000000000000000000000000000000000000000'O,
+ alg_enc := NG_NAS_ALG_ENC_NEA0,
+ k_nas_enc := '0000000000000000000000000000000000000000000000000000000000000000'O,
+ rx_count := 0,
+ tx_count := 0,
+ new_ctx := false,
+ use_int := false,
+ use_enc := false
+};
+
+/* determine if a received (from the IUT) message is downlink or not */
+private function f_rx_is_downlink(in NG_NAS_UE_State nus) return boolean
+{
+ if (nus.role == NG_NAS_ROLE_UE) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+/* determine if a message transmitted to the IUT message is downlink or not */
+private function f_tx_is_downlink(in NG_NAS_UE_State nus) return boolean
+{
+ return not f_rx_is_downlink(nus);
+}
+
+private function f_NG_NAS_check_ip(inout NG_NAS_UE_State nus,
+ in NG_SECURITY_PROTECTED_NAS_MESSAGE secp_nas) return boolean
+{
+ var octetstring data_with_seq := secp_nas.sequenceNumber & secp_nas.plainNASMessage;
+ var OCT4 exp_mac := f_NG_NAS_mac_calc(nus.alg_int, nus.k_nas_int, nus.rx_count, bit2int(tsc_NG_RegResult_3GPP),
+ f_rx_is_downlink(nus), data_with_seq);
+
+ if (nus.rx_count != oct2int(secp_nas.sequenceNumber)) {
+ setverdict(fail, "Received NAS SeqNr ", secp_nas.sequenceNumber,
+ " doesn't match expected SeqNr ", nus.rx_count, ": ", secp_nas, " | nus: ", nus);
+ return false;
+ }
+ if (exp_mac != secp_nas.messageAuthenticationCode) {
+ setverdict(fail, "Received NAS MAC ", secp_nas.messageAuthenticationCode,
+ " doesn't match expected MAC ", exp_mac, ": ", secp_nas, " | nus: ", nus);
+ return false;
+ }
+ return true;
+}
+
+/* try to decapsulate (MAC verify, decrypt) NAS message */
+function f_NG_NAS_try_decaps_dl(inout NG_NAS_UE_State nus, NG_NAS_DL_Message_Type nas) return NG_NAS_DL_Message_Type
+{
+ var NG_SECURITY_PROTECTED_NAS_MESSAGE secp_nas;
+
+ /* transparently pass through any non-protected NAS */
+ if (not match(nas, cr_NG_SECURITY_PROTECTED_NAS_MESSAGE)) {
+ return nas;
+ }
+
+ /* process any security-protected NAS */
+ secp_nas := nas.security_Protected_Nas_Message;
+ select (secp_nas.securityHeaderType) {
+ case ('0011'B) { /* IP with new 5GS security context */
+ nus.new_ctx := true;
+ nus.rx_count := 0;
+ nus.alg_int := NG_NAS_ALG_IP_NIA1; /* FIXME: from decoded inner message! */
+ if (not f_NG_NAS_check_ip(nus, secp_nas)) {
+ Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "f_NG_NAS_check_ip() failed");
+ }
+ nus.rx_count := nus.rx_count + 1;
+ return dec_NG_NAS_DL_Message_Type(secp_nas.plainNASMessage);
+ }
+ case ('0001'B) { /* IP only */
+ nus.new_ctx := false;
+ if (not f_NG_NAS_check_ip(nus, secp_nas)) {
+ Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "f_NG_NAS_check_ip() failed");
+ }
+ nus.rx_count := nus.rx_count + 1;
+ return dec_NG_NAS_DL_Message_Type(secp_nas.plainNASMessage);
+ }
+// case ('0010'B) { /* IP + ciphered */
+// nus.new_ctx := false;
+// if (not f_NG_NAS_check_ip(nus, secp_nas)) {
+// Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "f_NG_NAS_check_ip() failed");
+// }
+// nus.rx_count := nus.rx_count + 1;
+// f_nas_encrypt(nus.alg_enc, nus.k_nas_enc, nus.rx_count, 0,
+// f_rx_is_downlink(nus), secp_nas.nAS_Message);
+// return dec_NG_NAS_DL_Message_Type(secp_nas.plainNASMessage);
+// }
+// case ('0100'B) { /* IP + ciphered; new EPS security context */
+// nus.new_ctx := true;
+// nus.rx_count := 0;
+// if (not f_NG_NAS_check_ip(nus, secp_nas)) {
+// Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "f_NG_NAS_check_ip() failed");
+// }
+// f_nas_encrypt(nus.alg_enc, nus.k_nas_enc, nus.rx_count, 0,
+// f_rx_is_downlink(nus), secp_nas.nAS_Message);
+// nus.rx_count := nus.rx_count + 1;
+// return dec_NG_NAS_DL_Message_Type(secp_nas.plainNASMessage);
+// }
+ //case ('0101'B) { /* IP + partially ciphered */ }
+ //case ('1100'B) { /* Service Request Message */ }
+ case else {
+ Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str("Implement SecHdrType for ", secp_nas));
+ mtc.stop; /* make compiler happy about not returning. */
+ }
+ }
+}
+
}
diff --git a/library/NG_NAS_Osmo_Templates.ttcn b/library/NG_NAS_Osmo_Templates.ttcn
index 5887fa6..c6c242f 100644
--- a/library/NG_NAS_Osmo_Templates.ttcn
+++ b/library/NG_NAS_Osmo_Templates.ttcn
@@ -32,10 +32,10 @@
return len;
}
template (value) NG_UE_SecurityCapability
-cs_NG_UE_SecurityCapability(template (value) O1_Type ngeaCap := 'f0'O,
- template (value) O1_Type ngiaCap := '70'O,
- template (omit) O1_Type eeaCap := omit,
- template (omit) O1_Type eiaCap := omit) :=
+cs_NG_UE_SecurityCapabilityTLV(template (value) O1_Type ngeaCap := 'f0'O,
+ template (value) O1_Type ngiaCap := '70'O,
+ template (omit) O1_Type eeaCap := omit,
+ template (omit) O1_Type eiaCap := omit) :=
{
iei := '2E'O,
iel := int2oct(f_cs_NG_UE_SecurityCapability_length(eeaCap, eiaCap), 1),
@@ -46,6 +46,21 @@
spare := omit
};
+template (present) NG_UE_SecurityCapability
+cr_NG_UE_SecurityCapabilityLV(template (present) O1_Type ngeaCap := ?,
+ template (present) O1_Type ngiaCap := ?,
+ template O1_Type eeaCap := *,
+ template O1_Type eiaCap := *) :=
+{
+ iei := omit,
+ iel := ?,
+ ngeaCap := ngeaCap,
+ ngiaCap := ngiaCap,
+ eeaCap := eeaCap,
+ eiaCap := eiaCap,
+ spare := *
+};
+
template (value) AuthenticationResponseParameter
cs_AuthenticationResponseParameter(template (value) B32_128_Type p_ExpectedRES) :=
{
@@ -134,4 +149,34 @@
}
}
+/* 24.501 cl. 8.2.25 */
+template (present) NG_NAS_DL_Message_Type
+cr_NG_SECURITY_MODE_COMMAND(template (present) NG_NAS_SecurityAlgorithms p_Algs := ?,
+ template (present) NAS_KsiValue p_KeySetId := ?,
+ template (present) NG_UE_SecurityCapability p_UECap := ?,
+ template IMEISV_Request p_IMEISV := *,
+ template NAS_SecurityAlgorithms p_EPSAlgs := *,
+ template AdditionalSecurityInfo p_AddInfo := *,
+ template EAP_Message p_EAP := *,
+ template ABBA p_ABBA := *,
+ template S1_UE_SecurityCapability p_ReplayedSecurityCap := *) :=
+{
+ security_Mode_Command := {
+ protocolDiscriminator := tsc_EPD_GMM, /* cl. 9.2 M V 1 */
+ spareHalfOctet := tsc_SpareHalfOctet, /* cl. 9.3 M V 1/2 */
+ securityHeaderType := tsc_SHT_NoSecurityProtection,
+ messageType := tsc_MT_NG_SecurityModeCommand, /* cl. 9.7 M V 1 */
+ nasSecurityAlgorithms := p_Algs, /* cl. 9.11.3.34 M V 1 */
+ spareHalfOctet2 := tsc_SpareHalfOctet, /* cl. 9.3 M V 1/2 */
+ ngNasKSI := {iei := omit, tsc := ?, nasKeySetId := p_KeySetId}, // FIXME FSCOM To be enhanced
+ ueSecurityCapability := p_UECap, /* cl. 9.11.3.54 M LV 3-9 */
+ imeisvRequest := p_IMEISV, /* cl. 9.11.3.28 O TV 1 IEI=E */
+ epsSecurityAlgorithms := p_EPSAlgs, /* cl. 9.11.3.25 O TV 2 IEI=57 */
+ add5GSecurityInfo := p_AddInfo, /* cl. 9.11.3.12 O TLV 3 IEI=36 */
+ eapMessage := p_EAP, /* cl. 9.11.2.2 O TLV-E 7-1503 IEI=78 */
+ abba := p_ABBA, /* cl. 9.11.3.10 O TLV 4-n IEI=38 Dec18 */
+ replayedUESecurityCap := p_ReplayedSecurityCap /* cl. 9.11.3.48 O TLV 4-7 IEI=19 Dec18 */
+ }
+}
+
}
diff --git a/library/ng_crypto/key_derivation.c b/library/ng_crypto/key_derivation.c
index 29e0849..3951f9a 100644
--- a/library/ng_crypto/key_derivation.c
+++ b/library/ng_crypto/key_derivation.c
@@ -5,6 +5,38 @@
#include "key_derivation.h"
+/* 3GPP TS 33.501 A.2 KAUSF derivation function */
+void kdf_kausf(const uint8_t *ck, const uint8_t *ik,
+ const uint8_t *serving_network_name, uint16_t serving_network_name_len,
+ const uint8_t *autn,
+ uint8_t *out)
+{
+ uint8_t s[1024];
+ uint8_t key[32];
+ size_t pos = 0;
+ uint16_t lenbe;
+ int i;
+
+ memcpy(&key[0], ck, 16);
+ memcpy(&key[16], ik, 16);
+
+ s[pos++] = 0x6A; /* FC Value */
+
+ memcpy(&s[pos], serving_network_name, serving_network_name_len);
+ pos += serving_network_name_len;
+ lenbe = htons(serving_network_name_len);
+ memcpy(&s[pos], &lenbe, 2);
+ pos += 2;
+
+ memcpy(&s[pos], autn, 6);
+ pos += 6;
+ lenbe = htons(6);
+ memcpy(&s[pos], &lenbe, 2);
+ pos += 2;
+
+ gnutls_hmac_fast(GNUTLS_MAC_SHA256, key, sizeof(key), s, pos, out);
+}
+
/* 3GPP TS 33.501 A.4 RES* and XRES* derivation function */
void kdf_xres_star(const uint8_t *serving_network_name,
uint16_t serving_network_name_len,
@@ -47,3 +79,78 @@
memcpy(out, tmp_out+16, 16);
}
+
+/* 3GPP TS 33.501 A.6 KSEAF derivation function */
+void kdf_kseaf(const uint8_t *kausf,
+ const uint8_t *serving_network_name, uint16_t serving_network_name_len,
+ uint8_t *out)
+{
+ uint8_t s[1024];
+ size_t pos = 0;
+ uint16_t lenbe;
+ int i;
+
+ s[pos++] = 0x6C; /* FC Value */
+
+ memcpy(&s[pos], serving_network_name, serving_network_name_len);
+ pos += serving_network_name_len;
+ lenbe = htons(serving_network_name_len);
+ memcpy(&s[pos], &lenbe, 2);
+ pos += 2;
+
+ gnutls_hmac_fast(GNUTLS_MAC_SHA256, kausf, 32, s, pos, out);
+}
+
+/* 3GPP TS 33.501 A.7 KAMF derivation function */
+void kdf_kamf(const uint8_t *kseaf,
+ const uint8_t *supi, uint16_t supi_len,
+ const uint8_t *abba,
+ uint8_t *out)
+{
+ uint8_t s[1024];
+ size_t pos = 0;
+ uint16_t lenbe;
+ int i;
+
+ s[pos++] = 0x6D; /* FC Value */
+
+ memcpy(&s[pos], supi, supi_len);
+ pos += supi_len;
+ lenbe = htons(supi_len);
+ memcpy(&s[pos], &lenbe, 2);
+ pos += 2;
+
+ memcpy(&s[pos], abba, 2);
+ pos += 2;
+ lenbe = htons(2);
+ memcpy(&s[pos], &lenbe, 2);
+ pos += 2;
+
+ gnutls_hmac_fast(GNUTLS_MAC_SHA256, kseaf, 32, s, pos, out);
+}
+
+/* 3GPP TS 33.501 A.8 Algorithm key derivation functions */
+void kdf_ng_nas_algo(const uint8_t *kamf,
+ uint8_t algo_type,
+ uint8_t algo_id,
+ uint8_t *out)
+{
+ uint8_t s[1024];
+ size_t pos = 0;
+ uint16_t lenbe;
+ int i;
+
+ s[pos++] = 0x69; /* FC Value */
+
+ s[pos++] = algo_type;
+ lenbe = htons(1);
+ memcpy(&s[pos], &lenbe, 2);
+ pos += 2;
+
+ s[pos++] = algo_id;
+ lenbe = htons(1);
+ memcpy(&s[pos], &lenbe, 2);
+ pos += 2;
+
+ gnutls_hmac_fast(GNUTLS_MAC_SHA256, kamf, 32, s, pos, out);
+}
diff --git a/library/ng_crypto/key_derivation.h b/library/ng_crypto/key_derivation.h
index f78d746..d5279c2 100644
--- a/library/ng_crypto/key_derivation.h
+++ b/library/ng_crypto/key_derivation.h
@@ -3,6 +3,20 @@
#include <stdint.h>
#include <unistd.h>
+void kdf_kausf(const uint8_t *ck, const uint8_t *ik,
+ const uint8_t *serving_network_name, uint16_t serving_network_name_len,
+ const uint8_t *autn,
+ uint8_t *out);
+
+void kdf_kseaf(const uint8_t *kausf,
+ const uint8_t *serving_network_name, uint16_t serving_network_name_len,
+ uint8_t *out);
+
+void kdf_kamf(const uint8_t *kseaf,
+ const uint8_t *supi, uint16_t supi_len,
+ const uint8_t *abba,
+ uint8_t *out);
+
void kdf_xres_star(const uint8_t *serving_network_name,
uint16_t serving_network_name_len,
const uint8_t *ck,
@@ -10,3 +24,8 @@
const uint8_t *rand,
const uint8_t *xres, size_t xres_len,
uint8_t *out);
+
+void kdf_ng_nas_algo(const uint8_t *kamf,
+ uint8_t algo_type,
+ uint8_t algo_id,
+ uint8_t *out);
To view, visit change 40440. To unsubscribe, or for help writing mail filters, visit settings.