module LIB_NG_NAS_Functions {

    // Lib_NG_NAS
    import from Lib_NG_NAS_Interface all;
    import from Lib_NG_NAS_Templates all;
    import from Lib_NG_NAS_Pixits all;

    // NG_NAS
    import from NG_NAS_TypeDefs all;
    import from NG_NAS_TestSystem all;

    // Lib3GPP
    import from CommonDefs all;
    import from NG_NAS_MsgContainers all;
    import from NAS_CommonTypeDefs all;
    import from NAS_CommonTemplates all;
    import from NG_NAS_Common all;
    import from NAS_AuthenticationCommon all;
    import from NG_NAS_SecurityFunctions all;

    // Lib_NGAP
    import from NGAP_IEs language "ASN.1:2002" all;
    import from NGAP_Constants language "ASN.1:2002" all;
    import from NGAP_PDU_Descriptions language "ASN.1:2002" all;
    import from LibNGAP_Pixits all;
    import from LibNGAP_Templates all;
    import from LibNGAP_Interface all;
    import from LibNGAP_Steps all;

    /**
     * @desc Function to send NGSetupRequest message and await for NGSetupResponse message
     */
    function f_send_NGSetupRequest_await_NGSetupRespone() runs on NGNASComponent {
        f_send_NGAP_PDU(
                        m_ngap_initMsg(
                                        m_n2_NGSetupRequest(
                                                            m_globalRANNodeID_globalGNB_ID(
                                                                                            m_ie_globalGnbId(
                                                                                            PX_PLMN_IDENTITY,
                                                                                            PX_GNB_ID,//'0000000000000000000000'B,//in template (value) bitstring p_gnbId,
                                                                                            -//in template (omit) GlobalGNB_ID.iE_Extensions p_iE_Extensions := omit
                                                            )),
                                                            {
                                                                m_supportedTAItem(
                                                                    PX_TACode,
                                                                    {
                                                                        m_ie_broadcastPLMNItem(
                                                                            PX_PLMN_IDENTITY,//m_ie_pLMNIdentity('00f110'O),
                                                                            {
                                                                                m_sliceSupportItem(
                                                                                        m_s_NSSAI(
                                                                                            PX_SST,
                                                                                            -,//      in template (omit) SD p_sD := omit,
                                                                                            -//      in template (omit) S_NSSAI.iE_Extensions p_iE_Extensions := omit
                                                                                        ),
                                                                                        -//in template (omit) SliceSupportItem.iE_Extensions p_iE_Extensions := omit
                                                                                    )
                                                                            }
                                                                        )
                                                                    },//in template (value) BroadcastPLMNList p_broadcastPLMNList,
                                                                    -//in template (omit) SupportedTAItem.iE_Extensions p_iE_Extensions := omit
                                                                )
                                                            },
                                                            PX_PAGING_DRX
        )));                    
        f_recv_NGAP_PDU(
                        mw_ngap_succMsg(
                                        mw_n2_NGSetupResponse(
                                                              mw_aMFName(PX_AMF_NAME),
                                                              {
                                                              *,
                                                              mw_servedGUAMIItem(
                                                                                  mw_gUAMI(
                                                                                          PX_PLMN_IDENTITY
                                                                                  )),
                                                              *
                                                              }
                        )));
    }

    /**
     * @desc Function to send NAS RegistrationRequest message
     * @see NG_NASTemplateFunctions.f_Check_NG_RegistrationReqMsg
     */
    function f_send_NasRegistrationRequest() runs on NGNASComponent {
        vt_NgNasUl_Msg := m_NG_REGISTRATION_REQUEST(
                                                    {'0'B,'001'B},
                                                    '111'B,
                                                    '0'B,
                                                    m_NG_MobileIdentitySUCI(PX_SUPI_FORMAT,PX_SUPI_DIGITS) // TODO Use PIXIT here?
                                                    );
        vc_sendNAS_PDU := bit2oct(encvalue(valueof(vt_NgNasUl_Msg)));
        f_send_NGAP_PDU(
                        m_ngap_initMsg(
                                        m_n2_UplinkNASTransport(
                                                                PX_AMF_UE_NGAP_ID,
                                                                PX_RAN_UE_NGAP_ID, 
                                                                vc_sendNAS_PDU,
                                                                m_uPTransportLayerInformation_userLocationInformationNR(
                                                                                                                        m_userLocationInformationNR(
                                                                                                                                                    m_nR_CGI,
                                                                                                                                                    m_tAI
                        )))));
    }

