/******************************************************************************/
// @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.
//   Trademark 2010, 3GPP Organizational Partners (ARIB, ATIS, CCSA, ETSI, TTA, TTC).
//   All rights reserved.
// @version: IWD_10wk37
// $Date: 2010-09-17 11:39:57 +0200 (Fri, 17 Sep 2010) $
// $Rev: 3955 $
/******************************************************************************/
module EUTRA_SecuritySteps {
  import from CommonDefs all;
  import from EUTRA_CommonDefs all;
  import from EUTRA_RRC_ASN1_Definitions language "ASN.1:1997" all;
  import from EUTRA_RRC_Templates all;
  import from EUTRA_ASP_TypeDefs all;
  import from EUTRA_ASP_SrbDefs all;
  import from EUTRA_ASP_NasCtrl all;
  import from EUTRA_AspCommon_Templates all;
  import from EUTRA_Security_Templates all;
  import from EUTRA_SRB_Templates all;
  import from EPS_NAS_Constants all;
  import from EPS_NAS_TypeDefs all;
  import from NAS_CommonTypeDefs all;
  import from EPS_NAS_Templates all;
  import from EUTRA_SecurityFunctions all;
  import from EUTRA_Component all;
  import from EUTRA_CellInfo all;
  import from EUTRA_ExternalSecurityFunctions all;
  import from EUTRA_NASCommonTemplates all;
  
  
  //============================================================================
  // AUTHENTICATION and SECURITY
  //============================================================================
  // Common Security Configuration Steps:
  /*
   * @desc      Common function to configure NAS security
   * @param     p_NAS_CTRL_REQ
   * @status    APPROVED
   */
  function f_EUTRA_SS_NAS_SecurityConfig(template (value) NAS_CTRL_REQ p_NAS_CTRL_REQ) runs on EUTRA_PTC
  {
    NASCTRL.send(p_NAS_CTRL_REQ);
    if (valueof(p_NAS_CTRL_REQ.Common.ControlInfo.CnfFlag) == true) {
      NASCTRL.receive(car_NasSecurity_CNF);
    }
  }
  
  //----------------------------------------------------------------------------
  /*
   * @desc      configure NAS security at the NasEmu
   * @param     p_NAS_Integrity
   * @param     p_NAS_Ciphering
   * @status    APPROVED
   */
  function f_EUTRA_SS_NAS_ConfigureSecurity(NAS_SecurityInfo_Type p_NAS_Integrity,
                                            NAS_SecurityInfo_Type p_NAS_Ciphering) runs on EUTRA_PTC
  { /* @sic R5s100135 sic@ */
    f_EUTRA_SS_NAS_SecurityConfig(cas_NasSecurityStart_REQ(cs_NAS_SecurityInit(p_NAS_Integrity.Algorithm,
                                                                               p_NAS_Integrity.K_NAS,
                                                                               p_NAS_Ciphering.Algorithm,
                                                                               p_NAS_Ciphering.K_NAS)));
  };
  
  //----------------------------------------------------------------------------
  /*
   * @desc      common function to query NAS COUNT from the NAS emulator
   * @status
   */
  function f_EUTRA_SS_NAS_CountGet() runs on EUTRA_PTC return NasCountInfo_Type
  {
    var NAS_CTRL_CNF v_NAS_CTRL_CNF;

    NASCTRL.send(cas_NasCountGet_REQ);
    NASCTRL.receive(car_NasCountGet_CNF) -> value v_NAS_CTRL_CNF;

    return v_NAS_CTRL_CNF.Confirm.NasCount.Get;
  }

  /*
   * @desc      function to query DL NAS COUNT from the NAS emulator
   * @status
   */
  function f_EUTRA_SS_NAS_CountGetDL() runs on EUTRA_PTC return O4_Type
  {
    var NasCountInfo_Type v_NasCountInfo := f_EUTRA_SS_NAS_CountGet();
    return v_NasCountInfo.DL;
  }

  //----------------------------------------------------------------------------
  /*
   * @desc      Common function to configure AS security
   * @param     p_CellId
   * @param     p_SYSTEM_CTRL_REQ
   * @status    APPROVED
   */
  function f_EUTRA_SS_AS_SecurityConfig(CellId_Type p_CellId,
                                        template (value) SYSTEM_CTRL_REQ p_SYSTEM_CTRL_REQ) runs on EUTRA_PTC
  {
    SYS.send(p_SYSTEM_CTRL_REQ);
    if (valueof(p_SYSTEM_CTRL_REQ.Common.ControlInfo.CnfFlag) == true) {
      SYS.receive(car_AsSecurity_CNF(p_CellId));
    }
  }
  
  //----------------------------------------------------------------------------
  /*
   * @desc      configure AS integrity protection at the SS
   * @param     p_CellId
   * @param     p_AS_IntegrityInfo
   * @param     p_TimingInfo        (default value: cs_TimingInfo_Now)
   * @status    APPROVED
   */
  function f_EUTRA_SS_RRC_EnableIntegrityProtection(CellId_Type p_CellId,
                                                    AS_IntegrityInfo_Type p_AS_IntegrityInfo,
                                                    template (value) TimingInfo_Type p_TimingInfo := cs_TimingInfo_Now) runs on EUTRA_PTC
  { /* @sic R5s100135, R5-100178 sic@ */
    /* PDCP sequence Number needs to be queried from PDCP; */

    var boolean v_CnfFlag := f_TimingInfo_IsNow(p_TimingInfo);
    f_EUTRA_SS_AS_SecurityConfig(p_CellId, cas_AsSecurityStart_REQ(p_CellId, cs_AS_IntProtActivate(p_AS_IntegrityInfo), p_TimingInfo, v_CnfFlag));
  };
  
