/******************************************************************************/
// @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-03 20:04:51 +0200 (Fri, 03 Sep 2010) $
// $Rev: 3810 $
/******************************************************************************/

module NAS_AuxiliaryDefsAndFunctions {

  import from CommonDefs all;
  import from Parameters all;
  import from NAS_CommonTypeDefs all;
  import from NAS_CommonTemplates all;
  import from CommonIP all;

  const B32_Type  tsc_AuthUndefinedB32 := oct2bit ('FFFFFFFF'O);         /* @status    APPROVED */
  const B128_Type tsc_AuthUndefinedB128 := tsc_AuthUndefinedB32 & tsc_AuthUndefinedB32 & tsc_AuthUndefinedB32 & tsc_AuthUndefinedB32;    /* @status    APPROVED */
  const B256_Type tsc_AuthUndefinedB256 := tsc_AuthUndefinedB128 & tsc_AuthUndefinedB128;        /* @status    APPROVED */
  const O4_Type  tsc_IPv4Mask := 'FFFFFFFF'O;                            /* @status    APPROVED */
  const O16_Type  tsc_IPv6Mask := 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF'O;   /* @status    APPROVED */

  type record Common_AuthenticationParams_Type {       /* parameters related/used to/by EUTRA/UTRAN/GERAN authentication
                                                          @status    APPROVED */
    B128_Type               RandValue,
    B128_Type               AUTN,
    B32_128_Type            XRES,
    B64_Type                KcGSM,
    B3_Type                 KeySeq,
    B128_Type               CK,
    B128_Type               IK
  };

  //----------------------------------------------------------------------------
  template (value) Common_AuthenticationParams_Type cs_CommonAuthParams_Init (B128_Type p_Rand) :=
  { /* @status    APPROVED */
    RandValue          := p_Rand,
    AUTN               := tsc_AuthUndefinedB128,
    XRES               := tsc_AuthUndefinedB128,
    KcGSM              := tsc_AuthUndefinedB32 & tsc_AuthUndefinedB32,
    KeySeq             := '111'B,
    CK                 := tsc_AuthUndefinedB128,
    IK                 := tsc_AuthUndefinedB128
  };

  template (value) PacketFilterComponent cs_PktFilterCompSingleRemotePort(UInt16_Type p_PortNumber,
                                                                          HalfOctet_Type p_EPS_BearerId) :=
  { // 3 octets
    /* @status    APPROVED */
    id := '50'O, // Single remote port
    packetFilterComponentValue := {
      singleRemotePort := int2oct(p_PortNumber+(hex2int(p_EPS_BearerId)-6), 2)
    }
  };
  
  template (value) PacketFilterComponent cs_PktFilterCompSingleRemotePort31160 (HalfOctet_Type p_EPS_BearerId) :=
    /* @status    APPROVED */
    cs_PktFilterCompSingleRemotePort(31160, p_EPS_BearerId);
  
  template (value) PacketFilterComponent cs_PktFilterCompSingleRemotePort61000 (HalfOctet_Type p_EPS_BearerId) :=
    /* @status    APPROVED */
    cs_PktFilterCompSingleRemotePort(61000, p_EPS_BearerId);
  
  template (value) PacketFilterComponent cs_PktFilterCompProtocolIdNextHeader :=
  { // 2 octets
    /* @status    APPROVED */
    id := '30'O, // Protocol Identifier / Next header type
    packetFilterComponentValue := {
      protocolIdNextHeader := '11'O  // UDP
    }
  };
    
  template (value) PacketFilterComponent cs_PktFilterCompIPv4RemoteAddress :=
  { // 9 octets
    /* @status    APPROVED */
    id := '10'O, // IPv4 remote address
    packetFilterComponentValue := {
      ipv4RemoteAddress := f_Convert_IPv4Addr2OctString(px_IPv4_RemoteAddress) & tsc_IPv4Mask // R5s100189
    }
  };
    
  template (value) PacketFilterComponent cs_PktFilterCompIPv6RemoteAddress :=
  { // 33 octets
    /* @status    APPROVED */
    id := '20'O, // IPv6 remote address
    packetFilterComponentValue := {
      ipv6RemoteAddress := f_Convert_IPv6Addr2OctString(px_IPv6_RemoteAddress) & tsc_IPv6Mask // R5s100189
    }
  };

