/******************************************************************************/
// @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: 2022-08-29 15:57:07 +0200 (Mon, 29 Aug 2022) $
// $Rev: 33841 $
/******************************************************************************/

module NAS_AuthenticationCommon {
  import from CommonDefs all;
  import from Parameters all;

  type record Common_AuthenticationParams_Type {       /* parameters related/used to/by EUTRA/UTRAN/GERAN authentication
                                                          @status    APPROVED (ENDC, IMS, IMS_IRAT, LTE, LTE_A_IRAT, LTE_A_PRO, LTE_A_R10_R11, LTE_A_R12, LTE_IRAT, MCX, NBIOT, NR5GC, NR5GC_IRAT, POS, SSNITZ, UTRAN) */
    B128_Type               RandValue,
    B128_Type               AUTN,
    B32_128_Type            XRES,
    B64_Type                KcGSM,
    B128_Type               Kc128,  // @sic R5s150121 sic@
    B3_Type                 KeySeq,
    B128_Type               CK,
    B128_Type               IK,
    integer                 XRESLength  optional  // @sic R5s120907 sic@
  };

  type enumerated AuthenticationError_Type {noError, macError, sqnFailure, macErrorPlusSepBit, sepBit0 };        /* @status    APPROVED (ENDC, IMS, IMS_IRAT, LTE, LTE_A_IRAT, LTE_A_PRO, LTE_A_R10_R11, LTE_A_R12, LTE_IRAT, MCX, NBIOT, NR5GC, NR5GC_IRAT, POS, SSNITZ, UTRAN) */

  const B32_Type  tsc_AuthUndefinedB32 := oct2bit ('FFFFFFFF'O);         /* @status    APPROVED (ENDC, IMS, IMS_IRAT, LTE, LTE_A_IRAT, LTE_A_PRO, LTE_A_R10_R11, LTE_A_R12, LTE_IRAT, MCX, NBIOT, NR5GC, NR5GC_IRAT, POS, SSNITZ, UTRAN) */
  const B128_Type tsc_AuthUndefinedB128 := tsc_AuthUndefinedB32 & tsc_AuthUndefinedB32 & tsc_AuthUndefinedB32 & tsc_AuthUndefinedB32;    /* @status    APPROVED (ENDC, IMS, IMS_IRAT, LTE, LTE_A_IRAT, LTE_A_PRO, LTE_A_R10_R11, LTE_A_R12, LTE_IRAT, MCX, NBIOT, NR5GC, NR5GC_IRAT, POS, SSNITZ, UTRAN) */
  const B256_Type tsc_AuthUndefinedB256 := tsc_AuthUndefinedB128 & tsc_AuthUndefinedB128;        /* @status    APPROVED (ENDC, IMS, IMS_IRAT, LTE, LTE_A_IRAT, LTE_A_PRO, LTE_A_R10_R11, LTE_A_R12, LTE_IRAT, NBIOT, NR5GC, NR5GC_IRAT, POS) */

  template (value) Common_AuthenticationParams_Type cs_CommonAuthParams_Init (template (value) B128_Type p_Rand) :=
  { /* @status    APPROVED (ENDC, IMS, IMS_IRAT, LTE, LTE_A_IRAT, LTE_A_PRO, LTE_A_R10_R11, LTE_A_R12, LTE_IRAT, MCX, NBIOT, NR5GC, NR5GC_IRAT, POS, SSNITZ, UTRAN) */
    RandValue          := p_Rand,
    AUTN               := tsc_AuthUndefinedB128,
    XRES               := tsc_AuthUndefinedB128,
    KcGSM              := tsc_AuthUndefinedB32 & tsc_AuthUndefinedB32,
    Kc128              := tsc_AuthUndefinedB128,   // @sic R5s150121 sic@
    KeySeq             := '111'B,
    CK                 := tsc_AuthUndefinedB128,
    IK                 := tsc_AuthUndefinedB128,
    XRESLength         := omit   // @sic R5s120907 sic@
  };

  //----------------------------------------------------------------------------
  /*
   * @desc      auxiliary function to increment KeySeq and deal with wrap around
   * @param     p_KeySeq
   * @return    B3_Type
   * @status    APPROVED (ENDC, IMS, IMS_IRAT, LTE, LTE_A_IRAT, LTE_A_PRO, LTE_A_R10_R11, LTE_A_R12, LTE_IRAT, MCX, NBIOT, NR5GC, NR5GC_IRAT, POS, SSNITZ, UTRAN)
   */
  function f_Authentication_IncrementKeySeq(B3_Type p_KeySeq) return B3_Type
  {
    var B3_Type v_KeySeq := int2bit(((bit2int(p_KeySeq)+1) mod 8), 3); //@sic R5s140298 R5s140503 sic@
    // The mod 8 is used as initial value is 7, hence results in first value of 0.
    // But if in a test case authentication is performed 7th time, it becomes 7 which is an undefined value, hence the check below added to avoid it.
    if (v_KeySeq == '111'B) {
      v_KeySeq := '000'B;
    }
    return v_KeySeq;
  }

  //----------------------------------------------------------------------------
  /*
   * @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
   * @param     p_AuthenticationError (default value: noError)
   * @return    Common_AuthenticationParams_Type
   * @status    APPROVED (ENDC, IMS, IMS_IRAT, LTE, LTE_A_IRAT, LTE_A_PRO, LTE_A_R10_R11, LTE_A_R12, LTE_IRAT, MCX, NBIOT, NR5GC, NR5GC_IRAT, POS, SSNITZ, UTRAN)
   */
  function f_AuthenticationInit(Common_AuthenticationParams_Type p_Auth_Params,
                                AuthenticationError_Type p_AuthenticationError := noError) return Common_AuthenticationParams_Type
  { /* @sic R5-123085 harmonisation of IMS (AuthenticationInit and the two error variants are needed for IMS too) sic@ */
    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;
    var B16_Type v_AuthAMF := px_AuthAMF;

    if (p_AuthenticationError == sqnFailure) {
      v_AuthAMF := '1111111111111111'B;      //AMF_resynch value
    }
    
    if (p_AuthenticationError == sepBit0) {
      v_AuthAMF := v_AuthAMF and4b '0111111111111111'B;      // separation bit forced to 0 (TC 9.1.2.7), the rest unchanged from PIXIT
    }

    if (p_AuthenticationError == macErrorPlusSepBit) { // @sic R5s130201 sic@
      v_AuthAMF := v_AuthAMF or4b '1000000000000000'B; // force the separation to 1, leave the rest unchanged
      v_AuthSQN := '111111111111111111111111111111111111111111111111'B;  // force SQN out of range
    }

    v_XDOut := v_Auth_Params.RandValue xor4b px_AuthK;
    v_CDOut := v_AuthSQN & v_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;

    if ((p_AuthenticationError == macError) or (p_AuthenticationError == macErrorPlusSepBit)) {  // @sic R5s130201 sic@
      v_MAC := not4b(v_MAC);  // @sic R5s110313 sic@
    }

    v_AUTN_2 := v_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;

    if (p_AuthenticationError == noError) {
      // ((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 := f_Authentication_IncrementKeySeq(v_Auth_Params.KeySeq);  // @sic R5s140894 sic@
      v_Auth_Params.Kc128 := substr (fx_KeyDerivationFunction ( tsc_KDF_HMAC_SHA_256, (v_Auth_Params.CK & v_Auth_Params.IK), '32'O ), 0, 128);  // @sic R5s150121, R5s150796 sic@
    }
    return v_Auth_Params;
  }
}