  //----------------------------------------------------------------------------
  /*
   * @desc      enable ciphering at the SS
   * @param     p_CellId
   * @param     p_AS_CipheringInfo
   * @param     p_TimingInfo        (default value: cs_TimingInfo_Now)
   * @status    APPROVED
   */
  function f_EUTRA_SS_RRC_EnableCipheringULandDL(CellId_Type p_CellId,
                                                 AS_CipheringInfo_Type p_AS_CipheringInfo,
                                                 template (value) TimingInfo_Type p_TimingInfo := cs_TimingInfo_Now) runs on EUTRA_PTC
  { /* @sic R5s100135, R5-100178 sic@ */
    var boolean v_CnfFlag := f_TimingInfo_IsNow(p_TimingInfo);
    f_EUTRA_SS_AS_SecurityConfig(p_CellId, cas_AsSecurityStart_REQ(p_CellId, cs_AS_CipheringActivate(p_AS_CipheringInfo), p_TimingInfo, v_CnfFlag));
  };
  
  //----------------------------------------------------------------------------
  /*
   * @desc      release AS security at the SS
   * @param     p_CellId
   * @param     p_TimingInfo         (default value: cs_TimingInfo_Now)
   * @status    APPROVED
   */
  function f_EUTRA_SS_RRC_ReleaseSecurity(CellId_Type p_CellId,
                                          template (value) TimingInfo_Type p_TimingInfo := cs_TimingInfo_Now )  runs on EUTRA_PTC
  { /* @sic R5s100072 sic@ */
    /* @sic R5s100135 sic@ */
    var boolean v_CnfFlag;
    if (match(valueof(p_TimingInfo), cs_TimingInfo_Now)) {
      v_CnfFlag := tsc_CnfReq;
    } else {
      v_CnfFlag := tsc_NoCnfReq;
    }
    f_EUTRA_SS_AS_SecurityConfig(p_CellId, cas_AsSecurityRelease_REQ(p_CellId, p_TimingInfo, v_CnfFlag));
  };
  
  //----------------------------------------------------------------------------
  /*
   * @desc      depending on the given format p_PdcpCount is converted to integer
   * @param     p_PdcpCount
   * @return    PdcpSQN_Type
   * @status    APPROVED
   */
  function fl_EUTRA_SQN_ConvertFromPdcpCount(PdcpCount_Type p_PdcpCount) return PdcpSQN_Type
  {
    var PdcpSQN_Type v_PdcpSQN;
    var bitstring v_Bitstring := p_PdcpCount.Value;
    
    select (p_PdcpCount.Format) {
      case (PdcpCount_Srb) {
        v_Bitstring := substr(v_Bitstring, 27, 5);
      }
      case (PdcpCount_DrbLongSQN) {
        v_Bitstring := substr(v_Bitstring, 20, 12);
      }
      case (PdcpCount_DrbShortSQN) {
        v_Bitstring := substr(v_Bitstring, 25, 7);
      }
    }
    v_PdcpSQN.Format := p_PdcpCount.Format;
    v_PdcpSQN.Value := bit2int(v_Bitstring);
    return v_PdcpSQN;
  }
  
  //----------------------------------------------------------------------------
  /*
   * @desc      increment PDCP SQN
   * @param     p_PdcpSQN
   * @return    PdcpSQN_Type
   * @status    APPROVED
   */
  function fl_EUTRA_SQN_Increment(PdcpSQN_Type p_PdcpSQN) return PdcpSQN_Type
  {
    var PdcpSQN_Type v_PdcpSQN := p_PdcpSQN;
    
    select (p_PdcpSQN.Format) {
      case (PdcpCount_Srb) {
        v_PdcpSQN.Value := (p_PdcpSQN.Value + 1) mod 32;
      }
      case (PdcpCount_DrbLongSQN) {
        v_PdcpSQN.Value := (p_PdcpSQN.Value + 1) mod 4096;
      }
      case (PdcpCount_DrbShortSQN) {
        v_PdcpSQN.Value := (p_PdcpSQN.Value + 1) mod 128
          }
    }
    return v_PdcpSQN;
  }
  
  //----------------------------------------------------------------------------
  /*
   * @desc      Common functon to enquire PDCP sequence numbers
   * @param     p_CellId
   * @return    PdcpCountInfoList_Type
   * @status    APPROVED
   */
  function f_EUTRA_SS_PdcpCount_GetAll(CellId_Type p_CellId) runs on EUTRA_PTC return PdcpCountInfoList_Type
  {
    var SYSTEM_CTRL_CNF  v_ReceivedAsp;
    
    SYS.send   (cas_PdcpCount_REQ (p_CellId, cs_PDCP_CountGet_AllRBs));
    SYS.receive(car_PdcpCount_CNF(p_CellId, {Get := ?})) -> value v_ReceivedAsp;

    return v_ReceivedAsp.Confirm.PdcpCount.Get;
  }
  
  //----------------------------------------------------------------------------

  /*
   * @desc      set pdcp count at the given cell
   * @param     p_CellId
   * @param     p_PdcpCountInfoList
   * @status    APPROVED
   */
  function f_EUTRA_SS_PdcpCount_Set(CellId_Type p_CellId,
                                    PdcpCountInfoList_Type p_PdcpCountInfoList) runs on EUTRA_PTC
  {
    SYS.send   (cas_PdcpCount_REQ (p_CellId, cs_PDCP_CountSet(p_PdcpCountInfoList)));
    SYS.receive(car_PdcpCount_CNF(p_CellId, {Set := true}));
  }
  
  //----------------------------------------------------------------------------
  /*
   * @desc      check whether given PdcpCountInfo is for a DRB and whether this DRB is in the given DrbList
   * @param     p_PdcpCountInfo
   * @param     p_AM_DrbList
   * @return    boolean
   * @status    APPROVED
   */
  function fl_PdcpCount_CheckAmDrb(PdcpCountInfo_Type p_PdcpCountInfo,
                                   DRB_IdentityList_Type p_AM_DrbList) return boolean
  {
    var integer k;
    var DRB_Identity v_Drb;
    
    if (match(p_PdcpCountInfo, cr_PdcpCountInfo_DRB(?))) {         // check whether it is a DRB
      v_Drb := p_PdcpCountInfo.RadioBearerId.Drb;                  // and
      for (k := 0; k < lengthof(p_AM_DrbList); k := k + 1) {       // check the given list of AM DRBs
        if (v_Drb == p_AM_DrbList[k]) {                            // ... whether it contains that DRB
          return true;
        }
      }
    }
    return false;
  }