    /**
     * @desc Function to await NAS NGSetupResponse message
     */
    function f_await_registration_accept() runs on NGNASComponent {
        // Await request for REGISTRATION_ACCEPT
        f_recv_NGAP_PDU(
                        mw_ngap_initMsg(
                                        mw_n2_DownlinkNASTransport(
                                                                   PX_AMF_UE_NGAP_ID,
                                                                   PX_RAN_UE_NGAP_ID,
                                                                   ? // AuthorizationRequest
        )));
        f_NASPDU_Get(vc_recvNGAP_PDU);
        var NG_NAS_DL_Message_Type v_message;
        if (f_Check_5GAKA_NAS_DL_Message(
                                         vc_recvNAS_PDU, 
                                         mw_NG_REGISTRATION_ACCEPT(
                                                                   -,
                                                                   ?,
                                                                   mw_PLMN_List(
                                                                                -, -, 
                                                                                {
                                                                                 *,
                                                                                 PX_PLMN_IDENTITY,
                                                                                 *
                                                                                }
                                                                   ),
                                                                   -,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,
                                                                   ?
                                                                   ),
                                         v_message) == false) {
            setverdict(fail);
            return;
        }
    }

    /**
     * @desc Function to await NAS AuthorizationRequest message and send AuthorizationResponse message with security computation for future encryption
     */
    function f_await_NasAuthorizationRequest_send_NasAuthorizationResponse() runs on NGNASComponent {
        // Await request for authorization
        f_recv_NGAP_PDU(
                        mw_ngap_initMsg(
                                        mw_n2_DownlinkNASTransport(
                                                                   PX_AMF_UE_NGAP_ID,
                                                                   PX_RAN_UE_NGAP_ID,
                                                                   ? // AuthorizationRequest
        )));
        f_NASPDU_Get(vc_recvNGAP_PDU);
        var NG_NAS_DL_Message_Type v_message;
        if (f_Check_5GAKA_NAS_DL_Message(
                                         vc_recvNAS_PDU, 
                                         mw_NG_AUTHENTICATION_REQUEST(
                                                                     '100'B,
                                                                     -,
                                                                     mw_GMM_AuthRAND,
                                                                     mw_GSM_AUTN
                                                                     ),
                                         v_message
                                         ) == false) {
            setverdict(fail);
            return;
        }
        // Compute RES/XRES
        f_5g_aka_compute_res_xres(v_message.authentication_Request.rand.randValue, v_message.authentication_Request.autn.aUTN, v_message.authentication_Request.abba, PX_PLMN_IDENTITY, oct2hex(PX_SUPI_DIGITS), vc_ng_nas_security_params_type);
        // Send response
        vt_NgNasUl_Msg := m_NG_AUTHENTICATION_RESPONSE(
                                                        { iei := '2d'O, iel := '10'O, res := vc_ng_nas_security_params_type.AuthParams.XRES }
                                                        );
        vc_sendNAS_PDU := bit2oct(encvalue(valueof(vt_NgNasUl_Msg)));
        f_send_NGAP_PDU(
                        m_ngap_initMsg(
                                        m_n2_UplinkNASTransport(
                                                                PX_AMF_UE_NGAP_ID,
                                                                PX_RAN_UE_NGAP_ID,
                                                                vc_sendNAS_PDU,
                                                                m_uPTransportLayerInformation_userLocationInformationNR(
                                                                                                                        m_userLocationInformationNR(
                                                                                                                                                    m_nR_CGI,
                                                                                                                                                    m_tAI
                        )))));
    }

    /********************************************************************************
     *
     *  FIXME Use or enhance check functions from 3GPP library such as NG_NASTemplateFunctions.f_Check_NG_SecurityModeCompleteMsg
     *
     ********************************************************************************/

