pespin has uploaded this change for review. ( https://gerrit.osmocom.org/c/osmo-ttcn3-hacks/+/40440?usp=email )
Change subject: 5gc: Implement NAS integrity check of DL messages ...................................................................... 5gc: Implement NAS integrity check of DL messages Change-Id: I118081af10f260513734550854c3a1751e32cbb4 --- M 5gc/C5G_Tests.ttcn M 5gc/gen_links.sh M 5gc/open5gs/open5gs-amf.yaml M 5gc/regen_makefile.sh 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, 499 insertions(+), 29 deletions(-) git pull ssh://gerrit.osmocom.org:29418/osmo-ttcn3-hacks refs/changes/40/40440/1 diff --git a/5gc/C5G_Tests.ttcn b/5gc/C5G_Tests.ttcn index 9cf5ec3..3695f69 100644 --- a/5gc/C5G_Tests.ttcn +++ b/5gc/C5G_Tests.ttcn @@ -40,6 +40,7 @@ import from NG_NAS_Osmo_Templates all; import from NG_NAS_Functions all; +import from NG_NAS_Osmo_Templates all; import from NG_CryptoFunctions all; /* (maximum) number of emulated eNBs */ @@ -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,8 +335,20 @@ 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); + 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_EIA1), + k_nas_enc := f_kdf_ng_nas_enc(g_pars.ue_pars.kamf, NG_NAS_ALG_ENC_EEA0) + } + }; + 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); log("Auth Response: RES=", res, ", SSN=", ssn, " / ", oct2char(ssn), ", RES*=", res_star); @@ -334,9 +359,8 @@ private altstep as_ngap_handle_sec_mode() runs on ConnHdlr { var NG_NAS_DL_Message_Type rx_nas; - [] 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); + [] NGAP.receive(cr_NG_SECURITY_MODE_COMMAND) -> value rx_nas { + log("PESPIN: Rx inner NAS: ", rx_nas); /* TODO: apply below integrity and ciphering based on Security Mode Command */ } } 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/open5gs/open5gs-amf.yaml b/5gc/open5gs/open5gs-amf.yaml index ca52090..e8da7a5 100644 --- a/5gc/open5gs/open5gs-amf.yaml +++ b/5gc/open5gs/open5gs-amf.yaml @@ -43,7 +43,7 @@ s_nssai: - sst: 1 security: - integrity_order : [ NIA2, NIA1, NIA0 ] + integrity_order : [ NIA1, NIA2, NIA0 ] ciphering_order : [ NEA0, NEA1, NEA2 ] network_name: full: Open5GS 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/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..8f63e09 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,11 +462,38 @@ 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 EIA0: */ + 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 EEA0: */ + 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 */ @@ -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..2ad0753 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_EIA0) { + return '00000000'O; + } + case (NG_NAS_ALG_IP_EIA1) { + 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_EIA0, /* no integrity protection */ + NG_NAS_ALG_IP_EIA1, /* SNOW-3G F9 based */ + NG_NAS_ALG_IP_EIA2, /* AES based */ + NG_NAS_ALG_IP_EIA3 /* ZUC */ +}; +type enumerated NG_NAS_ALG_ENC { + NG_NAS_ALG_ENC_EEA0, /* no encryption */ + NG_NAS_ALG_ENC_EEA1, /* SNOW-3G F8 based */ + NG_NAS_ALG_ENC_EEA2, /* AES based */ + NG_NAS_ALG_ENC_EEA3 /* 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_EIA0, + k_nas_int := '0000000000000000000000000000000000000000000000000000000000000000'O, + alg_enc := NG_NAS_ALG_ENC_EEA0, + 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_EIA1; /* 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..72878cb 100644 --- a/library/NG_NAS_Osmo_Templates.ttcn +++ b/library/NG_NAS_Osmo_Templates.ttcn @@ -134,4 +134,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 := ?, 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..9be5417 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); +} \ No newline at end of file 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 https://gerrit.osmocom.org/c/osmo-ttcn3-hacks/+/40440?usp=email To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings?usp=email Gerrit-MessageType: newchange Gerrit-Project: osmo-ttcn3-hacks Gerrit-Branch: master Gerrit-Change-Id: I118081af10f260513734550854c3a1751e32cbb4 Gerrit-Change-Number: 40440 Gerrit-PatchSet: 1 Gerrit-Owner: pespin <pes...@sysmocom.de>