  /*
   * @desc      Query all PDCP counts and give back list of the AM DRBs only
   *            (needed for handover)
   * @param     p_CellId
   * @param     p_AM_DrbList
   * @return    PdcpCountInfoList_Type
   * @status    APPROVED
   */
  function f_EUTRA_SS_PdcpCount_GetAmDrbs(CellId_Type p_CellId,
                                          DRB_IdentityList_Type p_AM_DrbList) runs on EUTRA_PTC return PdcpCountInfoList_Type
  {
    var PdcpCountInfoList_Type v_PdcpCountInfoListAll;     // SNs for all bearers
    var PdcpCountInfoList_Type v_PdcpCountInfoListAmDrbs;  // SNs for the AM DRBs only
    var integer i;
    var integer n := 0;
    
    v_PdcpCountInfoListAll := f_EUTRA_SS_PdcpCount_GetAll(p_CellId);
    
    for (i := 0; i < lengthof(v_PdcpCountInfoListAll); i:= i + 1) {
      if (fl_PdcpCount_CheckAmDrb(v_PdcpCountInfoListAll[i], p_AM_DrbList)) {
        v_PdcpCountInfoListAmDrbs[n] := v_PdcpCountInfoListAll[i];
      }
    }
    return v_PdcpCountInfoListAmDrbs;
  }

  /*
   * @desc      to hand over PDCP count of AM bearers from one cell to another (in case of HO the count values of the other bearers - SRBs and UM DRBs - are reset to 0)
   * @param     p_SourceCellId
   * @param     p_DestCellId
   * @param     p_AM_DrbList .. list of AM DRBs for which the SNs shall be handed over
   * @status    APPROVED
   */
  function f_EUTRA_SS_PdcpCount_Handover(CellId_Type p_SourceCellId,
                                         CellId_Type p_DestCellId,
                                         DRB_IdentityList_Type p_AM_DrbList) runs on EUTRA_PTC
  {
    var PdcpCountInfoList_Type v_PdcpCountInfoListAmDrbs;  // SNs for the AM DRBs only

    v_PdcpCountInfoListAmDrbs := f_EUTRA_SS_PdcpCount_GetAmDrbs(p_SourceCellId, p_AM_DrbList);
    f_EUTRA_SS_PdcpCount_Set(p_DestCellId, v_PdcpCountInfoListAmDrbs);
  }
  
  //----------------------------------------------------------------------------
  /*
   * @desc      Common functon to enquire PDCP sequence numbers and generate a ciphering activation list for SRB1, SRB2 and DRBs
   * @param     p_CellId
   * @return    SecurityActTimeList_Type
   * @status    APPROVED
   */
  function f_EUTRA_RRC_CipherActTime_Get(CellId_Type p_CellId) runs on EUTRA_PTC return SecurityActTimeList_Type
  {
    var PdcpCountInfoList_Type v_PdcpCountInfoList;
    var PdcpCountInfo_Type v_PdcpCountInfo;
    var PDCP_ActTime_Type v_PDCP_ActTimeUL;
    var PDCP_ActTime_Type v_PDCP_ActTimeDL;
    var RadioBearerId_Type v_RadioBearerId;
    var SecurityActTime_Type v_SecurityActTime;
    var integer I;
    var SecurityActTimeList_Type v_SecurityActTimeList;
    
    v_PdcpCountInfoList := f_EUTRA_SS_PdcpCount_GetAll(p_CellId); /* @sic R5s100240: additional improvement sic@ */
    
    for (I:=0; I < lengthof(v_PdcpCountInfoList); I:=I+1) {
      v_PdcpCountInfo := v_PdcpCountInfoList[I];
      if ((not ispresent(v_PdcpCountInfo.UL)) or (not ispresent(v_PdcpCountInfo.DL))) {
        FatalError(__FILE__, __LINE__, "missing PdcpCountInfo for UL or DL");
      }
      v_RadioBearerId := v_PdcpCountInfo.RadioBearerId;
      // Convert PdcpCount to integer value (UL an DL)
      v_PDCP_ActTimeUL.SQN := fl_EUTRA_SQN_ConvertFromPdcpCount(v_PdcpCountInfo.UL);
      v_PDCP_ActTimeDL.SQN := fl_EUTRA_SQN_ConvertFromPdcpCount(v_PdcpCountInfo.DL);
      
      // increment SQN for SRB1 (UL and DL)
      if (ischosen(v_RadioBearerId.Srb) and (v_RadioBearerId.Srb == tsc_SRB1)) {
        v_PDCP_ActTimeUL.SQN := fl_EUTRA_SQN_Increment(v_PDCP_ActTimeUL.SQN);
        v_PDCP_ActTimeDL.SQN := fl_EUTRA_SQN_Increment(v_PDCP_ActTimeDL.SQN);
      }
      // set activation time
      v_SecurityActTime.RadioBearerId := v_RadioBearerId;
      v_SecurityActTime.UL := v_PDCP_ActTimeUL;
      v_SecurityActTime.DL := v_PDCP_ActTimeDL;
      
      v_SecurityActTimeList[I] := v_SecurityActTime;
    }
    return v_SecurityActTimeList;
  }
  