    function f_await_NasSecurityModeCommand() runs on NGNASComponent {
        // Await SecurityModeCommand
        f_recv_NGAP_PDU(
                        mw_ngap_initMsg(
                                        mw_n2_DownlinkNASTransport(
                                                                    PX_AMF_UE_NGAP_ID,
                                                                    PX_RAN_UE_NGAP_ID,
                                                                    ? // Security mode command
        )));
        f_NASPDU_Get(vc_recvNGAP_PDU);
        var NG_NAS_DL_Message_Type v_message;
        if (f_Check_5GAKA_NAS_DL_Message(
                                         vc_recvNAS_PDU, 
                                         mw_NG_SECURITY_MODE_COMMAND,
                                         v_message
                                         ) == false) {
            setverdict(fail);
            return;
        }
    }

    function f_await_NasSecurityModeCommand_send_NasSecurityModeComplete() runs on NGNASComponent {
        // Await SecurityModeCommand
        f_recv_NGAP_PDU(
                        mw_ngap_initMsg(
                                        mw_n2_DownlinkNASTransport(
                                                                    PX_AMF_UE_NGAP_ID,
                                                                    PX_RAN_UE_NGAP_ID,
                                                                    ? // Security mode command
        )));
        f_NASPDU_Get(vc_recvNGAP_PDU);
        var NG_NAS_DL_Message_Type v_message;
        if (f_Check_5GAKA_NAS_DL_Message(
                                         vc_recvNAS_PDU, 
                                         mw_NG_SECURITY_MODE_COMMAND,
                                         v_message
                                         ) == false) {
            setverdict(fail);
            return;
        }
        // Send SecurityModeComplete
        vt_NgNasUl_Msg := m_NG_SECURITY_MODE_COMPLETE(
                                                      m_NG_MobileIdentitySUCI(PX_SUPI_FORMAT, PX_SUPI_DIGITS), //in template (value) NG_MobileIdentity p_IMEISV,
                                                      { iei := omit, iel := int2oct(0, 2), replayedNASMsgContainerValue := ''O } /* 24.301 cl. 9.9.3.51 @sic R5s170597 Baseline Moving sic@ */, //in template (value) ReplayedNASMessageContainer p_NASMsg,
                                                      m_NG_MobileIdentitySUCI(PX_SUPI_FORMAT,PX_SUPI_DIGITS) //in template (value) NG_MobileIdentity p_NonIMEISV_PEI
                                                      );
        var octetstring os := ''O; // FIXME f_EncodeAndCipher_NG_NasPdu(vt_NgNasUl_Msg, ); How to calculate NG_NAS_MSG_Request_Type
        vt_NgNasUl_Msg.security_Protected_Nas_Message := m_NG_SECURITY_PROTECTED_NAS_MESSAGE(
                                                              tsc_EPD_GMM,
                                                              tsc_SpareHalfOctet,
                                                              tsc_SHT_IntegrityProtected_Ciphered,
                                                              '00000000'O, // FIXME How to calculate MessageAuthenticationCode?
                                                              '00'O,
                                                              os // Cyphered NAS message
                                                              )
        vc_sendNAS_PDU := bit2oct(encvalue(valueof(vt_NgNasUl_Msg)));
        f_send_NGAP_PDU(
                        m_ngap_initMsg(
                                        m_n2_UplinkNASTransport(
                                                                PX_AMF_UE_NGAP_ID,
                                                                PX_RAN_UE_NGAP_ID,
                                                                vc_sendNAS_PDU,
                                                                m_uPTransportLayerInformation_userLocationInformationNR(
                                                                                                                        m_userLocationInformationNR(
                                                                                                                                                    m_nR_CGI,
                                                                                                                                                    m_tAI
                        )))));
    }

    function f_terminate_NasRegistrationRequest_with_reject() runs on NGNASComponent {
        // Send error message
        vt_NgNasUl_Msg := m_NG_AUTHENTICATION_FAILURE(
                                                      m_GMM_GSM_Cause(
                                                                      -,
                                                                      tsc_EmmCause_IllegalMe // '00000110'B;
                                                      ));
        vc_sendNAS_PDU := bit2oct(encvalue(valueof(vt_NgNasUl_Msg)));
        f_send_NGAP_PDU(
                        m_ngap_initMsg(
                                        m_n2_UplinkNASTransport(
                                                                PX_AMF_UE_NGAP_ID,
                                                                PX_RAN_UE_NGAP_ID,
                                                                vc_sendNAS_PDU,
                                                                m_uPTransportLayerInformation_userLocationInformationNR(
                                                                                                                        m_userLocationInformationNR(
                                                                                                                                                    m_nR_CGI,
                                                                                                                                                    m_tAI
                        )))));
    }

