/******************************************************************************/
// @copyright   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.
//   (c) 2023, 3GPP Organizational Partners (ARIB, ATIS, CCSA, ETSI, TSDSI, TTA, TTC).
//   All rights reserved.
// @version: IWD_23wk37
// $Date: 2023-03-21 13:21:41 +0100 (Tue, 21 Mar 2023) $
// $Rev: 35672 $
/******************************************************************************/
module NG_NAS_SecurityFunctions {
  import from CommonDefs all;
  import from NAS_CommonTypeDefs all;
  import from NAS_AuthenticationCommon all;
  import from NG_NasEmu_CtrlAspTypes all;
  import from EUTRA_NR_SecurityFunctions all;
  import from Parameters all;
  import from NAS_5GC_Parameters all;
  import from EAP_TypeDefs all;

  //============================================================================
  // constants and types

  type record NG_NAS_SecurityParams_Type {      /* @status    APPROVED (IMS, NR5GC, NR5GC_IRAT, POS) */
    // Keys shared with NR
    KDF_Type                KDF,
    B3_Type                 KSIamf,            // 3 bit KSIasme used when Authentication is performed by MME.
    B256_Type               Ks,                 // = Ck || IK
    B256_Type               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
    B256_Type               KAUSF,
    B256_Type               KSEAF,
    octetstring             ABBA,
    // NAS Security structures
    NG_NAS_SecurityInfo_Type   NAS_Integrity,
    NG_NAS_SecurityInfo_Type   NAS_Ciphering,
    Common_AuthenticationParams_Type AuthParams
  };

  //============================================================================
  // TEMPLATES
  //----------------------------------------------------------------------------

  const B4_Type tsc_NG_Integrity_Snow3G := '0001'B;   /* @status    APPROVED (NR5GC) */
  const B4_Type tsc_NG_Integrity_AES := '0010'B;      /* @status    APPROVED (NR5GC) */
  const B4_Type tsc_NG_Integrity_ZUC := '0011'B;      /* @status    APPROVED (NR5GC) */
  const B4_Type tsc_NG_Encryption_Snow3G := '0001'B;  /* @status    APPROVED (NR5GC) */
  const B4_Type tsc_NG_Encryption_AES := '0010'B;    /* @status    APPROVED (NR5GC) */
  const B4_Type tsc_NG_Encryption_ZUC := '0011'B;    /* @status    APPROVED (NR5GC) */

  template (value) NG_NAS_SecurityParams_Type cs_NG_NAS_SecurityParamsInit :=
  { /* @status    APPROVED (IMS, NR5GC, NR5GC_IRAT, POS) */
    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 (px_NAS_5GC_IntegrityAlgorithm, tsc_AuthUndefinedB128),
    NAS_Ciphering      := cs_NG_NAS_SecurityInfo (px_NAS_5GC_CipheringAlgorithm, tsc_AuthUndefinedB128),
    AuthParams         := cs_CommonAuthParams_Init (px_eAuthRAND)
  };

  template (value) NG_NAS_SecurityInfo_Type cs_NG_NAS_SecurityInfo(B4_Type p_Algo,
                                                                   B128_Key_Type p_Key) :=
  { /* @status    APPROVED (IMS, NR5GC, NR5GC_IRAT, POS) */
    Algorithm         := p_Algo,
    K_NAS             := p_Key
  };

  template (value) EAP_Message_Type cs_EAP_Request_AKAChallenge(O2_Type p_Length,
                                                                template (value) EAP_AT_RAND p_RAND,
                                                                template (value) EAP_AT_AUTN p_AUTN,
                                                                template (value) EAP_AT_KDF p_KDF,
                                                                template (value) EAP_AT_KDF_INPUT p_KDF_INPUT,
                                                                template (value) EAP_AT_MAC p_MAC,
                                                                O1_Type p_Id) :=
  { /* @status    APPROVED (IMS, NR5GC, NR5GC_IRAT, POS) */
   akaChallenge_Req := { // @sic R5s200288 sic@
    code      := '01'O,    // Request - RFC 4187 cl. 8.1
    id        := p_Id,    // RFC 3748 cl. 4   @sic R5s201410 sic@
    len       := p_Length, // RFC 3748 cl. 4
    type_     := '32'O,    // RFC 5448 cl. 6.1
    subtype   := '01'O,    // AKA-Challenge  as in RFC 4187 cl. 11
    reserved  := '0000'O,  // Set to 0 when sending, ignored by the receiver
    attributes := {
      rand := p_RAND,
      autn := p_AUTN,
      kdf  := p_KDF,
      kdfInput := p_KDF_INPUT,
      mac  := p_MAC,
      res  := omit,
      auts := omit,
      padd := omit,
      permIdReq := omit,
      notification := omit,
      anyIdReq := omit,
      id   := omit,
      versionList := omit,
      selectedVersion := omit,
      fullauthIdReq := omit,
      counter := omit,
      counterTooSmall := omit,
      nonceS  := omit,
      clientErrorCode := omit,
      iv   := omit,
      nextPseudonym := omit,
      nextReauthId  := omit,
      checkCode  := omit,
      resultInd  := omit
    }
   }
  };