  //----------------------------------------------------------------------------
  /*
   * @desc      Common functon to enquire PDCP sequence numbers and generate a ciphering activation list for SRB1, SRB2 and DRBs with SNs reset to 0 for SRBs and UM DRBs;
   *            Note: this is done immediately i.e. without timing info but HO scenarios may require timing info;
   *                  therefore between calling this function and reconfiguring ciphering there shall be no data on any of the given AM bearers in UL or DL
   * @param     p_CellId
   * @param     p_AM_DrbList
   * @return    SecurityActTimeList_Type
   * @status    APPROVED
   */
  function f_EUTRA_RRC_CipherActTime_GetForHO(CellId_Type p_CellId,
                                              DRB_IdentityList_Type p_AM_DrbList) runs on EUTRA_PTC return SecurityActTimeList_Type
  { /* Common functon to enquire PDCP sequence numbers and generate a ciphering activation list for SRB1, SRB2 and DRBs with SNs reset to 0 for SRBs and UM DRBs;
       Note: this is done immediately i.e. without timing info but HO scenarios may require timing info; therefore between calling this function and reconfiguring ciphering there shall be no data on any of the given AM bearers in UL or DL */
    
    var PdcpCountInfoList_Type v_PdcpCountInfoList;
    var PdcpCountInfo_Type v_PdcpCountInfo;
    var SecurityActTime_Type v_SecurityActTime;
    var SecurityActTimeList_Type v_SecurityActTimeList;
    var integer i;
  
    v_PdcpCountInfoList := f_EUTRA_SS_PdcpCount_GetAll(p_CellId);
    
    for (i := 0; i < lengthof(v_PdcpCountInfoList); i:= i + 1) {
      v_PdcpCountInfo := v_PdcpCountInfoList[i];
      
      if (not fl_PdcpCount_CheckAmDrb(v_PdcpCountInfo, p_AM_DrbList)) {
        v_PdcpCountInfo.UL.Value := int2bit(0, 32);
        v_PdcpCountInfo.DL.Value := int2bit(0, 32);
      }

      v_SecurityActTime.RadioBearerId := v_PdcpCountInfo.RadioBearerId;
      v_SecurityActTime.UL.SQN := fl_EUTRA_SQN_ConvertFromPdcpCount(v_PdcpCountInfo.UL);
      v_SecurityActTime.DL.SQN := fl_EUTRA_SQN_ConvertFromPdcpCount(v_PdcpCountInfo.DL);

      v_SecurityActTimeList[i] := v_SecurityActTime;
    }
    return v_SecurityActTimeList;
  }

  //============================================================================
  // Common RRC Steps:
  
  /*
   * @desc      Initial security activation (36.331 cl. 5.3.4)
   * @param     p_CellId
   * @param     p_IntegrityProtAlgorithm
   * @param     p_CipheringAlgorithm
   * @status    APPROVED
   */
  function f_EUTRA_RRC_InitialSecurityActivation(CellId_Type p_CellId,
                                                 IntegrityProtAlgorithm_Type    p_IntegrityProtAlgorithm,
                                                 CipheringAlgorithm_Type        p_CipheringAlgorithm) runs on EUTRA_PTC
  {
    var RRC_TransactionIdentifier v_RRC_TI := tsc_RRC_TI_Def;
    
    // send SecurityModeCommand on SRB1:
    SRB.send(cas_SRB1_RrcPdu_REQ(p_CellId,
                                 cs_TimingInfo_Now,
                                 cs_508_SecurityModeCommand(v_RRC_TI, p_IntegrityProtAlgorithm, p_CipheringAlgorithm)));
    // receive SecurityModeComplete on SRB1:
    SRB.receive(car_SRB1_RrcPdu_IND(p_CellId,
                                    cr_508_SecurityModeComplete(v_RRC_TI)));
  };
  
  //----------------------------------------------------------------------------
  /*
   * @desc      Activation of Initial security at SS[SRB 1,2 and DRB 1] and at the UE
   *            (36.331 cl. 5.3.4       Initial security activation)
   *            The parameter p_UL_NAS_Count shall be the NAS count of
   *            if [AUTN procedure is executed]
   *            then: The UL NAS Count of NAS SMC Complete, of the NAS SMC procedure executed after AUTN Proc.
   *            else
   *            then: The UL NAS count of the NAS UL message[which shall be security protected] received which triggered the RRC Connection establishment
   *            i.e. Attach Req[ in special cases], TAU Req, Service Req etc.
   *
   * @param     p_CellId
   * @param     p_SecurityParams
   * @param     p_NasCountUL
   * @return    EUTRA_SecurityParams_Type
   * @status    APPROVED
   */
  function f_EUTRA_RRC_ActivateSecurity(CellId_Type p_CellId,
                                        EUTRA_SecurityParams_Type p_SecurityParams,
                                        NasCount_Type p_NasCountUL) runs on EUTRA_PTC
    return EUTRA_SecurityParams_Type
  {
    var EUTRA_SecurityParams_Type v_SecurityParams := f_EUTRA_Authentication_InitAS(p_SecurityParams,
                                                                                    p_NasCountUL); /* Calculate Kenb and KRRCenc, KRRCint, KUPenc */

    // Read PDCP SQN for SRB1 and calcuate Ciphering Activation times
    v_SecurityParams.AS_Ciphering.ActTimeList := f_EUTRA_RRC_CipherActTime_Get(p_CellId);         /* note: this works only when timing info is now */
    // Note by default DRB is also pre-configured on SS side
    
    f_EUTRA_SS_RRC_EnableIntegrityProtection(p_CellId,
                                             v_SecurityParams.AS_Integrity );          /* SecurityModeCommand is integrity protected with the algorithm
                                                                                          being contained in the message; all sub-sequent RRC messages
                                                                                          are integrity protected in UL and DL (36.331 cl. 5.3.4.3) */
    f_EUTRA_SS_RRC_EnableCipheringULandDL(p_CellId,
                                          v_SecurityParams.AS_Ciphering);
    f_EUTRA_RRC_InitialSecurityActivation(p_CellId,
                                          v_SecurityParams.AS_Integrity.Algorithm,
                                          v_SecurityParams.AS_Ciphering.Algorithm);    /* SecurityModeComplete is not ciphered
                                                                                          acc. to 36.331 cl. 5.3.4.3 and 5.3.1.1 */
    
    return (v_SecurityParams);
  };