    function f_send_NGUERadioCApabilityInfoIndication() runs on NGNASComponent {
        f_send_NGAP_PDU(
                        m_ngap_initMsg(
                                       m_n2_UERadioCapabilityInfoIndication(
                                                                            -, -,
                                                                            ''O // FIXME FSCOM How to deal with UERadioCapability
                        )));
    }

    function f_await_NGInitialContextSetupRequest_send_NGInitialContextSetupRespone() runs on NGNASComponent {
        f_recv_NGAP_PDU(
                        mw_ngap_initMsg(
                                        mw_n2_InitialContextSetupRequest_withPDUSessionList( // FIXME FSCOM How to deal with it?

                        )));
        f_send_NGAP_PDU(
                        m_ngap_succMsg(
                                       m_n2_InitialContextSetupResponse_noPDUSessionResourceSetupListSURes
                        ));
    }

    function f_NASPDU_Get(inout NGAP_PDU p_PDU) runs on NGNASComponent {

        vc_recvNGAP_PDU := p_PDU;

        if (ischosen(p_PDU.initiatingMessage)) {
            // Nothing to do
            if (match(vc_recvNGAP_PDU.initiatingMessage,mw_n2_NGSetupRequest_base)){
                var integer numOfIEs := lengthof(vc_recvNGAP_PDU.initiatingMessage.value_.NGSetupRequest.protocolIEs)
                //log("Number of IEs:",numOfIEs);
                for (var integer i:=0;i<numOfIEs;i:=i+1){
                    //log(vc_recvNGAP_PDUDefault.initiatingMessage.value_.NGSetupRequest.protocolIEs[i]);
                }
            }else if (match(vc_recvNGAP_PDU.initiatingMessage,mw_n2_initialUeMessage_base_nas)){
                var integer numOfIEs := lengthof(vc_recvNGAP_PDU.initiatingMessage.value_.InitialUEMessage.protocolIEs)
                
                for (var integer i:=0;i<numOfIEs;i:=i+1){
                
                    if ( (vc_recvNGAP_PDU.initiatingMessage.value_.InitialUEMessage.protocolIEs[i].id) == 38 /*id_NAS_PDU*/){
                        vc_recvNAS_PDU := vc_recvNGAP_PDU.initiatingMessage.value_.InitialUEMessage.protocolIEs[i].value_.nAS_PDU;
                        var bitstring bs := oct2bit(vc_recvNAS_PDU);
                        var integer v_result := decvalue(bs,vc_recvNgNasUl_Msg);
                        log("****************************************");
                        log("InitialUEMessage - NAS-PDU! ");
                        log( vc_recvNAS_PDU );
                        log( vc_recvNgNasUl_Msg );
                        log("****************************************");

                    }
                }
            }else if (match(vc_recvNGAP_PDU.initiatingMessage,mw_n2_DownlinkNASTransport_base_nas)){
                var integer numOfIEs := lengthof(vc_recvNGAP_PDU.initiatingMessage.value_.DownlinkNASTransport.protocolIEs)
                
                for (var integer i:=0;i<numOfIEs;i:=i+1){
                
                    if ( (vc_recvNGAP_PDU.initiatingMessage.value_.DownlinkNASTransport.protocolIEs[i].id) == 38 /*id_NAS_PDU*/){
                        vc_recvNAS_PDU := vc_recvNGAP_PDU.initiatingMessage.value_.DownlinkNASTransport.protocolIEs[i].value_.nAS_PDU;
                        var bitstring bs := oct2bit(vc_recvNAS_PDU);
                        var integer v_result := decvalue(bs,vc_recvNgNasDl_Msg);
                        log("****************************************");
                        log("DownlinkNASTransport - NAS-PDU! ");
                        log( vc_recvNAS_PDU );
                        log( vc_recvNgNasDl_Msg );
                        log("****************************************");

                    }
                }
            }else if (match(vc_recvNGAP_PDU.initiatingMessage,mw_n2_UplinkNASTransport_base_nas)){
                var integer numOfIEs := lengthof(vc_recvNGAP_PDU.initiatingMessage.value_.UplinkNASTransport.protocolIEs)
                
                for (var integer i:=0;i<numOfIEs;i:=i+1){
                
                    if ( (vc_recvNGAP_PDU.initiatingMessage.value_.UplinkNASTransport.protocolIEs[i].id) == 38 /*id_NAS_PDU*/){
                        vc_recvNAS_PDU := vc_recvNGAP_PDU.initiatingMessage.value_.UplinkNASTransport.protocolIEs[i].value_.nAS_PDU;
                        var bitstring bs := oct2bit(vc_recvNAS_PDU);
                        var integer v_result := decvalue(bs,vc_recvNgNasUl_Msg);
                        log("****************************************");
                        log("UplinkNASTransport - NAS-PDU! ");
                        log( vc_recvNAS_PDU );
                        log( vc_recvNgNasUl_Msg );
                        log("****************************************");

                    }
                }
            }
        }
        if (ischosen(p_PDU.successfulOutcome)) {
            // Nothing to do
        }
        if (ischosen(p_PDU.unsuccessfulOutcome)) {
                // Nothing to do
        }

    } // End of function f_NASPDU_Get