  template (present) EAP_Message_Type cr_EAP_Response_AKAChallenge(O2_Type p_Length,
                                                                    template (present) EAP_AT_RES p_RES,
                                                                    template (present) EAP_AT_MAC p_MAC,
                                                                    template EAP_AT_RESULT_IND p_ResultInd := *,
                                                                    O1_Type p_Id) :=
  { /* @status    APPROVED (IMS, NR5GC, NR5GC_IRAT, POS) */
   akaChallenge_Rsp := { // @sic R5200288, R5s200400 sic@
    code      := '02'O,    // Response - RFC 4187 cl. 8.1
    id        := p_Id,    // RFC 3748 cl. 4     @sic R5s201483 sic@
    len       := p_Length, // RFC 3748 cl. 4
    type_     := '32'O,    // RFC 5448 cl. 6.1
    subtype   := '01'O,    // AKA-Challenge  as in RFC 4187 cl. 11
    reserved  := ?,        // Set to 0 when sending, ignored by the receiver
    attributes := {
      res  := p_RES,
      mac  := p_MAC,
      resultInd  := p_ResultInd, // @sic R5-201142 sic@
      padd := *,
      iv   := *,
      checkCode  := *,
      rand := omit,
      autn := omit,
      auts := omit,
      permIdReq := omit,
      notification := omit,
      anyIdReq := omit,
      id   := omit,
      versionList := omit,
      selectedVersion := omit,
      fullauthIdReq := omit,
      counter := omit,
      counterTooSmall := omit,
      nonceS  := omit,
      clientErrorCode := omit,
      nextPseudonym := omit,
      nextReauthId  := omit,
      kdf  := omit,
      kdfInput := omit
    }
   }
  };

  template (value) EAP_Message_Type cs_EAP_Success (O1_Type p_Id := '00'O) :=
  { /* @status    APPROVED (IMS, NR5GC, NR5GC_IRAT, POS) */
    success := {
      code      := '03'O, // Success
      id        := p_Id,    // RFC 3748 cl. 4
      len      := '0004'O // RFC 3748 cl. 4.1
    }
  };

  template (value) EAP_Message_Type cs_EAP_Failure (O1_Type p_Id := '00'O) :=
  { /* @status    APPROVED (NR5GC) */
    failure := {
      code      := '04'O, // Failure
      id        := p_Id,    // RFC 3748 cl. 4
      len      := '0004'O // RFC 3748 cl. 4.1
    }
  };

  template (value) EAP_AT_RAND cs_EAP_AT_RAND (O16_Type p_Rand) :=
  { /* @status    APPROVED (IMS, NR5GC, NR5GC_IRAT, POS) */
    // RFC 4187 cl. 10.6
    attributeType  := '01'O,
    len           := '05'O,
    reserved       := '0000'O,
    rand           := p_Rand
  };
  template (value) EAP_AT_AUTN cs_EAP_AT_AUTN (O16_Type p_AUTN) :=
  { /* @status    APPROVED (IMS, NR5GC, NR5GC_IRAT, POS) */
    // RFC 4187 cl. 10.7
    attributeType  := '02'O,
    len           := '05'O,
    reserved       := '0000'O,
    autn           := p_AUTN
  };
  template (value) EAP_AT_KDF cs_EAP_AT_KDF  :=
  { /* @status    APPROVED (IMS, NR5GC, NR5GC_IRAT, POS) */
    // RFC 5448 cl. 3.2
    attributeType  := '18'O,
    len           := '01'O,
    keyDerivationFunction := '0001'O // RFC 5448 cl. 3.3
  };
  template (value) EAP_AT_KDF_INPUT cs_EAP_AT_KDF_INPUT (octetstring p_Name, O2_Type p_ActualLength) :=
  { /* @status    APPROVED (IMS, NR5GC, NR5GC_IRAT, POS) */
    // RFC 5448 cl. 3.1
    attributeType   := '17'O,
    len            := int2oct(lengthof(p_Name)/4 + 1, 1),
    actualNetworkNamelen := p_ActualLength,
    networkName     := p_Name
  };
  template (value) EAP_AT_MAC cs_EAP_AT_MAC (O16_Type p_MAC) :=
  { /* @status    APPROVED (IMS, NR5GC, NR5GC_IRAT, POS) */
    // RFC 4187 cl. 10.15
    attributeType  := '0B'O,
    len           := '05'O,
    reserved       := '0000'O,
    mac           := p_MAC
  };
  template (present) EAP_AT_RES cr_EAP_AT_RES (octetstring p_Res, O2_Type p_ActualLength) :=
  { /* @status    APPROVED (IMS, NR5GC, NR5GC_IRAT, POS) */
    // RFC 4187 cl. 10.8
    attributeType := ('03'O),
    len           := int2oct(lengthof(p_Res)/4 + 1, 1),
    reslen        := p_ActualLength,
    res           := p_Res
  };