  //----------------------------------------------------------------------------
  /*
   * @desc      This function needs to be called when new Authentication procedure is executed and new Access stratum keys need to be taken into account
   *            This function does not send and receive RRCConnection Reconfiguration procedure
   * @param     p_TargetCellId
   * @param     p_SecurityParams
   * @param     p_NasCountUL
   * @param     p_TimingInfo        (default value: cs_TimingInfo_Now)
   * @param     p_AM_DrbList        (default value: tsc_DrbList_Def)
   * @return    EUTRA_SecurityParams_Type
   * @status    APPROVED
   */
  function f_EUTRA_AS_ActivateSecurity_NewAKA(CellId_Type p_TargetCellId,
                                              EUTRA_SecurityParams_Type p_SecurityParams,
                                              NasCount_Type p_NasCountUL,
                                              template (value) TimingInfo_Type p_TimingInfo := cs_TimingInfo_Now,
                                              DRB_IdentityList_Type p_AM_DrbList := tsc_DrbList_Def) runs on EUTRA_PTC
    return EUTRA_SecurityParams_Type
  {
    var EUTRA_SecurityParams_Type v_SecurityParams;
    var PhysCellId v_PhysicalCellId := f_EUTRA_CellInfo_GetPhyCellId(p_TargetCellId);
    var Frequency_IE_Type v_Frequency_IE_Cell := f_EUTRA_CellInfo_GetFrequencyIEs(p_TargetCellId);
    var ARFCN_ValueEUTRA v_EARFCN_DL := v_Frequency_IE_Cell.UL_DL_Earfcn.dl_CarrierFreq;
    var boolean v_IsSMC_Procedure := false;   // => Security is (re-)initialised for the non-SECURITY-MODE-COMMAND case

    v_SecurityParams := f_EUTRA_Authentication_InitAS(p_SecurityParams,
                                                      p_NasCountUL,
                                                      v_IsSMC_Procedure,
                                                      v_PhysicalCellId,
                                                      v_EARFCN_DL); /* Calculate Kenb and KRRCenc, KRRCint, KUPenc */
    
    // get list of activation times:
    // NOTE: this function queries the PDCP SNs now, i.e. without taking into account the p_TimingInfo;
    //       thats works fine for SRBs and UM DRBs since those are reset to 0; for AM DRBs there shall be no data until the HO is completed
    v_SecurityParams.AS_Ciphering.ActTimeList := f_EUTRA_RRC_CipherActTime_GetForHO(p_TargetCellId, p_AM_DrbList);
    
    f_EUTRA_SS_RRC_EnableIntegrityProtection(p_TargetCellId,
                                             v_SecurityParams.AS_Integrity,
                                             p_TimingInfo);

    f_EUTRA_SS_RRC_EnableCipheringULandDL(p_TargetCellId,
                                          v_SecurityParams.AS_Ciphering,
                                          p_TimingInfo);
    return (v_SecurityParams);
  };

  /*
   * @desc      Activation of RRC security at SS[SRB 1,2 and DRB 1] for Chaining and Re-keying of KeNB.
   *
   *            the parameter p_NCC will decide if it is chainnig [p_NCC != NCC associated with Kenb],
   *            or if Re-keying [p_NCC == NCC associated with Kenb] .
   *            This function needs to be called after sending RRCConnection reconfiguration for handover or
   *            RRC Connection Re-establishment when keyChangeIndicator received in the securityConfiguration is not set to TRUE
   *
   * @param     p_TargetCellId
   * @param     p_Auth_Params
   * @param     p_NCC
   * @param     p_TimingInfo        (default value: cs_TimingInfo_Now)
   * @param     p_AM_DrbList        (default value: tsc_DrbList_Def)
   * @return    EUTRA_SecurityParams_Type
   * @status    APPROVED
   */
  function f_EUTRA_SS_AS_ActivateSecurity_HO_Reest(CellId_Type p_TargetCellId,
                                                   EUTRA_SecurityParams_Type p_Auth_Params,
                                                   NextHopChainingCount p_NCC,
                                                   template (value) TimingInfo_Type p_TimingInfo := cs_TimingInfo_Now,
                                                   DRB_IdentityList_Type p_AM_DrbList := tsc_DrbList_Def) runs on EUTRA_PTC
    return EUTRA_SecurityParams_Type
  {
    var PhysCellId v_TargetPhysicalCellIdentity := f_EUTRA_CellInfo_GetPhyCellId(p_TargetCellId);
    var ARFCN_ValueEUTRA v_TargetCellEARFCN_DL := f_EUTRA_CellInfo_GetEARFCN_DL(p_TargetCellId); // @sic R5s100138
    var EUTRA_SecurityParams_Type v_SecurityParams := f_EUTRA_InitAS_KeyChaining_KeyRefresh(v_TargetPhysicalCellIdentity,
                                                                                            v_TargetCellEARFCN_DL,
                                                                                            p_Auth_Params,
                                                                                            p_NCC); /* Calculate Kenb and KRRCenc, KRRCint, KUPenc */
    // @sic R5s100138
    // Read PDCP SQN for SRB1 and calcuate Ciphering Activation times
    v_SecurityParams.AS_Ciphering.ActTimeList := f_EUTRA_RRC_CipherActTime_GetForHO (p_TargetCellId, p_AM_DrbList );
    // Note by default DRB is also pre-configured on SS side
    
    f_EUTRA_SS_RRC_EnableIntegrityProtection(p_TargetCellId,
                                             v_SecurityParams.AS_Integrity,
                                             p_TimingInfo);                           /* SecurityModeCommand is integrity protected with the algorithm
                                                                                         being contained in the message; all sub-sequent RRC messages
                                                                                         are integrity protected in UL and DL (36.331 cl. 5.3.4.3) */
    f_EUTRA_SS_RRC_EnableCipheringULandDL(p_TargetCellId,
                                          v_SecurityParams.AS_Ciphering,
                                          p_TimingInfo);
    return (v_SecurityParams);
  };
  
  //============================================================================
  // Common NAS Steps:
  