    function f_decode_5G_NAS_DL_Message(
                                        in NAS_PDU p_nas_pdu,
                                        out NG_NAS_DL_Message_Type p_NG_NAS_DL_Message_Type
                                        ) return boolean {
        log(">>> f_decode_5G_NAS_DL_Message: ", p_nas_pdu);

        var bitstring v_bs := oct2bit(p_nas_pdu);
        var NG_NAS_DL_Message_Type v_message;
        if (decvalue(v_bs, v_message) != 0) {
            log("f_decode_5G_NAS_DL_Message: decvalue failure");
            return false;
        }
        log("f_decode_5G_NAS_DL_Message: v_message: ", v_message);
        if (ischosen(v_message.security_Protected_Nas_Message)) {
            if (v_message.security_Protected_Nas_Message.securityHeaderType == '0100'B) {
                // TODO Cyphered message
                log("f_decode_5G_NAS_DL_Message: TODO Cyphered message");
                setverdict(inconc);
                return false;
            } else {
                v_bs := oct2bit(v_message.security_Protected_Nas_Message.plainNASMessage);
                if (decvalue(v_bs, v_message) != 0) {
                    log("f_decode_5G_NAS_DL_Message: decvalue failure");
                    return false;
                }
                log("f_decode_5G_NAS_DL_Message: v_message: ", v_message);
                if (not(match(v_message, p_NG_NAS_DL_Message_Type))) {
                    log(match(v_message, p_NG_NAS_DL_Message_Type));
                    return false;           
                }
            }
        }
        log("f_decode_5G_NAS_DL_Message: matching");

        return true;
    } // End of function f_decode_5G_NAS_DL_Message