  //----------------------------------------------------------------------------
  /*
   * @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);
  }

  /*
   * @desc      To build the NAI
   * @param     p_PLMN
   * @param     p_Identity
   * @return    octetstring
   */
  function fl_GetNAI (NAS_PlmnId p_PLMN, hexstring p_Identity := px_IMSI_Def) return octetstring
  {
    var charstring v_P0 := hex2str(p_Identity) & "@nai.5gc.mnc" & fl_Nas2SNN_MNC(p_PLMN) & ".mcc" & fl_Nas2SNN_MCC(p_PLMN) & ".3gppnetwork.org"; // @sic R5s230215 sic@
    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    B256_Type
   * @status    APPROVED (IMS, NR5GC, NR5GC_IRAT, POS)
   */
  function f_NG_Authentication_A2(Common_AuthenticationParams_Type p_AuthParams,
                                  KDF_Type       p_KDF,
                                  B256_Type      p_Ks,
                                  NAS_PlmnId     p_PLMN,
                                  template (omit) hexstring p_NID := omit) return B256_Type
  {
    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      CK' and IK' derivation function; As per annex A.3 of 33.501
   * @param     p_AuthParams
   * @param     p_KDF
   * @param     p_Ks
   * @param     p_PLMN
   * @param     p_NID  (default value: omit)
   * @return    B256_Type
   * @status    APPROVED (IMS, NR5GC, NR5GC_IRAT, POS)
   */
  function f_NG_Authentication_A3(Common_AuthenticationParams_Type p_AuthParams,
                                  KDF_Type       p_KDF,
                                  B256_Type      p_Ks,
                                  NAS_PlmnId     p_PLMN,
                                  template (omit) hexstring p_NID := omit) return B256_Type
  {
    const octetstring const_S20_FC := '20'O;
    var octetstring v_S;
    var octetstring v_P0;

    // Generation of String
    v_S := const_S20_FC;
    //FC = 0x20
    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    B128_Type
   * @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,
                                  B256_Type p_Key,
                                  template (omit) hexstring p_NID := omit) return B128_Type
  {
    const octetstring const_S6B_FC :='6B'O;
    var octetstring v_S;
    var octetstring v_P0;

    log(">>> f_NG_Authentication_A4: p_PLMN=", p_PLMN);
    log(">>> f_NG_Authentication_A4: p_AuthParams=", p_AuthParams);
    log(">>> f_NG_Authentication_A4: p_KDF=", p_KDF);
    log(">>> f_NG_Authentication_A4: p_Key=", p_Key);


    // Generation of String
    v_S := const_S6B_FC;
    //FC = 0x6B
    v_P0 := fl_GetServingNetworkName(p_PLMN, p_NID); // @sic R5s220753 sic@
    log("f_NG_Authentication_A4: v_P0=", v_P0);
    v_S :=  (v_S & v_P0);
    log("f_NG_Authentication_A4: v_S=", v_S);
    //P0 = serving network ID
    v_S :=  (v_S & int2oct(lengthof(v_P0), 2)) ;
    log("f_NG_Authentication_A4: v_S=", v_S);
    //L0 = length of serving network ID
    v_S :=   ( v_S & bit2oct ( p_AuthParams.RandValue ) );
    log("f_NG_Authentication_A4: v_S=", v_S);
    //P1 = RAND
    v_S :=   ( v_S & '0010'O );
    //L1 = length of RAND (i.e. 0x00 0x10)
    log("f_NG_Authentication_A4: v_S=", v_S);
    v_S :=   ( v_S & bit2oct (substr(p_AuthParams.XRES, 0, px_NAS_5GC_XRES_Length*8))); // @sic R5s190394 sic@
    log("f_NG_Authentication_A4: v_S=", v_S);
    //P2 = RES or XRES
    v_S :=   ( v_S & int2oct(px_NAS_5GC_XRES_Length, 2) ); // @sic R5w190117 sic@
    log("f_NG_Authentication_A4: v_S=", v_S);
    //L2 = length of RES/XRES (e.g. 0x00 0x10)

    var bitstring bs := fx_KeyDerivationFunction(p_KDF, p_Key, v_S); // Already 128 bits length

    return bs;
    //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    B256_Type
   * @status    APPROVED (IMS, NR5GC, NR5GC_IRAT, POS)
   */
  function f_NG_Authentication_A6(NAS_PlmnId p_PLMN,
                                  B256_Type p_KAUSF,
                                  KDF_Type p_KDF_Type,
                                 template (omit) hexstring p_NID := omit) return B256_Type
  {
    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);
  };

  //--------------------------------------------------------------------------
  /*
   * @desc      KAMF derivation function
   *            As per annex A.7 of 33.501
   * @param     p_SUPI
   * @param     p_KSEAF
   * @param     p_ABBA
   * @param     p_KDF_Type
   * @return    B256_Type
   * @status    APPROVED (IMS, NR5GC, NR5GC_IRAT, POS)
   */
  function f_NG_Authentication_A7(charstring p_SUPI,
                                  B256_Type p_KSEAF,
                                  octetstring p_ABBA,
                                  KDF_Type p_KDF_Type) return B256_Type
  {
    const octetstring const_S6D_FC :='6D'O;
    var octetstring v_S;
    var octetstring v_P0 := char2oct(p_SUPI); // @sic R5s190109 sic@
    var octetstring v_P1 := p_ABBA;

    // Generation of String
    v_S := const_S6D_FC;
    //FC = 0x6D
    v_S :=  (v_S & v_P0);
    //P0 = serving network ID
    v_S :=  (v_S & int2oct(lengthof(v_P0), 2)) ;
    //L0 = length of SUPI
    v_S :=  (v_S & v_P1);
    //P0 = serving network ID
    v_S :=  (v_S & int2oct(lengthof(v_P1), 2)) ;
    //L1 = length of ABBA

    return fx_KeyDerivationFunction(p_KDF_Type, p_KSEAF, v_S);
  }

  //--------------------------------------------------------------------------
  /*
   * @desc      SOR MAC-I AUSF derivation function
   *            As per annex A.17 of 33.501
   * @param     p_SoRHeader
   * @param     p_KAUSF
   * @param     p_Counter
   * @param     p_PLMNandAccessTechnologyList (default value: ''O)
   * @param     p_KDF_Type
   * @return    B128_Type
   * @status    APPROVED (NR5GC)
   */
  function f_NG_Authentication_A17(O1_Type p_SoRHeader,
                                  B256_Type p_KAUSF,
                                  O2_Type p_Counter,
                                  octetstring p_PLMNandAccessTechnologyList := ''O,
                                  KDF_Type p_KDF_Type) return B128_Type
  {
    const octetstring const_S77_FC :='77'O;
    var octetstring v_S;

    // Generation of String
    v_S := const_S77_FC;
    //FC = 0x77
    v_S :=  (v_S & p_SoRHeader);
    //P0 = SOR Header
    v_S :=  (v_S & '0001'O);
    //L0 = length of SOR Header
    v_S :=  (v_S & p_Counter);
    //P1 = Counter
    v_S :=  (v_S & '0002'O) ;
    //L1 = length of Counter
    if (lengthof (p_PLMNandAccessTechnologyList) > 0) { //P2 & L2 are optional
      v_S :=  (v_S & p_PLMNandAccessTechnologyList);
      v_S :=  (v_S & int2oct(lengthof(p_PLMNandAccessTechnologyList), 2)) ;
    }
    return substr(fx_KeyDerivationFunction(p_KDF_Type, p_KAUSF, v_S), 128, 128);
    // returns LSB 128 bits[truncated] of the key generated
  }

  //--------------------------------------------------------------------------
  /*
   * @desc      SOR MAC-I UE derivation function
   *            As per annex A.18 of 33.501
   * @param     p_KAUSF
   * @param     p_Counter
   * @param     p_KDF_Type
   * @return    B128_Type
   * @status    APPROVED (NR5GC)
   */
  function f_NG_Authentication_A18(B256_Type p_KAUSF,
                                  O2_Type p_Counter,
                                  KDF_Type p_KDF_Type) return B128_Type
  {
    const octetstring const_S78_FC :='78'O;
    var octetstring v_S;

    // Generation of String
    v_S := const_S78_FC;
    //FC = 0x78
    v_S :=  (v_S & '01'O);
    //P0 = SOR Ack
    v_S :=  (v_S & '0001'O);
    //L0 = length of SOR Ack
    v_S :=  (v_S & p_Counter);
    //P1 = Counter
    v_S :=  (v_S & '0002'O) ;
    //L1 = length of Counter

    return substr(fx_KeyDerivationFunction(p_KDF_Type, p_KAUSF, v_S), 128, 128);
    // returns LSB 128 bits[truncated] of the key generated
  }

  //--------------------------------------------------------------------------
  /*
   * @desc      HASH AMF derivation function
   *            As per annex H.2 of 33.501
   * @param     p_NASMsg (encoded as octetstring)
   * @param     p_KDF_Type
   * @return    B64_Type
   */
  function f_NG_HASH_AMF_H2(octetstring p_NASMsg,
                            KDF_Type p_KDF_Type) return B64_Type
  {
    var B256_Type v_Key := int2bit(0, 256);
    var B256_Type v_Result := fx_KeyDerivationFunction(p_KDF_Type, v_Key, p_NASMsg);

    return substr(v_Result, 192, 64);
  };

  //--------------------------------------------------------------------------
  //============================================================================
  // Initialisations:

  /*
   * @desc      Function to be used before executing Authentication procedure in NG NAS.
   *            The structure for NG_NAS_SecurityParams_Type passed as parameter shall have at least valid parameters for:
   *            IMSI, PLMN, NAS_IntegrityInfo_Type.NAS_IntAlgo, NAS_CipheringInfo_Type.NAS_CipherAlgo
   *            Execution of this fucntion will initialise following parameters
   *            randValue, CK, IK, Ks, aUTN, XRES,KSIamf, KAMF, KNASenc & KNASint
   * @param     p_Auth_Params
   * @param     p_PLMN
   * @param     p_Identity  (default value:  := px_IMSI_Def)
   * @param     p_AuthenticationError (default value: noError)
   * @param     p_Common_AuthenticationParams (default value: omit)
   * @param     p_NID  (default value: omit)
   * @return    NG_NAS_SecurityParams_Type
   * @status    APPROVED (IMS, NR5GC, NR5GC_IRAT, POS)
   */
  function f_NG_NAS_AuthenticationInit(NG_NAS_SecurityParams_Type p_Auth_Params,
                                       NAS_PlmnId p_PLMN,
                                       hexstring p_Identity := px_IMSI_Def,
                                       AuthenticationError_Type p_AuthenticationError := noError,
                                       template (omit) Common_AuthenticationParams_Type p_Common_AuthenticationParams := omit,
                                       template (omit) hexstring p_NID := omit) return NG_NAS_SecurityParams_Type
  {
    var NG_NAS_SecurityParams_Type v_Auth_Params := p_Auth_Params;
    var B128_Type v_XRESstar;

    // Generates Ck, Ik, AUTN, XRES
    if (isvalue(p_Common_AuthenticationParams)) {
      v_Auth_Params.AuthParams := valueof(p_Common_AuthenticationParams);
    } else {
      v_Auth_Params.AuthParams := f_AuthenticationInit(v_Auth_Params.AuthParams, p_AuthenticationError); // @sic R5-197217 sic@
    }

    v_Auth_Params.Ks := v_Auth_Params.AuthParams.CK & v_Auth_Params.AuthParams.IK;
    // As per 33.501 clause 6.1.3.2
    // Generates KAUSF
    v_Auth_Params.KAUSF := f_NG_Authentication_A2(v_Auth_Params.AuthParams,
                                                  v_Auth_Params.KDF,
                                                  v_Auth_Params.Ks,
                                                  p_PLMN,
                                                  p_NID); // @sic R5s220753 sic@
    // Generates XRES*  @sic R5s200400 sic@
    v_XRESstar := f_NG_Authentication_A4(p_PLMN,
                                         v_Auth_Params.AuthParams,
                                         v_Auth_Params.KDF,
                                         v_Auth_Params.Ks,
                                         p_NID); // @sic R5s220753 sic@
    // Store value for later comparison with value received in Response
    v_Auth_Params.AuthParams.XRES := v_XRESstar;

    // Now update the other parameters (common to 5GAKA and EAP)
    v_Auth_Params := fl_NG_NAS_AuthenticationInitOtherParams (v_Auth_Params, p_PLMN, hex2str(p_Identity), p_NID); // @sic R5s220753, R5s230215 sic@
    return v_Auth_Params;
  }

  /*
   * @desc      Function to initialise authentication parameters common to both 5G AKA and EAP
   * @param     p_Auth_Params
   * @param     p_PLMN
   * @param     p_Identity
   * @param     p_NID  (default value: omit)
   * @return    NG_NAS_SecurityParams_Type
   * @status    APPROVED (IMS, NR5GC, NR5GC_IRAT, POS)
   */
  function fl_NG_NAS_AuthenticationInitOtherParams(NG_NAS_SecurityParams_Type p_Auth_Params,
                                                   NAS_PlmnId p_PLMN,
                                                   charstring p_Identity,
                                                   template (omit) hexstring p_NID := omit) return NG_NAS_SecurityParams_Type
  {
    var NG_NAS_SecurityParams_Type v_Auth_Params := p_Auth_Params;

    // update KSI
    v_Auth_Params.KSIamf := f_Authentication_IncrementKeySeq(v_Auth_Params.KSIamf);

    // Generate KSEAF
    v_Auth_Params.KSEAF := f_NG_Authentication_A6(p_PLMN,
                                                  v_Auth_Params.KAUSF,
                                                  v_Auth_Params.KDF,
                                                  p_NID);  //@sic R5s220753 sic@

    // Generate KAMF
    v_Auth_Params.KAMF := f_NG_Authentication_A7(p_Identity, // @sic R5s190109, R5s230215 sic@
                                                 v_Auth_Params.KSEAF,
                                                 v_Auth_Params.ABBA,
                                                 v_Auth_Params.KDF);

    // Derive KNASenc key
    v_Auth_Params.NAS_Ciphering.K_NAS := f_NG_Authentication_A8(tsc_NAS_Enc_Alg,
                                                                v_Auth_Params.NAS_Ciphering.Algorithm,
                                                                v_Auth_Params.KAMF,
                                                                v_Auth_Params.KDF);
    // Derive KNASint key
    v_Auth_Params.NAS_Integrity.K_NAS := f_NG_Authentication_A8(tsc_NAS_Int_Alg,
                                                                v_Auth_Params.NAS_Integrity.Algorithm,
                                                                v_Auth_Params.KAMF,
                                                                v_Auth_Params.KDF);
    return v_Auth_Params;
  }

  /*
   * @desc      Function to be used before executing Authentication procedure in NG EAP-AKA to generated
   *            Key material and tokens according to TS 33.501 clause 6.1.3.1.
   *            References: TS 33.501 clause 6.1.3.1. RFC 5448 and  RFC 4187
   *                        TS 33.102 for AUTN computation and TS TS 34.108 for XRES, CK, IK for Test USIM
   * @param     p_Auth_Params
   * @param     p_PLMN
   * @param     p_Identity
   * @param     p_Common_AuthenticationParams (default value: omit)
   * @param     p_AuthenticationError (default value: noError)
   * @param     p_NID  (default value: omit)
   * @return    NG_NAS_SecurityParams_Type
   * @status    APPROVED (IMS, NR5GC, NR5GC_IRAT, POS)
   */
  function f_NG_EAP_AuthenticationInit(NG_NAS_SecurityParams_Type p_Auth_Params,
                                       NAS_PlmnId p_PLMN,
                                       charstring p_Identity,
                                       template (omit) Common_AuthenticationParams_Type p_Common_AuthenticationParams := omit,
                                       AuthenticationError_Type p_AuthenticationError := noError,
                                       template (omit) hexstring p_NID := omit) return NG_NAS_SecurityParams_Type
  {
    var NG_NAS_SecurityParams_Type v_Auth_Params := p_Auth_Params;
    var B256_Type v_Ck_Ik_;
    var B128_Type v_Ck_;
    var B128_Type v_Ik_;
    var bitstring v_MK;
    var B256_Type v_K;
    var charstring v_S;

    if (isvalue(p_Common_AuthenticationParams)) {
      v_Auth_Params.AuthParams := valueof(p_Common_AuthenticationParams);
    } else {
      // Generates Ck, Ik, AUTN, XRES according to TS 33.102 and TS 34.108
      v_Auth_Params.AuthParams := f_AuthenticationInit(v_Auth_Params.AuthParams, p_AuthenticationError); // @sic R5s201223 sic@
    }

    v_Auth_Params.Ks := v_Auth_Params.AuthParams.CK & v_Auth_Params.AuthParams.IK;
    // Generates Ck' and IK'
    v_Ck_Ik_ := f_NG_Authentication_A3(v_Auth_Params.AuthParams,
                                       v_Auth_Params.KDF,
                                       v_Auth_Params.Ks,
                                       p_PLMN,
                                       p_NID); // @sic R5s220753 sic@
    v_Ck_ := substr (v_Ck_Ik_, 0, 128);
    v_Ik_ := substr (v_Ck_Ik_, 128, 128);

    // RFC 5448 cl. 3.3
    v_K := v_Ik_ & v_Ck_;
    v_S := "EAP-AKA'" & p_Identity;
    // Master Key
    v_MK := f_ExtendedEAPAKA_PRF (v_K, char2oct (v_S));
    v_Auth_Params.MK := v_MK;

    // As per 33.501 clause 6.1.3.1
    // KAUSF is EMSK in RFC 5448 cl. 3.3
    v_Auth_Params.KAUSF := substr (v_MK, 1152, 256);

    // Now update the other parameters (common to 5GAKA and EAP)
    v_Auth_Params := fl_NG_NAS_AuthenticationInitOtherParams (v_Auth_Params, p_PLMN, p_Identity, p_NID); // @sic R5s220753, R5s230215 sic@
    return v_Auth_Params;
  }

  /*
   * @desc      Generate and encode EAP-AKA' challenge according TS 33.501 clause 6.1.3.1 and  RFC 5448
   * @param     p_Auth_Params
   * @param     p_PLMN
   * @param     p_Id (default: '00'O)
   * @param     p_NID  (default value: omit)
   * @return    octetstring
   * @status    APPROVED (IMS, NR5GC, NR5GC_IRAT, POS)
   */
  function f_NG_EAP_ChallengeReq(NG_NAS_SecurityParams_Type p_Auth_Params,
                                 NAS_PlmnId p_PLMN,
                                 O1_Type p_Id := '00'O,
                                 template (omit) hexstring p_NID := omit)
    return octetstring
  {
    var template (value) EAP_AT_RAND v_RAND;
    var template (value) EAP_AT_AUTN v_AUTN;  // @sic R5s200400 sic@
    var template (value) EAP_AT_KDF v_KDF;
    var template (value) EAP_AT_KDF_INPUT v_KDF_INPUT;
    var template (value) EAP_AT_MAC v_MAC;
    var O16_Type v_MACValue := '00000000000000000000000000000000'O;
    var bitstring v_K_aut;
    var template (value) EAP_Message_Type v_EAP_Message;
    var integer v_Msglen := 8; // @sic R5s200400 sic@
    var octetstring v_NetworkName := fl_GetServingNetworkName (p_PLMN, p_NID); // @sic R5s220753 sic@
    var bitstring v_EncodedMsg;
    var integer v_Len;
    var integer v_Paddinglen;
    var integer i;


    v_RAND := cs_EAP_AT_RAND (bit2oct(p_Auth_Params.AuthParams.RandValue));
    v_Msglen := v_Msglen + 5*4;

    v_AUTN := cs_EAP_AT_AUTN (bit2oct(p_Auth_Params.AuthParams.AUTN));
    v_Msglen := v_Msglen + 5*4;

    v_KDF := cs_EAP_AT_KDF;
    v_Msglen := v_Msglen + 4;

    v_Len := lengthof(v_NetworkName);
    if (v_Len mod 4 > 0) {
      v_Paddinglen := 4 - v_Len mod 4;
      for (i:=0; i < v_Paddinglen; i:=i+1) {
        v_NetworkName := v_NetworkName & '00'O;
      }
    }

    v_KDF_INPUT := cs_EAP_AT_KDF_INPUT (v_NetworkName, int2oct(v_Len, 2));
    v_Msglen := v_Msglen + lengthof(v_NetworkName) + 4;

    v_MAC := cs_EAP_AT_MAC (v_MACValue);
    v_Msglen := v_Msglen + 5*4;

    // Build the message with MAC=0
    v_EAP_Message := cs_EAP_Request_AKAChallenge(int2oct(v_Msglen, 2),
                                                 v_RAND, v_AUTN, v_KDF,
                                                 v_KDF_INPUT, v_MAC, p_Id); // @sic  R5s201410 sic@

    // Compute MAC according to RFC 5448 cl. 3.4.2
    v_EncodedMsg := encvalue(v_EAP_Message);
    v_K_aut := substr(p_Auth_Params.MK, 128,256); // RFC 5448 cl. 3.3 K_aut to be used for computing MAC @sic R5s200400 sic@
    v_MACValue := bit2oct(substr(f_HMAC_SHA_256(v_K_aut, bit2oct(v_EncodedMsg)), 0, 128)); // @sic R5s200400 sic@
    v_MAC := cs_EAP_AT_MAC (v_MACValue);

    // Finally return the complete EAP-AKA' challenge with correct MAC
    return (bit2oct(encvalue(cs_EAP_Request_AKAChallenge (int2oct(v_Msglen, 2),
                                                          v_RAND, v_AUTN, v_KDF,
                                                          v_KDF_INPUT, v_MAC, p_Id)))); // @sic R5s201410 sic@

  }

  /*
   * @desc      Decode and check EAP-AKA' challenge according TS 24.501 clause 5.4.1.2.2 and  RFC 5448
   *            This function only checks for a successful Authentication Response - it doesn't check for any EAP failure messages
   * @param     p_Auth_Params       (by reference)
   * @param     p_ReceivedEAP
   * @param     p_ResultInd         (default value: *)
   * @param     p_Id                (default value: '00'O)
   * @return    boolean
   * @status    APPROVED (IMS, NR5GC, NR5GC_IRAT, POS)
   */
  function f_NG_EAP_SuccessfulAuthenticationResponse(inout NG_NAS_SecurityParams_Type p_Auth_Params,
                                                     octetstring p_ReceivedEAP,
                                                     template EAP_AT_RESULT_IND p_ResultInd := *,
                                                     O1_Type p_Id := '00'O)
    return boolean
  {
    var boolean v_Result := false;
    var bitstring v_ReceivedEAP := oct2bit(p_ReceivedEAP);
    var O16_Type v_RxdMAC;
    var O16_Type v_MACValue := '00000000000000000000000000000000'O;
    var bitstring v_K_aut;
    var EAP_Message_Type v_EAP_Message;
    var B32_128_Type v_RESValue;
    var octetstring v_ModifiedEAP;
    var integer v_Len;
    var integer v_Paddinglen;
    var integer i;

    if (decvalue(v_ReceivedEAP, v_EAP_Message) != 0) {
      FatalError(__FILE__, __LINE__, "EAP message cannot be decoded");
    }

    //get length in bits of received RES (as it's a variable length, need to compare the correct length)
    v_Len := oct2int(v_EAP_Message.akaChallenge_Rsp.attributes.res.reslen);
    // store this in the global info
    p_Auth_Params.AuthParams.XRESLength := v_Len / 8;

    // Get the RES value from the global info
    v_RESValue := substr(p_Auth_Params.AuthParams.XRES, 0, v_Len);
    // Now pad it so that it's (hopefully) the same length of the received value
    if (v_Len mod 8 > 0 or (v_Len/8) mod 4 > 0) {
      v_Paddinglen := 8 - v_Len mod 8;   // pad to end of octet, in bits
      v_Paddinglen := (4 - ((v_Len + v_Paddinglen)/8) mod 4)*8; // pad to multiple of 4 octets, in bits
      for (i:=0; i < v_Paddinglen; i:=i+1) {
        v_RESValue := v_RESValue & '0'B;
      }
    }

    // Check message is AKAChallenge, length and AT_RES is correct
    if (match (v_EAP_Message, cr_EAP_Response_AKAChallenge(int2oct(lengthof(p_ReceivedEAP), 2), cr_EAP_AT_RES (bit2oct(v_RESValue), int2oct(v_Len, 2)), ?, p_ResultInd, p_Id))) { // @sic R5-201142, R5s201483 sic@
      // Then test MAC
      v_RxdMAC := v_EAP_Message.akaChallenge_Rsp.attributes.mac.mac;

      // First build the message with MAC=0
      v_ModifiedEAP := fl_NG_EAP_Response_ClearMACValue(p_ReceivedEAP); // @sic R5s210153 sic@
      // Compute MAC according to RFC 5448 cl. 3.4.2
      v_K_aut := substr(p_Auth_Params.MK, 128, 256); // RFC 5448 cl. 3.3 K_aut to be used for computing MAC @sic R5s200400 sic@
      v_MACValue := bit2oct(substr(f_HMAC_SHA_256(v_K_aut, v_ModifiedEAP), 0, 128)); // @sic R5s200400, R5s210153 sic@
      // Then test it against the received value
      if (match(v_RxdMAC, v_MACValue)) { // if the MAC also matches then the whole message is correct
        v_Result := true;
      }
    }
    return v_Result;
  }


  /*
   * @desc      Local function to find then replace the value in the MAC attribute of the received EAP message with all 0s,
   *            so that the MAC value can be calculated and checked in the TTCN. This function is required because
   *            the EAP attributes are defined as a set
   *            => the order of the attributes in the template received in the TTCN may not match order in undecoded message
   * @param     p_ReceivedEAP
   * @return    octetstring
   * @status    APPROVED (IMS, NR5GC, NR5GC_IRAT, POS)
   */
  function fl_NG_EAP_Response_ClearMACValue(octetstring p_ReceivedEAP) return octetstring
  {
    var octetstring v_ModifiedEAP := p_ReceivedEAP;
    var integer i := 8;                         /* according to RFC 4187 clause 8.1 the EAP-AKA header is 8 octets */
    var integer k;

    while (p_ReceivedEAP[i] != '0B'O) {         /* MAC attribute type is 11 according to RFC 4187 clause 11 */
      i := i + (oct2int(p_ReceivedEAP[i+1]) * 4);
      if (i > lengthof(p_ReceivedEAP)) {
        FatalError(__FILE__, __LINE__, "no MAC attribute found");
      }
    }
    i := i + 4;                                 /* first 4 octets are type, length, reserved (RFC 4187 clause 10.15) */
    for (k := 0; k < 16; k := k + 1) {
      v_ModifiedEAP[i+k] := '00'O;
    }
    return v_ModifiedEAP;
  }

}