  /*
   * @desc      24.301 cl. 5.4.2 Authentication procedure
   *            The parameter p_UL_NAS_Count shall be the NAS count of UL NAS message if security protected [i.e. Attach message]
   *            or the reseted NAS count.
   *            The structure for EUTRA_SecurityParams_Type passed as parameter shall have at least valid parameters for:
   *            IMSI, PLMN, NAS_IntegrityInfo_Type.NAS_IntAlgo, AS_IntegrityInfo_Type.IntProtAlgo
   *            NAS_CipheringInfo_Type.NAS_CipherAlgo, AS_CipheringInfo_Type.CiphAlgo.
   *            parameter shall be passed as TRUE if AUTHENTICATION_REQUEST & AUTHENTICATION_RESPONSE are to be NAS security protected
   *            else it shall be false.
   *
   * @param     p_CellId
   * @param     p_SRB
   * @param     p_SecurityParams
   * @param     p_SecurityStatusAuthRequest  - Security header to be used in Auth Request
   * @param     p_SecurityStatusAuthResponse - Security heaser expected in Auth Response
   * @param     p_PLMN
   * @return    EUTRA_SecurityParams_Type
   * @status    APPROVED
   */
  function f_EUTRA_NAS_Authentication(CellId_Type p_CellId,
                                      SRB_Identity_Type p_SRB,
                                      EUTRA_SecurityParams_Type p_SecurityParams,
                                      SecurityHeaderType p_SecurityStatusAuthRequest,
                                      template (present) SecurityHeaderType p_SecurityStatusAuthResponse, // @sic R5s090373 sic@
                                      NAS_PlmnId     p_PLMN)
    runs on EUTRA_PTC
    return EUTRA_SecurityParams_Type
  {
    var SRB_COMMON_IND v_ReceivedAsp;
    var B32_128_Type v_ReceivedRES;
    var EUTRA_SecurityParams_Type v_SecurityParams;
    
    // Evaluate Authentication parameters and generate new keys
    v_SecurityParams := f_EUTRA_Authentication_InitNAS(p_SecurityParams,p_PLMN);

    SRB.send(cas_SRB_NasPdu_REQ(p_CellId,
                                p_SRB,
                                cs_TimingInfo_Now,
                                cs_NAS_Request(p_SecurityStatusAuthRequest, cs_AUTHENTICATION_REQUEST(v_SecurityParams.KSIasme,
                                                                                                      v_SecurityParams.AuthParams.RandValue,
                                                                                                      v_SecurityParams.AuthParams.AUTN))));
    // receive AUTHENTICATION RESPONSE: @sic R5s090210 sic@
    alt {
      [] SRB.receive(car_SRB_NasPdu_IND(p_CellId,
                                        p_SRB,
                                        cr_NAS_Indication(p_SecurityStatusAuthResponse, cr_AUTHENTICATION_RESPONSE)))
        -> value v_ReceivedAsp {
        v_ReceivedRES := v_ReceivedAsp.Signalling.Nas[0].Pdu.Msg.aUTHENTICATION_RESPONSE.authenticationResponseParameter.res;
        if (v_ReceivedRES != substr(v_SecurityParams.AuthParams.XRES, 0, lengthof(v_ReceivedRES))) {
          f_EUTRA_SetVerdictFailOrInconc(__FILE__, __LINE__, "Authentication Failed");
        }
      }
      [] SRB.receive(car_SRB_NasPdu_IND(p_CellId,
                                        p_SRB,
                                        cr_NAS_Indication(?, cr_AUTHENTICATION_FAIL_Any)))
        {
          f_EUTRA_SetVerdictFailOrInconc(__FILE__, __LINE__, "Authentication Fail Message");
        }
    }
    return (v_SecurityParams);
  };
  
  //----------------------------------------------------------------------------
  /*
   * @desc      send SECURITY_MODE_COMMAND to the UE and wait for SECURITY_MODE_COMPLETE
   * @param     p_CellId
   * @param     p_SecurityParams
   * @param     p_SRB               (default value: tsc_SRB1)
   * @return    NasCount_Type
   * @status    APPROVED
   */
  function f_EUTRA_NAS_SecurityModeControl(CellId_Type p_CellId,
                                           EUTRA_SecurityParams_Type p_SecurityParams,
                                           SRB_Identity_Type p_SRB := tsc_SRB1)
    runs on EUTRA_PTC return NasCount_Type
  { /* @sic R5-101147 - no parameter p_NewSecurityCxt sic@ */
    var SRB_COMMON_IND v_ReceivedAsp;
    var NasCount_Type v_NasCountUL;
    
    // send NAS SECURITY MODE COMMAND (integrity protected acc. 24.301 cl. 5.4.3.2):
    SRB.send(cas_SRB_NasPdu_REQ(p_CellId,
                                p_SRB,
                                cs_TimingInfo_Now,
                                cs_NAS_Request(tsc_SHT_IntegrityProtected_NewSecurityContext,
                                               cs_508_SECURITY_MODE_COMMAND(p_SecurityParams.NAS_Ciphering.Algorithm,
                                                                            p_SecurityParams.NAS_Integrity.Algorithm,
                                                                            p_SecurityParams.KSIasme,
                                                                            p_SecurityParams.NAS_SecurityCap))));
    // receive NAS SECURITY MODE COMPLETE (integrity protected and ciphered acc. 24.301 cl. 5.4.3.3):
    SRB.receive(car_SRB_NasPdu_IND(p_CellId,
                                   p_SRB,
                                   cr_NAS_Indication(tsc_SHT_IntegrityProtected_Ciphered_NewSecurityContext, cr_508_SECURITY_MODE_COMPLETE)))
      -> value v_ReceivedAsp;
    
    v_NasCountUL := v_ReceivedAsp.Signalling.Nas[0].SecurityProtection.NasCount;
    return v_NasCountUL;
  };
  
  //----------------------------------------------------------------------------
  /*
   * @desc      Activation of NAS security at SS and at the UE (24.301 cl. 5.4.3)
   * @param     p_CellId
   * @param     p_SecurityParams
   * @param     p_SRB (optional)
   * @return    NasCount_Type
   * @status    APPROVED
   */
  function f_EUTRA_NAS_ActivateSecurity(CellId_Type p_CellId,
                                        EUTRA_SecurityParams_Type p_SecurityParams,
                                        SRB_Identity_Type p_SRB := tsc_SRB1) runs on EUTRA_PTC return NasCount_Type
  {
    var NasCount_Type v_NasCountUL;
    
    f_EUTRA_SS_NAS_ConfigureSecurity(p_SecurityParams.NAS_Integrity, p_SecurityParams.NAS_Ciphering); /* download security keys and reset NAS COUNT */
    
    v_NasCountUL := f_EUTRA_NAS_SecurityModeControl(p_CellId, p_SecurityParams, p_SRB);  // @sic R5-101147 sic@
    /* all NAS messages are ciphered from now on */
    return v_NasCountUL;
  };
  