    function f_Check_5GAKA_NAS_DL_Message(
                                          in NAS_PDU p_nas_pdu,
                                          in template NG_NAS_DL_Message_Type p_NG_NAS_DL_Message_Type,
                                          out NG_NAS_DL_Message_Type p_message
                                          ) return boolean {
        log(">>> f_Check_5GAKA_NAS_DL_Message: ", p_nas_pdu);

        var bitstring v_bs := oct2bit(p_nas_pdu);
        var NG_NAS_DL_Message_Type v_message;
        if (decvalue(v_bs, v_message) != 0) {
            log("f_Check_5GAKA_NAS_DL_Message: decvalue failure");
            return false;
        }
        log("f_Check_5GAKA_NAS_DL_Message: v_message: ", v_message);
        if (ischosen(v_message.security_Protected_Nas_Message)) {
            if (v_message.security_Protected_Nas_Message.securityHeaderType == '0010'B) {
                // TODO Cyphered message
                // Decipher NAS container
                // v_NonCleartextString := fx_NG_NasDeciphering(v_NonCleartextString,
                //                                 p_NasSecurityByRef.Ciphering.Algorithm,
                //                                 p_NasSecurityByRef.Ciphering.K_NAS,
                //                                 p_NasCount,
                //                                 p_NasSecurityByRef.BearerId);
                //v_message.security_Protected_Nas_Message.plainNASMessage := v_NonCleartextString;
                log("f_Check_5GAKA_NAS_DL_Message: TODO Cyphered message");
                setverdict(inconc);
                return false;
            }
            // Message was decrypted properly
            v_bs := oct2bit(v_message.security_Protected_Nas_Message.plainNASMessage);
            if (decvalue(v_bs, v_message) != 0) {
                log("f_Check_5GAKA_NAS_DL_Message: decvalue failure");
                return false;
            }
            log("f_Check_5GAKA_NAS_DL_Message: v_message: ", v_message);
        }
        if (not(match(v_message, p_NG_NAS_DL_Message_Type))) {
            log(match(v_message, p_NG_NAS_DL_Message_Type));
            return false;           
        }
        p_message := v_message;
        log("f_Check_5GAKA_NAS_DL_Message: matching: p_message: ", p_message);

        return true;
    } // End of function f_Check_5GAKA_NAS_DL_Message

    function f_Check_5GAKA_NAS_UL_Message(
                                          in NAS_PDU p_nas_pdu,
                                          in template NG_NAS_UL_Message_Type p_NG_NAS_UL_Message_Type
                                          ) return boolean {
        log(">>> f_Check_5GAKA_NAS_UL_Message: ", p_nas_pdu);

        var bitstring v_bs := oct2bit(p_nas_pdu);
        var NG_NAS_UL_Message_Type v_message;
        if (decvalue(v_bs, v_message) != 0) {
            log("f_Check_5GAKA_NAS_UL_Message: decvalue failure");
            return false;
        }
        log("f_Check_5GAKA_NAS_UL_Message: v_message: ", v_message);
        if (ischosen(v_message.security_Protected_Nas_Message)) {
            // TODO Decyphered message
            log("f_Check_5GAKA_NAS_UL_Message: TODO Decyphered message");
            setverdict(inconc);
            return false;
        } else {
            v_bs := oct2bit(v_message.security_Protected_Nas_Message.plainNASMessage);
            if (decvalue(v_bs, v_message) != 0) {
                log("f_Check_5GAKA_NAS_UL_Message: decvalue failure");
                return false;
            }
            log("f_Check_5GAKA_NAS_UL_Message: v_message: ", v_message);
            if (not(match(v_message, p_NG_NAS_UL_Message_Type))) {
                log(match(v_message, p_NG_NAS_UL_Message_Type));
                return false;           
            }
        }
        log("f_Check_5GAKA_NAS_UL_Message: matching");

        return true;
    } // End of function f_Check_5GAKA_NAS_UL_Message

    function f_Check_5GAKA_NAS_DL_Message_is_encrypted(in NAS_PDU p_nas_pdu) return boolean {
        log(">>> f_Check_5GAKA_NAS_DL_Message_is_encrypted: ", p_nas_pdu);

        var bitstring v_bs := oct2bit(p_nas_pdu);
        var NG_NAS_DL_Message_Type v_message;
        if (decvalue(v_bs, v_message) != 0) {
            log("f_Check_5GAKA_NAS_DL_Message_is_encrypted: decvalue failure");
            return false;
        }
        log("f_Check_5GAKA_NAS_DL_Message_is_encrypted: v_message: ", v_message);
        if (not ischosen(v_message.security_Protected_Nas_Message)) {
            return false
        }

        return true;
    } // End of function f_Check_5GAKA_NAS_DL_Message_is_encrypted

    function f_Check_5GAKA_NAS_UL_Message_is_encrypted(in NAS_PDU p_nas_pdu) return boolean {
        log(">>> f_Check_5GAKA_NAS_UL_Message_is_encrypted: ", p_nas_pdu);

        var bitstring v_bs := oct2bit(p_nas_pdu);
        var NG_NAS_UL_Message_Type v_message;
        if (decvalue(v_bs, v_message) != 0) {
            log("f_Check_5GAKA_NAS_UL_Message_is_encrypted: decvalue failure");
            return false;
        }
        log("f_Check_5GAKA_NAS_UL_Message_is_encrypted: v_message: ", v_message);
        if (not ischosen(v_message.security_Protected_Nas_Message)) {
            return false
        }

        return true;
    } // End of function f_Check_5GAKA_NAS_UL_Message_is_encrypted

