pespin has uploaded this change for review. ( https://gerrit.osmocom.org/c/osmo-ttcn3-hacks/+/40413?usp=email )
Change subject: WIP: Implement AKA authentication ......................................................................
WIP: Implement AKA authentication
Some of the functionalities from deps/nas.git c4d162995a625c072f28554e3157e97962f84988 are imported into our own files: * ttcn: Otherwise ttcn/Lib_NG_NAS/LIB_NG_NAS_Functions.ttcn brings in tons of dependencies about Emulation stuff which is not interesting for us. * .cc: We want to implement our own low level functions using our eclipse Titan API, as well as our own C lib dependencies (because some code for those dependencies is not really available in the repo).
Change-Id: I11527f47e4310863124f3f02148e3f71da7d911e --- M 5gc/C5G_Tests.ttcn A 5gc/NG_CryptoFunctionDefs.cc A 5gc/NG_CryptoFunctions.ttcn M 5gc/gen_links.sh M 5gc/regen_makefile.sh M library/General_Types.ttcn 6 files changed, 735 insertions(+), 6 deletions(-)
git pull ssh://gerrit.osmocom.org:29418/osmo-ttcn3-hacks refs/changes/13/40413/1
diff --git a/5gc/C5G_Tests.ttcn b/5gc/C5G_Tests.ttcn index 9b45298..fa8c336 100644 --- a/5gc/C5G_Tests.ttcn +++ b/5gc/C5G_Tests.ttcn @@ -31,14 +31,14 @@ import from NGAP_Emulation all;
import from NAS_CommonTypeDefs all; - +import from NAS_CommonTemplates all; import from NG_NAS_Common all; import from NG_NAS_MsgContainers all; -import from NAS_CommonTemplates all; import from NG_NAS_Templates all;
import from NG_NAS_Osmo_Templates all; import from NG_NAS_Functions all; +import from NG_CryptoFunctions all;
/* (maximum) number of emulated eNBs */ const integer NUM_NGRAN := 1; @@ -211,12 +211,18 @@ iE_Extensions := omit }
-private function f_SUCI_IMSI() runs on ConnHdlr return octetstring { +private function f_imsi_plmn_id() runs on ConnHdlr return PLMNIdentity { var hexstring imsi := g_pars.ue_pars.imsi; var GsmMcc mcc := substr(imsi, 0, 3); var GsmMnc mnc := substr(imsi, 3, 2); - var octetstring imsi_suffix := imsi_hex2oct(substr(imsi, lengthof(imsi)-10, 10)); - return f_enc_mcc_mnc(mcc, mnc) & '21430001'O & imsi_suffix; + return f_enc_mcc_mnc(mcc, mnc); +} + +private function f_SUCI_IMSI() runs on ConnHdlr return octetstring { + var hexstring imsi := g_pars.ue_pars.imsi; + var PLMNIdentity plmn_id := f_imsi_plmn_id(); + var octetstring imsi_suffix := imsi_hex2oct(substr(imsi, lengthof(imsi) - 10, 10)); + return plmn_id & '21430001'O & imsi_suffix; }
friend function f_ngap_setup(integer idx := 0, template (omit) NGAP_IEs.Cause cause := omit) runs on MTC_CT { @@ -270,17 +276,27 @@ } }
+/* 3GPP TS 24.501 5.4.1.3.2, 3GPP TS 33.501 6.1.3.2 */ private altstep as_ngap_handle_auth() runs on ConnHdlr { var NG_NAS_DL_Message_Type rx_nas; var template (present) NAS_KeySetIdentifier kset_id := f_tr_ConnHdlr_kset_id(); [] NGAP.receive(cr_NG_AUTHENTICATION_REQUEST) -> value rx_nas { + var NG_NAS_SecurityParams_Type vc_ng_nas_security_params_type := {}; log("Rx NAS message: ", rx_nas);
g_pars.kset_id := rx_nas.authentication_Request.ngNasKeySetId;
+ f_5g_aka_compute_res_xres(rx_nas.authentication_Request.rand.randValue, + rx_nas.authentication_Request.autn.aUTN, + rx_nas.authentication_Request.abba, + f_imsi_plmn_id(), + oct2hex(f_SUCI_IMSI()), + oct2bit('00000000000000000000000000000000'O), /* TODO: pass secret key!!! */ + vc_ng_nas_security_params_type); + /* TODO: generate proper RES, 3GPP TS 33.501 A.4 */ const OCT8 res := '6a91970e838fd079'O; - NGAP.send(cs_NG_AUTHENTICATION_RESPONSE(cs_AuthenticationResponseParameter(oct2bit(res)))); + NGAP.send(cs_NG_AUTHENTICATION_RESPONSE(cs_AuthenticationResponseParameter(vc_ng_nas_security_params_type.AuthParams.XRES))); } }
diff --git a/5gc/NG_CryptoFunctionDefs.cc b/5gc/NG_CryptoFunctionDefs.cc new file mode 100644 index 0000000..5fdb120 --- /dev/null +++ b/5gc/NG_CryptoFunctionDefs.cc @@ -0,0 +1,334 @@ +/* AKA USIM Utility functions */ +/** + * @author ETSI / TTF041 + * @version $URL$ + * $Id$ + * @desc This module provides test functions for NG_NAS tests. + * @copyright ETSI Copyright Notification + * No part may be reproduced except as authorized by written permission. + * The copyright and the foregoing restriction extend to reproduction in all media. + * All rights reserved. + * @see ETSI TS + */ + +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <stdint.h> +#include <gnutls/crypto.h> + +#include <Addfunc.hh> +#include <Encdec.hh> +#include <Boolean.hh> +#include <Integer.hh> +#include <Octetstring.hh> +#include <Bitstring.hh> + +#include "rijndael.hh" +#include "opc.hh" + +//#define DEBUG + +#ifdef DEBUG +static __thread char hexd_buff[4096]; +static const char hex_chars[] = "0123456789abcdef"; + +static const char *_osmo_hexdump_buf(char *out_buf, size_t out_buf_size, const unsigned char *buf, int len, const char *delim, + bool delim_after_last) +{ + int i; + char *cur = out_buf; + size_t delim_len; + + if (!out_buf || !out_buf_size) + return ""; + + delim = delim ? : ""; + delim_len = strlen(delim); + + for (i = 0; i < len; i++) { + const char *delimp = delim; + int len_remain = out_buf_size - (cur - out_buf) - 1; + if (len_remain < (2 + delim_len) + && !(!delim_after_last && i == (len - 1) && len_remain >= 2)) + break; + + *cur++ = hex_chars[buf[i] >> 4]; + *cur++ = hex_chars[buf[i] & 0xf]; + + if (i == (len - 1) && !delim_after_last) + break; + + while (len_remain > 1 && *delimp) { + *cur++ = *delimp++; + len_remain--; + } + } + *cur = '\0'; + return out_buf; +} + +static char *_osmo_hexdump(const unsigned char *buf, int len) +{ + _osmo_hexdump_buf(hexd_buff, sizeof(hexd_buff), buf, len, "", true); + return hexd_buff; +} +#endif + +namespace NG__CryptoFunctions { + + static uint8_t OP[16] = {0}; // FIXME FSCOM To be refined. This is a Q&D implementation + + BITSTRING fx__KeyDerivationFunction(const INTEGER& p__KDF, const BITSTRING& p__Key, const OCTETSTRING& p__String){ + + if (p__KDF != 1) { + TTCN_error("pespin: fx__KeyDerivationFunction(p__KDF!=1) NOT IMPLEMENTED!"); + return int2bit(0, 0); + } + //TODO: is this correct? + TTCN_Buffer ttcn_buf_key(bit2oct(p__Key)); + TTCN_Buffer ttcn_buf_str(p__String); + uint8_t out[256]; + + gnutls_hmac_fast(GNUTLS_MAC_SHA256, ttcn_buf_key.get_data(), ttcn_buf_key.get_len(), + ttcn_buf_str.get_data(), ttcn_buf_str.get_len(), out); + OCTETSTRING res(sizeof(out), out); + return oct2bit(res); + } + + void fx__set__op(const OCTETSTRING& p_op) { + std::memcpy(OP, static_cast<const unsigned char*>(p_op), 16); + } + + INTEGER fx__f1(const BITSTRING& p_authK, const BITSTRING& p_rand, const BITSTRING& p_sqn, const BITSTRING& p_amf, BITSTRING& p_mac_a) { + + rijndael r; + OCTETSTRING authK = bit2oct(p_authK); + r.rijndael_key_schedule(authK); + opc op(r, OP); + uint8_t op_c[16] = { 0x00 }; + op.compute_opc(op_c); + + OCTETSTRING rand = bit2oct(p_rand); + uint8_t rijndael_input[16] = { 0x00 }; + for (int i = 0; i < 16; i++) { + rijndael_input[i] = rand[i].get_octet() ^ op_c[i]; + } // End of 'for' statement + uint8_t temp[16] = { 0x00 }; + r.rijndael_encrypt(rijndael_input, temp); + + OCTETSTRING sqn = bit2oct(p_sqn); + uint8_t in1[16] = { 0x00 }; + for (int i = 0; i < 6; i++) { + in1[i] = sqn[i].get_octet(); + in1[i + 8] = sqn[i].get_octet(); + } // End of 'for' statement + OCTETSTRING amf = bit2oct(p_amf); + for (int i = 0; i < 2; i++) { + in1[i + 6] = amf[i].get_octet(); + in1[i + 14] = amf[i].get_octet(); + } // End of 'for' statement + + // XOR op_c and in1, rotate by r1=64, and XOR on the constant c1 (which is all zeroes) + for (int i = 0; i < 16; i++) { + rijndael_input[(i + 8) % 16] = in1[i] ^ op_c[i]; + } + + // XOR on the value temp computed before + for (int i = 0; i < 16; i++) { + rijndael_input[i] ^= temp[i]; + } // End of 'for' statement + + uint8_t out1[16] = { 0x00 }; + r.rijndael_encrypt(rijndael_input, out1); + for (int i = 0; i < 16; i++) { + out1[i] ^= op_c[i]; + } // End of 'for' statement + + uint8_t mac_a[8] = { 0x00 }; + for (int i = 0; i < 8; i++) { + mac_a[i] = out1[i]; + } // End of 'for' statement + OCTETSTRING os(8, static_cast<const unsigned char*>(&mac_a[0])); + p_mac_a = oct2bit(os); + + return 0; + } + + INTEGER fx__f1star(const BITSTRING& p_authK, const BITSTRING& p_rand, const BITSTRING& p_sqn, const BITSTRING& p_amf, BITSTRING& p_mac_s) { + + rijndael r; + OCTETSTRING authK = bit2oct(p_authK); + r.rijndael_key_schedule(authK); + opc op(r, OP); + uint8_t op_c[16] = { 0x00 }; + op.compute_opc(op_c); + + OCTETSTRING rand = bit2oct(p_rand); + uint8_t rijndael_input[16] = { 0x00 }; + for (int i = 0; i < 16; i++) { + rijndael_input[i] = rand[i].get_octet() ^ op_c[i]; + } // End of 'for' statement + uint8_t temp[16] = { 0x00 }; + r.rijndael_encrypt(rijndael_input, temp); + + OCTETSTRING sqn = bit2oct(p_sqn); + uint8_t in1[16] = { 0x00 }; + for (int i = 0; i < 6; i++) { + in1[i] = sqn[i].get_octet(); + in1[i + 8] = sqn[i].get_octet(); + } // End of 'for' statement + OCTETSTRING amf = bit2oct(p_amf); + for (int i = 0; i < 2; i++) { + in1[i + 6] = amf[i].get_octet(); + in1[i + 14] = amf[i].get_octet(); + } // End of 'for' statement + + // XOR op_c and in1, rotate by r1=64, and XOR on the constant c1 (which is all zeroes) + for (int i = 0; i < 16; i++) { + rijndael_input[(i + 8) % 16] = in1[i] ^ op_c[i]; + } + + // XOR on the value temp computed before + for (int i = 0; i < 16; i++) { + rijndael_input[i] ^= temp[i]; + } // End of 'for' statement + + uint8_t out1[16] = { 0x00 }; + r.rijndael_encrypt(rijndael_input, out1); + for (int i = 0; i < 16; i++) { + out1[i] ^= op_c[i]; + } // End of 'for' statement + + uint8_t mac_s[8] = { 0x00 }; + for (int i = 0; i < 8; i++) { + mac_s[i] = out1[i + 8]; + } // End of 'for' statement + OCTETSTRING os(8, static_cast<const unsigned char*>(&mac_s[0])); + p_mac_s = oct2bit(os); + + return 0; + } + + INTEGER fx__f2345(const BITSTRING& p_authK, const BITSTRING& p_rand, BITSTRING& p_res, BITSTRING& p_ck, BITSTRING& p_ik, BITSTRING& p_ak) { + rijndael r; + OCTETSTRING authK = bit2oct(p_authK); + r.rijndael_key_schedule(authK); + opc op(r, OP); + uint8_t op_c[16] = { 0x00 }; + op.compute_opc(op_c); + + OCTETSTRING rand = bit2oct(p_rand); + uint8_t rijndael_input[16] = { 0x00 }; + for (int i = 0; i < 16; i++) { + rijndael_input[i] = rand[i].get_octet() ^ op_c[i]; + } // End of 'for' statement + uint8_t temp[16] = { 0x00 }; + r.rijndael_encrypt(rijndael_input, temp); + + // To obtain output block OUT2: XOR OPc and TEMP, rotate by r2=0, and XOR on the constant c2 (which is all zeroes except that the last bit is 1) + for (int i = 0; i < 16; i++) { + rijndael_input[i] = temp[i] ^ op_c[i]; + } // End of 'for' statement + rijndael_input[15] ^= 1; + + uint8_t out[16] = { 0x00 }; + r.rijndael_encrypt(rijndael_input, out); + for (int i = 0; i < 16; i++) { + out[i] ^= op_c[i]; + } // End of 'for' statement + + uint8_t res[8] = { 0x00 }; + for (int i = 0; i < 8; i++) { + res[i] = out[i + 8]; + } // End of 'for' statement + + uint8_t ak[6] = { 0x00 }; + for (int i = 0; i < 6; i++) { + ak[i] = out[i]; + } // End of 'for' statement + + // To obtain output block OUT3: XOR OPc and TEMP, rotate by r3=32, and XOR on the constant c3 (which is all zeroes except that the next to last bit is 1) + for (int i = 0; i < 16; i++) { + rijndael_input[(i + 12) % 16] = temp[i] ^ op_c[i]; + } // End of 'for' statement + rijndael_input[15] ^= 2; + + r.rijndael_encrypt(rijndael_input, out); + for (int i = 0; i < 16; i++) { + out[i] ^= op_c[i]; + } // End of 'for' statement + + uint8_t ck[16] = { 0x00 }; + for (int i = 0; i < 16; i++) { + ck[i] = out[i]; + } // End of 'for' statement + + // To obtain output block OUT4: XOR OPc and TEMP, rotate by r4=64, and XOR on the constant c4 (which is all zeroes except that the 2nd from last bit is 1) + for (int i = 0; i < 16; i++) { + rijndael_input[(i+8) % 16] = temp[i] ^ op_c[i]; + } // End of 'for' statement + rijndael_input[15] ^= 4; + + r.rijndael_encrypt(rijndael_input, out); + for (int i = 0; i < 16; i++) { + out[i] ^= op_c[i]; + } // End of 'for' statement + + uint8_t ik[16] = { 0x00 }; + for (int i = 0; i < 16; i++) { + ik[i] = out[i]; + } // End of 'for' statement + + OCTETSTRING os(8, static_cast<const unsigned char*>(&res[0])); + p_res = oct2bit(os); + os = OCTETSTRING(16, static_cast<const unsigned char*>(&ik[0])); + p_ik = oct2bit(os); + os = OCTETSTRING(16, static_cast<const unsigned char*>(&ck[0])); + p_ck = oct2bit(os); + os = OCTETSTRING(6, static_cast<const unsigned char*>(&ak[0])); + p_ak = oct2bit(os); + + return 0; + } + + INTEGER fx__f5star(const BITSTRING& p_authK, const BITSTRING& p_rand, BITSTRING& p_ak) { + + rijndael r; + OCTETSTRING authK = bit2oct(p_authK); + r.rijndael_key_schedule(authK); + opc op(r, OP); + uint8_t op_c[16] = { 0x00 }; + op.compute_opc(op_c); + + OCTETSTRING rand = bit2oct(p_rand); + uint8_t rijndael_input[16] = { 0x00 }; + for (int i = 0; i < 16; i++) { + rijndael_input[i] = rand[i].get_octet() ^ op_c[i]; + } // End of 'for' statement + uint8_t temp[16] = { 0x00 }; + r.rijndael_encrypt(rijndael_input, temp); + + // To obtain output block OUT5: XOR OPc and TEMP, rotate by r5=96, and XOR on the constant c5 (which is all zeroes except that the 3rd from last bit is 1) + for (int i = 0; i < 16; i++) { + rijndael_input[(i + 4) % 16] = temp[i] ^ op_c[i]; + } // End of 'for' statement + rijndael_input[15] ^= 8; + + uint8_t out[16] = { 0x00 }; + r.rijndael_encrypt(rijndael_input, out); + for (int i = 0; i < 16; i++) { + out[i] ^= op_c[i]; + } // End of 'for' statement + + uint8_t ak[6] = { 0x00 }; + for (int i = 0; i < 6; i++) { + ak[i] = out[i]; + } + OCTETSTRING os(6, static_cast<const unsigned char*>(&ak[0])); + p_ak = oct2bit(os); + + return 0; + } + +} // namespace diff --git a/5gc/NG_CryptoFunctions.ttcn b/5gc/NG_CryptoFunctions.ttcn new file mode 100644 index 0000000..e4f266b --- /dev/null +++ b/5gc/NG_CryptoFunctions.ttcn @@ -0,0 +1,369 @@ +/* Utility functions from ogslib imported to TTCN-3 + * + * (C) 2019 Harald Welte laforge@gnumonks.org + * All rights reserved. + * + * Released under the terms of GNU General Public License, Version 2 or + * (at your option) any later version. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +module NG_CryptoFunctions { + +import from General_Types all; +import from Misc_Helpers all; + +import from NAS_CommonTypeDefs all; +import from NG_NAS_TypeDefs all; + +/********************************************************************************* + * low-level API (external C/C++ code) + *********************************************************************************/ + +/* 3GPP TS 33.102 Figure 9, 3GPP TS 35.206 Annex 3 */ +external function fx_f1(in BIT128 p_authK, in BIT128 p_rand, in BIT48 v_sqn, in BIT16 v_amf, out BIT64 v_mac_a) return integer; +external function fx_f1star(in BIT128 p_authK, in BIT128 p_rand, in BIT48 v_sqn, in BIT16 v_amf, out BIT64 v_mac_s) return integer; +external function fx_f2345(in BIT128 p_authK, in BIT128 p_rand, out BIT64 v_res, out BIT128 v_ck, out BIT128 v_ik, out BIT48 v_ak) return integer; +external function fx_f5star(in BIT128 p_authK, in BIT128 p_rand, out BIT48 p_ak) return integer; + +external function fx_KeyDerivationFunction(KDF_Type p_KDF, + bitstring p_Key, + octetstring p_String) return BIT256; + +/********************************************************************************* + * mid-level API + *********************************************************************************/ +type bitstring BIT32_128 length(32..128) +const integer tsc_KDF_HMAC_SHA_256 := 1; /* @status APPROVED (IMS, IMS_IRAT, LTE, LTE_IRAT, POS, SSNITZ, UTRAN) */ +const integer tsc_KDF_Spare3 := 4; +type integer KDF_Type ( tsc_KDF_HMAC_SHA_256 .. tsc_KDF_Spare3 ); + +type record NG_NAS_SecurityInfo_Type { /* ciphering/integrity algorithm and key */ + BIT4 Algorithm, /* acc. to 24.501 cl. 9.10.3.32 NAS security algorithms */ + BIT128 K_NAS /* key acc. 33.501 Figure 6.2-2 (K_NASint, K_NASenc) */ +}; + +const BIT32 tsc_AuthUndefinedB32 := oct2bit ('FFFFFFFF'O); /* @status APPROVED (ENDC, IMS, IMS_IRAT, LTE, LTE_A_IRAT, LTE_A_PRO, LTE_A_R10_R11, LTE_A_R12, LTE_IRAT, MCX, NBIOT, NR5GC, NR5GC_IRAT, POS, SSNITZ, UTRAN) */ +const BIT128 tsc_AuthUndefinedB128 := tsc_AuthUndefinedB32 & tsc_AuthUndefinedB32 & tsc_AuthUndefinedB32 & tsc_AuthUndefinedB32; /* @status APPROVED (ENDC, IMS, IMS_IRAT, LTE, LTE_A_IRAT, LTE_A_PRO, LTE_A_R10_R11, LTE_A_R12, LTE_IRAT, MCX, NBIOT, NR5GC, NR5GC_IRAT, POS, SSNITZ, UTRAN) */ +const BIT256 tsc_AuthUndefinedB256 := tsc_AuthUndefinedB128 & tsc_AuthUndefinedB128; /* @status APPROVED (ENDC, IMS, IMS_IRAT, LTE, LTE_A_IRAT, LTE_A_PRO, LTE_A_R10_R11, LTE_A_R12, LTE_IRAT, NBIOT, NR5GC, NR5GC_IRAT, POS) */ + +type record Common_AuthenticationParams_Type { /* parameters related/used to/by EUTRA/UTRAN/GERAN authentication + @status APPROVED (ENDC, IMS, IMS_IRAT, LTE, LTE_A_IRAT, LTE_A_PRO, LTE_A_R10_R11, LTE_A_R12, LTE_IRAT, MCX, NBIOT, NR5GC, NR5GC_IRAT, POS, SSNITZ, UTRAN) */ + BIT128 RandValue, + BIT128 AUTN, + BIT32_128 XRES, + BIT64 KcGSM, + BIT128 Kc128, // @sic R5s150121 sic@ + BIT3 KeySeq, + BIT128 CK, + BIT128 IK, + integer XRESLength optional // @sic R5s120907 sic@ +}; + +type record NG_NAS_SecurityParams_Type { + // Keys shared with NR + KDF_Type KDF, + BIT3 KSIamf, // 3 bit KSIasme used when Authentication is performed by MME. + BIT256 Ks, // = Ck || IK + BIT256 KAMF, + // NAS keys + bitstring MK, // Master key MK in RFC 5448 to be used to derive other keys acc. TS 33.501 cl. 6.2.1 + BIT256 KAUSF, + BIT256 KSEAF, + octetstring ABBA, + // NAS Security structures + NG_NAS_SecurityInfo_Type NAS_Integrity, + NG_NAS_SecurityInfo_Type NAS_Ciphering, + Common_AuthenticationParams_Type AuthParams +}; + +const BIT4 tsc_NG_Integrity_Snow3G := '0001'B; /* @status APPROVED (NR5GC) */ +const BIT4 tsc_NG_Integrity_AES := '0010'B; /* @status APPROVED (NR5GC) */ +const BIT4 tsc_NG_Integrity_ZUC := '0011'B; /* @status APPROVED (NR5GC) */ +const BIT4 tsc_NG_Encryption_Snow3G := '0001'B; /* @status APPROVED (NR5GC) */ +const BIT4 tsc_NG_Encryption_AES := '0010'B; /* @status APPROVED (NR5GC) */ +const BIT4 tsc_NG_Encryption_ZUC := '0011'B; /* @status APPROVED (NR5GC) */ + +template (value) NG_NAS_SecurityInfo_Type cs_NG_NAS_SecurityInfo(BIT4 p_Algo, + BIT128 p_Key) := +{ + Algorithm := p_Algo, + K_NAS := p_Key +}; + +template (value) Common_AuthenticationParams_Type cs_CommonAuthParams_Init (template (value) BIT128 p_Rand) := +{ /* @status APPROVED (ENDC, IMS, IMS_IRAT, LTE, LTE_A_IRAT, LTE_A_PRO, LTE_A_R10_R11, LTE_A_R12, LTE_IRAT, MCX, NBIOT, NR5GC, NR5GC_IRAT, POS, SSNITZ, UTRAN) */ + RandValue := p_Rand, + AUTN := tsc_AuthUndefinedB128, + XRES := tsc_AuthUndefinedB128, + KcGSM := tsc_AuthUndefinedB32 & tsc_AuthUndefinedB32, + Kc128 := tsc_AuthUndefinedB128, // @sic R5s150121 sic@ + KeySeq := '111'B, + CK := tsc_AuthUndefinedB128, + IK := tsc_AuthUndefinedB128, + XRESLength := omit // @sic R5s120907 sic@ +}; + +template (value) NG_NAS_SecurityParams_Type cs_NG_NAS_SecurityParamsInit := +{ + KDF := tsc_KDF_HMAC_SHA_256, + KSIamf := '111'B, //un initialised + Ks := tsc_AuthUndefinedB256, + KAMF := tsc_AuthUndefinedB256, + MK := tsc_AuthUndefinedB256, + KAUSF := tsc_AuthUndefinedB256, + KSEAF := tsc_AuthUndefinedB256, + ABBA := '0000'O, + NAS_Integrity := cs_NG_NAS_SecurityInfo ('0010'B, tsc_AuthUndefinedB128), + NAS_Ciphering := cs_NG_NAS_SecurityInfo ('0010'B, tsc_AuthUndefinedB128), + AuthParams := cs_CommonAuthParams_Init (oct2bit('A3DE0C6D363E30C364A4078F1BF8D577'O)) +}; + +//---------------------------------------------------------------------------- +/* +* @desc Used when building the network name from the PLMN +* @param p_NAS_PlmnId +* @return charstring +* @status APPROVED (IMS, NR5GC, NR5GC_IRAT, POS) +*/ +function fl_Nas2SNN_MNC(NAS_PlmnId p_NAS_PlmnId) return charstring +{ + var charstring v_MNC := ""; + var hexstring v_PLMN_hexstring := oct2hex(p_NAS_PlmnId); + + if (v_PLMN_hexstring[2] == 'F'H) { + v_MNC := "0"; + } + v_MNC := v_MNC & hex2str(v_PLMN_hexstring[5]) & hex2str(v_PLMN_hexstring[4]); + if (v_PLMN_hexstring[2] != 'F'H) { // add last digit if not F + v_MNC := v_MNC & hex2str(v_PLMN_hexstring[2]); + } + return v_MNC; +} + +/* +* @desc Used when building the network name from the PLMN +* @param p_NAS_PlmnId +* @return charstring +* @status APPROVED (IMS, NR5GC, NR5GC_IRAT, POS) +*/ +function fl_Nas2SNN_MCC(NAS_PlmnId p_NAS_PlmnId) return charstring +{ + var charstring v_MCC; + var hexstring v_PLMN_hexstring := oct2hex(p_NAS_PlmnId); + + v_MCC := hex2str(v_PLMN_hexstring[1]) & hex2str(v_PLMN_hexstring[0]) & hex2str(v_PLMN_hexstring[3]); + + return v_MCC; +} + +/* +* @desc To build the Serving Network Name +* @param p_PLMN +* @param p_NID (default value: omit) +* @return octetstring +* @status APPROVED (IMS, NR5GC, NR5GC_IRAT, POS) +*/ +function fl_GetServingNetworkName (NAS_PlmnId p_PLMN, + template (omit) hexstring p_NID := omit) return octetstring +{ + var charstring v_P0 := "5G:mnc" & fl_Nas2SNN_MNC(p_PLMN) & ".mcc" & fl_Nas2SNN_MCC(p_PLMN) & ".3gppnetwork.org"; + if (isvalue(p_NID)) { // @sic R5s220753 sic@ + v_P0 := v_P0 & ":" & hex2str (valueof(p_NID)); + } + return char2oct (v_P0); +} + +//============================================================================ +// Group of S functions defined in Annex A of 33.501 +//---------------------------------------------------------------------------- +/* +* @desc KAUSF derivation function; As per annex A.2 of 33.501 +* @param p_AuthParams +* @param p_KDF +* @param p_Ks +* @param p_PLMN +* @param p_NID (default value: omit) +* @return BIT256 +* @status APPROVED (IMS, NR5GC, NR5GC_IRAT, POS) +*/ +function f_NG_Authentication_A2(Common_AuthenticationParams_Type p_AuthParams, + KDF_Type p_KDF, + BIT256 p_Ks, + NAS_PlmnId p_PLMN, + template (omit) hexstring p_NID := omit) return BIT256 +{ + const octetstring const_S6A_FC := '6A'O; + var octetstring v_S; + var octetstring v_P0; + + // Generation of String + v_S := const_S6A_FC; + //FC = 0x6A + v_P0 := fl_GetServingNetworkName(p_PLMN, p_NID); // @sic R5s220753 sic@ + v_S := (v_S & v_P0); + //P0 = serving network ID + v_S := (v_S & int2oct(lengthof(v_P0), 2)) ; + //L0 = length of serving network ID (i.e. 0x00 0x03) + v_S := ( v_S & ( substr (( bit2oct ( p_AuthParams.AUTN )) , 0,6 ))); + //P1 = SQN XOR AK + // to have MSB 6 bytes which is SQN xor AK and truncated as SQN xor AK is first 6 bytes of AUTN + v_S := ( v_S & '0006'O ); + //L1 = length of SQN XOR AK (i.e. 0x00 0x06) + return fx_KeyDerivationFunction( p_KDF, p_Ks, v_S ); +} + +//-------------------------------------------------------------------------- +/* +* @desc RES* and XRES* derivation functions +* As per annex A.4 of 33.501 +* @param p_PLMN +* @param p_AuthParams +* @param p_KDF +* @param p_Key +* @param p_NID (default value: omit) +* @return BIT128 +* @status APPROVED (IMS, NR5GC, NR5GC_IRAT, POS) +*/ +function f_NG_Authentication_A4(NAS_PlmnId p_PLMN, + Common_AuthenticationParams_Type p_AuthParams, + KDF_Type p_KDF, + BIT256 p_Key, + template (omit) hexstring p_NID := omit) return BIT128 +{ + const octetstring const_S6B_FC :='6B'O; + var octetstring v_S; + var octetstring v_P0; + const integer px_NAS_5GC_XRES_Length := 16; + + // Generation of String + v_S := const_S6B_FC; + //FC = 0x6B + v_P0 := fl_GetServingNetworkName(p_PLMN, p_NID); // @sic R5s220753 sic@ + v_S := (v_S & v_P0); + //P0 = serving network ID + v_S := (v_S & int2oct(lengthof(v_P0), 2)) ; + //L0 = length of serving network ID + v_S := ( v_S & bit2oct ( p_AuthParams.RandValue ) ); + //P1 = RAND + v_S := ( v_S & '0010'O ); + //L1 = length of RAND (i.e. 0x00 0x10) + v_S := ( v_S & bit2oct (substr(p_AuthParams.XRES, 0, px_NAS_5GC_XRES_Length*8))); // @sic R5s190394 sic@ + //P2 = RES or XRES + v_S := ( v_S & int2oct(px_NAS_5GC_XRES_Length, 2) ); // @sic R5w190117 sic@ + //L2 = length of RES/XRES (e.g. 0x00 0x10) + + return substr(fx_KeyDerivationFunction(p_KDF, p_Key, v_S), 128, 128); +// returns LSB 128 bits[truncated] of the key generated +} + +//-------------------------------------------------------------------------- +/* +* @desc KSEAF derivation function +* As per annex A.6 of 33.501 +* @param p_PLMN +* @param p_KAUSF +* @param p_KDF_Type +* @param p_NID (default value: omit) +* @return BIT256 +* @status APPROVED (IMS, NR5GC, NR5GC_IRAT, POS) +*/ +function f_NG_Authentication_A6(NAS_PlmnId p_PLMN, + BIT256 p_KAUSF, + KDF_Type p_KDF_Type, + template (omit) hexstring p_NID := omit) return BIT256 +{ + const octetstring const_S6C_FC :='6C'O; + var octetstring v_S; + var octetstring v_P0; + + // Generation of String + v_S := const_S6C_FC; + //FC = 0x6C + v_P0 := fl_GetServingNetworkName(p_PLMN, p_NID); // @sic R5s220753 sic@ + v_S := (v_S & v_P0); + //P0 = serving network ID + v_S := (v_S & int2oct(lengthof(v_P0), 2)) ; + //L0 = length of serving network ID + + return fx_KeyDerivationFunction(p_KDF_Type, p_KAUSF, v_S); +}; + +/* Compute RES and XRES values based on rAND & AUTN received in the NG_AUTHENTICATION_REQUEST. + * 3GPP TS 33.102 Figure 9, 3GPP TS 35.206 Annex 3 */ +function f_5g_aka_compute_res_xres(BIT128 p_rand, + BIT128 p_autn, + ABBA p_abba, + NAS_PlmnId p_PLMN, + hexstring p_NID, + BIT128 usim_secret_key, + inout NG_NAS_SecurityParams_Type pars) +return boolean { + + pars := valueof(cs_NG_NAS_SecurityParamsInit); + log("f_5g_aka_compute_res_xres: KDF=", pars.KDF); + pars.ABBA := p_abba.abbaValue; + pars.AuthParams.RandValue := p_rand; + pars.AuthParams.AUTN := p_autn; + + // Extract SQN from v_sqn_ak and XOR it with calculated MAC + var BIT48 v_sqn_ak := substr(p_autn, 0, 48); + var BIT16 v_amf := substr(p_autn, 48, 16); + var BIT64 v_mac := substr(p_autn, 64, 64); + + // Calculate RES, CK, IK and AK in one step + var BIT128 v_ck; + var BIT128 v_ik; + var BIT48 v_ak; + var BIT64 v_res; + if (fx_f2345(usim_secret_key, p_rand, v_res, v_ck, v_ik, v_ak) == -1) { + return false; + } + pars.AuthParams.CK := v_ck; + pars.AuthParams.IK := v_ik; + pars.Ks := pars.AuthParams.CK & pars.AuthParams.IK; + var BIT48 v_sqn := v_sqn_ak xor4b v_ak; + + // Verify that MAC was accepted + var BIT64 v_mac_p; + if (fx_f1(usim_secret_key, p_rand, v_sqn, v_amf, v_mac_p) == -1) { + return false; + } + if (v_mac != v_mac_p) { + return false; + } + + // Compute XRES + pars.AuthParams.XRES := f_NG_Authentication_A4(p_PLMN, + pars.AuthParams, + pars.KDF, + pars.Ks, + p_NID); + + // Generate KAUSF + pars.KAUSF := f_NG_Authentication_A2(pars.AuthParams, + pars.KDF, + pars.Ks, + p_PLMN, + p_NID); + + // Generate KSEAF + pars.KSEAF := f_NG_Authentication_A6(p_PLMN, + pars.KAUSF, + pars.KDF, + p_NID + ); + + // TODO Generate KMAF + // pars.KAMF := f_NG_Authentication_A7(p_Identity, + // pars.KSEAF, + // pars.ABBA, + // pars.KDF + // ); + + return true; +} + + +} // namespace diff --git a/5gc/gen_links.sh b/5gc/gen_links.sh index 295ca24..8b70010 100755 --- a/5gc/gen_links.sh +++ b/5gc/gen_links.sh @@ -25,6 +25,10 @@ FILES="common_ext.cc " gen_links $DIR $FILES
+DIR=$BASEDIR/nas/ccsrc/Protocols/FiveG_AKA +FILES="opc.cc opc.hh rijndael.cc rijndael.hh " +gen_links $DIR $FILES + DIR=$BASEDIR/nas/ttcn/Lib3GPP/Common FILES="CommonDefs.ttcn " gen_links $DIR $FILES diff --git a/5gc/regen_makefile.sh b/5gc/regen_makefile.sh index 44f7a56..c76046b 100755 --- a/5gc/regen_makefile.sh +++ b/5gc/regen_makefile.sh @@ -9,8 +9,10 @@ IPL4asp_discovery.cc Native_FunctionDefs.cc common_ext.cc + opc.cc rijndael.cc NGAP_CodecPort_CtrlFunctDef.cc NGAP_EncDec.cc + NG_CryptoFunctionDefs.cc TCCConversion.cc TCCEncoding.cc TCCInterface.cc diff --git a/library/General_Types.ttcn b/library/General_Types.ttcn index 2daa593..163ec47 100644 --- a/library/General_Types.ttcn +++ b/library/General_Types.ttcn @@ -84,7 +84,11 @@ type bitstring BIT30 length(30) with { variant "FIELDLENGTH(30)" }; type bitstring BIT31 length(31) with { variant "FIELDLENGTH(31)" }; type bitstring BIT32 length(32) with { variant "FIELDLENGTH(32)" }; + type bitstring BIT48 length(48) with { variant "FIELDLENGTH(48)" }; type bitstring BIT56 length(56) with { variant "FIELDLENGTH(56)" }; + type bitstring BIT64 length(64) with { variant "FIELDLENGTH(64)" }; + type bitstring BIT128 length(128) with { variant "FIELDLENGTH(128)" }; + type bitstring BIT256 length(256) with { variant "FIELDLENGTH(256)" };
//**************************************************** // Octetstrings