  //============================================================================
  // Functions to selectively set Ciphering or Integrity Algorithms
  
  /*
   * @desc      To Set the AS Integrity Algorithm
   * @param     p_AS_IntegrityProtAlgorithm
   * @status    APPROVED
   */
  function f_EUTRA_AS_IntegrityAlgorithm_Set(IntegrityProtAlgorithm_Type p_AS_IntegrityProtAlgorithm) runs on EUTRA_PTC
  {
    var EUTRA_SecurityParams_Type v_Auth_Params := f_EUTRA_Security_Get();
    v_Auth_Params.AS_Integrity.Algorithm :=p_AS_IntegrityProtAlgorithm;
    f_EUTRA_Security_Set (v_Auth_Params);
  };

  /*
   * @desc      To Set the AS Ciphering Algorithm
   * @param     p_AS_CipheringAlgorithm
   * @status    APPROVED
   */
  function f_EUTRA_AS_CipheringAlgorithm_Set(CipheringAlgorithm_Type p_AS_CipheringAlgorithm) runs on EUTRA_PTC
  {
    var EUTRA_SecurityParams_Type v_Auth_Params := f_EUTRA_Security_Get();
    v_Auth_Params.AS_Ciphering.Algorithm := p_AS_CipheringAlgorithm;
    f_EUTRA_Security_Set (v_Auth_Params);
  };
  
  /*
   * @desc      To Set the NAS Integrity Algorithm
   * @param     p_NAS_IntegrityProtAlgorithm
   * @status    APPROVED
   */
  function f_EUTRA_NAS_IntegrityAlgorithm_Set(B3_Type p_NAS_IntegrityProtAlgorithm) runs on EUTRA_PTC
  {
    var EUTRA_SecurityParams_Type v_Auth_Params := f_EUTRA_Security_Get();
    v_Auth_Params.NAS_Integrity.Algorithm := p_NAS_IntegrityProtAlgorithm;
    f_EUTRA_Security_Set (v_Auth_Params);
  };
  
  /*
   * @desc      To Set the NAS Ciphering Algorithm
   * @param     p_NAS_CipheringAlgorithm
   * @status    APPROVED
   */
  function f_EUTRA_NAS_CipheringAlgorithm_Set(B3_Type p_NAS_CipheringAlgorithm) runs on EUTRA_PTC
  {
    var EUTRA_SecurityParams_Type v_Auth_Params := f_EUTRA_Security_Get();
    v_Auth_Params.NAS_Ciphering.Algorithm := p_NAS_CipheringAlgorithm;
    f_EUTRA_Security_Set (v_Auth_Params);
  };

  //============================================================================
  /*
   * @desc      Function to encode VarShortMAC from ASN.1 to octetstring
   * @param     p_VarShortMAC_Input
   * @return    octetstring
   * @status    APPROVED
   */
  function fl_Encode_VarShortMAC (VarShortMAC_Input_Type p_VarShortMAC_Input) return octetstring
  {
    var bitstring v_PhysCellId;
    var bitstring v_Bitstring_VarShortMAC_Input;
    var octetstring v_Octetstring_VarShortMAC_Input;
    
    v_PhysCellId := int2bit(p_VarShortMAC_Input.physCellId, 9);
    
    //Place IEs of VarShortMAC_Input into single bitstring
    v_Bitstring_VarShortMAC_Input := p_VarShortMAC_Input.cellIdentity & v_PhysCellId & p_VarShortMAC_Input.c_RNTI;
    
    //Octet align the bit string
    v_Bitstring_VarShortMAC_Input := f_OctetAlignedBitString(v_Bitstring_VarShortMAC_Input);  // @sic R5s100324 sic@

    //Convert bitstring to octet aligned octetstring
    v_Octetstring_VarShortMAC_Input := bit2oct(v_Bitstring_VarShortMAC_Input);
    
    return (v_Octetstring_VarShortMAC_Input);
  }

   /*
   * @desc      Function to calculate shortMAC-I that is used as a parameter in RRCConnectionReestablishmentRequest
   * @param     p_CellId_Source
   * @param     p_CellId_Current
   * @return    ShortMAC_I
   * @status    APPROVED
   */
  function f_Calculate_ShortMAC(CellId_Type p_CellId_Source,
                                CellId_Type p_CellId_Current) runs on EUTRA_PTC return ShortMAC_I
  {
    var ShortMAC_I v_ShortMAC_I;
    var O4_Type v_CalculatedMac;
    var EUTRA_SecurityParams_Type v_SecurityParams := f_EUTRA_Security_Get();
    var VarShortMAC_Input_Type v_VarShortMAC_Input;
    var octetstring v_Encoded_VarShortMAC_Input;
    var O4_Type v_PdcpCount := 'FFFFFFFF'O;
    var B5_Type v_BearerId := '11111'B;    /* R5-101050: BearerId changed to 5 bits */
    var integer v_Direction := 1;
    
    v_VarShortMAC_Input.cellIdentity := f_EUTRA_CellInfo_GetCellIdentity ( p_CellId_Current );/* Acc. 36.331 ch. 7.1 Set to CellIdentity of the current cell. */
    v_VarShortMAC_Input.physCellId := f_EUTRA_CellInfo_GetPhyCellId ( p_CellId_Source );      /* Set to the physical cell identity of the cell the UE was connected to prior to the failure. */
    v_VarShortMAC_Input.c_RNTI := f_EUTRA_CellInfo_GetC_RNTI ( p_CellId_Source );             /* Set to C-RNTI that the UE had in the cell it was connected to prior to the failure. */
    
    /* According to 36.331 cl. 5.3.7.4
       2>  set the shortMAC-I to the 16 least significant bits of the MAC-I calculated:
       3>  over the ASN.1 encoded VarShortMAC-Input;
       3>  with the KRRCint key and integrity protection algorithm that was used in the
       source cell (handover and mobility from E-UTRA failure) or of the cell in which
       the trigger for the re-establishment occurred (other cases); and
       3>  with all input bits for COUNT, BEARER and DIRECTION set to binary ones; */
    
    v_Encoded_VarShortMAC_Input := fl_Encode_VarShortMAC (v_VarShortMAC_Input);
    
    v_CalculatedMac := fx_AsIntegrityAlgorithm(v_Encoded_VarShortMAC_Input,
                                               v_SecurityParams.AS_Integrity.Algorithm,
                                               v_SecurityParams.AS_Integrity.KRRCint,
                                               v_PdcpCount,
                                               v_BearerId,
                                               v_Direction);
    
    v_ShortMAC_I := substr(oct2bit(v_CalculatedMac),16,16); /* According to 36.331 ch. 5.3.7.4 2 Set the shortMAC-I to the 16 least significant bits of the MAC-I calculated */
    
    return v_ShortMAC_I;
    
  }
  