  //----------------------------------------------------------------------------
  /*
   * @desc      Get the Dedicated TFT according to 36.508 clause 6.6.2
   * @param     p_PacketFilterNumber ... is the reference packet filter in Table 6.6.2-2
   * @param     p_EPS_BearerId
   * @return    template (value) PacketFilter
   * @status    APPROVED
   */
  function f_Get_508_PacketFilter(integer p_PacketFilterNumber,
                                  HalfOctet_Type p_EPS_BearerId) return template (value) PacketFilter
  {
    var integer v_FilterCount := 0;
    var template (value) PacketFilter v_Filter;
    
    if (pc_IPv4){
      v_Filter.contents[0] := cs_PktFilterCompIPv4RemoteAddress;
      v_FilterCount := v_FilterCount + 9;
    }
    else { // IPv6
      v_Filter.contents[0] := cs_PktFilterCompIPv6RemoteAddress;
      v_FilterCount := v_FilterCount + 33;
    }
    
    select (p_PacketFilterNumber){
      case (1) {
        v_Filter.iei         := '10'O;
        v_Filter.precedence  := int2bit((hex2int(p_EPS_BearerId)-6), 8);
        v_Filter.contents[1] := cs_PktFilterCompSingleRemotePort31160(p_EPS_BearerId);
        v_FilterCount := v_FilterCount + 3;
        v_Filter.contents[2] := cs_PktFilterCompProtocolIdNextHeader;
        v_FilterCount := v_FilterCount + 2;
      }
      case (2) {
        // Update to values in Reference packet filter #2
        v_Filter.iei         :=  '21'O; // UL only filter, ID=1          @sic R5-104702 sic@
        v_Filter.precedence  := int2bit((hex2int(p_EPS_BearerId)-6+8), 8); // @sic R5-104702 sic@
        v_Filter.contents[1] := cs_PktFilterCompSingleRemotePort61000(p_EPS_BearerId);
        v_FilterCount := v_FilterCount + 3;
        v_Filter.contents[2] := cs_PktFilterCompProtocolIdNextHeader;
        v_FilterCount := v_FilterCount + 2
          }
      case (3){
        // Update to values in Reference packet filter #3
        v_Filter.iei         :=  '32'O; // Bidirectional filter, ID=2  @sic R5-104702 sic@
        v_Filter.precedence  := '00001111'B;
      }
    }
    v_Filter.iel := int2oct(v_FilterCount, 1);
    
    return v_Filter;
  }

  //----------------------------------------------------------------------------
  /*
   * @desc      Convert from IMSI or IMEI of type hexstring to octetstring (to be used in f_Imsi2MobileIdentity and f_Imei2MobileIdentity)
   * @param     p_IMSI
   * @return    octetstring
   * @status    APPROVED
   */
  function fl_ImsiImei2Octetstring(hexstring p_IMSI) return octetstring
  {
    var integer v_Length := lengthof(p_IMSI);
    var integer v_Odd := (v_Length rem 2);
    var octetstring v_Other := '00'O;
    var integer I;
    var integer K;
    
    if (v_Odd == 0) {                   // If length is even
      v_Length := v_Length + 1;
      p_IMSI := p_IMSI & 'F'H;          // add '1111' on to the end of the IMSI
    }
    
    // Reverse each pair of digits
    // First digit is not included as it is treated differently
    K:=0;
    for (I:=1; I < v_Length - 1; I:=I+2) {
      v_Other[K] := hex2oct(p_IMSI[I+1] & p_IMSI[I]);
      K := K + 1;
    }
    
    return v_Other;
  }
  
  //----------------------------------------------------------------------------
  /*
   * @desc      Convert from IMSI of type hexstring to NAS MobileIdentity
   * @param     p_IMSI
   * @return    template (value) MobileIdentity
   * @status    APPROVED
   */
  function f_Imsi2MobileIdentity(hexstring p_IMSI) return template (value) MobileIdentity
  {
    var integer v_ImsiLength := lengthof(p_IMSI);
    var integer v_Odd := (v_ImsiLength rem 2);
    var octetstring v_Other  := fl_ImsiImei2Octetstring (p_IMSI);  // @sic R5s100092 sic@
    var B4_Type v_FirstDigit := hex2bit(p_IMSI[0]);
    var B1_Type v_OddEvenInd := int2bit(v_Odd, 1);

    return cds_MobileIdentityImsi_lv(v_FirstDigit, v_OddEvenInd, v_Other);
  }
  