    group fiveg_aka_security_network { // FIXME FSCOM To be re-organize later to create a security library

        /**
        * @desc Compute RES and XRES values based on rAND & AUTN received in the NG_AUTHENTICATION_REQUEST
        * @param [in] p_rand
        * @param [in] p_autn
        * @param [in] KeySeq
        * @param [in,out] p_ng_nas_security_params_type Built security parameters
        */
        function f_5g_aka_compute_res_xres(
                                           in B128_Type p_rand,
                                           in B128_Type p_autn,
                                           in ABBA p_abba,
                                           in NAS_PlmnId p_PLMN,
                                           in hexstring p_NID,
                                           inout NG_NAS_SecurityParams_Type p_ng_nas_security_params_type
                                           ) return boolean {
            log(">>> f_5g_aka_compute_res_xres: ", p_ng_nas_security_params_type);

            p_ng_nas_security_params_type := valueof(cs_NG_NAS_SecurityParamsInit);
            log("f_5g_aka_compute_res_xres: KDF=", p_ng_nas_security_params_type.KDF);
            p_ng_nas_security_params_type.ABBA := p_abba.abbaValue;
            p_ng_nas_security_params_type.AuthParams.RandValue := p_rand;
            p_ng_nas_security_params_type.AuthParams.AUTN := p_autn;

            log("f_5g_aka_compute_res_xres: PX_USIM_SECRET_KEY=", PX_USIM_SECRET_KEY);

            // Extract SQN from v_sqn_ak and XOR it with calculated MAC
            var B48_Type v_sqn_ak := substr(p_autn, 0, 48);
            var B16_Type v_amf := substr(p_autn, 48, 16);
            var B64_Type v_mac := substr(p_autn, 64, 64);
            log("f_5g_aka_compute_res_xres: v_sqn_ak=", v_sqn_ak);
            log("f_5g_aka_compute_res_xres: v_amf=", v_amf);
            log("f_5g_aka_compute_res_xres: v_mac=", v_mac);

            // Set OP
            f_set_op(PX_USIM_OPERATOR_VARIANT_ALGORITHM_CONFIGURATION);
            // Calculate RES, CK, IK and AK in one step
            var B128_Type v_ck;
            var B128_Type v_ik;
            var B48_Type v_ak;
            var B64_Type v_res;
            if (f_f2345(PX_USIM_SECRET_KEY, p_rand, v_res, v_ck, v_ik, v_ak) == -1) {
                return false;
            }
            p_ng_nas_security_params_type.AuthParams.CK := v_ck;
            log("f_5g_aka_compute_res_xres: CK=", p_ng_nas_security_params_type.AuthParams.CK);
            p_ng_nas_security_params_type.AuthParams.IK := v_ik;
            log("f_5g_aka_compute_res_xres: IK=", p_ng_nas_security_params_type.AuthParams.IK);
            p_ng_nas_security_params_type.Ks := p_ng_nas_security_params_type.AuthParams.CK & p_ng_nas_security_params_type.AuthParams.IK;
            log("f_5g_aka_compute_res_xres: Ks=", p_ng_nas_security_params_type.Ks);
            var B48_Type v_sqn := v_sqn_ak xor4b v_ak; 
            log("f_5g_aka_compute_res_xres: v_sqn=", v_sqn);

            // Verify that MAC was accepted
            var B64_Type v_mac_p;
            if (f_f1(PX_USIM_SECRET_KEY, p_rand, v_sqn, v_amf, v_mac_p) == -1) {
                return false;
            }
            log("f_5g_aka_compute_res_xres: v_mac_p=", v_mac_p);
            if (v_mac != v_mac_p) {
                return false;
            }

            // Compute XRES
            p_ng_nas_security_params_type.AuthParams.XRES := f_NG_Authentication_A4(p_PLMN,
                                                                                    p_ng_nas_security_params_type.AuthParams,
                                                                                    p_ng_nas_security_params_type.KDF,
                                                                                    p_ng_nas_security_params_type.Ks,
                                                                                    p_NID);
            p_ng_nas_security_params_type.AuthParams.XRESLength := lengthof(bit2oct(p_ng_nas_security_params_type.AuthParams.XRES));
            log("f_5g_aka_compute_res_xres: XRES=", p_ng_nas_security_params_type.AuthParams.XRES);
            log("f_5g_aka_compute_res_xres: XRESLength=", p_ng_nas_security_params_type.AuthParams.XRESLength);

            // Generate KAUSF
            p_ng_nas_security_params_type.KAUSF := f_NG_Authentication_A2(p_ng_nas_security_params_type.AuthParams,
                                                                          p_ng_nas_security_params_type.KDF,
                                                                          p_ng_nas_security_params_type.Ks,
                                                                          p_PLMN,
                                                                          p_NID);
            log("f_5g_aka_compute_res_xres: KAUSF=", p_ng_nas_security_params_type.KAUSF);

            // Generate KSEAF
            p_ng_nas_security_params_type.KSEAF := f_NG_Authentication_A6(p_PLMN,
                                                                          p_ng_nas_security_params_type.KAUSF,
                                                                          p_ng_nas_security_params_type.KDF,
                                                                          p_NID
                                                                          );
            log("f_5g_aka_compute_res_xres: KSEAF=", p_ng_nas_security_params_type.KSEAF);

            // TODO Generate KMAF
            // p_ng_nas_security_params_type.KAMF := f_NG_Authentication_A7(p_Identity,
            //                                                              p_ng_nas_security_params_type.KSEAF,
            //                                                              p_ng_nas_security_params_type.ABBA,
            //                                                              p_ng_nas_security_params_type.KDF
            //                                                              );
            //log("f_5g_aka_compute_res_xres: KAMF=", p_ng_nas_security_params_type.KAMF);

            // TODO Generate KMAF


            log("<<< f_5g_aka_compute_res_xres: ", p_ng_nas_security_params_type);
            return true;
        } // End of function f_5g_aka_compute_res_xres

        function f_set_op(in O16_Type p_op) {
            fx_set_op(p_op);
        }

        function f_f1(in B128_Type p_authK, in B128_Type p_rand, in B48_Type v_sqn, in B16_Type v_amf, out B64_Type v_mac_a) return integer {
            return fx_f1(p_authK, p_rand, v_sqn, v_amf, v_mac_a);
        }

        function f_f1star(in B128_Type p_authK, in B128_Type p_rand, in B48_Type v_sqn, in B16_Type v_amf, out B64_Type v_mac_s) return integer {
            return fx_f1star(p_authK, p_rand, v_sqn, v_amf, v_mac_s);
        }

        function f_f2345(in B128_Type p_authK, in B128_Type p_rand, out B64_Type v_res, out B128_Type v_ck, out B128_Type v_ik, out B48_Type v_ak) return integer {
            return fx_f2345(p_authK, p_rand, v_res, v_ik, v_ck, v_ak);
        }

        function f_f5star(in B128_Type p_authK, in B128_Type p_rand, out B48_Type p_ak) return integer {
            return fx_f5star(p_authK, p_rand, p_ak);
        }

        external function fx_set_op(in O16_Type p_op);
        external function fx_f1(in B128_Type p_authK, in B128_Type p_rand, in B48_Type v_sqn, in B16_Type v_amf, out B64_Type v_mac_a) return integer;
        external function fx_f1star(in B128_Type p_authK, in B128_Type p_rand, in B48_Type v_sqn, in B16_Type v_amf, out B64_Type v_mac_s) return integer;
        external function fx_f2345(in B128_Type p_authK, in B128_Type p_rand, out B64_Type v_res, out B128_Type v_ck, out B128_Type v_ik, out B48_Type v_ak) return integer;
        external function fx_f5star(in B128_Type p_authK, in B128_Type p_rand, out B48_Type p_ak) return integer;

    } // End of function fiveg_aka_security_network

} // End of module LIB_NG_NAS_Functions