  //shortMAC <<

  //============================================================================
  /*
  // @sic R5-102227
  * @desc      to be called during TAU after mobility from other RAT to store NONCEUE sent by UE
  * @status
  */
  function f_EUTRA_Security_NonceUE_Set(B32_Type p_NonceUE) runs on EUTRA_PTC {
    var EUTRA_SecurityParams_Type v_AuthParams := f_EUTRA_Security_Get();
    v_AuthParams.NonceUE := p_NonceUE;
    f_EUTRA_Security_Set(v_AuthParams);
  };
  
  //============================================================================
  /*
   * @desc      to be called during TAU after mobility from other RAT to initialise NONCEMME
   * @status
   //@sic R5-102227
   */
  function f_EUTRA_Security_NonceMME_Init(B32_Type p_NonceMME := tsc_NonceMME) runs on EUTRA_PTC {
    var EUTRA_SecurityParams_Type v_AuthParams := f_EUTRA_Security_Get();
    v_AuthParams.NonceMME := p_NonceMME;
    f_EUTRA_Security_Set(v_AuthParams);
  };
  
  //----------------------------------------------------------------------------
  /*
  // @sic R5-102227
  * @desc      send SECURITY_MODE_COMMAND to the UE and wait for SECURITY_MODE_COMPLETE
   *            to be used in TAU procedure after UE has reselected from Other RAT cell and Native security context is not available
   * @param     p_CellId
   * @param     p_SecurityParams
   * @param     p_NewSecurityCxt (optional) // @sic R5-101147 sic@ now not used
   * @param     p_SRB (optional)
   * @return    NasCount_Type
   * @status
   */
  function f_EUTRA_NAS_SecurityModeControl_MappedContext(CellId_Type p_CellId,
                                                         EUTRA_SecurityParams_Type p_SecurityParams,
                                                         SRB_Identity_Type p_SRB := tsc_SRB1)
    runs on EUTRA_PTC return NasCount_Type
  {
    var SRB_COMMON_IND v_ReceivedAsp;
    var NasCount_Type v_NasCountUL;
    
    // send NAS SECURITY MODE COMMAND (integrity protected acc. 24.301 cl. 5.4.3.2):
    // Include SGSN KSN, replayed nonce UE and Nonce MME.
    SRB.send(cas_SRB_NasPdu_REQ(p_CellId,
                                p_SRB,
                                cs_TimingInfo_Now,
                                cs_NAS_Request(tsc_SHT_IntegrityProtected_NewSecurityContext,
                                               cds_SECURITY_MODE_COMMAND_MappedContext(p_SecurityParams.NAS_Ciphering.Algorithm,
                                                                                       p_SecurityParams.NAS_Integrity.Algorithm,
                                                                                       p_SecurityParams.KSIsgsn,
                                                                                       p_SecurityParams.NAS_SecurityCap,
                                                                                       p_SecurityParams.NonceUE,
                                                                                       p_SecurityParams.NonceMME))));
    // receive NAS SECURITY MODE COMPLETE (integrity protected and ciphered acc. 24.301 cl. 5.4.3.3):
    SRB.receive(car_SRB_NasPdu_IND(p_CellId,
                                   p_SRB,
                                   cr_NAS_Indication(tsc_SHT_IntegrityProtected_Ciphered_NewSecurityContext, cr_508_SECURITY_MODE_COMPLETE)))
      -> value v_ReceivedAsp;
    
    v_NasCountUL := v_ReceivedAsp.Signalling.Nas[0].SecurityProtection.NasCount;
    return v_NasCountUL;
  };
  //----------------------------------------------------------------------------
  /*
  // @sic R5-102227
  * @desc      Activation of NAS security at SS and at the UE (24.301 cl. 5.4.3)
  *            to be used in TAU procedure after UE has reselected from Other RAT cell and Native security context is not available
  * @param     p_CellId
  * @param     p_SecurityParams
  * @param     p_SRB (optional)
  * @return    NasCount_Type
  * @status
  */
  function f_EUTRA_NAS_ActivateSecurity_MappedContext(CellId_Type p_CellId,
                                                      EUTRA_SecurityParams_Type p_SecurityParams,
                                                      SRB_Identity_Type p_SRB := tsc_SRB1) runs on EUTRA_PTC return NasCount_Type
  {
    var NasCount_Type v_NasCountUL;
    
    f_EUTRA_SS_NAS_ConfigureSecurity(p_SecurityParams.NAS_Integrity, p_SecurityParams.NAS_Ciphering); /* download security keys and reset NAS COUNT */
    
    v_NasCountUL := f_EUTRA_NAS_SecurityModeControl_MappedContext(p_CellId, p_SecurityParams, p_SRB);  // @sic R5-101147 sic@
    /* all NAS messages are ciphered from now on */
    return v_NasCountUL;
  };
  
}