  //----------------------------------------------------------------------------
  /*
   * @desc      Convert from IMEI of type hexstring to NAS MobileIdentity
   * @param     p_IMEI
   * @return    template (present) MobileIdentity
   * @status    APPROVED
   */
  function f_Imei2MobileIdentity(hexstring p_IMEI) return template (present) MobileIdentity
  {
    var integer v_ImeiLength := lengthof(p_IMEI);
    var integer v_Odd := (v_ImeiLength rem 2);
    var octetstring v_Other  := fl_ImsiImei2Octetstring (p_IMEI);
    var B4_Type v_FirstDigit := hex2bit(p_IMEI[0]);
    var B1_Type v_OddEvenInd := int2bit(v_Odd, 1);

    return cr_MobileIdentityImei(v_FirstDigit, v_OddEvenInd, v_Other);
  }

  //----------------------------------------------------------------------------
  /*
   * @desc      Calculation is done according to 34.108, clause 8.1.2 and 33.102, clause 6.8.1.2
   *            Generation of Ck, Ik, AUTN and XRES
   * @param     p_Auth_Params
   * @return    Common_AuthenticationParams_Type
   * @status    APPROVED
   */
  function f_AuthenticationInit(Common_AuthenticationParams_Type p_Auth_Params) return Common_AuthenticationParams_Type
  {
    var Common_AuthenticationParams_Type v_Auth_Params := p_Auth_Params;
    var B128_Type v_XDOut;
    var B80_Type v_AUTN_2;
    var B64_Type v_CDOut;
    var B64_Type v_XDOut_Half;
    var B64_Type v_MAC;
    var B48_Type v_AK;
    var B48_Type v_AUTN_1;
    var B48_Type v_AuthSQN := '000000000000000000000000000000000000000000000000'B;  // This is supposed to be a TSV, but I don't think it was ever updated in TTCN2

    v_XDOut := v_Auth_Params.RandValue xor4b px_AuthK;
    v_CDOut := v_AuthSQN & px_AuthAMF;
    v_XDOut_Half := substr( v_XDOut, 0, 64);
    v_AK := substr( v_XDOut, 24, 48);
    v_AUTN_1 := v_AuthSQN xor4b v_AK;
    v_MAC := v_XDOut_Half xor4b v_CDOut;
    v_AUTN_2 := px_AuthAMF & v_MAC;
    v_Auth_Params.AUTN   := v_AUTN_1 & v_AUTN_2;
    // v_IKey := 128 bits of v_XDOut, wrapped, starting from offset 16
    v_Auth_Params.IK := substr( v_XDOut, 16, (128 - 16)) & substr( v_XDOut, 0, 16);
    // v_CKey := 128 bits of v_XDOut, wrapped, starting from offset 8
    v_Auth_Params.CK   := substr( v_XDOut, 8, (128 - 8)) & substr( v_XDOut, 0, 8);
    v_Auth_Params.XRES := v_XDOut;
    // ((CK1 XOR CK2) XOR (IK1 XOR IK2))
    v_Auth_Params.KcGSM := (substr( v_Auth_Params.CK, 0, 64) xor4b substr( v_Auth_Params.CK, 64, 64)) xor4b (substr( v_Auth_Params.IK, 0, 64) xor4b substr( v_Auth_Params.IK, 64, 64));
    v_Auth_Params.KeySeq := int2bit(((bit2int(v_Auth_Params.KeySeq)+1) mod 8), 3);

    return (v_Auth_Params);
  }
  //----------------------------------------------------------------------------
  
  function f_ConvertIA5toDefaultGsm7Bit(charstring p_Text) return octetstring
  { /* @desc  to convert a given IA5 string into GSM 7-bit codig acc. to TS 23.038 */
    var octetstring v_IA5Buffer := char2oct(p_Text);
    var octetstring v_Buffer;
    var B8_Type v_CharBits;
    var B8_Type v_Remainder;
    var integer v_BitPos := 0;
    var integer v_OctPos := 0;
    var integer i;

    for (i:=0; i<lengthof(p_Text); i := i+1) {
      v_CharBits := oct2bit(v_IA5Buffer[i] and4b '7f'O);

      if (v_BitPos == 0) {
        v_Remainder := v_CharBits;
      } else {
        v_Buffer[v_OctPos] := bit2oct((v_CharBits << v_BitPos) or4b v_Remainder);
        v_OctPos := v_OctPos + 1;
        v_Remainder := v_CharBits >> (8 - v_BitPos);
      }
      v_BitPos := (v_BitPos + 7) mod 8;
    }
    if (v_BitPos > 0) {
      v_Buffer[v_OctPos] := bit2oct(v_Remainder);
    }
    return v_Buffer;
  }

 
}
