/*
* Copyright (c) 2005-2010 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:   IKEv1/IPSEC SA negotiation 
*
*/

#include <x509cert.h>
#include <x500dn.h>
#include <random.h>
#include <vpnlogmessages.rsg>

#include "ikev1negotiation.h"
#include "ikedebug.h"
#include "ikev1isakmpstream.h"
#include "ikev1timeout.h"
#include "ikev1payload.h"
#include "ikev1plugin.h"
#include "ikev1crack.h"
#include "ikev1trans.h"
#include "ipsecsadata.h"
#include "ipsecsalifetime.h"
#include "pfkeymsg.h"
#include "kmdapi.h"
#include "ikev1pkiservice.h"
#include "ikev1crypto.h"
#include "ikev1natdiscovery.h"
#include "ikev1private.h"
#include "vpnapidefs.h"
#include "ikepkiutils.h"
#include "vpnclientuids.h"
#include "ikecalist.h"
#include "ikecaelem.h"
#include "ikecert.h"
#include "ikev1pluginsession.h"
#include "ikesocketdefs.h"
#include "kmdeventloggerif.h"
#include "pfkeyextdatautil.h"
#include "ipsecsalist.h"

const TInt KSubjectName              = 1;

/////////////////////////////////////////////////////////////////////////////////
// CIkev1Negotiation related stuff
/////////////////////////////////////////////////////////////////////////////////


//
//Proto negotiation to send a informational payload and be destroyed
//
CIkev1Negotiation* CIkev1Negotiation::NewL( CIkev1PluginSession* aPluginSession,
                                            CPFKeySocketIf& aPFKeySocketIf,
                                            MIkeDebug& aDebug,
                                            const TInetAddr& aRemote,
                                            const TCookie& aInitiator,
                                            const TCookie& aResponder )
    {   	
	CIkev1Negotiation* self = new ( ELeave ) CIkev1Negotiation( aPluginSession,
                                                                aPFKeySocketIf,
                                                                aDebug );
	CleanupStack::PushL( self );
	self->iRemoteAddr = aRemote;
	self->iCookie_I = aInitiator;
	self->iCookie_R = aResponder;     
	self->iExchange = ISAKMP_EXCHANGE_INFO;   
	self->iDOI = IPSEC_DOI;
	self->iTimer = CIkev1Timeout::NewL( *self );	
	CleanupStack::Pop( self );	
	return self;
    }

CIkev1Negotiation* CIkev1Negotiation::NewL( CIkev1PluginSession* aPluginSession,
                                            CPFKeySocketIf& aPFKeySocketIf,
                                            MIkeDebug& aDebug,
                                            const TInetAddr& aRemote,
                                            const TCookie& aInitiator,
                                            TBool aAutoLogin ) 
    {
	CIkev1Negotiation* self = new ( ELeave ) CIkev1Negotiation( aPluginSession,
                                                                aPFKeySocketIf,
	                                                            aDebug,
	                                                            aRemote,
	                                                            aInitiator );
	CleanupStack::PushL( self );
    self->ConstructL( aAutoLogin );
    CleanupStack::Pop( self );
	return self;
    }

CIkev1Negotiation* CIkev1Negotiation::NewL( CIkev1PluginSession* aPluginSession,
                                            CPFKeySocketIf& aPFKeySocketIf,
                                            MIkeDebug& aDebug,
                                            TIkev1SAData* aIkev1SAdata,
                                            TUint aRole,
                                            const TPfkeyMessage *aReq )
    {
    CIkev1Negotiation* self = new ( ELeave ) CIkev1Negotiation( aPluginSession,
                                                                aPFKeySocketIf,
                                                                aDebug );
    CleanupStack::PushL( self );
    self->ConstructL( aIkev1SAdata, aRole, aReq );
    CleanupStack::Pop( self );
    return self;
    }


CIkev1Negotiation* CIkev1Negotiation::NewL( CIkev1PluginSession* aPluginSession,
                                            CPFKeySocketIf& aPFKeySocketIf,
                                            MIkeDebug& aDebug,
                                            const TInetAddr& aRemote,
                                            TBool aAutoLogin ) 
    {
	CIkev1Negotiation* self = new ( ELeave ) CIkev1Negotiation( aPluginSession,
                                                                aPFKeySocketIf,
	                                                            aDebug,
	                                                            aRemote );
	CleanupStack::PushL( self );
    self->iCookie_I = self->CreateCookieL();
    self->ConstructL( aAutoLogin );
    CleanupStack::Pop( self );
	return self;
    }

CIkev1Negotiation* CIkev1Negotiation::NewL( CIkev1PluginSession* aPluginSession,
                                            CPFKeySocketIf& aPFKeySocketIf,
                                            MIkeDebug& aDebug,
                                            const TInetAddr& aRemote,
                                            const TPfkeyMessage& aReq )
    {    
    CIkev1Negotiation* self = new (  ELeave ) CIkev1Negotiation( aPluginSession,
                                                                 aPFKeySocketIf,
                                                                 aDebug,
                                                                 aRemote );
    CleanupStack::PushL( self );
    self->iCookie_I = self->CreateCookieL();
    self->ConstructL( aReq );
    CleanupStack::Pop( self );
    return self;
    }

//
// Destructor
//
CIkev1Negotiation::~CIkev1Negotiation()
    {
    DEBUG_LOG1( _L("CIkev1Negotiation::~CIkev1Negotiation this=0x%x"), this );
    
    
    if ( iRole == INITIATOR &&
         iAcquirePending )
        {
        // Response with error to pending Acquire.
        AcquireSAErrorResponse( KKmdIkeNegotFailed );
        }
    
    delete iIpsecSaSpiRetriever;
    delete iSavedIkeMsgBfr;
    
    if( iPluginSession )
        { 		
	    iPluginSession->RemoveNegotiation( this );  
        }
		
    iCookie_I.FillZ(ISAKMP_COOKIE_SIZE);
    
    if(iTimer)
        {
        iTimer->Cancel();
        delete iTimer;
        }

    delete iOwnCert;
    delete iPeerX509Cert;
    delete iPeerTrustedCA;  // Peer trusted CA name 
    delete iICA1;
    delete iICA2;

    delete iPkiService;     // Trusted CA certificate list

    delete iSAPayload;
    delete iPeerIdentPayload;
    delete iOwnIdentPayload;
    
    //Keys
    delete iOwnKeys;        //structure containing the public and private keys
    delete iOwnPublicKey;   //Public Key

    //Phase_I Proposal
    TAttrib *attr;
    while ((attr = iProposal_I.iAttrList) != NULL)
    {
        iProposal_I.iAttrList = attr->iNext;
        delete attr;
    }

    delete iChosenProposal_I.iAttrList; //Only one transformation chosen

    //Phase_II proposals (include transformations)
    delete iProposal_IIList;

    //Phase_II chosen Proposal
    delete iChosenProp_IIList;
    delete iInboundSPIList;
    delete iCRACKneg;
    delete iTransactionNeg; // Transaction exchange 
    delete iInternalAddr;
    delete iNatDiscovery;
	delete iSARekeyInfo;
	delete iLastMsg;

	delete iDialog;
    delete iDialogInfo;
}


//
// CIkev1Negotiation
//  Constructor for remote initiated negotiation
//
CIkev1Negotiation::CIkev1Negotiation( CIkev1PluginSession* aPluginSession,
                                      CPFKeySocketIf& aPFKeySocketIf,
                                      MIkeDebug& aDebug,
                                      const TInetAddr& aRemote,
                                      const TCookie& aInitiator )
 : iCookie_I( aInitiator ),
   iRemoteAddr( aRemote ),
   iPluginSession( aPluginSession ),
   iPFKeySocketIf( aPFKeySocketIf ),
   iDebug( aDebug )
{

    DEBUG_LOG1( _L("CIkev1Negotiation::CIkev1Negotiation, RESPONDER this=0x%x"), this );
    iCookie_R.FillZ(ISAKMP_COOKIE_SIZE);
    iSAId = iPluginSession->GetSAId();
    iPluginSession->LinkNegotiation(this);         
    iRole = RESPONDER;
    iStage = 1; //next phase for the responder is 2
}

//
//  Constructor for local initiated negotiation
//

CIkev1Negotiation::CIkev1Negotiation( CIkev1PluginSession* aPluginSession,
                                      CPFKeySocketIf& aPFKeySocketIf,
                                      MIkeDebug& aDebug,
                                      const TInetAddr& aRemote )
 : iRemoteAddr( aRemote ),
   iPluginSession( aPluginSession ),
   iPFKeySocketIf( aPFKeySocketIf ),
   iDebug( aDebug )
{
    DEBUG_LOG1( _L("CIkev1Negotiation::CIkev1Negotiation, INITIATOR this=0x%x"), this );
    iCookie_I.FillZ(ISAKMP_COOKIE_SIZE);
    iCookie_R.FillZ(ISAKMP_COOKIE_SIZE);
    iSAId = iPluginSession->GetSAId();
    iPluginSession->LinkNegotiation(this);     
    iRole = INITIATOR;
    iStage = 0;
}

//
//  Constructor for Phase II initiated negotiations
//

CIkev1Negotiation::CIkev1Negotiation( CIkev1PluginSession* aPluginSession,
                                      CPFKeySocketIf& aPFKeySocketIf,
                                      MIkeDebug& aDebug )
 : iPluginSession( aPluginSession ),
   iPFKeySocketIf( aPFKeySocketIf ),
   iDebug( aDebug )
{ 
    DEBUG_LOG1( _L("CIkev1Negotiation::CIkev1Negotiation, this=0x%x"), this );
    iCookie_I.FillZ(ISAKMP_COOKIE_SIZE);
    iCookie_R.FillZ(ISAKMP_COOKIE_SIZE);    
    iSAId = iPluginSession->GetSAId();
    iPluginSession->LinkNegotiation(this); 
}

//Used when creating a negotiation as a RESPONDER!
//Used also when Initiator (= Phase 1 negotiation stated by the policy activation)
//Leaves if error detected
void CIkev1Negotiation::ConstructL(TBool aAutoLogin)
{
    iIpsecSaSpiRetriever = CIpsecSaSpiRetriever::NewL( *this,
                                                       iPFKeySocketIf );

    iAutoLogin = aAutoLogin;
    CommonConstructL();
}


//Used when an Acquire is received
//Leaves if error detected
void CIkev1Negotiation::ConstructL(const TPfkeyMessage &aReq)
{
    iIpsecSaSpiRetriever = CIpsecSaSpiRetriever::NewL( *this,
                                                       iPFKeySocketIf );

    CommonConstructL();     
    GetAcquireDataL(aReq);
}

//Used when initiating a new Phase II negotiation from a negotiated ISAKMP SA.
void CIkev1Negotiation::ConstructL( TIkev1SAData* aSA,
                                    TUint aRole,
                                    const TPfkeyMessage *aReq )
{

    iIpsecSaSpiRetriever = CIpsecSaSpiRetriever::NewL( *this,
                                                       iPFKeySocketIf );

    iPhase = PHASE_II;

    //Almost like common construct
    iTimer = CIkev1Timeout::NewL(*this);    

    //Phase II proposal lists
    iProposal_IIList = new (ELeave) CProposal_IIList(1);

    iRemoteAddr.SetPort(IKE_PORT);

    iSeq = aSA->iSeq;
    iLocalAddr  = aSA->iLocalAddr;
    iRemoteAddr = aSA->iRemoteAddr;         // Remote Address from peer
    iLastRemoteAddr = aSA->iDestinAddr;     // Remote Address last transmitted
    
    iLastIKEMsgInfo = aSA->iLastIKEMsgInfo;
    iLastMsg = HBufC8::NewL(4096);
    if ( aSA->iLastMsg != NULL )
        {        
        *iLastMsg = *aSA->iLastMsg;        
        }
    
    iRole = aRole;          // If we are initiator or responder

    //Header Data
    iCookie_I = aSA->iCookie_I; // Initiator Cookie (Used with responder to create KEYID)
    iCookie_R = aSA->iCookie_R; // Responder Cookie
    iPrevExchange = aSA->iPrevExchange;
    iExchange = IKE_QUICK_MODE;

    iMessageId = RandomMessageId();

    iFlags = aSA->iFlags;           // Flags in the msg header
    
//  iNotifyMessageId = aSA->iHdr.iNotifyMessageId;   // Message Id. for Informational Exchanges

    //SA Data
    iDOI = IPSEC_DOI;
    
    iChosenProposal_I.iProtocol = PROTO_ISAKMP;
    iChosenProposal_I.iProposalNum = 0;
//    iChosenProposal_I.iSPI.Copy(aSA->iChosenProposal_I.iSPI);
    iChosenProposal_I.iNumTransforms = 1;
    iChosenProposal_I.iAttrList = new (ELeave) TAttrib; //allocated now and deleted when destroying the obj
//  *iChosenProposal_I.iAttrList = *aSA->iChosenProposal_I.iAttrList;
    iChosenProposal_I.iAttrList->iEncrAlg = aSA->iEncrAlg;
    iChosenProposal_I.iAttrList->iHashAlg = aSA->iHashAlg;
    iChosenProposal_I.iAttrList->iGroupDesc = aSA->iGroupDesc;
    iChosenProposal_I.iAttrList->iGroupType = aSA->iGroupType;
    iChosenProposal_I.iAttrList->iKeyLength = aSA->iKeyLength;  
    if ( aSA->iLifeTimeSecs )
       iChosenProposal_I.iAttrList->iLifeDurationSecs.Copy((TUint8 *)&aSA->iLifeTimeSecs, sizeof(TUint32)); 
    if ( aSA->iLifeTimeKB )
        iChosenProposal_I.iAttrList->iLifeDurationKBytes.Copy((TUint8 *)&aSA->iLifeTimeKB, sizeof(TUint32));    

 
    iHostData = aSA->iIkeData;
    if ( iHostData->iCAList && iHostData->iCAList->Count() )
    {
        iPkiService = CIkeV1PkiService::NewL(iHostData, iDebug);                                           
    }   
    
    if (aRole == INITIATOR)
    {
        GetAcquireDataL(*aReq); //Data needed from the acquire
    }
    else    //RESPONDER 
    {
        iAcquireSeq = 1;        //msg Seq. number
    }

    //
    //NAT Traversal data
    //
    iFamiliarPeer   = aSA->iFamiliarPeer;
    iNAT_T_Required = aSA->iNAT_T_Required;
    
    //
    //Copy an Internal address object (if exists) to the new negotiation 
    //
    if ( aSA->iVirtualIp )
    {
       iInternalAddr = CInternalAddress::NewL(*(aSA->iVirtualIp));    
    }

    iNAT_D_Flags  = aSA->iNAT_D_Flags;  
    if ( iNAT_D_Flags ) {
       //
       // Use IETF specified NAT traversal
       //
       iNatDiscovery = CIkev1NatDiscovery::NewL(aSA->iNAT_D_Flags);
    }
    
    //Keys  (DH Generated public value when own)
    iSKEYID.Copy(aSA->iSKEYID);
    iSKEYID_d.Copy(aSA->iSKEYID_d);
    iSKEYID_a.Copy(aSA->iSKEYID_a);
    iSKEYID_e.Copy(aSA->iSKEYID_e);
    
    //IV used by des_cbc and des3_cbc is 8 but digest returns 16 bytes for MDx and 20 for SHS (first 8 used)
    iIV = aSA->iIV;     //normal IV
    iIVSize = iIV.Length(); 

    iLastIV = aSA->iLastIV; //Saves the last IV of PHASE_I to compute iNotifIV everytime

    //Nonces
//    iNONCE_I = aSA->iNONCE_I;
//    iNONCE_R = aSA->iNONCE_R;
    //
    // If the ISAKMP SA (=aSA) has been originally negotiated due the policy activation (iAutoLogin = ETrue)
    // The iLocalAddr maybe then undefined in aSA.
    // If local end is acting as an initiator, method GetAcquireDataL updates iLocalAddr in CIkev1Negotiation object.
    // In that case take iLocalAddr value from there and store it to CIsakmpSa (aSA) object.
    // If local end is acting as a responder, resolve iLocalAddr value.
    //
    if ( aSA->iAutoLogin )
    {
        if ( aRole == INITIATOR)    
             iLocalAddr = aSA->iLocalAddr;
        else User::LeaveIfError( iPluginSession->GetLocalAddress( iLocalAddr ) );
    }
    iDPDSupported = aSA->iDPDSupported;
    
    iSAId = aSA->iSAId;    // Reference to existin ISAKMP SA
    iStage = 1; //if initiator the stage will be set in a subsequent call to InitPhase_IIL()
}

//Leaves if error detected
void CIkev1Negotiation::CommonConstructL()
{
    iPhase = PHASE_I;
    iTimer = CIkev1Timeout::NewL(*this);    

    //Phase II proposal lists
    iProposal_IIList = new (ELeave) CProposal_IIList(1);

    iRemoteAddr.SetPort(IKE_PORT);
    
    iHostData = &iPluginSession->IkeData();
    if (!iHostData)
    {
        DEBUG_LOG(_L("The host has no data. Negotiation aborted"));
        User::Leave(KKmdIkeNoPolicyErr);
    }   

	if ( iHostData->iCAList && iHostData->iCAList->Count() )
	{
		iPkiService = CIkeV1PkiService::NewL(iHostData, iDebug);
		TInt stat ( EFalse );
		stat=ReadCAsL(iHostData->iCAList);  // Build trusted CA list
		if ( !stat )
		{
			SetErrorStatus( KVpnErrInvalidCaCertFile );
			User::Leave(KVpnErrInvalidCaCertFile);
		}
	}	
    iSendCert = iHostData->iAlwaysSendCert; //If true will always be sent, otherwise only with a CR

    //Proposals
    if (!BuildProposals1L())
    {
        SetFinished();
        User::Leave(KKmdIkePolicyFileErr); //if any error returns
    }

    iEncoding = X509_CERT_SIG;  //Only type in use

    iChosenProposal_I.iAttrList = new (ELeave) TAttrib; //allocated now and deleted when destroying the obj   
    User::LeaveIfError( iPluginSession->GetLocalAddress( iLocalAddr ) );

    //PFKEY data (is overwritten if initiator because we use the data given in the acquire

    iAcquireSeq = 1;//msg Seq. number of acquire needed for the UPDATE
    iSeq = 1;       //Sequence number for pfkey messages

	iLastRemoteAddr = iRemoteAddr;   // Used as destination address when sending data
#ifdef _DEBUG	
	TBuf<40> txt_addr;
    iRemoteAddr.OutputWithScope(txt_addr);
#endif            
    
    iLastMsg = HBufC8::NewL(4096);
    
    DEBUG_LOG1( _L("New negotiation with Host %S"),
            &txt_addr );

    if ( !iHostData->iUseNatProbing && iHostData->iEspUdpPort == 0 )
	{
       //
       // Use IETF specified NAT traversal
       //
       iNatDiscovery = CIkev1NatDiscovery::NewL(0);   
    }
	
	if ( iRole == RESPONDER )
	{
	   //
	   // Get base value internal address (=VPN virtual IP)
	   //
	   iInternalAddr = iPluginSession->InternalAddressL();
	}	
}


TBool CIkev1Negotiation::ReadCAsL(CArrayFixFlat<TCertInfo*> *aCAList)
{

	TBool Status = iPkiService->ImportCACertsL(aCAList);

#ifdef _DEBUG	
	if ( !Status )
	    {
	    DEBUG_LOG( _L("Trusted CA list creation failed!") );
	    }
#endif // _DEBUG
	
	return Status;
			   
}

TBool CIkev1Negotiation::ReadOwnCertL()
{
    //
    // Read own certificate from PKI store using own trusted CA as
    // specified issuer
    //
    TBool Status = EFalse;
    _LIT(KVpnApplUid, "101F7993");
    if ( iPkiService && iHostData->iCAList )
		{
       TCertInfo* CertInfo;
	   HBufC8* CAName = HBufC8::NewLC(256);  	   
       TInt i = 0;
       if ( iHostData->iCAList->At(0)->iData!=KVpnApplUid )
          {
	       while ( i < iHostData->iCAList->Count() )
			   {

	           CertInfo = iHostData->iCAList->At(i);
	           CAName->Des().Copy(CertInfo->iData); // Assure that CA name is in ASCII format
               if ( iPkiService->ReadUserCertWithNameL(CAName->Des(), iHostData, EFalse) == KErrNone )
				   {
				   Status = ETrue;			   
				   delete iOwnCert;  // delete old if exists			   
				   iOwnCert = iPkiService->GetCertificate();
				   delete iPeerTrustedCA;  // for sure
				   iPeerTrustedCA = iPkiService->GetTrustedCA();
                   CleanupStack::PopAndDestroy(CAName);   //CAName
                   CAName=NULL;
				   break;
				   }
               i ++;           
               }
              if (!Status)
                  {
                  CleanupStack::PopAndDestroy(CAName);
                  CAName=NULL;
                  }
           }
       else
           {
           CIkeCaList* trustedCaList = iPkiService->CaList();
           CleanupStack::PopAndDestroy(CAName);        
           CAName=NULL;
           while ( i < trustedCaList->Count() )
               {
               
               CIkeCaElem* CaElem = (*trustedCaList)[i];                     
               CAName = IkeCert::GetCertificateFieldDERL(CaElem->Certificate(), KSubjectName);
               if (CAName == NULL)
                  {
                   User::Leave(KErrArgument);
                  }
               CleanupStack::PushL(CAName);
               if ( iPkiService->ReadUserCertWithNameL(*CAName, iHostData, ETrue) == KErrNone)
                   {
                   Status = ETrue;             
                   delete iOwnCert;  // delete old if exists               
                   iOwnCert = iPkiService->GetCertificate();
                   delete iPeerTrustedCA;
                   iPeerTrustedCA = iPkiService->GetTrustedCA();
                   CleanupStack::PopAndDestroy(CAName);        
                   CAName=NULL;
                   
                   
                   break;
                   }
               i ++;
               
               CleanupStack::PopAndDestroy(CAName);
               CAName=NULL;
               } 
           }
       if ( !Status )
		   {	   
          DEBUG_LOG( _L("Error loading Own Certificate!") );
		   }		   
    }
    return Status;
}



void CIkev1Negotiation::GetAcquireDataL(const TPfkeyMessage &aReq)
{
    // ACQUIRE Contains: <Base, Addr(Src & Dest) (Addr(Proxy)), (Ident(S & D)), (Sensitivity), Proposal>
    
    iLocalAddr = *aReq.iSrcAddr.iAddr;    //Copies our own address because it's the only way to know it

    //Phase II proposals
    //Only one combination received so only one transform
    CProposal_II *prop_II = new (ELeave) CProposal_II();
    CleanupStack::PushL(prop_II);
    prop_II->ConstructL();
    iProposal_IIList->AppendL(prop_II);
    CleanupStack::Pop();            //prop_II safe in case of leave

    prop_II->iProposalNum   = FIRST_IPSEC_PROPOSAL;
    prop_II->iNumTransforms = 1;
    prop_II->iReplayWindowLength = aReq.iProposal.iExt->sadb_prop_replay;
    TAttrib_II *attr_II = new (ELeave) TAttrib_II();
    CleanupStack::PushL(attr_II);
    prop_II->iAttrList->AppendL(attr_II);   //added into the proposal so it's safe if function leaves
    CleanupStack::Pop();            //attr_II safe in case of leave

    attr_II->iTransformNum = FIRST_IPSEC_TRANSFORM;

    if (aReq.iBase.iMsg->sadb_msg_satype==SADB_SATYPE_AH)
    {
        prop_II->iProtocol = PROTO_IPSEC_AH;
        if (aReq.iProposal.iComb->sadb_comb_auth == SADB_AALG_MD5HMAC)
        {
            attr_II->iTransformID = AH_MD5;
            attr_II->iAuthAlg = DOI_HMAC_MD5;
        }
        else if (aReq.iProposal.iComb->sadb_comb_auth == SADB_AALG_SHA1HMAC)
        {
            attr_II->iTransformID=AH_SHA;
            attr_II->iAuthAlg = DOI_HMAC_SHA;
        }
        else
        {
            DEBUG_LOG(_L("Unsupported Authentication Algorithm in Acquire"));
			SetFinished();					
            return;
        }
        // No auth with variable encryption
    }
    else if (aReq.iBase.iMsg->sadb_msg_satype == SADB_SATYPE_ESP)
    {
        prop_II->iProtocol=PROTO_IPSEC_ESP;
        /* Request ESP from peer host */

        attr_II->iTransformID = aReq.iProposal.iComb->sadb_comb_encrypt;
		switch ( attr_II->iTransformID )
		{
			case ESP_DES_CBC:
			case ESP_3DES_CBC:
			case ESP_NULL:
				break;
				
			case ESP_AES_CBC:
				attr_II->iKeyLength = aReq.iProposal.iComb->sadb_comb_encrypt_maxbits;
				break;

			default:
				DEBUG_LOG(_L("IPsec Encryption algorithm is not implemented. Wrong algorithms file"));
				SetFinished();
				return;
				
		}	
        if (aReq.iProposal.iComb->sadb_comb_auth != SADB_AALG_NONE)
        {
            if (aReq.iProposal.iComb->sadb_comb_auth == SADB_AALG_MD5HMAC)
                attr_II->iAuthAlg = DOI_HMAC_MD5;
            else if (aReq.iProposal.iComb->sadb_comb_auth == SADB_AALG_SHA1HMAC)
                attr_II->iAuthAlg = DOI_HMAC_SHA;
            else
            {
                DEBUG_LOG(_L("Unsupported Authentication Algorithm in Acquire"));
				SetFinished();
				return;
            }
        }
    }
    
    //Check if PFS in use...
    if (aReq.iProposal.iComb->sadb_comb_flags & SADB_SAFLAGS_PFS)
    {
        iPFS=ETrue;
        switch (iHostData->iGroupDesc_II)
        {
        case IKE_PARSER_MODP_768:
            attr_II->iGroupDesc = MODP_768;
            break;
        case IKE_PARSER_MODP_1024:
            attr_II->iGroupDesc = MODP_1024;
            break;
        case IKE_PARSER_MODP_1536:
            attr_II->iGroupDesc = MODP_1536;
            break;
        case IKE_PARSER_MODP_2048:
            attr_II->iGroupDesc = MODP_2048;
            break;
        default:
            DEBUG_LOG(_L("ISAKMP Proposals error (Bad Group description)"));
			SetFinished();
			return;
        }
    }
    else
    {
        iPFS=EFalse;
        attr_II->iGroupDesc = 0;    //No group assigned because no PFS
    }

    if (aReq.iProposal.iComb->sadb_comb_flags & SADB_SAFLAGS_TUNNEL)
    {
        attr_II->iEncMode = DOI_TUNNEL;
    }
    else
    {
        attr_II->iEncMode = DOI_TRANSPORT;
    }

    iIDLocalPort = (TUint16)aReq.iSrcAddr.iAddr->Port();
    iIDRemotePort = (TUint16)aReq.iDstAddr.iAddr->Port();
    iIDProtocol = aReq.iDstAddr.iExt->sadb_address_proto;   //May be needed for phase II ID

    //Source Identity
    if (aReq.iSrcIdent.iExt)
    {
        if (aReq.iSrcIdent.iExt->sadb_ident_type == SADB_IDENTTYPE_PREFIX)
        {
			if ( !ProcessIdentityData(aReq.iSrcIdent.iData, &iLocalIDType_II,
								      &iLocalAddr1_ID_II, &iLocalAddr2_ID_II) )
			{
				SetFinished();
				return;
			}	
		}
        else    //Invalid identity type
        {
            DEBUG_LOG1(_L("Invalid Local identity type (%d)"), aReq.iSrcIdent.iExt->sadb_ident_type);
			SetFinished();
			return;
        }
    }
    else
    {
        //We need to assign a default address if other info is to be sent in the same payload
        if (attr_II->iEncMode == DOI_TUNNEL || iIDLocalPort != 0 || iIDRemotePort != 0 ||
            iIDProtocol != 0 || aReq.iDstIdent.iExt)
        {
            DEBUG_LOG(_L("Local Identity not defined and needed. Using Own address as local identity."));
            iLocalAddr1_ID_II = iLocalAddr;
            if ((iLocalAddr.Family() == KAfInet) || iLocalAddr.IsV4Mapped() )
                iLocalIDType_II = ID_IPV4_ADDR;
            else
                iLocalIDType_II = ID_IPV6_ADDR;
            iDefaultLocalID = ETrue;    //Must be sent but won't be used when updating the SAD
        }
    }

    //Destination Identity
    if (aReq.iDstIdent.iExt)
    {
        if (aReq.iDstIdent.iExt->sadb_ident_type == SADB_IDENTTYPE_PREFIX)
        {
			if ( !ProcessIdentityData(aReq.iDstIdent.iData, &iRemoteIDType_II,
									  &iRemoteAddr1_ID_II, &iRemoteAddr2_ID_II) )
			{
				SetFinished();
				return;
			}
        }
        else    //Invalid identity type
        {
            
            DEBUG_LOG1( _L("Invalid Destination identity type (%d)"), aReq.iDstIdent.iExt->sadb_ident_type );
			SetFinished();
			return;
        }
    }
    else
    {
        //
        // RemoteID_II is required only if LocalID_II already exists
		// If transports mode
        //      Build RemoteID_II for Quick mode negotiation from specified remote IP address
        // else Use subnet 0/0 as remote id
		//
        if ( iLocalIDType_II != 0 )
        {
		   if (	attr_II->iEncMode == DOI_TUNNEL ) {
			  if ( iLocalIDType_II == ID_IPV4_ADDR || iLocalIDType_II == ID_IPV4_ADDR_SUBNET ) {
				 iRemoteAddr1_ID_II.Init(KAfInet);
				 iRemoteAddr2_ID_II.Init(KAfInet); 				 
				 iRemoteIDType_II = ID_IPV4_ADDR_SUBNET;
			  }	   
			  else {
			     iRemoteAddr1_ID_II.Init(KAfInet6);
				 iRemoteAddr2_ID_II.Init(KAfInet6); 				 				 
				 iRemoteIDType_II = ID_IPV6_ADDR_SUBNET;
			  }	 
		   }
		   else {
              iRemoteAddr1_ID_II = *aReq.iDstAddr.iAddr;
			  if ( iRemoteAddr1_ID_II.Family() == KAfInet6 )			  
				   iRemoteIDType_II = ID_IPV6_ADDR;
			  else iRemoteIDType_II = ID_IPV4_ADDR;
		   }   
           iDefaultRemoteID = ETrue;  //Must be sent but won't be used when updating the SAD   
        }  
    }

    SetPhase2LifeDurations( aReq.iProposal.iComb->sadb_comb_soft_addtime,
                            aReq.iProposal.iComb->sadb_comb_hard_addtime,
                            aReq.iProposal.iComb->sadb_comb_soft_bytes,
                            aReq.iProposal.iComb->sadb_comb_hard_bytes,
                            *attr_II,
                            EFalse );

    //Save some pf_key data to use later in PFKEY_UPDATE msg
    iAcquireSeq = aReq.iBase.iMsg->sadb_msg_seq;        //msg Seq. number
    iPfkeyAcquirePID = aReq.iBase.iMsg->sadb_msg_pid;   //msg PID.
    iAcquirePending = ETrue;
    DEBUG_LOG2( _L("Acq seq= %d , PID= %d"), iAcquireSeq, iPfkeyAcquirePID );
    
}

//
//  CIkev1Negotiation::ExecuteL()
//  An ISAKMP message has been received belonging to this negotiation.
//  Process the message and advance the negotiation session to appropriate
//  next state/stage.
//
TBool CIkev1Negotiation::ExecuteL( const ThdrISAKMP& aHdr,
                                   const TInetAddr& aRemote,
                                   TInt aLocalPort )
{
    aLocalPort = aLocalPort;

    TBool ret=EFalse;
    const ThdrISAKMP *hdr;
    TUint8 *msg = NULL;   //to place the new msg
    TBuf8<IKEV1_MAX_IV_SIZE> tmp_IV(iIV);   //Temporal IV. Used to update the real one if the msg OK
    
    TLastIKEMsg msg_info(aHdr); //For retransmitted IKE msg detection
    if ( IsRetransmit(msg_info) ) {
       DEBUG_LOG(_L("Retransmitted IKE message received."));       
       TBool FloatedPort = EFalse; 
       if ( iNAT_D_Flags & (REMOTE_END_NAT + LOCAL_END_NAT) )
           FloatedPort = ETrue;
       TPtr8 lastMsg(iLastMsg->Des());
       iPluginSession->SendIkeMsgL(lastMsg, iLastRemoteAddr, FloatedPort);        
       return EFalse;              
    }

    if ( iPhase == PHASE_II &&
         aHdr.GetExchange() != IKE_QUICK_MODE )
    {
        DEBUG_LOG(_L("Bad packet (retransmission?)"));
#ifdef _DEBUG                    
        const TPtrC8 ikeMsgPtr( (TUint8 *)&aHdr, (TUint16)aHdr.GetLength() );
        TInetAddr dstAddr;
        iPluginSession->GetLocalAddress( dstAddr );
        dstAddr.SetPort( aLocalPort );
        TRACE_MSG_IKEV1( ikeMsgPtr, iRemoteAddr, dstAddr );
#endif // _DEBUG                            

        SetFinished();
        return EFalse;  //Bad packet, is a retransmission
    }    
    
	iLastRemoteAddr = aRemote;  //Save last remote address (used in NAT cases)	

    iLengthLeft = aHdr.GetLength(); //Used to check the size in the payload are OK

    DEBUG_LOG2( _L("---------- Phase %d - Stage %d ----------"), iPhase, iStage );

    if ((iStage==1) && (iPhase==PHASE_I))   //Only saved for the first message
        iExchange = aHdr.GetExchange();

    DEBUG_LOG1( _L("Exchange %d"), aHdr.GetExchange() );

    if (aHdr.GetFlags() & ISAKMP_HDR_EFLAG) //if encrypted
    {
        //before anything, prints the packet
        DEBUG_LOG(_L("Received message (encr)."));
        DEBUG_LOG(_L("Decrypting..."));
        msg = new (ELeave)(TUint8[aHdr.GetLength()]);    //to place the new msg
		CleanupStack::PushL(msg);
		
        Mem::Copy(msg,(TUint8 *)&aHdr,sizeof(aHdr));    //The header is not encrypted

        if ((iPhase==PHASE_II) && (iStage == 1))
        {
            iMessageId = aHdr.GetMessageId();   //Saves the ID to compute IV
            DEBUG_LOG(_L("Quick IV:"));
            ComputeIVL(iIV, iMessageId);
        }

        DecryptL((TUint8 *)aHdr.Next(),&msg[sizeof(aHdr)], aHdr.GetLength()-sizeof(aHdr),iIV, iSKEYID_e, iChosenProposal_I.iAttrList->iEncrAlg);
        if ((iStage == 6 && iExchange == ISAKMP_EXCHANGE_ID) || 
            (iStage == 3 && iExchange == ISAKMP_EXCHANGE_AGGR))
        {
            iLastIV.Copy(iIV);  //Saves last IV in Phase 1
            DEBUG_LOG(_L("Last IV Saved!"));
        }
        hdr=(ThdrISAKMP *)msg;  //decrypted msg

    }
    else if (iFlags & ISAKMP_HDR_EFLAG)  // IKE message SHOULD be encrypted
    {
        hdr=&aHdr;  //no encryption    
        DEBUG_LOG(_L("Received message."));
#ifdef _DEBUG                    
        const TPtrC8 ikeMsgPtr( (TUint8 *)hdr, (TUint16)hdr->GetLength() );
        TInetAddr dstAddr;
        iPluginSession->GetLocalAddress( dstAddr );
        dstAddr.SetPort( aLocalPort );
        TRACE_MSG_IKEV1( ikeMsgPtr, iRemoteAddr, dstAddr );
#endif // _DEBUG                            
        DEBUG_LOG(_L("The message is NOT encrypted (ignored)"));
        return EFalse;
    }
    else
        hdr=&aHdr;  //no encryption

    DEBUG_LOG(_L("Received message."));
#ifdef _DEBUG                    
    const TPtrC8 ikeMsgPtr( (TUint8 *)hdr, (TUint16)hdr->GetLength() );
    TInetAddr dstAddr;
    iPluginSession->GetLocalAddress( dstAddr );
    dstAddr.SetPort( aLocalPort );
    TRACE_MSG_IKEV1( ikeMsgPtr, iRemoteAddr, dstAddr );
#endif // _DEBUG                            

    if (iPhase==PHASE_I)
        ret = Phase_IExchangeL(*hdr);//MAIN MODE && AGGRESSIVE MODE
    else
    {
        ret = Phase_IIExchangeL(*hdr);//QUICK MODE 
    }
    if (!ret)   //Incorrect packet. Restore the IV
    {
        DEBUG_LOG(_L("Restoring previous IV"));
        iIV.Copy(tmp_IV);
    }
    else    //correct packet
    {
        SaveRetransmitInfo(msg_info); // store new last received IKE message info        
    }

    if ( msg )    //If used erase it (when encryption)
 	   CleanupStack::PopAndDestroy(); 

    return ret;
}

TBool CIkev1Negotiation::ExecutePhase2L( const ThdrISAKMP &aHdr,
                                         const TInetAddr &aRemote,
                                         TInt aLocalPort )
{
    return ExecuteL( aHdr, aRemote, aLocalPort );
}

//
// CIkev1Negotiation::ExecuteTransactionL
// An ISAKMP Transaction exchange message received.
// Call CTransNegotiation::ExecuteL method and process returned status
//
TBool CIkev1Negotiation::ExecuteTransactionL( const ThdrISAKMP& aHdr,
                                              const TInetAddr& aRemote,
                                              TInt aLocalPort )
{
    TInt  status;
	TBool ret = ETrue;
    if ( iTransactionNeg ) {
       status = iTransactionNeg->ExecuteL( aHdr, aRemote, aLocalPort );
       if ( status == TRANSACTION_SUCCESS ) {
          //
          // XAUTH / CONFIG-MODE completed succesfully
          //
          IsakmpPhase1CompletedL(); 
       }
       else {
          if ( status == TRANSACTION_FAILED ) {
             //
             // XAUTH / CONFIG-MODE completed succesfully
             //
			 LOG_KMD_EVENT( MKmdEventLoggerIf::KLogError,
			                R_VPN_MSG_VPN_GW_AUTH_FAIL,
			                KKmdIkeAuthFailedErr,
			                iPluginSession->VpnIapId(),
			                &iRemoteAddr );
			 SendDeleteL( PROTO_ISAKMP );
			 SetErrorStatus( KKmdIkeAuthFailedErr );
          }
       }    
    }
	else ret = EFalse;

    return ret;
}


//
// CIkev1Negotiation::AuthDialogCompletedL
// Authentication dialog is completed. Check CAuthDialogInfo object ID
// and call eithet CIKECRACKNegotiation::ProcessUserResponseL or
// CTransNegotiation::ProcessUserResponseL to handle dialog data
//
void CIkev1Negotiation::AuthDialogCompletedL(CAuthDialogInfo *aUserInfo)
{
	if ( !aUserInfo || (!aUserInfo->iUsername && !aUserInfo->iSecret) ) 
	{
	   DEBUG_LOG(_L("Legacy authentication cancelled by user!"));		
	   SendDeleteL(PROTO_ISAKMP);		
	   LOG_KMD_EVENT( MKmdEventLoggerIf::KLogError,
	                  R_VPN_MSG_VPN_GW_AUTH_FAIL,
	                  KErrCancel,
	                  iPluginSession->VpnIapId(),
	                  &iRemoteAddr );
	   SetErrorStatus(KErrCancel);
	   return;
	}	
		
    TInt status;

    if ( aUserInfo->GetObjId() == XAUTH_DIALOG_ID ) 
    {
       if ( iTransactionNeg )
            status = iTransactionNeg->ProcessUserResponseL(aUserInfo);       
       else status = TRANSACTION_FAILED;
       
       if ( status == TRANSACTION_FAILED ) 
       {
         /*--------------------------------------------------------
          *
          *  XAUTH negotiation failed. Negotiation shall be deleted
          *
          *--------------------------------------------------------*/
		  LOG_KMD_EVENT( MKmdEventLoggerIf::KLogError,
		                 R_VPN_MSG_VPN_GW_AUTH_FAIL,
		                 status,
		                 iPluginSession->VpnIapId(),
		                 &iRemoteAddr );
		  SetErrorStatus(KKmdIkeAuthFailedErr);
          AcquireSAErrorResponse(KKmdIkeAuthFailedErr);
       }      
    }
    else 
    {
       if ( iCRACKneg )
       {
            status = iCRACKneg->ProcessUserResponseL(aUserInfo);       

           if ( status == CRACK_FAILED ) 
           {
             /*--------------------------------------------------------
              *
              *  Crack negotiation failed. Negotiation shall be deleted
              *
              *--------------------------------------------------------*/
              LOG_KMD_EVENT( MKmdEventLoggerIf::KLogError,
                             R_VPN_MSG_VPN_GW_AUTH_FAIL,
                             status,
                             iPluginSession->VpnIapId(),
                             &iRemoteAddr );		   
              SetErrorStatus(KKmdIkeAuthFailedErr);		  
              AcquireSAErrorResponse(KKmdIkeAuthFailedErr);
           }
       }
       else 
           status = ProcessUserResponseL(aUserInfo);;
           
    }
}


//
// CIkev1Negotiation::StartCRACKAuthL
//      Start CRACK authentication phase of IKE phase I negotiation
//      - Create CIKECRACKNegotiation object and call it`s ConstructL
//      - Set iStage variable to 7 to indicate that CRACK authentication
//        is going
//
TBool CIkev1Negotiation::StartCRACKAuthL()
{
TInt status;    

    iStage = 7;
    
    if ( !iCRACKneg ) {
       iCRACKneg = new(ELeave) CIKECRACKNegotiation( iDebug );
	   TBuf<2> DummyDomain;
	   //
	   // If the IKE Id-type value is configured to value "Opaque
	   // String" and iFQDN length is larger than zero in the current
	   // policy ==> iFQDN value contains "Group name" value
	   // which shall be conveyed to the peer in CRACK "Domain name"
	   // attribute  
	   //
       iFlags |= ISAKMP_HDR_EFLAG; //From now on encryption is used    
	   
	   if ( (iHostData->iIdType == ID_KEY_ID) && (iHostData->iFQDN.Length() > 0 ) )
            status = iCRACKneg->ConstructL(iHostData->iCRACKLAMType, this, iHostData->iFQDN);
	   else status = iCRACKneg->ConstructL(iHostData->iCRACKLAMType, this, DummyDomain);
       if ( status == CRACK_FAILED ) {
          return EFalse;
       }
    }
    
    return ETrue;
}

//
// CIkev1Negotiation::IsakmpPhase1CompletedL
// This method is called when an ISAKMP phase 1 negotiation is succesfully
// completed. The following actions are taken:
// -- If either Extended authentication or/and Config mode required
//    If iTransactionNeg data member exists it means that XAUTH/CONFIG mode
//    has been succesfully completed.
//    If iTransactionNeg data member is NULL, XAUTH/CONFIG mode shall be initiated
// -- If No XAUTH/CONFIG mode (or if XAUTH/CONFIG mode already completed).
//    If iAutoLogin is TRUE save ISAKMP SA and deconstruct negotiation.
//    If iAutoLogin is False, save ISAKMP SA and;
//       if negotiation role is initiator continue with Quick mode exchange. 
//       if negotiation role is responder deconstruct negotiation.
//
TBool CIkev1Negotiation::IsakmpPhase1CompletedL()
{
    TBool Status;

    if ( iTransactionNeg ) {
       //
       // Try to get Internal address information and delete iTransactionNeg
       //
       delete iInternalAddr;  
       iInternalAddr = iTransactionNeg->GetInternalAddr();
       delete iTransactionNeg;
       iTransactionNeg = NULL;
    }
    else {
       if ( (iRole == INITIATOR) && (iHostData->iUseXauth || iHostData->iUseCfgMode) )
	   {
		  TBool useModeCfg;
		  if ( iSARekeyInfo )
		       useModeCfg = EFalse;  // Use existing virtual Ip, if any
		  else 
		      useModeCfg = iHostData->iUseCfgMode; 
		  
          iTransactionNeg = CTransNegotiation::NewL( iHostData->iUseXauth,
                                                     useModeCfg,
                                                     iPluginSession,
                                                     this,
                                                     iDebug );
          
          // If only MODE_CFG is needed a request is sent
          if(useModeCfg && !iHostData->iUseXauth)
              iTransactionNeg->BuildConfigRequestL();
          
          iStage = 8;                    
          return ETrue; 
       }       
    }   

	SaveISAKMPSAL();

    if ( iAutoLogin ) {
       //
       // ISAKMP Phase 1 completed. Quick mode is NOT started because there is no acquire pending
       // but phase 1 negotiation is started by the policy activation.   
       //
       CInternalAddress* internalAddr = NULL;
       if ( iInternalAddr != NULL )
           {
           internalAddr = CInternalAddress::NewL(*iInternalAddr);        
           }
       iPluginSession->IkeSaCompleted(KErrNone, internalAddr);
	   SetFinished(); // Causes negotiation object destruction	   
	   iAutoLogin = EFalse; 	   
       Status     = EFalse;
    }
    else {
	   if ( iRole == INITIATOR ) {
	      TBool internalAddressChanged = EFalse;
		  if ( iInternalAddr ) {
		     //
		     // Report internal IP address changed event
		     //
	         internalAddressChanged = iPluginSession->InternalAddressChangedL(*iInternalAddr);
		  }
		  if ( (!iSARekeyInfo ||
		       iPhaseIIAfterIkeSaRekey) &&
		       !internalAddressChanged )
		  {	  
             iPhaseIIAfterIkeSaRekey = EFalse;
             iPrevExchange = iExchange;  //Needed to know how to begin Phase II (Sending or receiving)
		     iExchange  = IKE_QUICK_MODE;
		     iPhase     = PHASE_II;
		     iStage     = 1;
 	         Status     = ETrue;
             iMessageId = RandomMessageId(); 
             InitPhase2L();  //Immediately inits PHASE_II. No reply expected.
		  }
		  else {
			  //
			  // Rekeyed IKE SA or internal address changed. No IKE quick mode started
		      // Pending acquire will be failed, if internal address has changed.
			  //
			  SetFinished(); 
			  Status    = EFalse;
		  }	  
	   }
	   else {
	      SetFinished(); // Causes negotiation object destruction
		  Status    = EFalse;
	   }	   
    }
	
	LOG_KMD_EVENT( MKmdEventLoggerIf::KLogInfo,
	               R_VPN_MSG_VPN_GW_AUTH_OK,
	               KErrNone,
	               iPluginSession->VpnIapId(),
	               &iRemoteAddr );

	TInt NatStatus = iNAT_D_Flags;   // Record "IETF" NAT status  
	if ( NatStatus == 0 )
	{	
	   if ( iNAT_T_Required )
			NatStatus = 4;           // Local end is NAT:ted (discovered via "Nokia" NAT-T)    
	   else if ( !iHostData->iUseNatProbing )
		       NatStatus = iHostData->iEspUdpPort; // "Forced" ESP UDP encapsulation ?
	}

	TInetAddr localAddr;
	iPluginSession->GetLocalAddress( localAddr );
    LOG_KMD_EVENT2( MKmdEventLoggerIf::KLogInfo,
                    R_VPN_MSG_ADDR_INFO_FOR_VPN_AP,
                    NatStatus, iPluginSession->VpnIapId(),
                    (iInternalAddr ? &iInternalAddr->iClientIntAddr : NULL),
                    &localAddr );	

    return Status;
}   


//Sends the initial IKE packets to start the negotiation. PHASE I
void CIkev1Negotiation::InitNegotiationL()   //Equiv. to stage 1
{
    
    if (iProposal_I.iAttrList->iAuthMethod == IKE_A_CRACK &&
        !iHostData->iCRACKLAMUserName && 
        !iHostData->iCRACKLAMPassword &&
        !iCRACKLAMUserName && 
        !iCRACKLAMPassword)
        {
            
            iDialog     = CIkev1Dialog::NewL( iPluginSession, iPluginSession->DialogAnchor(), iDebug );
            iDialogInfo = new(ELeave) CAuthDialogInfo(iPluginSession, DIALOG_INFO_ID, SAId(), 0);
            iDialog->GetAsyncUNPWDialogL(iDialogInfo, (MIkeDialogComplete*)this);
            return;
    }
    TIkev1IsakmpStream* msg = SaveIkeMsgBfr( new (ELeave) TIkev1IsakmpStream(iDebug) );
	
    TInt  vendor_id_type;

    //Main mode stage 1

    switch (iHostData->iMode)
    {
        case IKE_PARSER_MAIN:
            iExchange = ISAKMP_EXCHANGE_ID;     // Identity Protection (Main mode in IKE)
            DEBUG_LOG(_L("IKE: Initiating negotiation (Main Mode)"));
            break;
        case IKE_PARSER_AGGRESSIVE:
            iExchange = ISAKMP_EXCHANGE_AGGR;   // Agressive
            DEBUG_LOG(_L("IKE: Initiating negotiation (Aggressive Mode)"));
            break;
        default:
            DEBUG_LOG1(_L("Bad Mode used (%d)"), iHostData->iMode);
            return;
    }
    iStage = 1;
    DEBUG_LOG2(_L("---------- Phase %d - Stage %d ----------"),iPhase, iStage);

    iCookie_R.FillZ(ISAKMP_COOKIE_SIZE);  //Set responder Cookie to 0

    iDOI = IPSEC_DOI;
    iEncoding = X509_CERT_SIG;  //Only cert Allowed
    msg->IsakmpInit(this);
    msg->IsakmpSa();

    const TUint8 *ptr = msg->iBuf.Ptr() + sizeof(ThdrISAKMP);
    const TSAISAKMP *sa = (TSAISAKMP*)ptr;
    //Generic payload NOT included
    iSAPayloadSize = sa->GetLength() - sizeof(TPayloadISAKMP);
    delete iSAPayload;
	iSAPayload = NULL;
    iSAPayload = new (ELeave) TUint8[iSAPayloadSize];   
    ptr += sizeof(TPayloadISAKMP);  
    Mem::Copy(iSAPayload, ptr,iSAPayloadSize);

    TBool cert_required = EFalse;   //If any proposal requires a cert to send a CR if needed
    TBool preshared_key = EFalse;   //Preshared key authentication        
    
    TAttrib *transf = iProposal_I.iAttrList;
    for (TInt i=0; (i < iProposal_I.iNumTransforms) && (!cert_required); i++)
    {
        switch (transf->iAuthMethod)
        {
        case RSA_SIG:   //Proposals involving certificates
        case DSS_SIG:
            cert_required = ETrue;
            break;
        case IKE_A_CRACK:           
            cert_required = ETrue;
            break;
        default:    // No cert involved
            preshared_key = ETrue;
            transf = transf->iNext;
        }
    }
    

    if (iExchange == ISAKMP_EXCHANGE_AGGR) //Aggressive contains more payloads
    {
        if ( preshared_key && !cert_required ) {
           //
           // Only pre-shared key authentication proposal(s) exists
           // Check if is necessary to ask user name/password (= IKE ID/preshared key)
           // from user
           //
           if ( CheckCredentials(iHostData) != KErrNone ) {
              DEBUG_LOG(_L("Failed to get credentials for Aggressive pre-shared auth!"));
              SetFinished();
              return;
           }
        }   
        ComputeNonceL();         //Nonce to be sent
        if ( cert_required && !iOwnCert ) 
           ReadOwnCertL();    // For possible DER ASN1 distuingish name Ident
        msg->IsakmpKeyL();
        msg->IsakmpNonce();
        msg->IsakmpOwnIdentL();
		
        //For aggressive mode we send a CR if a cert is going to be needed
        if ((!iPeerX509Cert) && (cert_required))
        {
           msg->IsakmpCertificateReqL();   
        }
        if ( iHostData->iUseNatProbing )
             vendor_id_type = EXPANDED_VENDOR_ID;
        else vendor_id_type = HASH_VENDOR_ID;           
        msg->IsakmpVendorId(vendor_id_type,
                            (TUint8*)iCookie_I.Ptr(),
                            (TUint8*)iCookie_R.Ptr(), iLocalAddr);
            
    }
	
	if ( iHostData->iDPDHeartBeat != 0 )
	   BuildDPDVendorId(*msg);
	
    if ( iNatDiscovery ) {
       iNatDiscovery->BuildNatVendorId(*msg);
       iNatDiscovery->BuildRfcNatVendorId(*msg);
    }
	
    if ( iHostData->iUseXauth || iHostData->iUseCfgMode ) {  
       CTransNegotiation::BuildXauthVendorId(*msg);  
    }

    SendL(*msg);
    iStage = 2;
}

//Builds the proposal list from the structure in the engine
TBool CIkev1Negotiation::BuildProposals1L()
{
    TProposalData *p_list = iHostData->iPropList;

    if (!p_list)
    {
        DEBUG_LOG(_L("ERROR: No proposals in the configuration file. Negotiation Cancelled"));
        return EFalse;
    }
    iProposal_I.iSPI.FillZ(4);  //filled with 0 (not send anyway)
    iProposal_I.iProposalNum   = FIRST_ISAKMP_PROPOSAL; 
    iProposal_I.iProtocol      = PROTO_ISAKMP;
    iProposal_I.iNumTransforms = 0;
    
    TUint8 trans_num = FIRST_ISAKMP_TRANSFORM;

    TAttrib *prev=NULL,*attrlist=NULL;
    
    while ( p_list )
    {
        iProposal_I.iNumTransforms ++;
        attrlist = new (ELeave) TAttrib;
        if (!iProposal_I.iAttrList) //First transform
            iProposal_I.iAttrList=attrlist; //attrlist safe
        if (prev)
            prev->iNext = attrlist;
        prev = attrlist;
        
        attrlist->iTransformID  = KEY_IKE; //only one allowed by Protocol ISAKMP (KEY_IKE)
        attrlist->iTransformNum = trans_num;
        trans_num++;        
        switch (p_list->iEncrAlg)
        {
        case IKE_PARSER_DES_CBC:
            attrlist->iEncrAlg=DES_CBC;     //DES_CBC
            break;
        case IKE_PARSER_DES3_CBC:
            attrlist->iEncrAlg=DES3_CBC;    //DES3_CBC
            break;
        case IKE_PARSER_AES_CBC:
            attrlist->iEncrAlg   = AES_CBC;     //AES_CBC
            attrlist->iKeyLength = (TUint16)p_list->iEncrKeyLth;
            if (!attrlist->iKeyLength)
               attrlist->iKeyLength = 128; //default AES key size   
            break;
        default:
            DEBUG_LOG(_L("ISAKMP Proposals error (Bad Encryption algorithm)"));
            return EFalse;
        }
        switch (p_list->iHashAlg)
        {
        case IKE_PARSER_MD5:
            attrlist->iHashAlg=HASH_MD5;    //HASH_MD5
            break;
        case IKE_PARSER_SHA1:
            attrlist->iHashAlg=HASH_SHA1;   //HASH_SHA1
            break;
        default:
            DEBUG_LOG(_L("ISAKMP Proposals error (Bad Hash algorithm)"));
            return EFalse;
        }
        switch (p_list->iAuthMeth)
        {
        case IKE_PARSER_RSA_SIG:
            attrlist->iAuthMethod=RSA_SIG;
            break;
        case IKE_PARSER_DSS_SIG:
            attrlist->iAuthMethod=DSS_SIG;
            break;
        case IKE_PARSER_PRE_SHARED:
            attrlist->iAuthMethod=PRE_SHARED;           
            break;
        case IKE_PARSER_CRACK:
            attrlist->iAuthMethod=IKE_A_CRACK; //CRACK authentication
            break;
            
        default:
            DEBUG_LOG(_L("ISAKMP Proposals error (Bad Authentication Method)"));
            return EFalse;
        }
        
        switch (p_list->iGroupDesc)
        {
        case IKE_PARSER_MODP_768:
            attrlist->iGroupDesc = MODP_768;
            break;
        case IKE_PARSER_MODP_1024:
            attrlist->iGroupDesc = MODP_1024;
            break;
        case IKE_PARSER_MODP_1536:
            attrlist->iGroupDesc = MODP_1536;
            break;
        case IKE_PARSER_MODP_2048:
            attrlist->iGroupDesc = MODP_2048;
            break;
        default:
            DEBUG_LOG(_L("ISAKMP Proposals error (Bad Group description)"));
            return EFalse;      
        }

        switch (p_list->iGroupType)
        {
        case IKE_PARSER_MODP:
            attrlist->iGroupType = MODP;
            break;
        case IKE_PARSER_DEFAULT:
            break;
        default:
            DEBUG_LOG(_L("ISAKMP Proposals error (Bad Group Type)"));
            return EFalse;
        }

        switch (p_list->iPRF)
        {
        case IKE_PARSER_DES3_CBC_MAC:
            attrlist->iPRF=OAKLEY_PRF_3DES_CBC_MAC;
            break;
        case IKE_PARSER_NONE:
            break;
        default:
            DEBUG_LOG(_L("ISAKMP Proposals error (Bad PRF specified)"));
            return EFalse;
        }

        TUint32 lifetime = ByteOrder::Swap32(p_list->iLifetimeSec);
        if (lifetime)
            attrlist->iLifeDurationSecs.Copy((TUint8 *)&lifetime, sizeof(lifetime));

        lifetime = ByteOrder::Swap32(p_list->iLifetimeKb);
        if (lifetime)
            attrlist->iLifeDurationKBytes.Copy((TUint8 *)&lifetime, sizeof(lifetime));

        //
        // Store parameters for extended authentication
        //
        attrlist->iXauthUsed = iHostData->iUseXauth;
        attrlist->iRole      = iRole;
        
        p_list = p_list->iNext;
        
    }
    attrlist->iNext=NULL;   //Last transform


    return ETrue;
}

//Builds Phase_II proposals from the config. file to when acting as RESPONDER to see if 
//proposals received are acceptable.
TInt CIkev1Negotiation::BuildProposals2L()
{
    CIpsecSaSpecList* SaList = NULL;
    TInetAddr empty_addr;  //empty address
    TInt err = KErrNone;
    
    TRAP(err,
        if ( iLocalIDType_II == 0 )  //Local ID not received (so remote neither) so it's the same as gateway
            {
            TInetAddr localSelector(iLocalAddr);
            localSelector.SetPort(iIDLocalPort);
            TInetAddr remoteSelector(iRemoteAddr);
            remoteSelector.SetPort(iIDRemotePort);
            
            SaList = iPluginSession->GetIpseSaSpecListLC(localSelector, empty_addr, 
                                                         remoteSelector, empty_addr,
                                                         iIDProtocol );
            }
        else    // either none or both (RFC 2409 5.5 so we must have both)
            {
            TInetAddr localSelector(iLocalAddr1_ID_II);
            localSelector.SetPort(iIDLocalPort);
            TInetAddr remoteSelector(iRemoteAddr1_ID_II);
            remoteSelector.SetPort(iIDRemotePort);
    
            
            SaList = iPluginSession->GetIpseSaSpecListLC(localSelector,
                                                         iLocalAddr2_ID_II,
                                                         remoteSelector,
                                                         iRemoteAddr2_ID_II,
                                                         iIDProtocol );
            }
        
        CleanupStack::Pop(SaList);
    );
    
    if (err != KErrNone)
        return err;
    
    CleanupStack::PushL(SaList);

    //Phase II proposals

    CProposal_II *prop = NULL;
    TAttrib_II *attr_II = NULL;
    const TIpsecSaSpec *spec = NULL;
    TInt count = SaList->Count();
    TInt i     = 0;

    while ( i < count ) 
    {
        prop = new (ELeave) CProposal_II();
        CleanupStack::PushL(prop);
        prop->ConstructL(1);
        iProposal_IIList->AppendL(prop);
        CleanupStack::Pop();    //prop safe
        //
        // Only 1 proposal which may be AND'd (many prop with same num) if many bundles
        // Only 1 transform because no OR'ing implemented in IPSEC
        //
        prop->iProposalNum   = FIRST_IPSEC_PROPOSAL;
        prop->iNumTransforms = 1;  

        attr_II = new (ELeave) TAttrib_II();
        CleanupStack::PushL(attr_II);
        prop->iAttrList->AppendL(attr_II);
        CleanupStack::Pop();    //attr_II safe
        
        attr_II->iTransformNum = FIRST_IPSEC_TRANSFORM;     

        spec = &(SaList->At(i));
        
        if (spec->iType == SADB_SATYPE_AH)
        {
            prop->iProtocol=PROTO_IPSEC_AH;
            if (spec->iAalg == SADB_AALG_MD5HMAC)
            {
                attr_II->iTransformID = AH_MD5;
                attr_II->iAuthAlg = DOI_HMAC_MD5;
            }
            else if (spec->iAalg == SADB_AALG_SHA1HMAC)
            {
                attr_II->iTransformID=AH_SHA;
                attr_II->iAuthAlg = DOI_HMAC_SHA;
            }
            else
            {
                DEBUG_LOG(_L("Unsupported Authentication Algorithm in IPsec Policy"));
                err = KKmdIkeNoProposalErr;
                break;
            }
            // No auth with variable encryption
        }
        else if (spec->iType == SADB_SATYPE_ESP)
        {
            prop->iProtocol = PROTO_IPSEC_ESP;
            // Request ESP from peer host

            attr_II->iTransformID = spec->iEalg;
			switch ( attr_II->iTransformID )
			{
				case ESP_DES_CBC:
				case ESP_3DES_CBC:
				case ESP_NULL:
					break;

				case ESP_AES_CBC:
					attr_II->iKeyLength = spec->iEalgLen;   //If 0 it won't be sent
					break;

				default:
					DEBUG_LOG(_L("IPsec Encryption algorithm is not implemented. Wrong algorithms file"));
	                err = KKmdIkeNoProposalErr;
	                break;
			}	
			
			if (err != KErrNone)
			    {
			    break;
			    }
			
            if (spec->iAalg != SADB_AALG_NONE)
            {
                if (spec->iAalg == SADB_AALG_MD5HMAC)
                    attr_II->iAuthAlg = DOI_HMAC_MD5;
                else if (spec->iAalg == SADB_AALG_SHA1HMAC)
                    attr_II->iAuthAlg = DOI_HMAC_SHA;
                else
                {
                    DEBUG_LOG(_L("Unsupported Authentication Algorithm in IPsec Policy"));
                    err = KKmdIkeNoProposalErr;
                    break;
                }
            }
        }

        //Check if PFS in use...
        iPFS = spec->iPfs;

        if (spec->iTransportMode)
             attr_II->iEncMode = DOI_TRANSPORT;
        else attr_II->iEncMode = DOI_TUNNEL;

        //////////////////////////////////////////////////////////
        //
        // Check if remote identity defined in Security Policy
        // If it is not set iDefaultLocalID = ETrue.
        // This prevents destination identity information to IPSEC
        // in PFKEY Update- and Add primitives
        // (see UpdateSADatabaseL() method)
        //
        //////////////////////////////////////////////////////////
        if ( spec->iRemoteIdentity.Length() == 0 )
           iDefaultLocalID = ETrue;
		if ( !ExamineRemoteIdentity(spec->iRemoteIdentity) )
		{
			DEBUG_LOG(_L("Remote Identity mismatch with IPsec Policy"));
            err = KKmdIkeNoProposalErr;
            break;
		}
		
	    SetPhase2LifeDurations( spec->iSoft.iAddTime,
                                spec->iHard.iAddTime,
                                spec->iSoft.iBytes,
                                spec->iHard.iBytes,
	                            *attr_II,
	                            ETrue );

	    if (iPFS)
        {
            switch (iHostData->iGroupDesc_II)
            {
            case IKE_PARSER_MODP_768:
                attr_II->iGroupDesc = MODP_768;
                break;
            case IKE_PARSER_MODP_1024:
                attr_II->iGroupDesc = MODP_1024;
                break;
            case IKE_PARSER_MODP_1536:
                attr_II->iGroupDesc = MODP_1536;
                break;
            case IKE_PARSER_MODP_2048:
                attr_II->iGroupDesc = MODP_2048;
                break;
            default:    //Shouldn't happen but the error will be detected later
                err = KKmdIkeNoProposalErr;
                break;
            }
            
            if (err != KErrNone)
                {
                break;
                }            
        }
        
        prop->iReplayWindowLength = spec->iReplayWindowLength;

        i ++;
    }   //while

    if (err != KErrNone)
        {
        delete iProposal_IIList;
        iProposal_IIList = NULL;        
        }
    
    CleanupStack::PopAndDestroy(SaList);  //SAList
    
    return err;
}

TBool CIkev1Negotiation::ExamineRemoteIdentity(const TDesC8& aRemoteIdInPolicy)
{
   //////////////////////////////////////////////////////////////////
   //
   // This method is called when we are acting as a Quick mode responder.   
   // The purpose of this method is compare remote Identity information
   // received in Quick Mode IKE message (=IDi) to the remote Identity
   // information in the local Ipsec Policy.
   // If Identitiea are NOT matching the FALSE response is returned.
   // There is the following special "kludge" is done in these test:
   // Identity IPv4 address and IPv4 subnet with prefix 32 are
   // interpreted to be same as well as IPv6 address and IPv6 subnet
   // with prefix 128. When this situation occurs a special flag 
   // (iSwapRemoteIdType = ETrue) is set in CIkev1Negotiation object
   //
   //////////////////////////////////////////////////////////////////
   TBool      AddrMatch;   
   TInetAddr  IpAddr;
   TInetAddr  IpMask;
   TUint8     IdType;
   TInt       PrefixLength = 0;
   TInetAddr* PrefixPtr    = NULL;      
   
   if ( !ProcessIdentityData(aRemoteIdInPolicy, &IdType, &IpAddr, &IpMask) )
	  return EFalse;
   
   AddrMatch = IpAddr.Match(iRemoteAddr1_ID_II);

   if ( AddrMatch )
   {	   
      if ( (IdType == ID_IPV4_ADDR_SUBNET ) || (IdType == ID_IPV6_ADDR_SUBNET) ) 
      {
		 // 
		 // Identity in policy is IP subnet
		 //
	     if ( iRemoteIDType_II == IdType )
		 {
		    // 
		    // Both identity in policy and identity in IKE IDi payload are
		    // IP subnets. The subnet masks MUST match, too
		    //
			AddrMatch = IpMask.Match(iRemoteAddr2_ID_II);
		 }
		 else
		 {
			 if ( ( (IdType == ID_IPV4_ADDR_SUBNET ) && (iRemoteIDType_II == ID_IPV4_ADDR)) ||
			      ( (IdType == ID_IPV6_ADDR_SUBNET ) && (iRemoteIDType_II == ID_IPV6_ADDR)))
			 {	 
			    // 
			    // Identity in IKE IDi payload is an IP address
			    // Do the special check: There must be full mask in the
			    // subnet identity configured in policy
			    //
				if ( IdType == ID_IPV6_ADDR_SUBNET )
					 PrefixLength = 128;
				else PrefixLength = 32;
				PrefixPtr = &IpMask;
				DEBUG_LOG(_L("Peer is using IP address IDi, full mask subnet required in local end!"));
			 }
			 else AddrMatch = EFalse;  
		 }	 
      }	   
      else
      {
	     // 
	     // Identity in policy is IP address.
	     //
		 if ( ( (IdType == ID_IPV4_ADDR ) && (iRemoteIDType_II == ID_IPV4_ADDR_SUBNET)) ||
			  ( (IdType == ID_IPV6_ADDR ) && (iRemoteIDType_II == ID_IPV6_ADDR_SUBNET)))
		 {	 
				// 
				// Identity in IKE IDi payload is an IP subnet
				// Do the special check: There must be full mask in the
				// subnet identity in IKE IDi payload
				//
			 if ( IdType == ID_IPV6_ADDR )
			      PrefixLength = 128;
			 else PrefixLength = 32;
			 PrefixPtr = &iRemoteAddr2_ID_II;
			 DEBUG_LOG(_L("An IP address ID used in local end, full mask subnet required in peer IDi!"));
		 }
		 else if ( iRemoteIDType_II != IdType )
				 AddrMatch = EFalse;
		 
      }
   }

   if ( PrefixPtr )
   {	   	   
	  if ( PrefixLength == PrefixLen(*PrefixPtr) )
	  {	  
	  	 iSwapRemoteIdType = ETrue;
		 DEBUG_LOG(_L("Required ID OK, modified remote IDi informed to IPsec!"));		 
	  }	 
	  else AddrMatch = EFalse;
   }
   
   return AddrMatch;
}

TBool CIkev1Negotiation::ProcessIdentityData(const TDesC8& aIdentity, TUint8* aToIdType,
										TInetAddr* aToIpAddr1, TInetAddr* aToIpAddr2)
{
   if ( !aToIdType || !aToIpAddr1 || !aToIpAddr2 )
	  return EFalse;
   
   aToIpAddr1->Init(KAFUnspec);
   aToIpAddr2->Init(KAFUnspec);
   *aToIdType = 0;
		   
   if ( aIdentity.Length() )
   {	   
      TInt offset = aIdentity.Find(_L8("/"));
   
      switch (offset)
      {
	     case KErrNotFound:  //Simple address
         { 
#ifdef _UNICODE
		    HBufC *unibuf = HBufC::New(aIdentity.Length());
			if ( !unibuf )
			    return EFalse;	
	        unibuf->Des().Copy(aIdentity);
	    	if ( aToIpAddr1->Input(unibuf->Des()) != KErrNone )
	    	{
				delete unibuf;
		    	DEBUG_LOG(_L("Bad IP address identity!"));
				return EFalse;
	    	}  
		    delete unibuf;
#else
		    if (aToIpAddr1->Input(aIdentity) != KErrNone)
		    {
				DEBUG_LOG(_L("Bad IP address identity!"));
				return EFalse;
		    }
#endif  
		    if ( aToIpAddr1->Family() == KAfInet )
			     *aToIdType = ID_IPV4_ADDR;
		    else *aToIdType = ID_IPV6_ADDR;
		    break;
     	 }
		 
	     default:    //Subnet
	     {
				//addr1 - subnet
		 TInt prefix_len;	 
#ifdef _UNICODE
		    HBufC *unibuf = HBufC::New(aIdentity.Length());
			if ( !unibuf )
				return EFalse;
			unibuf->Des().Copy(aIdentity);			
		    TPtrC addr_buf(unibuf->Ptr(), offset);
		    if (aToIpAddr1->Input(addr_buf) != KErrNone)
		    {
				delete unibuf;				
			    DEBUG_LOG(_L("Bad Subnet Identity address!"));
				return EFalse;
		    }
		    TPtrC prefix_ptr(unibuf->Ptr() + offset + 1, unibuf->Length() - offset - 1);
#else
		    TPtrC addr_buf(aIdentity.Ptr(), offset);
			if (aToIpAddr1->.Input(addr_buf) != KErrNone)
			{
				DEB(LogError(_L("Bad Subnet Identity address!"));)
				return EFalse;
			}
		    TPtrC prefix_ptr(aIdentity.Ptr() + offset + 1, aIdentity.Length() - offset - 1);
#endif  
				//addr2 - mask
		    TLex lex(prefix_ptr);
		    if (lex.Val(prefix_len) != KErrNone)
		    {
			   DEBUG_LOG(_L("Bad Subnet Identity PREFIX Length!"));
			   return EFalse;
		    }
#ifdef _UNICODE
		    delete unibuf;
#endif
			if ( aToIpAddr1->Family() == KAfInet )
			{
			   if ( prefix_len > 32 )	
			   {
				  DEBUG_LOG(_L("Bad Subnet Identity: Prefix too long!"));
				  return EFalse;
			   }
			   *aToIdType = ID_IPV4_ADDR_SUBNET;
			   PrefixMask(*aToIpAddr2, prefix_len, KAfInet);
			}
			else    //KAfInet6
			{
				if ( prefix_len > 128 )	
				{
					DEBUG_LOG(_L("Bad Subnet Identity: Prefix too long!"));
					return EFalse;
				}
				*aToIdType = ID_IPV6_ADDR_SUBNET;
				PrefixMask(*aToIpAddr2, prefix_len, KAfInet);
			}

		 }
		 
	  } //end switch
   }	  

   return ETrue;
}

//First msg of PHASE_II as initiator
void CIkev1Negotiation::InitPhase2L()
{
    //Quick mode stage 1
    DEBUG_LOG(_L("-------- IKE: Initiating PHASE II --------"));
    iPhase = PHASE_II;
    iDOI=IPSEC_DOI;
    iStage = 1;

    GetSPIL();
    //the rest will be done in ReceiveSPI
}

//Requests an SPI from the kernel

void CIkev1Negotiation::GetSPIL()
{
    CProposal_IIList *propII_List;
    CProposal_II *prop=NULL;
    TUint8 sa_type = 0;

    DEBUG_LOG1(_L("GetSPI in stage: %d"), iStage);

    iInboundSPIList = new (ELeave) CArrayFixFlat<TSPINode>(1);

    if (iRole == RESPONDER) //If Phase II proposal Chosen
        propII_List = iChosenProp_IIList;   //If RESPONDER
    else
        propII_List = iProposal_IIList; //If INITIATOR

    TInt i, count = propII_List->Count();
    for (i = 0; i < count; i++) //May have many Phase_II proposals
    {
        prop = propII_List->At(i);
        if (prop->iProtocol == PROTO_IPSEC_AH)
            sa_type = SADB_SATYPE_AH;
        else if (prop->iProtocol == PROTO_IPSEC_ESP)
            sa_type = SADB_SATYPE_ESP;
        else
            sa_type = 0;    //Unknown Protocol

        TInetAddr myAddr(iLocalAddr);
		if ( myAddr.IsUnspecified() )
		    User::LeaveIfError( iPluginSession->GetLocalAddress( myAddr ) );
        TInetAddr peerAddr(iRemoteAddr);
        peerAddr.SetPort(0);
        TSPINode node;
        node.iPropNum = prop->iProposalNum;
        node.iSeq = iSeq;
        iInboundSPIList->AppendL(node);
        
        
        __ASSERT_DEBUG( iIpsecSaSpiRetriever != NULL,
                        User::Invariant() );
        iIpsecSaSpiRetriever->Cancel();
        iIpsecSaSpiRetriever->GetIpsecSaSpi( iSeq++, sa_type, peerAddr, myAddr );        
        iPendingSPI++;      //To know when all received in ReceiveSPIL()
    }

    DEBUG_LOG1(_L("GetSPI seq= %d"), iSeq);
}

//aSPI received in Network order.
void CIkev1Negotiation::ReceiveSPIL(TUint32 aSPI, TUint32 aSeq)
{
	TIkev1IsakmpStream* msg = SaveIkeMsgBfr( new (ELeave) TIkev1IsakmpStream(iDebug) );
	
    DEBUG_LOG2(_L("ReceiveSPIL: SPI=%x, Seq=%u"), ByteOrder::Swap32(aSPI), aSeq);

    CProposal_IIList *propII_List = iProposal_IIList;
    TInt i;
    for (i = 0; i < iInboundSPIList->Count(); i++)
    {
        if (iInboundSPIList->At(i).iSeq == aSeq)
        {
            iInboundSPIList->At(i).iSPI = aSPI;
            break;
        }
        //prop = prop->iNext;   //To assign the SPI to the correct proposal
    }
    if (iRole==INITIATOR)
        propII_List->At(i)->iSPI.Copy((TUint8*)&aSPI,sizeof(TUint32));  //needed to send it to the other Peer

    if (--iPendingSPI)  // Check if still waiting for some SPI
        return;

    iExchange = IKE_QUICK_MODE; //Current mode being used.
    //Builds and send the packet

    if ((iHostData->iCommit) && (iRole==RESPONDER)) //Responder  && if commit bit used we set the flag
        iFlags |= ISAKMP_HDR_CFLAG; //Sets the Commit bit if this side set it else

    msg->IsakmpInit(this);
    msg->IsakmpHashL();
    msg->IsakmpSa();
    ComputeNonceL();     //Computes a new Nonce for PHASE_II
    msg->IsakmpNonce();
    if (iPFS)   //Only sent if PFS in use...
        msg->IsakmpKeyL();

    if (iStage==1)  //Initiator
    {
        //
        // If Internal address private extension used change iLocalAddr1_ID_II to
        // correspond that address
        //
        if ( iInternalAddr ) {
           iLocalAddr1_ID_II = iInternalAddr->iClientIntAddr;       
           iLocalIDType_II   = ID_IPV4_ADDR;
           iDefaultLocalID   = ETrue;  
        }   
        DEBUG_LOG(_L("PhaseII IV:"));   //New IV for phase II
        ComputeIVL(iIV, iMessageId);
        //IDs must be sent if TUNNEL mode or is explicitly specified in the acquire
        if ((!iLocalAddr1_ID_II.IsUnspecified()) || (!iRemoteAddr1_ID_II.IsUnspecified()) ||
            (iIDProtocol != 0) || (iIDLocalPort != 0) || (iIDRemotePort != 0))
        {
            msg->IsakmpOwnIdentL();     //Own proxy
            msg->IsakmpPeerIdentL();    //Peer Proxy
        }
    }
    else if (iStage==2) //Responder
    {
        if (iIDReceived)    //If received we send it back, otherwise no
        {
            msg->IsakmpPeerIdentL();
            msg->IsakmpOwnIdentL(); 
        }
        if (iHostData->iResponderLifetime)
            CheckSendResponderLifetime(*msg);    //Adds to the message the RESPONDER_LIFETIME payload if needed
    }
    else return;

    if (iHostData->iReplayStatus)
    {
        DEBUG_LOG(_L("Constructing REPLAY-STATUS"));
    
        TInt i;
        CProposal_II *prop;
        for (i = 0 ; i < iProposal_IIList->Count(); i++)
        {
            prop = iProposal_IIList->At(i);
            msg->IsakmpReplayStatus(prop->iProtocol, iInboundSPIList->At(i).iSPI, prop->iReplayWindowLength);
        }
    }
    msg->IsakmpHashContL(); //inserts the hash in the correct position of the buffer
    SendL(*msg);

    iStage++;   //Next stage
}

void CIkev1Negotiation::AcquireSAErrorResponse(TInt aError)
	{
	DEBUG_LOG(_L("CIkev1Negotiation::AcquireSAErrorResponse"));
		
	if ( iProposal_IIList )
		{
		for ( TInt j=0; j<iProposal_IIList->Count(); j++ )
			{			
			TIpsecSAData sa_data;	
		    sa_data.iSeq = iAcquireSeq;
		    sa_data.iPid = iPfkeyAcquirePID;
			sa_data.iSPI = 0;	
			sa_data.iDst = iRemoteAddr;
			
			CProposal_II* prop_II = iProposal_IIList->At( j );

			if ( prop_II->iProtocol == PROTO_IPSEC_AH )
				{
				sa_data.iSAType = SADB_SATYPE_AH;				
				}
	        else if ( prop_II->iProtocol == PROTO_IPSEC_ESP )
	            {
	            sa_data.iSAType = SADB_SATYPE_ESP;
	            }
			iPluginSession->AcquireSAError( sa_data, aError );
			}
		}
    iAcquirePending = EFalse;
	}

//Will update the outbound
void CIkev1Negotiation::UpdateSADatabaseL()
{
#ifdef _DEBUG
    TBuf<40> addr_buf;
#endif    
    TUint8 sa_type=0;
    TUint8 auth_alg=0;
    TUint8 encr_alg=0;
    TUint32 updateSPI,addSPI;
    TIpsecSAData sa_data;
    TBool   TunnelMode;
	//
    // Received Phase II key (Might be Auth + Encr keys)
	// (Buffers are allocated for max 1024 bits key material
	HBufC8* outboundKey_II = HBufC8::NewLC(128);
	HBufC8* inboundKey_II  = HBufC8::NewLC(128);     	
    TPtrC8 in_authKey(0,0), out_authKey(0,0);
    TPtrC8 in_encrKey(0,0), out_encrKey(0,0);
    //Identities
#ifdef _UNICODE	
	TBuf<80> id_work;
#endif						
	HBufC8* local_id   = HBufC8::NewLC(128);
	HBufC8* remote_id  = HBufC8::NewLC(128);
    // ESP UDP Encapsulation extension data	
	HBufC8* gen_ext_data = HBufC8::NewLC(128);     	
	TPtr8 GenExtData((TUint8*)gen_ext_data->Ptr(), 0, gen_ext_data->Des().MaxLength());
	
	DEBUG_LOG(_L("---UPDATING SAD---"));	
	
    if ((!iDefaultLocalID) && (iLocalIDType_II != 0))
    {
        switch (iLocalIDType_II)
        {   
        case ID_IPV4_ADDR:
        case ID_IPV6_ADDR:
#ifdef _UNICODE				
            iLocalAddr1_ID_II.OutputWithScope(id_work);
			local_id->Des().Copy(id_work);			
#else
			iLocalAddr1_ID_II.OutputWithScope(local_id->Des());			
#endif			
            break;
			
        case ID_IPV4_ADDR_SUBNET:
        case ID_IPV6_ADDR_SUBNET:
#ifdef _UNICODE							
            iLocalAddr1_ID_II.OutputWithScope(id_work);
			local_id->Des().Copy(id_work);			
#else
			iLocalAddr1_ID_II.OutputWithScope(local_id->Des());			
#endif						
            local_id->Des().AppendFormat(_L8("/%d"),PrefixLen(iLocalAddr2_ID_II));  //PrefixLen can't fail because checked before
            break;
        default:    //Should never come here
            DEBUG_LOG1(_L("Local ID type %d not supported"), iLocalIDType_II);
			CleanupStack::PopAndDestroy(5);  //key buffer, identities and ESP UDP encaps data
            return;
        }
    }

    if ((!iDefaultRemoteID) && (iRemoteIDType_II != 0) )
    {
        switch (iRemoteIDType_II)
        {   
        case ID_IPV4_ADDR:
        case ID_IPV6_ADDR:
#ifdef _UNICODE				
			iRemoteAddr1_ID_II.OutputWithScope(id_work);
			remote_id->Des().Copy(id_work);			
#else
			iRemoteAddr1_ID_II.OutputWithScope(remote_id->Des());			
#endif
			if ( iSwapRemoteIdType )
			{
			   if ( iRemoteIDType_II == ID_IPV4_ADDR )
				    remote_id->Des().AppendFormat(_L8("/%d"), 32);
			   else remote_id->Des().AppendFormat(_L8("/%d"), 128);
			}	
            break;
			
        case ID_IPV4_ADDR_SUBNET:
        case ID_IPV6_ADDR_SUBNET:
#ifdef _UNICODE				
			iRemoteAddr1_ID_II.OutputWithScope(id_work);
			remote_id->Des().Copy(id_work);			
#else
			iRemoteAddr1_ID_II.OutputWithScope(remote_id->Des());			
#endif
			if ( !iSwapRemoteIdType )
               remote_id->Des().AppendFormat(_L8("/%d"),PrefixLen(iRemoteAddr2_ID_II));    //PrefixLen can't fail because checked before
            break;
			
        default:    //Should never come here
            DEBUG_LOG1(_L("Remote ID type %d not supported"), iRemoteIDType_II);
			CleanupStack::PopAndDestroy(5);  //key buffer, identities and ESP UDP encaps data					
            return;
        }
    }

    TUint32 flags = 0;
    if (iPFS)
    {
        flags = SADB_SAFLAGS_PFS;
        DEBUG_LOG(_L("PFS enabled"));
    }

    TUint32 addPID;
    if (iRole==INITIATOR)
        addPID = iPfkeyAcquirePID;  //Require to remove the Larval SA
    else
        addPID = iPluginSession->Uid();
    
    DEBUG_LOG2(_L("SAD seq= %d , PID= %d"), iAcquireSeq, addPID);

    TInt i, j;
    for (i = 0; i < iInboundSPIList->Count(); i++)
    {
        if (iInboundSPIList->At(i).iPropNum == iProposalNum)
            break;
    }

    CProposal_II *prop;
    TSPINode inboundspi_node;
    TInt key_len, encr_len, auth_len;
    TChosenAttrib_II *attr_II;
    TInt count = iChosenProp_IIList->Count();

    for (j = 0 ; j < count; j++)
    {
        prop = iChosenProp_IIList->At(j);
        inboundspi_node = iInboundSPIList->At(i);
        attr_II = (TChosenAttrib_II *)prop->iAttrList->At(0);   //only 1 transform is chosen no matter how many there are
        if (prop->iProtocol == PROTO_IPSEC_AH)
        {
            
            sa_type  = SADB_SATYPE_AH;
            encr_alg = 0;
            auth_alg = attr_II->iTransformID;
			auth_len = (TInt)HMAC_KeyLength((TUint8)auth_alg);
			
			TPtr8 AHOutKey((TUint8*)outboundKey_II->Ptr(), 0, outboundKey_II->Des().MaxLength());
			TPtr8 AHInKey((TUint8*)inboundKey_II->Ptr(), 0, inboundKey_II->Des().MaxLength());
			
            ComputeKeys2L(prop, auth_len, inboundspi_node, AHOutKey, AHInKey);
            in_encrKey.Set(NULL, 0);
            out_encrKey.Set(NULL, 0);
            in_authKey.Set(inboundKey_II->Ptr(), auth_len/8);
            out_authKey.Set(outboundKey_II->Ptr(),auth_len/8);
        }
        else if (prop->iProtocol == PROTO_IPSEC_ESP)
        {
            sa_type = SADB_SATYPE_ESP;
            encr_alg = attr_II->iTransformID;
            if (attr_II->iKeyLength!=0)
                encr_len = attr_II->iKeyLength;
            else    //not sent means constant size or variable and use default
            {
				switch ( encr_alg )
				{
					case ESP_DES_CBC:
						encr_len = 64;
						break;
					case ESP_3DES_CBC:
						encr_len = 3*64;
						break;
					case ESP_NULL:
						encr_len = 0;
						break;
					case ESP_AES_CBC:
						encr_len = 128;
						break;
					default:	
						encr_len = 0;
						break;
						
				}	
            }

            if (attr_II->iAuthAlg==DOI_HMAC_MD5)
                auth_alg = SADB_AALG_MD5HMAC;
            else if (attr_II->iAuthAlg==DOI_HMAC_SHA)
                auth_alg = SADB_AALG_SHA1HMAC;
            else
                auth_alg = 0;

            auth_len = (TInt)HMAC_KeyLength((TUint8)auth_alg);
            key_len = encr_len + auth_len;

			TPtr8 ESPOutKey((TUint8*)outboundKey_II->Ptr(), 0, outboundKey_II->Des().MaxLength());
			TPtr8 ESPInKey((TUint8*)inboundKey_II->Ptr(), 0, inboundKey_II->Des().MaxLength());			
            ComputeKeys2L(prop, key_len, inboundspi_node, ESPOutKey, ESPInKey);

            in_encrKey.Set(inboundKey_II->Ptr(), encr_len/8);
            out_encrKey.Set(outboundKey_II->Ptr(), encr_len/8);

            //If no HMAC selected the next instr does nothing because size will be 0
            in_authKey.Set(inboundKey_II->Ptr() + in_encrKey.Length(),auth_len/8);
            out_authKey.Set(outboundKey_II->Ptr() + out_encrKey.Length(),auth_len/8);
            //
            // Nokia specific NAT traversal info (=ESP UDP tunneling)  
            // If iNAT_T_Required is true connection is over NAT:ted
            // newtork (=local end behind NAT).
			//
            if ( iNAT_T_Required ) {
               flags |= SADB_SAFLAGS_NAT_T;
            }
        }
        else
		{	
			DEBUG_LOG1(_L("Unknown IPsec protocol %d"), prop->iProtocol);
			CleanupStack::PopAndDestroy(5);  //key buffer, identities and ESP UDP encaps data					
			return;
		}	

        updateSPI = inboundspi_node.iSPI;
        Mem::Copy((TUint8*)&addSPI, prop->iSPI.Ptr(), sizeof(TUint32));

        TInetAddr local_addr(iLocalAddr);
        local_addr.SetPort(iIDLocalPort);
        TInetAddr remote_addr(iRemoteAddr);    
        remote_addr.SetPort(iIDRemotePort);
    
        //This will be always outbound
        
        TInt64 time(0), bytes(0);
        TPtrC8 time_ptr(attr_II->iLifeDurationSecs);
        TPtrC8 bytes_ptr(attr_II->iLifeDurationKBytes);
        if (attr_II->iReducedLifeSecs.Length() != 0)
            time_ptr.Set(attr_II->iReducedLifeSecs);

        if (attr_II->iReducedLifeKBytes.Length() != 0)
            bytes_ptr.Set(attr_II->iReducedLifeKBytes);

        ComputeLifetimes_II(time_ptr, bytes_ptr, time, bytes);
        if (time == 0)  //default lifetime applied
            time = DEFAULT_IPSEC_SA_LIFETIME;
        
        if (iHardLifetime > time)
            {
            DEBUG_LOG2(_L("Time %u, hard lifetime %d"),
                    I64LOW(time), I64LOW(iHardLifetime));
            time = iHardLifetime;
            }

        if (attr_II->iEncMode==DOI_TUNNEL)
        {
            TunnelMode = ETrue;
            DEBUG_LOG(_L("TUNNEL MODE"));
#ifdef _DEBUG            
            iLocalAddr1_ID_II.OutputWithScope(addr_buf);
            DEBUG_LOG1(_L("Local ID: net %S"), &addr_buf);
            
            if ((iLocalIDType_II == ID_IPV4_ADDR_SUBNET) || (iLocalIDType_II == ID_IPV6_ADDR_SUBNET))
                {
                iLocalAddr2_ID_II.OutputWithScope(addr_buf);
                DEBUG_LOG1(_L(", mask %S"), &addr_buf);
                }
            DEBUG_LOG1(_L(" (port %d)"), iIDLocalPort);
            iRemoteAddr1_ID_II.OutputWithScope(addr_buf);
            DEBUG_LOG1(_L("Remote ID: addr %S"), &addr_buf);
            if ((iRemoteIDType_II == ID_IPV4_ADDR_SUBNET) || (iRemoteIDType_II == ID_IPV6_ADDR_SUBNET))
                {
                iRemoteAddr2_ID_II.OutputWithScope(addr_buf);
                DEBUG_LOG1(_L(", mask %S"), &addr_buf);
                }
            DEBUG_LOG1(_L(" (port %d)"), iIDRemotePort);
#endif // _DEBUG            
            
        }
        else {
            TunnelMode = EFalse;        
            DEBUG_LOG(_L("TRANSPORT MODE"));
        }
        DEBUG_LOG1(_L("Sec Lifetime set to %u"),I64LOW(time));
        DEBUG_LOG1(_L("KB Lifetime set to %u"),I64LOW(bytes));

        //Inbound SA.
        sa_data.iSAType = sa_type;
        sa_data.iSeq = iAcquireSeq;
        sa_data.iSrc = remote_addr;
        sa_data.iDst = local_addr;
        sa_data.iProtocol = iIDProtocol;
        sa_data.iSrcIdType = iRemoteIDType_II;
        sa_data.iDstIdType = iLocalIDType_II;       
        sa_data.iSrcIdent.Set((TUint8 *)remote_id->Ptr(), remote_id->Length(), remote_id->Length());
        sa_data.iDstIdent.Set((TUint8 *)local_id->Ptr(), local_id->Length(), local_id->Length());
        sa_data.iPid = iPluginSession->Uid();
        sa_data.iSPI = updateSPI;
        sa_data.iAuthAlg = auth_alg;
        sa_data.iEncrAlg = encr_alg;
        sa_data.iAuthKey.Set(in_authKey);
        sa_data.iEncrKey.Set(in_encrKey);
        TIpsecSALifetime lifetime(0, bytes, time, 0);
        sa_data.iHard = &lifetime;
        sa_data.iReplayWindowLength = prop->iReplayWindowLength;

        if ( (sa_type == SADB_SATYPE_ESP) && (GenExtData.Length() == 0) ) {     
           //
           // Store possible NAT traversal info for IPSEC to do ESP UDP encapsulation correctly
           //
            PFKeyExtDataUtil::BuildUdpEncExtensionData( GenExtData,
                                                        iNAT_D_Flags,
                                                        (flags & SADB_SAFLAGS_NAT_T),
                                                        iHostData->iUseNatProbing,
                                                        (TUint16)iHostData->iEspUdpPort, 
                                                        UDP_KEEPALIVE_TIME,
                                                        iLastRemoteAddr,
                                                        iRemoteOriginalAddr );
        }
        
        if ( GenExtData.Length() ) {
           sa_data.iGenericExtension.Set(GenExtData);      
        }

        if ( TunnelMode ) {
           //
           // Get VPN interface index 
           //
           TUint32 vpnInterfaceIndex = iPluginSession->VpnInterfaceIndex();
           if ( vpnInterfaceIndex != 0 )
               {
               sa_data.iInternalAddress.Init( KAfInet6 );
               sa_data.iInternalAddress.SetScope( vpnInterfaceIndex );
               flags |= SADB_SAFLAGS_INT_ADDR;
               }
           }
        sa_data.iFlags = flags;
		iPluginSession->UpdateSAL(sa_data);
		TIpsecSPI SpiData;
		SpiData.iProtocol = sa_type;
		SpiData.iSrcAddr  = remote_addr;
		SpiData.iDstAddr  = local_addr;
		SpiData.iSPI      = updateSPI;
		SpiData.iInbound  = ETrue;
		iPluginSession->AddIpsecSPIToSAL(iSAId, SpiData);

        sa_data.iFlags &= ~SADB_SAFLAGS_INT_ADDR; //No VPN interface index to outbound SA
        //Outbound SA.
        //First check there's no other SA with the same parameters and
        //erase it if happens (very unlikely, but still possible)
		SpiData.iDstAddr = remote_addr;
		SpiData.iSrcAddr = local_addr;
		SpiData.iSPI     = addSPI;
		SpiData.iInbound = EFalse;
		if ( iPluginSession->DeleteIpsecSpi(iSAId, addSPI, EFalse) )		
        {
            DEBUG_LOG(_L("Deleting previously negotiated IPsec SA"));
			iPluginSession->DeleteIpsecSA(SpiData.iSPI, SpiData.iSrcAddr, SpiData.iDstAddr, 
                                              SpiData.iProtocol);
        }
        //Some changes in the SA, the rest is the same
        sa_data.iSrc = local_addr;
        sa_data.iDst = remote_addr;
        sa_data.iSrcIdType = iLocalIDType_II;
        sa_data.iDstIdType = iRemoteIDType_II;
        sa_data.iSrcIdent.Set((TUint8 *)local_id->Ptr(), local_id->Length(), local_id->Length());
        sa_data.iDstIdent.Set((TUint8 *)remote_id->Ptr(), remote_id->Length(), remote_id->Length());
        sa_data.iPid = addPID;
        sa_data.iSPI = addSPI;
        sa_data.iAuthKey.Set(out_authKey);
        sa_data.iEncrKey.Set(out_encrKey);
        
        iPluginSession->AddSAL(sa_data);
		iPluginSession->AddIpsecSPIToSAL(iSAId, SpiData);
        
        i++;    //To get the correct SPIs from iInboundSPIList
	} //end for

	CleanupStack::PopAndDestroy(5);  //key buffer, identities and ESP UDP encaps data
	
	iAcquirePending = EFalse;
	
}

void CIkev1Negotiation::ComputeLifetimes_II(const TDesC8 &aLifetime, const TDesC8 &aLifesize, TInt64 &aTime, TInt64 &aBytes)
{
    TInt64 maxnum = MAKE_TINT64(0x7fffffffu, 0xffffffffu);
        
    if (Desc8ToTInt64(aLifetime, aTime) != KErrNone)
        {
        DEBUG_LOG(_L("Phase_II Lifetime(sec) Overflowed Setting to maximum value"));
        }
    if (Desc8ToTInt64(aLifesize, aBytes) != KErrNone) {
        DEBUG_LOG(_L("Phase_II Lifetime(kbytes) Overflowed Setting to maximum value"));
    }       
    else 
    {   if (aBytes < maxnum / 1024) //Make sure no overflow
            aBytes = aBytes * 1024; //KB to Bytes
        else
        {
            aBytes = MAKE_TINT64(KMaxTInt, KMaxTUint);            
            DEBUG_LOG(_L("Phase_II Lifetime(kbytes) Overflowed Setting to maximum value"));
        }
    }

}


TBool CIkev1Negotiation::Phase_IExchangeL(const ThdrISAKMP &aHdr)
{       
    if (!ProcessHeaderL(aHdr))
        return EFalse;

    DEBUG_LOG2(_L("---------- Phase %d - Stage %d ----------"),iPhase, iStage);
    if (iPhase == PHASE_I)
    {
        if (aHdr.GetExchange() == ISAKMP_EXCHANGE_ID)
            MainModeReplyL();   //Main Mode
        else
            AggressiveReplyL(); //Aggressive Mode
    }
    else
    {
        QuickModeReplyL();
    }
    return ETrue;
}

TBool CIkev1Negotiation::Phase_IIExchangeL(const ThdrISAKMP &aHdr)
{       
    if (!ProcessHeaderL(aHdr))
        return EFalse;

    DEBUG_LOG2(_L("---------- Phase %d - Stage %d ----------"),iPhase, iStage);

    QuickModeReplyL();

    return ETrue;
}

void CIkev1Negotiation::QuickModeReplyL()
{

	TIkev1IsakmpStream* msg = SaveIkeMsgBfr( new (ELeave) TIkev1IsakmpStream(iDebug) );	
    switch(iStage)
    {
    //INITIATOR cases
    case 3: //Send last message (if no COMMIT)
        msg->IsakmpInit(this);
        msg->IsakmpHashL();
        SendL(*msg);
        iStage = 4;  

        if (iRecvFlags & ISAKMP_HDR_CFLAG)  //Commit Bit set
             return; //Not finished yet. We wait for CONNECTED
        
        //No Commot bit, Update SA database
        UpdateSADatabaseL();
        SetFinished();    //No more stages.
        break;
	case 1: 
		DEBUG_LOG(_L("QuickModeReplyL in Stage 1 ?"));		
		break;
    case 5: //Send last message (extra message waiting for commit(
        //No more processing required
        SetFinished();    //No more stages.
        break;
    //RESPONDER cases
    case 2:
        GetSPIL();
        //rest done in receiveSPI
        break;
    case 4:
        if (iRecvFlags & ISAKMP_HDR_CFLAG)  //Commit Bit set
        {
            DEBUG_LOG(_L("Sending CONNECTED Status message"));
            msg->IsakmpInit(this);

            //HASH Payload only if payload protected with encryption
            if (iFlags & ISAKMP_HDR_EFLAG)
                msg->IsakmpHashL();
        
            msg->IsakmpNotification(CONNECTED, iChosenProp_IIList->At(0)->iProtocol);

            if (iFlags & ISAKMP_HDR_EFLAG)
                msg->IsakmpHashContL();
                
            SendL(*msg);
        }
        SetFinished();
        break;
    }
}

//Builds and sends a Phase I reply for Main Mode
void CIkev1Negotiation::MainModeReplyL()
{
	TIkev1IsakmpStream* msg = SaveIkeMsgBfr( new (ELeave) TIkev1IsakmpStream(iDebug) );		
    TInt          vendor_type;
 
    switch(iStage)
    {
    //
    //RESPONDER replies
    //
    case 2: 
        msg->IsakmpInit(this);
        msg->IsakmpSa();
        if ( iHostData->iDPDHeartBeat != 0 )
           BuildDPDVendorId(*msg);        
        if ( iNatDiscovery ) {
           iNatDiscovery->BuildNatVendorId(*msg); 
           iNatDiscovery->BuildRfcNatVendorId(*msg);
        }   
        SendL(*msg);
        iStage = 3;
        break;
    case 4:
        ComputeNonceL();         //Nonce to be sent
        msg->IsakmpInit(this);
        msg->IsakmpKeyL();
        msg->IsakmpNonce();
        if (((iChosenProposal_I.iAttrList->iAuthMethod == RSA_SIG) ||
             (iChosenProposal_I.iAttrList->iAuthMethod == DSS_SIG)) && !iPeerX509Cert)
           msg->IsakmpCertificateReqL();
		if ( iNatDiscovery ) {
			iNatDiscovery->BuildDiscoveryPayloadsL(*msg, iChosenProposal_I.iAttrList->iHashAlg,
				                                  (TUint8*)iCookie_I.Ptr(), (TUint8*)iCookie_R.Ptr(),
				                                  iLocalAddr, iLastRemoteAddr);    
		}
		SendL(*msg);
        iStage = 5; //next stage
        iFlags |= ISAKMP_HDR_EFLAG; //From now on encryption is used
        if (!ComputeKeysL())    //Generates keying material for encryption stages
            return; //error don't send reply packet
        break;
    case 6:
        msg->IsakmpInit(this);
        
        switch(iChosenProposal_I.iAttrList->iAuthMethod)
        {
        case RSA_SIG:
        case DSS_SIG:
            msg->IsakmpOwnIdentL();
            msg->IsakmpCertificateL();
            msg->IsakmpSignatureL();
            break;
        case PRE_SHARED:
            msg->IsakmpOwnIdentL();
            msg->IsakmpHashL();
            break;
        }
        
        if ( iHostData->iInitialContact &&   
             iRole == INITIATOR &&
             iSARekeyInfo == NULL ) 
        {
           DEBUG_LOG(_L("Constructing INITIAL-CONTACT"));
           msg->IsakmpNotification(DOI_INITIAL_CONTACT, PROTO_ISAKMP); //Not protected by the hash!
        }
        
        SendL(*msg);

		IsakmpPhase1CompletedL();
        break;
    //
    //INITIATOR replies
    //
    case 3:
        ComputeNonceL();         //Nonce to be sent
        msg->IsakmpInit(this);
        msg->IsakmpKeyL();
        msg->IsakmpNonce();
        if ( iChosenProposal_I.iAttrList->iAuthMethod == IKE_A_CRACK && !iPeerX509Cert )
		{
            msg->IsakmpCertificateReqL();
        }
        if ( iHostData->iUseNatProbing )
             vendor_type = EXPANDED_VENDOR_ID;
        else vendor_type = HASH_VENDOR_ID;
        msg->IsakmpVendorId(vendor_type,       
                              (TUint8*)iCookie_I.Ptr(),
                              (TUint8*)iCookie_R.Ptr(), iLocalAddr);
        if ( iNatDiscovery )
		{
           iNatDiscovery->BuildDiscoveryPayloadsL(*msg, iChosenProposal_I.iAttrList->iHashAlg,
                                                 (TUint8*)iCookie_I.Ptr(), (TUint8*)iCookie_R.Ptr(),
                                                 iLocalAddr, iLastRemoteAddr);    
        }   
        SendL(*msg);
        iStage = 4; //next stage
        break;

    case 5:
        iFlags |= ISAKMP_HDR_EFLAG; //From now on encryption is used
        if (!ComputeKeysL())    //Generates keying material for encryption stages
            return; //error don't send reply packet
        
        msg->IsakmpInit(this);
        
        switch(iChosenProposal_I.iAttrList->iAuthMethod)
        {
        case RSA_SIG:
        case DSS_SIG:
            msg->IsakmpOwnIdentL(); //Also fills iOwnIdentPayload!
            msg->IsakmpCertificateL();
            msg->IsakmpSignatureL();
            if (!iPeerX509Cert)
               msg->IsakmpCertificateReqL(); 
            break;
        case PRE_SHARED:
            msg->IsakmpOwnIdentL();
            msg->IsakmpHashL();
            break;
        }
        
        
        if ( iFamiliarPeer && iHostData->iUseInternalAddr )
           msg->IsakmpIntnet(0);  /* null IPV4 address as parameter */

		if ( iHostData->iInitialContact &&
		    !iPluginSession->FindIkev1SADataWithAddr(iRemoteAddr) &&
            iRole == INITIATOR &&
		    iSARekeyInfo == NULL ) 
		{
			DEBUG_LOG(_L("Constructing INITIAL-CONTACT"));
			msg->IsakmpNotification(DOI_INITIAL_CONTACT, PROTO_ISAKMP); //Not protected by the hash!
		}
		
        SendL(*msg);
        iStage = 6; //next stage
        
        break;
    case 7:
        // CRACK authentication going. No actions required here !
        break;
    default:
        DEBUG_LOG1(_L("Main mode Wrong Phase number requested (%d) !!"),iStage);
    }

}


//Builds and sends a Phase I reply for Aggressive Mode
void CIkev1Negotiation::AggressiveReplyL()
{
	TIkev1IsakmpStream* msg = SaveIkeMsgBfr( new (ELeave) TIkev1IsakmpStream(iDebug) );			
	
    switch(iStage)
    {
    case 2://RESPONDER Sends the 2nd msg. of the exchange
        ComputeNonceL();         //Nonce to be sent
        msg->IsakmpInit(this);
        msg->IsakmpSa();
        msg->IsakmpKeyL();
        msg->IsakmpNonce();
        msg->IsakmpOwnIdentL();
        
        if (!ComputeKeysL()) //Needed to compute hash before computing signature
		{
		   return;
		}   
        
        if ( iHostData->iDPDHeartBeat != 0 )
           BuildDPDVendorId(*msg);        
        if ( iNatDiscovery ) {
           iNatDiscovery->BuildNatVendorId(*msg);
           iNatDiscovery->BuildRfcNatVendorId(*msg);
           iNatDiscovery->BuildDiscoveryPayloadsL(*msg, iChosenProposal_I.iAttrList->iHashAlg,
                                                 (TUint8*)iCookie_I.Ptr(), (TUint8*)iCookie_R.Ptr(),
                                                 iLocalAddr, iLastRemoteAddr);    
        }   
        
        switch (iChosenProposal_I.iAttrList->iAuthMethod)
        {
        case RSA_SIG:
        case DSS_SIG:
            msg->IsakmpCertificateL();
            msg->IsakmpSignatureL();
            if (!iPeerX509Cert) //No stored cert so send a CR
            {
                msg->IsakmpCertificateReqL();
            }
            break;
        case PRE_SHARED:
            msg->IsakmpHashL();
            break;
        }
        SendL(*msg);
		
        iStage = 3; //next stage
		
        break;
    case 3://INITIATOR
        iFlags |= ISAKMP_HDR_EFLAG; //From now on encryption is used                
        msg->IsakmpInit(this);
        if ( iNatDiscovery ) {
           iNatDiscovery->BuildDiscoveryPayloadsL(*msg, iChosenProposal_I.iAttrList->iHashAlg,
                                                 (TUint8*)iCookie_I.Ptr(), (TUint8*)iCookie_R.Ptr(),
                                                 iLocalAddr, iLastRemoteAddr);    
        }   
        
        switch (iChosenProposal_I.iAttrList->iAuthMethod)
        {
        case RSA_SIG:
        case DSS_SIG:
            msg->IsakmpCertificateL();   
            msg->IsakmpSignatureL(); 
            break;
        case PRE_SHARED:
            msg->IsakmpHashL();
            break;
        }
		if ( iHostData->iInitialContact &&
		     !iPluginSession->FindIkev1SADataWithAddr(iRemoteAddr) &&
             iRole == INITIATOR &&
		     iSARekeyInfo == NULL ) //Only sent if no ISAKMP SA established
		{
			DEBUG_LOG(_L("Constructing INITIAL-CONTACT"));
			msg->IsakmpNotification(DOI_INITIAL_CONTACT, PROTO_ISAKMP); //Not protected by the hash!
		}

        SendL(*msg);
        IsakmpPhase1CompletedL();       
        break;
	case 4:
		// Aggressive mode as responder completed
		DEBUG_LOG(_L("Aggressive mode as responder completed"));		
		break;
		
    case 7:
        // CRACK authentication going. No actions required here !
        break;
    default:
        DEBUG_LOG1(_L("Main mode Wrong Phase number requested (%d) !! "),iStage);
    }
	
}

void CIkev1Negotiation::SaveISAKMPSAL()
{
	//
	// Create a new IKEv1 SA object       
	//
	TIkev1SAData SaData;
	SaData.iCookie_I       = iCookie_I;
	SaData.iCookie_R       = iCookie_R;
	SaData.iSAId           = iSAId;
	SaData.iSAState        = 0;
	SaData.iInitiator      = (iRole == INITIATOR);
	SaData.iAutoLogin      = iAutoLogin;
	SaData.iDPDSupported   = iDPDSupported;
	SaData.iFamiliarPeer   = iFamiliarPeer;
	SaData.iNAT_T_Required = iNAT_T_Required;
	SaData.iNAT_D_Flags    = iNAT_D_Flags;
	SaData.iIkeData        = iHostData;
	SaData.iLocalAddr      = iLocalAddr;
	SaData.iRemoteAddr     = iRemoteAddr;
	SaData.iDestinAddr     = iLastRemoteAddr;
	SaData.iVirtualIp      = iInternalAddr;
	SaData.iSeq            = iSeq;
	SaData.iPrevExchange   = iPrevExchange;
	SaData.iFlags          = iFlags;
	
	SaData.iEncrAlg        = iChosenProposal_I.iAttrList->iEncrAlg;
	SaData.iHashAlg        = iChosenProposal_I.iAttrList->iHashAlg;
	SaData.iGroupDesc      = iChosenProposal_I.iAttrList->iGroupDesc;
	SaData.iGroupType      = iChosenProposal_I.iAttrList->iGroupType;				
	SaData.iKeyLength      = iChosenProposal_I.iAttrList->iKeyLength;

	TUint32 Lifetime = 0;
	TUint Len = iChosenProposal_I.iAttrList->iLifeDurationSecs.Length();
	if (Len > 0)
	{
		if (Len > sizeof(TUint32))
		{
			Lifetime = KMaxTUint32;
		}
		else    // (len <= sizeof(TUint32))
		{
			Mem::Copy(&Lifetime, iChosenProposal_I.iAttrList->iLifeDurationSecs.Ptr(), Len);
			Lifetime = ByteOrder::Swap32(Lifetime);
			Lifetime = Lifetime >> (sizeof(TUint32)*8 - Len*8);   //To set the correct value (shift in bits)
		}
	}
	SaData.iLifeTimeSecs = Lifetime;
	
	Lifetime = 0;
	Len = iChosenProposal_I.iAttrList->iLifeDurationKBytes.Length();
	if (Len > 0)
	{
		if (Len > sizeof(TUint32))
		{
			Lifetime = KMaxTUint32;
		}
		else    // (len <= sizeof(TUint32))
		{
			Mem::Copy(&Lifetime, iChosenProposal_I.iAttrList->iLifeDurationKBytes.Ptr(), Len);
			Lifetime = ByteOrder::Swap32(Lifetime);
			Lifetime = Lifetime >> (sizeof(TUint32)*8 - Len*8);   //To set the correct value (shift in bits)
		}
	}
	SaData.iLifeTimeKB = Lifetime;

	SaData.iSKEYID     = iSKEYID;
	SaData.iSKEYID_d   = iSKEYID_d;
	SaData.iSKEYID_a   = iSKEYID_a;
	SaData.iSKEYID_e   = iSKEYID_e;
	SaData.iLastIV     = iLastIV;
	SaData.iIV         = iIV;

	if ( iDPDSupported && iHostData->iDPDHeartBeat )
	{
	   //
	   //  Initialize DPD protocol parameters in TIkev1SAData
	   //
		TPtr8 ptr((TUint8*)&SaData.iDPDSequence, sizeof(TUint32));	 
		ptr.SetLength(sizeof(TUint32));
		TRandom::RandomL(ptr);	
		SaData.iDPDSequence &= 0x7fffffff;
		SaData.iDPDRetry = 0;
		SaData.iPendingDPDSequence  = 0;
		SaData.iExpectedDPDSequence = 0;		
	}	
		
	iLastIKEMsgInfo.Store(SaData.iLastIKEMsgInfo);
    SaData.iLastMsg = iLastMsg;
	iPluginSession->CreateIkev1SAL(SaData, iSARekeyInfo);   // Add rekey info later
}

TBool CIkev1Negotiation::ProcessHeaderL(const ThdrISAKMP &aHdr)
{
    //checks on the header    
    if (!CheckCookies(aHdr.GetCookieI(), aHdr.GetCookieR()))
        return EFalse;

    if (iStage == 1)
    {
        if (iPhase == PHASE_I)
        {
            iCookie_I = aHdr.GetCookieI();  //save initiator cookie
            iCookie_R = CreateCookieL(); //create responder cookie
        }
        iMessageId = aHdr.GetMessageId();
    }
    
    //checks on the header    
    if (!CheckPayloadCode(aHdr.GetPayload()))
        return EFalse;

    if (!CheckVersionL(aHdr.GetVersion()))
        return EFalse;
    
    if (!CheckExchangeTypeL(aHdr.GetExchange()))
        return EFalse;

    if (!CheckFlagsL(aHdr.GetFlags()))
        return EFalse;
    iRecvFlags = aHdr.GetFlags();   //Save the flags for later use

    if (!CheckMessageIdL(aHdr.GetMessageId()))
        return EFalse;

    iLengthLeft -= ISAKMP_HEADER_SIZE;  //Updates the length left in the buffer

    //EVEN stages RESPONDER, ODD ones INITIATOR
    DEBUG_LOG(_L("Processing packet..."));
    if (iPhase == PHASE_I)
    {
        switch (iStage)
        {
            case 1:
                if (!ProcessStage1L(aHdr))
                    return EFalse;
                break;
            case 2:
                if (!ProcessStage2L(aHdr))
                    return EFalse;
                break;
            case 3:
                if (iExchange == ISAKMP_EXCHANGE_ID)
                {
                    if (!ProcessStage3MainL(aHdr))
                        return EFalse;
                }
                else    //ISAKMP_EXCHANGE_AGGR
                {
                    if (!ProcessStage3AggrL(aHdr))
                        return EFalse;
                }
                break;
            case 4:
                if (!ProcessStage4L(aHdr))
                    return EFalse;
                break;
            case 5:
                if (!ProcessStage5L(aHdr))
                    return EFalse;
                break;
            case 6:
                if (!ProcessStage6L(aHdr))
                    return EFalse;
                break;
            case 7:
                if (!ProcessStage7L(aHdr))   /* For CRACK negotiation */
                    return EFalse;
                break;
            default:
                return EFalse;
        }
    }
    else    //PHASE_II
    {
        switch (iStage)
        {
            case 1:
                if (!ProcessStage1Phase2L(aHdr))
                    return EFalse;
                break;
            case 2:
                if (!ProcessStage2Phase2L(aHdr))
                    return EFalse;
                break;
            case 3:
                if (!ProcessStage3Phase2L(aHdr))
                    return EFalse;
                break;
            case 4:
                if (!ProcessCONNECTEDL(aHdr))
                    return EFalse;
                break;
            default:
                DEBUG_LOG(_L("Quick Bad Stage"));
        }
    }
    return ETrue;
}

//Process payloads appearing in Stage 1. Responder Role
TBool CIkev1Negotiation::ProcessStage1L(const ThdrISAKMP &aHdr)
{
	CIkev1Payloads* payload = CIkev1Payloads::NewL(aHdr, *this, iDebug);
	if (!payload)
	   return EFalse;	

	CleanupStack::PushL(payload);
	
    //SA Payload processing (all modes)
    if (!ProcessSAL(payload->iSa, NULL))
    {
		CleanupStack::PopAndDestroy();  //payload
        return EFalse;
    }

	//Process vendor ID:s  
	ProcessVendorL(payload->iVids);
	
    TUint16 auth_method = iChosenProposal_I.iAttrList->iAuthMethod;
	
	switch (auth_method)
	{
		case RSA_SIG:
		case DSS_SIG:
			//Process the possible CR payloads
			if ( !ProcessCertificateReqArrayL(payload->iCertReqs) ) {
				CleanupStack::PopAndDestroy();  //payload
				return EFalse;
			}
			//Process the possible CERT payloads
			if ( !ProcessCertificateArrayL(payload->iCerts) ) {
				CleanupStack::PopAndDestroy();  //payload
				return EFalse;
			}
			break;

		default:
			break;
	}

	
    if (iExchange == ISAKMP_EXCHANGE_ID)
    {
		if ( payload->iKe || payload->iNonce || payload->iHash || payload->iSign ||
			 payload->iIds->Count() )
		{
			CleanupStack::PopAndDestroy();  //payload
			return EFalse;
		}	
    }
    else //ISAKMP_EXCHANGE_AGGR
    {
		if ( !payload->iKe || !payload->iNonce || (payload->iIds->Count() != 1) )
		{
			CleanupStack::PopAndDestroy();  //payload
			return EFalse;
		}	
		
        //Key Payload processing (all modes)
        if (!ProcessKeyL(payload->iKe))
        {
            CleanupStack::PopAndDestroy();  //payload
            return EFalse;
        }
		TIdentISAKMP* id_payload    = (TIdentISAKMP*)payload->iIds->At(0);  // The first ID		
		TNonceISAKMP* nonce_payload = (TNonceISAKMP*)payload->iNonce;
		
        //Nonce Payload processing
        if (!ProcessNonceL(nonce_payload))
        {
			CleanupStack::PopAndDestroy();  //payload
            return EFalse;
        }
        //ID Payload processing
        if (!CheckIdentL(id_payload))
        {
			CleanupStack::PopAndDestroy();  //payload
			return EFalse;
        }
        //Save the ident payload for HASH_I/R computing
        delete iPeerIdentPayload;
		iPeerIdentPayload = NULL;
        iPeerIdentPayloadSize = id_payload->GetLength() - sizeof(TPayloadISAKMP);
        iPeerIdentPayload = new (ELeave) TUint8[iPeerIdentPayloadSize];
        Mem::Copy(iPeerIdentPayload,((TUint8 *)id_payload)+sizeof(TPayloadISAKMP),iPeerIdentPayloadSize);
        
        if ( iNatDiscovery )
		{
		   if ( payload->iNatDs->Count() )
		   {	   
               iNAT_D_Flags = iNatDiscovery->CheckDiscoveryPayloadsL(payload->iNatDs, iChosenProposal_I.iAttrList->iHashAlg,
                                                                    (TUint8*)iCookie_I.Ptr(), (TUint8*)iCookie_R.Ptr(),
                                                                     iLocalAddr, iLastRemoteAddr);
		       if ( iNAT_D_Flags )
			      iLastRemoteAddr.SetPort(FLOATED_IKE_PORT);
		   }
		   else
		   {
			   if ( iLastRemoteAddr.Port() == FLOATED_IKE_PORT )
				   iNAT_D_Flags |= LOCAL_END_NAT; 
		   }	   
        }
        
    }//end aggressive

				   
    CleanupStack::PopAndDestroy();  //payload
    iStage = 2;
    return ETrue;
    
}
                
//Process payloads appearing in Stage 3. Initiator Role
TBool CIkev1Negotiation::ProcessStage2L(const ThdrISAKMP &aHdr)
{
	CIkev1Payloads* payload = CIkev1Payloads::NewL(aHdr, *this, iDebug);
	if (!payload)
		return EFalse;
	
	CleanupStack::PushL(payload);
	
	iCookie_R = aHdr.GetCookieR();    //Save the responder Cookie	

    //SA Payload processing (all modes)
    if (!ProcessSAL(payload->iSa, NULL)) {
        CleanupStack::PopAndDestroy();  // payload
        return EFalse;
    }

	ProcessVendorL(payload->iVids);
	
    if ( iAutoLogin && (iLocalAddr.Family() == KAFUnspec ) ) {
        User::LeaveIfError( iPluginSession->GetLocalAddress( iLocalAddr ) ); //No local address, get it now (used later with NAT traversal)
    }

    //NOW we know the auth method chosen!
    TUint16 auth_method = iChosenProposal_I.iAttrList->iAuthMethod;

    switch (auth_method)
    {
    case RSA_SIG:
    case DSS_SIG:
        //Process the possible CR payloads
        if ( !ProcessCertificateReqArrayL(payload->iCertReqs) )
		{
            CleanupStack::PopAndDestroy();  //payload
            return EFalse;
        }
	    if (!ProcessCertificateArrayL(payload->iCerts))
		{
		    CleanupStack::PopAndDestroy();  //payload
		    return EFalse;
 		}
        break;
		
    default:
		break;
    }

	if (iExchange == ISAKMP_EXCHANGE_ID)
	{
		if ( payload->iKe || payload->iNonce || payload->iHash || payload->iSign ||
			 payload->iIds->Count() )
		{
			CleanupStack::PopAndDestroy();  //payload
			return EFalse;
		}	
	}
    else //ISAKMP_EXCHANGE_AGGR
    {
		if ( !payload->iKe || 
		     !payload->iNonce || 
		     (!payload->iHash && auth_method == PRE_SHARED) // hash is a must only with PSK
		     || (payload->iIds->Count() != 1) )
		{
			CleanupStack::PopAndDestroy();  //payload
			return EFalse;
		}	
        //Key Payload processing (all modes)
        if (!ProcessKeyL(payload->iKe))
		{
			CleanupStack::PopAndDestroy();  //payload			
            return EFalse;
		}
		TIdentISAKMP* id_payload    = (TIdentISAKMP*)payload->iIds->At(0);  // The first ID		
		TNonceISAKMP* nonce_payload = (TNonceISAKMP*)payload->iNonce;
		
		//Nonce Payload processing
		if (!ProcessNonceL(nonce_payload))
		{
			CleanupStack::PopAndDestroy();  //payload
			return EFalse;
		}
		
		//ID Payload processing
		if (!CheckIdentL(id_payload))
		{
			CleanupStack::PopAndDestroy();  //payload
			return EFalse;
		}
		
        //Save the ident payload for HASH_I/R computing
        delete iPeerIdentPayload;
		iPeerIdentPayload = NULL;
        iPeerIdentPayloadSize = id_payload->GetLength()-sizeof(TPayloadISAKMP);
        iPeerIdentPayload = new (ELeave) TUint8[iPeerIdentPayloadSize];
        Mem::Copy(iPeerIdentPayload,((TUint8 *)id_payload)+sizeof(TPayloadISAKMP),iPeerIdentPayloadSize);
        
        if (!ComputeKeysL())    //Computes the keys to be used. Needed to compute HASH_R
		{
			CleanupStack::PopAndDestroy();  //payload
			return EFalse;
		}	
			
        switch (auth_method)
        {
        case PRE_SHARED:
			if (!ProcessHashL(payload->iHash))
			{
				CleanupStack::PopAndDestroy();  //payload			
				return EFalse;
			}
			DEBUG_LOG(_L("HASH OK!"));
            break;
        case RSA_SIG:
        case DSS_SIG:
        case IKE_A_CRACK:
            //Signature payload processing and checking
            if (!ProcessSignatureL(payload->iSign)) {
				CleanupStack::PopAndDestroy();  //payload	
                return EFalse;
			}	
            if ( auth_method == IKE_A_CRACK ) {
			   CleanupStack::PopAndDestroy();  //payload					
               return StartCRACKAuthL();
            }
			else {
				if (!CertifyRemoteIdentityL(id_payload))
				{
					DEBUG_LOG(_L("ProcessStage2L RSA_SIG CertifyRemoteIdentityL failed"));
					DEBUG_LOG(_L("AUTHENTICATION_FAILED"));
					SetErrorStatus( KKmdIkeAuthFailedErr );
					SendNotifyL(AUTHENTICATION_FAILED);
					CleanupStack::PopAndDestroy();  //payload			
					return EFalse;
				}
			}	
            break;

		default:
			break;
			
		}
		
		if ( iNatDiscovery && payload->iNatDs->Count()) {
			iNAT_D_Flags = iNatDiscovery->CheckDiscoveryPayloadsL(payload->iNatDs, iChosenProposal_I.iAttrList->iHashAlg,
				                                                 (TUint8*)iCookie_I.Ptr(), (TUint8*)iCookie_R.Ptr(),
				                                                  iLocalAddr, iLastRemoteAddr);
			if ( iNAT_D_Flags )
				iLastRemoteAddr.SetPort(FLOATED_IKE_PORT); 
		}
    }
	
	CleanupStack::PopAndDestroy();  //payload
	
    iStage = 3;
    return ETrue;
}

// Process payloads appearing in Stage 4. Order NOT relevant. Only Main Mode
// Handles message: HDR, KE, [HASH(1),] <IDii>PubKey_r, <Ni>PubKey_r for RSAENCR
// Handles message: HDR, KE, Ni for the rest [CERTREQ] in certificates
TBool CIkev1Negotiation::ProcessStage3MainL(const ThdrISAKMP &aHdr)
{
	CIkev1Payloads* payload = CIkev1Payloads::NewL(aHdr, *this, iDebug);
	if (!payload)
		return EFalse;

	CleanupStack::PushL(payload);

	if ( payload->iSa || !payload->iNonce || payload->iHash || payload->iSign )
	{
		CleanupStack::PopAndDestroy();  //payload
		return EFalse;
	}	
	
    TUint16 auth_method = iChosenProposal_I.iAttrList->iAuthMethod;
	
    //Process payloads
    //Main mode (ONLY)
    //Key Payload processing (all methods)
    if (!ProcessKeyL(payload->iKe))
    {
        CleanupStack::PopAndDestroy();  //payload
        return EFalse;
    }

    if ((auth_method == RSA_SIG) || (auth_method == DSS_SIG))
    {
        //Process the possible CR payloads
        if ( !ProcessCertificateReqArrayL(payload->iCertReqs) )
		{
            CleanupStack::PopAndDestroy();  //payload
            return EFalse;
        }
    }

	TNonceISAKMP* nonce_payload = (TNonceISAKMP*)payload->iNonce;
	
	ProcessVendorL(payload->iVids);
	
	if ( iNatDiscovery && payload->iNatDs->Count() )
	{ 
		iNAT_D_Flags = iNatDiscovery->CheckDiscoveryPayloadsL(payload->iNatDs, iChosenProposal_I.iAttrList->iHashAlg,
		                                                     (TUint8*)iCookie_I.Ptr(), (TUint8*)iCookie_R.Ptr(),
                                                   			  iLocalAddr, iLastRemoteAddr);
		if ( iNAT_D_Flags )
			iLastRemoteAddr.SetPort(FLOATED_IKE_PORT); 
	}
	
    //Nonce Payload processing (all methods)
	TBool Status = ProcessNonceL(nonce_payload);
	if ( Status )
	{
	   iStage = 4;
	}   
	CleanupStack::PopAndDestroy();  //payload
	
    return Status;
}


//Process payloads appearing in 3(Aggressive). Order NOT relevant
TBool CIkev1Negotiation::ProcessStage3AggrL(const ThdrISAKMP &aHdr)
{
	CIkev1Payloads* payload = CIkev1Payloads::NewL(aHdr, *this, iDebug);
	if (!payload)
		return EFalse;

	CleanupStack::PushL(payload);

    TUint16 auth_method = iChosenProposal_I.iAttrList->iAuthMethod;

    //Mode-dependent processing
    switch (auth_method)
    {
    case PRE_SHARED:
        //Hash payload processing
        if (!ProcessHashL(payload->iHash))
		{
			CleanupStack::PopAndDestroy();  //payload			
            return EFalse;
		}
        DEBUG_LOG(_L("HASH OK!"));
        break;
    case RSA_SIG:
    case DSS_SIG:
        //Certificate payload processing
        if (!ProcessCertificateArrayL(payload->iCerts))
		{
			CleanupStack::PopAndDestroy();  //payload
            return EFalse;
		}
        //Signature payload processing
        if (!ProcessSignatureL(payload->iSign))
		{	
			CleanupStack::PopAndDestroy();  //payload			
            return EFalse;
		}	
        break;
		
    default:
		break;
    }

	ProcessVendorL(payload->iVids);			
	
	if ( iNatDiscovery && payload->iNatDs->Count() ) { 
		iNAT_D_Flags = iNatDiscovery->CheckDiscoveryPayloadsL(payload->iNatDs, iChosenProposal_I.iAttrList->iHashAlg,
			                                                 (TUint8*)iCookie_I.Ptr(), (TUint8*)iCookie_R.Ptr(),
			                                                  iLocalAddr, iLastRemoteAddr);
		if ( iNAT_D_Flags )
			iLastRemoteAddr.SetPort(FLOATED_IKE_PORT); 
	}

	CleanupStack::PopAndDestroy();  //payload
	
    iFlags |= ISAKMP_HDR_EFLAG; //From now on encryption is used
	IsakmpPhase1CompletedL();
	iStage = 4;                 
    return ETrue;

}


//Process payloads appearing in Stage 5. Order NOT relevant. ONLY for MAIN Mode
// Preshared and signatures: HDR, KE, Nr
// RSA Encr. : HDR, <IDir>PubKey_i, <Nr_b>PubKey_i
// RSA Revised Encr. : HDR, <Nr_b>PubKey_i , <KE_b>Ke_r, <IDir>Ke_r
TBool CIkev1Negotiation::ProcessStage4L(const ThdrISAKMP &aHdr)
{
	CIkev1Payloads* payload = CIkev1Payloads::NewL(aHdr, *this, iDebug);
	if (!payload)
		return EFalse;

	CleanupStack::PushL(payload);

	if (  payload->iSa || !payload->iNonce || !payload->iKe )
	{
		CleanupStack::PopAndDestroy();  //payload
		return EFalse;
	}	

	TUint16 auth_method = iChosenProposal_I.iAttrList->iAuthMethod;	

    if ((auth_method == RSA_SIG) || (auth_method == DSS_SIG))
    {
        //Process the possible CR payloads
        if ( !ProcessCertificateReqArrayL(payload->iCertReqs) )
		{
            CleanupStack::PopAndDestroy();  //payload
            return EFalse;
        }
    }

    //Key Payload processing (all methods)
    if (!ProcessKeyL(payload->iKe))
	{
		CleanupStack::PopAndDestroy();  //payload		
        return EFalse;
	}
	if ( auth_method != PRE_SHARED ) {
		if (!ProcessCertificateArrayL(payload->iCerts)) {
			CleanupStack::PopAndDestroy();  //payload
			return EFalse;
		}
	}	
	TNonceISAKMP* nonce_payload = (TNonceISAKMP*)payload->iNonce;	
	
    //Nonce Payload processing (all modes)
	TBool Status = ProcessNonceL(nonce_payload);
    if (!Status)
    {
		CleanupStack::PopAndDestroy();  //payload
        return EFalse;
    }
	ProcessVendorL(payload->iVids);
	
	if ( iNatDiscovery && payload->iNatDs->Count() )
	{ 
		iNAT_D_Flags = iNatDiscovery->CheckDiscoveryPayloadsL(payload->iNatDs, iChosenProposal_I.iAttrList->iHashAlg,
		                                                   	 (TUint8*)iCookie_I.Ptr(), (TUint8*)iCookie_R.Ptr(),
			                                                  iLocalAddr, iLastRemoteAddr);
		if ( iNAT_D_Flags )
			iLastRemoteAddr.SetPort(FLOATED_IKE_PORT); 
	}

    if ( auth_method == IKE_A_CRACK )
	{
       /*---------------------------------------------------
        * Process message: HDR, [CERT], KEr, Nr, SIG
        * - Verify CRACK signature and if OK
        * - Initialize CRACK authentication
        *---------------------------------------------------*/
		Status = ComputeKeysL();
        if ( Status )  //Generates keying material for encryption stages
		{
			Status = ProcessSignatureL(payload->iSign); 
			if ( Status ) //Signature payload processing and checking
			{
				Status = StartCRACKAuthL();   			   
			} 	   
		}
    }
	else
	{
	    iStage = 5;	    
	}	

	CleanupStack::PopAndDestroy();  //payload			
    return Status;
}


//Process payloads appearing in Stage 6. Order NOT relevant
TBool CIkev1Negotiation::ProcessStage5L(const ThdrISAKMP &aHdr)
{
	CIkev1Payloads* payload = CIkev1Payloads::NewL(aHdr, *this, iDebug);
	if (!payload)
		return EFalse;

	CleanupStack::PushL(payload);
	if (  payload->iSa || payload->iNonce || payload->iKe )
	{
		CleanupStack::PopAndDestroy();  //payload
		return EFalse;
	}	

    TUint16 auth_method      = iChosenProposal_I.iAttrList->iAuthMethod;
	TIdentISAKMP* id_payload = NULL;
	// ID Payload processing (all modes)
	if ( payload->iIds->Count() != 1 )
	{
		CleanupStack::PopAndDestroy();  //payload
		return EFalse;
	}
	id_payload = (TIdentISAKMP*)payload->iIds->At(0);  // The first ID						
	if (!CheckIdentL(id_payload))
	{
		CleanupStack::PopAndDestroy();  //payload						
		return EFalse;
	}	
		//Save the ident payload for HASH_I/R computing
	delete iPeerIdentPayload;
	iPeerIdentPayload = NULL;		
	iPeerIdentPayloadSize=id_payload->GetLength()-sizeof(TPayloadISAKMP);
	iPeerIdentPayload = new (ELeave) TUint8[iPeerIdentPayloadSize];
	Mem::Copy(iPeerIdentPayload,((TUint8 *)id_payload)+sizeof(TPayloadISAKMP),iPeerIdentPayloadSize);

    //Mode-dependent processing
    switch (auth_method)
    {
    case PRE_SHARED:
        //Hash payload processing
        if (!ProcessHashL(payload->iHash))
		{
			CleanupStack::PopAndDestroy();  //payload			
            return EFalse;
		}
		DEBUG_LOG(_L("HASH OK!"));		
        break;
    case RSA_SIG:
    case DSS_SIG:
        //Certificate payload processing
        if (!ProcessCertificateArrayL(payload->iCerts))
		{
			CleanupStack::PopAndDestroy();  //payload									
            return EFalse;
		}
		if (!CertifyRemoteIdentityL(id_payload))
		{
    		DEBUG_LOG(_L("ProcessStage5L RSA_SIG CertifyRemoteIdentityL failed"));
			DEBUG_LOG(_L("AUTHENTICATION_FAILED"));
			SetErrorStatus( KKmdIkeAuthFailedErr );
			SendNotifyL(AUTHENTICATION_FAILED);
			CleanupStack::PopAndDestroy();  //payload			
			return EFalse;
		}
        //Signature payload processing and checking
        if (!ProcessSignatureL(payload->iSign))
		{
			CleanupStack::PopAndDestroy();  //payload
            return EFalse;
		}
		//Process the possible CR payloads (needed if we are responder)
		if ( iRole == RESPONDER )
		{	
		   if ( !ProcessCertificateReqArrayL(payload->iCertReqs) )
		   {
			  CleanupStack::PopAndDestroy();  //payload
			  return EFalse;
		   }
		   if ( !iOwnCert ) 
			   ReadOwnCertL(); // Peer does not required a specific cert. Get any
		}   
        break;
		
    default:
		break;
    }//end switch

    // Process notification payloads
	TInt i = 0;
	while ( i < payload->iNotifs->Count() ) {
		if (!ProcessNotificationL(payload->iNotifs->At(i)))
		{
			CleanupStack::PopAndDestroy();  //payload
			return EFalse;
		}		
        i ++; 		
	}	

	ProcessVendorL(payload->iVids);

	CleanupStack::PopAndDestroy();  //payload			
	
    if (iExchange == ISAKMP_EXCHANGE_ID)
         iStage = 6;
    else iStage = 3;

    return ETrue;
}


//Process payloads appearing in Stage 6(Main)  Order NOT relevant
TBool CIkev1Negotiation::ProcessStage6L(const ThdrISAKMP &aHdr)
{
	CIkev1Payloads* payload = CIkev1Payloads::NewL(aHdr, *this, iDebug);
	if (!payload)
		return EFalse;

	CleanupStack::PushL(payload);
	if (  payload->iSa || payload->iNonce || payload->iKe )
	{
		CleanupStack::PopAndDestroy();  //payload
		return EFalse;
	}	
	

    TUint16 auth_method = iChosenProposal_I.iAttrList->iAuthMethod;
	TIdentISAKMP* id_payload = NULL;
    //ID Payload processing (all modes)
	if ( payload->iIds->Count() != 1 )
	{
		CleanupStack::PopAndDestroy();  //payload
		return EFalse;
	}
	id_payload = (TIdentISAKMP*)payload->iIds->At(0);  // The first ID						
	if (!CheckIdentL(id_payload))
	{
		CleanupStack::PopAndDestroy();  //payload
		return EFalse;
	}	
	//Save the ident payload for HASH_I/R computing
    delete iPeerIdentPayload;
	iPeerIdentPayload = NULL;		
	iPeerIdentPayloadSize=id_payload->GetLength()-sizeof(TPayloadISAKMP);
	iPeerIdentPayload = new (ELeave) TUint8[iPeerIdentPayloadSize];
	Mem::Copy(iPeerIdentPayload,((TUint8 *)id_payload)+sizeof(TPayloadISAKMP),iPeerIdentPayloadSize);

    //Mode-dependent processing
    switch (auth_method)
    {
    case PRE_SHARED:
        //Hash payload processing
        if (!ProcessHashL(payload->iHash))
		{
			CleanupStack::PopAndDestroy();  //payload			
            return EFalse;
		}
		DEBUG_LOG(_L("HASH OK!"));		
        break;
    case RSA_SIG:
    case DSS_SIG:
		//Certificate payload processing
		if (!ProcessCertificateArrayL(payload->iCerts))
		{
			CleanupStack::PopAndDestroy();  //payload									
			return EFalse;
		}
		if (!CertifyRemoteIdentityL(id_payload))
		{
			DEBUG_LOG(_L("ProcessStage6L RSA_SIG CertifyRemoteIdentityL failed"));
			DEBUG_LOG(_L("AUTHENTICATION_FAILED"));
			SetErrorStatus( KKmdIkeAuthFailedErr );
			SendNotifyL(AUTHENTICATION_FAILED);
			CleanupStack::PopAndDestroy();  //payload			
			return EFalse;
		}
		//Signature payload processing and checking
		if (!ProcessSignatureL(payload->iSign))
		{
			CleanupStack::PopAndDestroy();  //payload
			return EFalse;
		}
        break;
    default:
        break;
    }

	// Process notification payloads
	TInt i = 0;
	while ( i < payload->iNotifs->Count() ) {
		if (!ProcessNotificationL(payload->iNotifs->At(i)))
		{
			CleanupStack::PopAndDestroy();  //payload
			return EFalse;
		}		
		i ++; 		
	}	

	ProcessVendorL(payload->iVids);	
    // Internal address payload processing
    ProcessIntAddrL(payload->iIaddr);

	CleanupStack::PopAndDestroy();  //payload	

	return IsakmpPhase1CompletedL();	
	
}

//Process payloads appearing in Stage 7 = CRACK authentication going
TBool CIkev1Negotiation::ProcessStage7L(const ThdrISAKMP &aHdr)
{
TBool status = ETrue;
TInt  crack_status;

    if ( iCRACKneg ) {
       crack_status = iCRACKneg->ExecuteCRACKMsgL(aHdr);
       
       switch ( crack_status ) {
           
           case CRACK_SUCCESS:
               /*-------------------------------------------------------
                * CRACK authentication has been succesfully completed
                * Take actions to start Quick mode negotiation
                *------------------------------------------------------*/
               delete iCRACKneg;
               iCRACKneg = NULL;
               iLastIV.Copy(iIV);  //Saves last IV in Phase 1
               DEBUG_LOG(_L("Last IV Saved!"));
               IsakmpPhase1CompletedL();
               break;
               
           case CRACK_CONTINUE:
               /*----------------------------------------------------------
                * CRACK authentication continues, no further actions needed
                *----------------------------------------------------------*/
               break;

           case CRACK_IGNORE_MSG:
               /*----------------------------------------------------------
                * CRACK authentication continues, received message ignored
                *----------------------------------------------------------*/
               status = EFalse;            
               break;
               
           default:
               /*----------------------------------------------------------
                * CRACK authentication failed, negotiation failed
                *----------------------------------------------------------*/
			   LOG_KMD_EVENT( MKmdEventLoggerIf::KLogError,
			                  R_VPN_MSG_VPN_GW_AUTH_FAIL,
			                  KKmdIkeAuthFailedErr,
			                  iPluginSession->VpnIapId(),
			                  &iRemoteAddr );		   			   
               status    = EFalse;                         
			   SetErrorStatus(KKmdIkeAuthFailedErr);
               AcquireSAErrorResponse(KKmdIkeAuthFailedErr);
               break;

       }       
    }

    return status;
}

//Called as a RESPONDER for PHASE_II
//Checks HASH(1),SA,KE,NONCE,[ID,ID] from INITIATOR
TBool CIkev1Negotiation::ProcessStage1Phase2L(const ThdrISAKMP &aHdr)
{
	CIkev1Payloads* payload = CIkev1Payloads::NewL(aHdr, *this, iDebug);
	if (!payload)
		return EFalse;

	CleanupStack::PushL(payload);
	
	if (  !payload->iSa || !payload->iNonce )
	{
		CleanupStack::PopAndDestroy();  //payload
		return EFalse;
	}	
	
    //Payload Processing

    CProposal_IIList *recv_proposals = new (ELeave) CProposal_IIList(1);    //, *prop
    CleanupStack::PushL(recv_proposals);

    //SA
    if (!ProcessSAL(payload->iSa, (TUint8 *)recv_proposals))
    {
        CleanupStack::PopAndDestroy(2); //recv_proposals + payload
        return EFalse;
    }

    //ID payloads (if existing)
	if ( payload->iIds->Count() == 2 )
	{
       if (!ProcessStage1_II_IDsL(payload->iIds->At(0), payload->iIds->At(1), recv_proposals))
       {
          CleanupStack::PopAndDestroy(2); //recv_proposals + payload
          return EFalse;
       }
	}
	else if ( payload->iIds->Count() != 0 )
	{
		DEBUG_LOG(_L("Unsupported Phase II ID payload count"));		
		CleanupStack::PopAndDestroy(2); //recv_proposals + payload
		return EFalse;
	}	
    //After ID to know what address to search in the "SAD"
	TInt err = BuildProposals2L();
    if (err != KErrNone)
    {
        DEBUG_LOG(_L("NO_PROPOSAL_CHOSEN: No policy matching"));
        SendNotifyL(NO_PROPOSAL_CHOSEN);
        CleanupStack::PopAndDestroy(2); //recv_proposals + payload
        return EFalse;
    }

    //Contains the transform nums matching if multiple proposals
    CTransModifierList *trans_array = new (ELeave) CTransModifierList(1);
    CleanupStack::PushL(trans_array);

    TInt num = iProposal_IIList->MultiMatchL(recv_proposals, iRole == RESPONDER, trans_array);//If RESPONDER relaxed comparison (no lifetimes checked)
    iProposalNum = num; // Set to num not 1
#ifdef _DEBUG
    TBuf<128> err_buf;
#endif    
    if (num < 0)
    {
#ifdef _DEBUG    
        err_buf.Copy(_L("NO_PROPOSAL_CHOSEN: Phase II proposal not accepted - "));
        AppendAttributeError(num, err_buf);
        DEBUG_LOG(err_buf);
#endif        
        SendNotifyL(NO_PROPOSAL_CHOSEN);
        CleanupStack::PopAndDestroy(3); //transarray + recv_proposals + payload
        return EFalse;
    }

    //Copy the chosen transform
    //Actually is the same one as recv_proposals because we should always receive a single proposal with a single transform.
    CreateChosenProposalL(recv_proposals, num, trans_array);

    CleanupStack::PopAndDestroy(2); //transarray + recv_proposals

    //Process the possible NOTIFICATION payloads
    for (TInt i = 0; i < payload->iNotifs->Count(); i++)
    {
        if (!ProcessNotificationL(payload->iNotifs->At(i)))
        {
            CleanupStack::PopAndDestroy();  //payload
            return EFalse;
        }
    }
    
    if (!ProcessNonceL(payload->iNonce))
	{
		CleanupStack::PopAndDestroy();  //payload		
        return EFalse;
	}
    //HASH
    if (!ProcessHash2L(aHdr, payload->iHash, payload->iPadding))
	{
		CleanupStack::PopAndDestroy();  //payload				
        return EFalse;
	} 
    
    if (!ProcessKeyL(payload->iKe))
	{
		CleanupStack::PopAndDestroy();  //payload								
        return EFalse;
	}

	ProcessVendorL(payload->iVids);		
	
    if ( iNatDiscovery ) {
       iNAT_D_Flags |= iNatDiscovery->GetPeerOriginalAddress(payload->iNatOa, iRemoteOriginalAddr, iChosenProp_IIList);
    }
	
	CleanupStack::PopAndDestroy();  //notif_payload_array	
    iStage = 2;
    return ETrue;
}

//Creates a new proposal list from a selected part of another list (Certain proposal and transforms) 
void CIkev1Negotiation::CreateChosenProposalL(CProposal_IIList* aPropList, TInt aPropNum, CTransModifierList *aTransArray)
{

    CProposal_II *prop, *new_propII;
    TAttrib_II *attr_II;
    TChosenAttrib_II *new_attr_II;
    TInt count = aPropList->Count();
    TInt i = 0;
    TInt j = 0;

    //
    // Find selected proroposal from list
    //
    while ( i < count)
    {
        if (aPropList->At(i)->iProposalNum == aPropNum)
            break;
        i ++;
    }
    
    TTransModifier *tmodif;
    TInt64 own_time, own_bytes, peer_time, peer_bytes, responderLifetime_time, responderLifetime_bytes;
    delete iChosenProp_IIList;  //Must be erased because can contain data from previous retransmissions
	iChosenProp_IIList = NULL;	
    iChosenProp_IIList = new (ELeave) CProposal_IIList(1);
    while ( i < count )
    {
        prop = aPropList->At(i);
        if ( prop->iProposalNum != aPropNum)
            break; // Stop, another Proposal 

        new_propII = new (ELeave) CProposal_II();
        CleanupStack::PushL(new_propII);
        new_propII->ConstructL(1);
        iChosenProp_IIList->AppendL(new_propII);
        CleanupStack::Pop();    //new_propII safe

        new_propII->iProtocol = prop->iProtocol;
        new_propII->iNumTransforms = 1; //We only choose 1 transform for each proposal
        new_propII->iProposalNum   = (TUint8)aPropNum;
        new_propII->iSPI.Copy(prop->iSPI);
        tmodif = aTransArray->At(j);
        new_propII->iReplayWindowLength = tmodif->iReplayWindowLength;

        attr_II = prop->iAttrList->At(tmodif->iTransNum);   //look for the chosen transform in the prop
        ComputeLifetimes_II(tmodif->iReducedLifeSecs, tmodif->iReducedLifeKBytes, own_time, own_bytes);
        ComputeLifetimes_II(attr_II->iLifeDurationSecs, attr_II->iLifeDurationKBytes, peer_time, peer_bytes);
        ComputeLifetimes_II(tmodif->iResponderLifetimeSecs, tmodif->iResponderLifetimeKBytes, responderLifetime_time, responderLifetime_bytes);
        
        //Only copy the chosen transform
        new_attr_II = new (ELeave) TChosenAttrib_II();
        CleanupStack::PushL(new_attr_II);
        new_attr_II->Copy(*attr_II);
        if ((peer_time > own_time) && (own_time != 0))
        {
            new_attr_II->iReducedLifeSecs.Set(tmodif->iReducedLifeSecs);
            DEBUG_LOG1(_L("Lifetime bigger than the one set. Reducing to %d"), own_time);
        }
        else
            new_attr_II->iReducedLifeSecs.Set(NULL, 0);
            
        if ((peer_bytes > own_bytes) && (own_bytes != 0))
        {
            new_attr_II->iReducedLifeKBytes.Set(tmodif->iReducedLifeKBytes);
            DEBUG_LOG1(_L("Lifesize bigger than the one set. Reducing to %d"), own_bytes);
        }
        else
            new_attr_II->iReducedLifeKBytes.Set(NULL, 0);
        
        if (responderLifetime_time)
        {
            new_attr_II->iResponderLifetimeSecs = tmodif->iResponderLifetimeSecs;
            DEBUG_LOG1(_L("Responder lifetime set to %d"), responderLifetime_time);        
        }

        if (responderLifetime_bytes)
        {
            new_attr_II->iResponderLifetimeKBytes = tmodif->iResponderLifetimeKBytes;
            DEBUG_LOG1(_L("Responder lifetime in bytes set to %d"), responderLifetime_bytes);        
        }
        
        new_propII->iAttrList->AppendL(new_attr_II);
        CleanupStack::Pop();    //new_attrII safe

        j++;  // Next transform modifer
        i++;  // Next proposal
        
    }
}


//Called as a INITIATOR for PHASE_II
//Checks HASH(1),SA, ,NONCE,[KE] [ID,ID] from RESPONDER
TBool CIkev1Negotiation::ProcessStage2Phase2L(const ThdrISAKMP &aHdr)
{
	CIkev1Payloads* payload = CIkev1Payloads::NewL(aHdr, *this, iDebug);
	if (!payload)
		return EFalse;

	CleanupStack::PushL(payload);

	if (  !payload->iSa || !payload->iNonce  )
	{
		CleanupStack::PopAndDestroy();  //payload
		return EFalse;
	}	

    CProposal_IIList *recv_proposals = new (ELeave) CProposal_IIList(1);
    CleanupStack::PushL(recv_proposals);

	//SA
	if (!ProcessSAL(payload->iSa, (TUint8 *)recv_proposals))
	{
		CleanupStack::PopAndDestroy(2); //recv_proposals + payload
		return EFalse;
	}

	//ID payloads (if existing)
	if ( payload->iIds->Count() == 2 )
	{
		if (!ProcessStage2_II_IDsL(payload->iIds->At(0), payload->iIds->At(1)))
		{
			CleanupStack::PopAndDestroy(2); //recv_proposals + payload
			return EFalse;
		}
	}
	else if ( payload->iIds->Count() != 0 )
	{
		DEBUG_LOG(_L("Unsupported Phase II ID payload count"));		
				CleanupStack::PopAndDestroy(2); //recv_proposals + payload
		return EFalse;
	}	
	
    //Contains the transform nums matching if multiple proposals
    CTransModifierList *trans_array = new (ELeave) CTransModifierList(1);
    CleanupStack::PushL(trans_array);

    //Check the received proposals match the proposed one (Got from
    //acquire msg.) 
   	TInt num = iProposal_IIList->MultiMatchL(recv_proposals, iRole == RESPONDER, trans_array);//If RESPONDER relaxed comparison (no lifetimes checked)
#ifdef _DEBUG   	
    TBuf<128> err_buf;
#endif    
    if (num < 0)
    {
#ifdef _DEBUG    
        err_buf.Copy(_L("BAD_PROPOSAL_SYNTAX: Phase II reply doesn't match proposal - "));
        AppendAttributeError(num, err_buf);
        DEBUG_LOG(err_buf);
#endif        
        SetErrorStatus( KKmdIkeNegotFailed );
        SendNotifyL(BAD_PROPOSAL_SYNTAX);
        CleanupStack::PopAndDestroy(3); //transarray + recv_proposals + payload
        return EFalse;
    }
    // iProposalNum set to correspond local proposal numbering
    iProposalNum = trans_array->At(0)->iPropNum; 
    //Copy the chosen transform
    CreateChosenProposalL(recv_proposals, num, trans_array);

    CleanupStack::PopAndDestroy(2); //transarray + recv_proposals

	//Process the possible NOTIFICATION payloads
	for (TInt i = 0; i < payload->iNotifs->Count(); i++)
	{
		if (!ProcessNotificationL(payload->iNotifs->At(i)))
		{
			CleanupStack::PopAndDestroy();  //payload
			return EFalse;
		}
	}
	//NONCE
   	if (!ProcessNonceL(payload->iNonce))
   	{
    	CleanupStack::PopAndDestroy();  //payload		
	    return EFalse;
	}
	//HASH
	if (!ProcessHash2L(aHdr, payload->iHash, payload->iPadding))
	{
		CleanupStack::PopAndDestroy();  //payload				
		return EFalse;
	} 
	//KEY (if present (PFS))
	if (!ProcessKeyL(payload->iKe))
	{
	    SetErrorStatus( KKmdIkeNegotFailed );
		SendNotifyL(PAYLOAD_MALFORMED);					
		CleanupStack::PopAndDestroy();  //payload
		return EFalse;
	}   

	ProcessVendorL(payload->iVids);
	
	if ( iNatDiscovery ) {
		iNAT_D_Flags |= iNatDiscovery->GetPeerOriginalAddress(payload->iNatOa, iRemoteOriginalAddr, iChosenProp_IIList);
	}

    if ( iRecvFlags & ISAKMP_HDR_CFLAG )   //Commit Bit set       
    {
        iFlags |= ISAKMP_HDR_CFLAG; //Sets the Commit bit if this side set it else
        DEBUG_LOG(_L("SAD update delayed until CONNECTED received"));
    }

	CleanupStack::PopAndDestroy();  //payload				
    iStage = 3;
    return ETrue;

}

TBool CIkev1Negotiation::ProcessStage3Phase2L(const ThdrISAKMP &aHdr)
{
	CIkev1Payloads* payload = CIkev1Payloads::NewL(aHdr, *this, iDebug);
	if (!payload)
		return EFalse;

	CleanupStack::PushL(payload);

	if ( payload->iSa || payload->iNonce || payload->iKe || payload->iIds->Count() != 0 ) 
	{
		CleanupStack::PopAndDestroy();  //payload
		return EFalse;
	}	
	
    //Hash Payload
	TBool Status = ProcessHash2L(aHdr, payload->iHash, payload->iPadding);
    if ( Status )
	{
       	//END OF THE PHASE II (Quick mode) negotiation.
		//Now we need to update the PFKEY SA database
		ProcessVendorL(payload->iVids);				
		UpdateSADatabaseL();
		iStage = 4;
	}
	CleanupStack::PopAndDestroy();  //payload		
    return Status;
}

TBool CIkev1Negotiation::ProcessCONNECTEDL(const ThdrISAKMP &aHdr)
{
	CIkev1Payloads* payload = CIkev1Payloads::NewL(aHdr, *this, iDebug);
	if (!payload)
		return EFalse;

	CleanupStack::PushL(payload);

	if ( !payload->iHash || (payload->iNotifs->Count() != 1) || payload->iSa ||
		 payload->iNonce || payload->iKe || (payload->iIds->Count() != 0) ) 
	{
		DEBUG_LOG(_L("PAYLOAD_MALFORMED (no hash or notfic payload)"));
		SetErrorStatus( KKmdIkeNegotFailed );
		SendNotifyL(PAYLOAD_MALFORMED);
		CleanupStack::PopAndDestroy();  //payload
		return EFalse;
	}	

    //Checks if the hash value is OK. Here because need the
    //notification payload
	const TNotificationISAKMP* notif_payload = payload->iNotifs->At(0);
	TBool Status = VerifyInformationalHashL(payload->iHash, notif_payload, iMessageId);	 
    if ( Status )
    {  //Hash OK
		if ((notif_payload->GetDOI() == IPSEC_DOI) && (notif_payload->GetMsgType() == CONNECTED))
		{
			//END OF THE PHASE II (Quick mode) negotiation.
			//Now we need to update the PFKEY SA database
			DEBUG_LOG(_L("CONNECTED message received. Updating SAD"));
			UpdateSADatabaseL();
			iStage = 5;			
		}
    }
    else   
    {
		DEBUG_LOG(_L("AUTHENTICATION_FAILED (Informational hash)"));
		SetErrorStatus( KKmdIkeAuthFailedErr );
		SendNotifyL(AUTHENTICATION_FAILED);
    }
	CleanupStack::PopAndDestroy();  //payload		
    return Status;
}

//returns KErrNone if OK, otherwise error already treated.
TBool CIkev1Negotiation::ProcessSAL(const TSAISAKMP *aSA, TUint8 *aRecvProposals)
{
    //payload not present
    if (!aSA)
    {
        DEBUG_LOG(_L("NO SA PAYLOAD"));
        SetErrorStatus( KKmdIkeNegotFailed );
        SendNotifyL(PAYLOAD_MALFORMED);
        return EFalse;
    }

    //requires a special length check to know the proposals and transforms are not bigger than 
    //the size specified in th SA
    TUint32 SALengthLeft = aSA->GetLength() - aSA->Size();

    TUint8 next_payload = aSA->GetPayload();
    if ((next_payload == ISAKMP_PAYLOAD_P) || (next_payload == ISAKMP_PAYLOAD_T))
    {
        DEBUG_LOG(_L("INVALID_PAYLOAD_TYPE (Bad next payload for the SA)"));
        SetErrorStatus( KKmdIkeNegotFailed );
        SendNotifyL(INVALID_PAYLOAD_TYPE);   // Payloads and transforms are processed as a part of the SA,
        return EFalse;
    }

    TUint32 doi=aSA->GetDOI();
    if (!CheckDOI(doi))
    {
        DEBUG_LOG(_L("DOI_NOT_SUPPORTED in SA payload"));
        SetErrorStatus( KKmdIkeNegotFailed );
        SendNotifyL(DOI_NOT_SUPPORTED);  //send the informational exchange
        return EFalse;
    }

    if (iStage == 1)
        iDOI = doi;

    //Process SITuation depending on DOI. In this implementation always is IPSEC DOI
    if (!CheckSituationL(aSA->GetSIT()))
        return EFalse;

    if (aSA->HasLDId()) //If no Labeled Domain Identifier no more processing for the SA
    {
        DEBUG_LOG(_L("Label Domain Identifier (LDI) not supported"));
        return EFalse;
    }
    
    if (iPhase==PHASE_I)    //Only used in Phase_I. PHASE_II hashs check it directly from received paylaod
    {
        //Store the SA payload for further calculations in Hash.
        if (iRole==RESPONDER)
        {
            iSAPayloadSize = aSA->GetLength()-sizeof(TPayloadISAKMP);
            delete iSAPayload;
			iSAPayload = NULL;
            iSAPayload = new (ELeave) (TUint8[iSAPayloadSize]); //Generic payload NOT included
            Mem::Copy(iSAPayload,(((TUint8 *)aSA)+sizeof(TPayloadISAKMP)),iSAPayloadSize);
        }
    }
    const TPayloadISAKMP *payload = aSA->Payload();
    return ProcessProposalsL(payload, SALengthLeft, aRecvProposals);
}


//Do the process required for proposals and transforms. The aPayload given must be a proposal
TBool CIkev1Negotiation::ProcessProposalsL(const TPayloadISAKMP *aPayload, TUint32 aLengthLeft, TUint8 *aRecvProposals)
{
    TProposalISAKMP *proposal;
    const TPayloadISAKMP *ppayload=aPayload;
    TUint8 payType = ppayload->GetPayload();
    TUint32 len_left;
    CProposal_IIList *recv_proposals = (CProposal_IIList *)aRecvProposals;
    CProposal_II *auxProp_II;
    TBool found = EFalse;   //At least 1 transform matching

    //Many Proposals. The RESPONDER MUST choose a transform for each proposal or reject the 
    //full suite of attributes.
    do  
    {       
        //General payload check
        if ((payType != ISAKMP_PAYLOAD_NONE) && (payType != ISAKMP_PAYLOAD_P))
        {
            DEBUG_LOG(_L("INVALID_PAYLOAD_TYPE (Bad next payload for the proposal)"));
            SetErrorStatus( KKmdIkeNegotFailed );
            SendNotifyL(INVALID_PAYLOAD_TYPE);
            return EFalse;
        }

        if (aPayload->GetReserved() != 0)   //Must be always 0
        {
            DEBUG_LOG(_L("INVALID RESERVED FIELD"));
            SetErrorStatus( KKmdIkeNegotFailed );
            SendNotifyL(PAYLOAD_MALFORMED);
            return EFalse;
        }
        
        //requires special length check
        if ((aPayload->GetLength() < MIN_ISAKMP_PAYLOAD_SIZE) || (aPayload->GetLength() > aLengthLeft))
        {
            DEBUG_LOG(_L("BAD PAYLOAD SIZE"));
            SetErrorStatus( KKmdIkeNegotFailed );
            SendNotifyL(PAYLOAD_MALFORMED);
            return EFalse;
        }

        aLengthLeft -= aPayload->GetLength();   //dcreases the length of the prop. and its transforms

        proposal = TProposalISAKMP::Ptr(ppayload);
        
        
        if (!CheckProtocolL(proposal->GetProtocol()))
            return EFalse;

        if (!CheckSPIL(proposal))
            return EFalse;

        len_left = proposal->GetLength() - (proposal->Size() + proposal->GetSPISize()); 
        //len_left contains the length of the transforms only

        TInt ret = KErrNotFound;
        if (iPhase == PHASE_I)
        {
            iChosenProposal_I.iProtocol = proposal->GetProtocol();
            iChosenProposal_I.iSPI.Copy((TUint8 *)proposal->SPI(), proposal->GetSPISize());
            iChosenProposal_I.iNumTransforms = 1;   //Phase I only one transf chosen
            iChosenProposal_I.iProposalNum = proposal->GetNum();//Not compulsory but preferable to speed up the search process in the peer
            
            ret = ProcessTransformsL(ppayload,len_left);
            
            if (ret == KErrNone)
                return ETrue;//valid transform found
            else if (ret != KErrNotFound)
            {
                DEBUG_LOG(_L("ATTRIBUTES_NOT_SUPPORTED"));
                SetErrorStatus( KKmdIkeNegotFailed );
                SendNotifyL(ATTRIBUTES_NOT_SUPPORTED);
                return EFalse;
            }
            //If not found there may be other proposal to check
        }
        else    //PHASE_II
        {
            auxProp_II = new (ELeave) CProposal_II();
            CleanupStack::PushL(auxProp_II);
            auxProp_II->ConstructL(proposal->GetNumTrans());
            auxProp_II->iProtocol=proposal->GetProtocol();
            auxProp_II->iSPI.Copy((TUint8 *)proposal->SPI(), proposal->GetSPISize());
            auxProp_II->iNumTransforms = proposal->GetNumTrans();   //Number of transforms in the proposal
            auxProp_II->iProposalNum = proposal->GetNum();  //Proposal num
            
            ret = ProcessTransforms2L(ppayload, auxProp_II, len_left);
            if (ret==KErrNone)//valid transform found
            {   
                //Adds the new proposal to the list of chosen proposals
                recv_proposals->AppendL(auxProp_II);
                CleanupStack::Pop();    //auxProp_II safe
                found = ETrue;
                //go for the next proposal
            }
            else
            {
                DEBUG_LOG(_L("ATTRIBUTES_NOT_SUPPORTED"));
                SendNotifyL(ATTRIBUTES_NOT_SUPPORTED);
                CleanupStack::PopAndDestroy();  //delete auxProp_II;    //delete the current proposal
                return EFalse;
            }
        }

        payType = ppayload->GetPayload();
        ppayload = ppayload->Next();    //Next payload if there's any
    } while (payType!=ISAKMP_PAYLOAD_NONE); //Proposal loop

    if (!found)
    {
        DEBUG_LOG(_L("ATTRIBUTES_NOT_SUPPORTED"));
        SendNotifyL(ATTRIBUTES_NOT_SUPPORTED);
        return EFalse;
    }

    return found;
}

//processes all the  PHASE I transforms. The parameter payload is the proposal containing
//the transforms to be able to access the # of transforms.
//Returns if any transform accepted (KErrNone) or not (KErrNotFound) or processing error (KErrGeneral)
TInt CIkev1Negotiation::ProcessTransformsL(const TPayloadISAKMP *aPayload,TUint32 aLengthLeft)
{
    DEBUG_LOG(_L("-> CIkev1Negotiation::ProcessTransformsL()"));
    TUint16 reason;             
    TInt ret = KErrGeneral;
     const TTransformISAKMP *transf;
    const TProposalISAKMP *proposal=TProposalISAKMP::Ptr(aPayload);
    //First transform. Not Next() because would be the next proposal or non-sa payload
    const TPayloadISAKMP *tpayload=proposal->Payload();
    
    TInt payType = tpayload->GetPayload();  //Type of the payload following the first transform
    transf = TTransformISAKMP::Ptr(tpayload);
    
    TInt numTransf  = (TInt)transf->GetNum();  // First transform number
    TInt lastTransf = numTransf + (TInt)proposal->GetNumTrans(); // Last transform number
    
    while ( numTransf < lastTransf ) 
    {
        //only permited payload codes
        if ((payType != ISAKMP_PAYLOAD_NONE) && (payType != ISAKMP_PAYLOAD_T))
        {
            DEBUG_LOG(_L("INVALID_PAYLOAD_TYPE (Bad next payload for the transform)"));
            SendNotifyL(INVALID_PAYLOAD_TYPE);
            return KErrGeneral;
        }

        if (tpayload->GetReserved() != 0)   //Must be always 0
        {
            DEBUG_LOG(_L("INVALID RESERVED FIELD"));
            SendNotifyL(PAYLOAD_MALFORMED);
            return KErrGeneral;
        }
        
        //requires special length check
        if ((tpayload->GetLength() < MIN_ISAKMP_PAYLOAD_SIZE) || (tpayload->GetLength() > aLengthLeft))
        {
            DEBUG_LOG(_L("BAD PAYLOAD SIZE"));
            SendNotifyL(PAYLOAD_MALFORMED);
            return KErrGeneral;
        }
        
        if (!CheckTransformID(PROTO_ISAKMP,transf->GetID()))
            {
            DEBUG_LOG(_L(" Continue"));

            numTransf++;                          // Next supposed transform #              
            payType = tpayload->GetPayload();
            tpayload = tpayload->Next();    //next payload (transform)
            transf = TTransformISAKMP::Ptr(tpayload);

            continue;   //If fails, transform discarded!, not error
            }
        
        if ( transf->GetNum() != numTransf )  //Not the correct #
        {
            DEBUG_LOG1(_L("BAD_PROPOSAL_SYNTAX (Non conscutive transform number (%d)"),transf->GetNum());
            SendNotifyL(BAD_PROPOSAL_SYNTAX);
            DEBUG_LOG(_L("<- CIkev1Negotiation::ProcessTransformsL() KErrGeneral"));
            return KErrGeneral;
        }

        if (transf->GetReserved() != 0 )    //Should be always 0
        {
            DEBUG_LOG(_L("INVALID RESERVED FIELD"));
            SendNotifyL(PAYLOAD_MALFORMED);
            DEBUG_LOG(_L("<- CIkev1Negotiation::ProcessTransformsL() KErrGeneral"));
            return KErrGeneral;
        }

        numTransf++;                          // Next supposed transform #              
        //Attributes to be checked depending on Transf ID
        TAttrib attrib;
        
        ret = ProcessAttributesL(tpayload, &attrib);
        
        if (ret != KErrNone)
        {
            if (ret != KErrNotFound)
                {
                DEBUG_LOG(_L("<- CIkev1Negotiation::ProcessTransformsL() KErrGeneral"));
                return KErrGeneral; //Error in the attributes. Already reported
                }
            //Not accepted but correct
        }
        else    // Accepted attributes if (AttrChosen(attrib))
        {  
            //Checks the response or proposal is the same as one of our proposals
            TAttrib *attr_list = iProposal_I.iAttrList;
            TInt ret = KErrNotFound;
#ifdef _DEBUG            
            TBuf<256> buf;
#endif            
            while (attr_list && (ret != KErrNone) )
            {
                ret = attrib.Compare(*attr_list, iRole==RESPONDER); //If RESPONDER relaxed comparison (no lifetimes checked)                
                if (ret != KErrNone)
                {
#ifdef _DEBUG                
                    DEBUG_LOG1(_L("Transform #%d not matching proposal Reason: "), attrib.iTransformNum);
                    AppendAttributeError(ret, buf);
                    DEBUG_LOG(buf);
#endif                    
                }
                attr_list = attr_list->iNext;   //next transform proposed

            }
            if (ret == KErrNone)
            {
               *iChosenProposal_I.iAttrList = attrib;       
               DEBUG_LOG(_L("<- CIkev1Negotiation::ProcessTransformsL() KErrNone"));
               return KErrNone; //If the attibute are supported there's no need to check more SA
            }   
            else    //No proposal matches
            {
               if ( numTransf == lastTransf ) //Is there more transforms to check
               {    
                  if (iRole == INITIATOR)
                  {
                      reason = BAD_PROPOSAL_SYNTAX;
                      DEBUG_LOG(_L("BAD_PROPOSAL_SYNTAX (Phase I reply don't match proposal)"));
                      SetErrorStatus( KKmdIkeNegotFailed );
                  }                           
                  else
                  {
                      reason = NO_PROPOSAL_CHOSEN;                    
                      DEBUG_LOG(_L("NO_PROPOSAL_CHOSEN (Received Proposal doesn't match accepted attributes (Check own proposals))"));
                  }           
                  SendNotifyL(reason);
                  DEBUG_LOG(_L("<- CIkev1Negotiation::ProcessTransformsL() KErrNotFound"));
                  return KErrNotFound;
               }    
            }
        }

        payType = tpayload->GetPayload();
        tpayload = tpayload->Next();    //next payload (transform)
        transf = TTransformISAKMP::Ptr(tpayload);
        
    }

    DEBUG_LOG(_L("<- CIkev1Negotiation::ProcessTransformsL() KErrNotFound"));

    //if reaches this point meanse no transform accepted in this proposal
    //Notifies to the upper level
    return KErrNotFound;

}


//Logs the error when comparing a proposal
//void CIkev1Negotiation::LogAttributeError(TInt aTransformNum, TInt aErr)
void CIkev1Negotiation::AppendAttributeError(TInt aErr, TDes &aBuf) const
{
#ifndef _DEBUG
    (void)aErr;
    (void)aBuf;
#endif

#ifdef _DEBUG
    switch (aErr)
    {
    case KErrNotFound:
        aBuf.Append(_L("No proposals\n"));
        break;
    case KErrTransformID:
        aBuf.Append(_L("Different Transform Algorithm\n"));
        break;
    case KErrEncrAlg:
        aBuf.Append(_L("Different Encryption Algorithm\n"));
        break;
    case KErrHashAlg:
        aBuf.Append(_L("Different Hash Algorithm\n"));
        break;
    case KErrAuthMethod:
        aBuf.Append(_L("Different Authentication Method\n"));
        break;
    case KErrGroupDesc:
        aBuf.Append(_L("Different Group Description\n"));
        break;
    case KErrGroupType:
        aBuf.Append(_L("Different Group Type\n"));
        break;
    case KErrGroupPrime:
        aBuf.Append(_L("Different Group Prime\n"));
        break;
    case KErrGroupGen1:
        aBuf.Append(_L("Different Group Generator 1\n"));
        break;
    case KErrGroupGen2:
        aBuf.Append(_L("Different Group Generator 2\n"));
        break;
    case KErrGroupCurveA:
        aBuf.Append(_L("Different Group Curve A\n"));
        break;
    case KErrGroupCurveB:
        aBuf.Append(_L("Different Group Curve A\n"));
        break;
    case KErrPRF:
        aBuf.Append(_L("Different PRF\n"));
        break;
    case KErrKeyLength:
        aBuf.Append(_L("Different Key Length\n"));
        break;
    case KErrFieldSize:
        aBuf.Append(_L("Different Field Size\n"));
        break;
    case KErrGroupOrder:
        aBuf.Append(_L("Different Group Order\n"));
        break;
    case KErrLifeTime:
        aBuf.Append(_L("Different Lifetime\n"));
        break;
    case KErrLifeSize:
        aBuf.Append(_L("Different LifeSize\n"));
        break;
    case KErrEncMode:
        aBuf.Append(_L("Different Encapsulation Mode\n"));
        break;
    case KErrAuthAlg:
        aBuf.Append(_L("Different Authentication Algorithm\n"));
        break;
    case KErrKeyRounds:
        aBuf.Append(_L("Different Key Rounds\n"));
        break;
    case KErrComprDicSize:
        aBuf.Append(_L("Different Compress Dictionary Size\n"));
        break;
    case KErrComprPrivAlg:
        aBuf.Append(_L("Different Compress Private Algorithm\n"));
        break;
    case KErrTransformNum:
        aBuf.Append(_L("Different Transform Num.\n"));
        break;
    case KErrPropProtocol:
        aBuf.Append(_L("Proposals have different protocol.\n"));
        break;
    case KErrNoTransforms:
        aBuf.Append(_L("Proposal has no transforms \n"));
        break;
    case KErrNoRemoteProposals:
        aBuf.Append(_L("Remote Proposals list is empty\n"));
        break;
    case KErrNoLocalProposals:
        aBuf.Append(_L("Local Proposals list is empty\n"));
        break;
    case KErrPropNumberMismatch:
        aBuf.Append(_L("The proposals lists have diferent number of AND'd proposals"));
        break;
    default:
        aBuf.Append(_L("Unknown\n"));
    }
#endif  
}

//processes all the PHASE II transforms. The parameter payload is the proposal containing
//the transforms to be able to access the # of transforms.
//Returns if any transform accepted (KErrNone) or not (KErrNotFound) or processing error (KErrGeneral)
TInt CIkev1Negotiation::ProcessTransforms2L(const TPayloadISAKMP *aPayload,CProposal_II *aProp,TUint32 aLengthLeft)
{
    DEBUG_LOG(_L("-> CIkev1Negotiation::ProcessTransforms2L()"));

    TInt ret;
    const TTransformISAKMP *transf;
    const TProposalISAKMP *proposal=TProposalISAKMP::Ptr(aPayload);
    //First transform. Not Next() because would be the next proposal or non-sa payload
    const TPayloadISAKMP *tpayload = proposal->Payload();

    TAttrib_II *attr_II = NULL; 
    TInt payType = tpayload->GetPayload();  //Type of the payload following the first transform
    transf = TTransformISAKMP::Ptr(tpayload);
    
    TInt numTransf  = (TInt)transf->GetNum();  // First transform number
    TInt lastTransf = numTransf + (TInt)proposal->GetNumTrans(); // Last transform number   

    while ( numTransf < lastTransf ) 
    {
        //only permited payload codes
        if ((payType != ISAKMP_PAYLOAD_NONE) && (payType != ISAKMP_PAYLOAD_T))
        {
            DEBUG_LOG(_L("INVALID_PAYLOAD_TYPE (Bad next payload for the transform)"));
            SendNotifyL(INVALID_PAYLOAD_TYPE);
            return KErrGeneral;
        }
        
        if (tpayload->GetReserved() != 0)   //Must be always 0
        {
            DEBUG_LOG(_L("INVALID RESERVED FIELD"));
            SendNotifyL(PAYLOAD_MALFORMED);
            return KErrGeneral;
        }
        
        //requires special length check
        if ((tpayload->GetLength() < MIN_ISAKMP_PAYLOAD_SIZE) || (tpayload->GetLength() > aLengthLeft))
        {
            DEBUG_LOG(_L("BAD PAYLOAD SIZE"));
            SendNotifyL(PAYLOAD_MALFORMED);
            return KErrGeneral;
        }
        
        if (!CheckTransformID(aProp->iProtocol,transf->GetID()))
            {
            DEBUG_LOG(_L(" Transform doesn't match, moving on to the next one"));
            payType = tpayload->GetPayload();
            tpayload = tpayload->Next();    //next payload (transform)
            transf = TTransformISAKMP::Ptr(tpayload);
            numTransf++;                          // Next supposed transform #
            continue;   //If fails, transform discarded!, not error
            }
        
        if ( transf->GetNum() != numTransf )  //Not the correct #. Must be consecutive
        {   
            DEBUG_LOG1(_L("BAD_PROPOSAL_SYNTAX (Non conscutive transform number (%d)"),transf->GetNum());
            SendNotifyL(BAD_PROPOSAL_SYNTAX);
            return KErrGeneral;
        }

        if (transf->GetReserved() != 0 )    //Must be always 0
        {
            DEBUG_LOG(_L("INVALID RESERVED FIELD"));
            SendNotifyL(PAYLOAD_MALFORMED);
            return KErrGeneral;
        }
        
        //Attributes to be checked depending on Transf ID
        attr_II = new (ELeave) TAttrib_II();
        CleanupStack::PushL(attr_II);
        ret = ProcessAttributes2L(tpayload, attr_II, aProp->iProtocol);
        if (ret != KErrNone)    //Some invalid attribute
        {
            DEBUG_LOG(_L(" Invalid attribute"));
            CleanupStack::PopAndDestroy();  //delete attr_II;
            if (ret != KErrNotFound)
                return KErrGeneral; //Error in the attributes. Already reported
            else    //Invalid transform- Ignored
            {
                DEBUG_LOG2(_L("Transform %d of proposal %d ignored"), transf->GetNum(), aProp->iProposalNum);
            }
        }
        else    //Accepted, must check if really proposed or if matches the configuration if RESPONDER
        {
            DEBUG_LOG(_L(" Adding new attribute"));
            aProp->iAttrList->AppendL(attr_II); //Add the new attribute
            CleanupStack::Pop();    //attr_II saf
        }
        payType = tpayload->GetPayload();
        tpayload = tpayload->Next();    //next payload (transform)
        transf = TTransformISAKMP::Ptr(tpayload);
        numTransf++;                          // Next supposed transform #
        
    }

    //No valid transform found
    if (aProp->iAttrList->Count() == 0)
        {
        DEBUG_LOG(_L("<- CIkev1Negotiation::ProcessTransforms2L() KErrNotFound"));
        return KErrNotFound;
        }

    DEBUG_LOG(_L("<- CIkev1Negotiation::ProcessTransforms2L() KErrNone"));
    return KErrNone;
}


//Copies and checks the values of the attributes. The parameter aPayload must be a transform
//aAttrib will contain the sent attributes if the return value is KErrNone, otherwise should be
//ignore because there was an error reading them (KErrGeneral) or the transform was not accepted (KErrNotFound)
TInt CIkev1Negotiation::ProcessAttributesL(const TPayloadISAKMP *aPayload, TAttrib *aAttrib)
{
    const TTransformISAKMP *transf = TTransformISAKMP::Ptr(aPayload);
    TInt length= aPayload->GetLength() - sizeof(*transf); //To process the attribs
    TDataISAKMP *attr= transf->SAAttrib();
    
    aAttrib->iTransformNum = transf->GetNum();
    aAttrib->iTransformID  = transf->GetID();
    //
	// Store parameters for extended authentication
	//
	aAttrib->iXauthUsed = iHostData->iUseXauth;
	aAttrib->iRole      = iRole;
	
    TUint16 lifeType  = 0; //No type assigned yet
    TUint32 lifeValue = 0; //No value assigned yet
    TUint16 val;
    
    while ( length>0 )
    {
        length = length - attr->Size();
        if (length<0)   //Mismatch between lengths!!!
        {
            DEBUG_LOG(_L("BAD_PROPOSAL_SYNTAX (Length mismatch in the attibutes)"));
            SendNotifyL(BAD_PROPOSAL_SYNTAX);
            return KErrGeneral;
        }
        switch (attr->Type())
        {
        case OAKLEY_ATTR_TYPE_ENCR_ALG:
            if (!CheckEncrAlg(attr->Value()))
                return KErrNotFound;
            aAttrib->iEncrAlg=attr->Value();
            break;
        case OAKLEY_ATTR_TYPE_HASH_ALG:
            if (!CheckHashAlg(attr->Value()))
                return KErrNotFound;
            aAttrib->iHashAlg = attr->Value();
            break;
        case OAKLEY_ATTR_TYPE_AUTH_METH:
            val = CTransNegotiation::GetAuthMethod(attr->Value(), iHostData->iUseXauth, iRole);
            if (!CheckAuthMethod(val))
                return KErrNotFound;
            aAttrib->iAuthMethod = val;
            break;
        case OAKLEY_ATTR_TYPE_GROUP_DESC:
            if (!CheckGroupDesc(attr->Value()))
                return KErrNotFound;
            aAttrib->iGroupDesc = attr->Value();
            break;
        case OAKLEY_ATTR_TYPE_GROUP_TYPE:
            if (!CheckGroupType(attr->Value()))
                return KErrNotFound;
            aAttrib->iGroupType = attr->Value();
            break;
        case OAKLEY_ATTR_TYPE_GROUP_PRIME:
            if (attr->IsBasic())
            {
                val = attr->Value();
                aAttrib->iGroupPrime.Copy((TUint8*)&val, sizeof(val));
            }
            else
                aAttrib->iGroupPrime.Copy(attr->VarValue(),attr->Length());
            break;
        case OAKLEY_ATTR_TYPE_GROUP_GEN1:
            if (attr->IsBasic())
            {
                val = attr->Value();
                aAttrib->iGroupGen1.Copy((TUint8*)&val, sizeof(val));
            }
            else
                aAttrib->iGroupGen1.Copy(attr->VarValue(),attr->Length());
            break;
        case OAKLEY_ATTR_TYPE_GROUP_GEN2:
            if (attr->IsBasic())
            {
                val = attr->Value();
                aAttrib->iGroupGen2.Copy((TUint8*)&val, sizeof(val));
            }
            else
                aAttrib->iGroupGen2.Copy(attr->VarValue(),attr->Length());
            break;
        case OAKLEY_ATTR_TYPE_GROUP_CRVA:
            if (attr->IsBasic())
            {
                val = attr->Value();
                aAttrib->iGroupCurveA.Copy((TUint8*)&val, sizeof(val));
            }
            else
                aAttrib->iGroupCurveA.Copy(attr->VarValue(),attr->Length());
            break;
        case OAKLEY_ATTR_TYPE_GROUP_CRVB:
            if (attr->IsBasic())
            {
                val = attr->Value();
                aAttrib->iGroupCurveB.Copy((TUint8*)&val, sizeof(val));
            }
            else
                aAttrib->iGroupCurveB.Copy(attr->VarValue(),attr->Length());
            break;
        case OAKLEY_ATTR_TYPE_LIFE_TYPE:
            lifeType = attr->Value();
            if (!CheckLifeType(lifeType))
            {
                DEBUG_LOG(_L("BAD_PROPOSAL_SYNTAX (Invalid lifetime type)"));
                SendNotifyL(BAD_PROPOSAL_SYNTAX);
                return KErrGeneral;
            }
            break;
        case OAKLEY_ATTR_TYPE_LIFE_DUR:
            if (attr->IsBasic())
            {
                lifeValue = ByteOrder::Swap32(attr->Value());
                if (lifeType == SECONDS)
                    aAttrib->iLifeDurationSecs.Copy((TUint8 *)&lifeValue, sizeof(lifeValue));
                else if (lifeType == KBYTES)
                    aAttrib->iLifeDurationKBytes.Copy((TUint8 *)&lifeValue, sizeof(lifeValue));
                else
                {
                    DEBUG_LOG(_L("BAD_PROPOSAL_SYNTAX (Invalid lifetime type)"));
                    SendNotifyL(BAD_PROPOSAL_SYNTAX);
                    return KErrGeneral;
                }
            }
            else    //Not basic
            {
                if (lifeType == SECONDS)
                    aAttrib->iLifeDurationSecs.Copy(attr->VarValue(),attr->Length());
                else if (lifeType == KBYTES)
                    aAttrib->iLifeDurationKBytes.Copy(attr->VarValue(),attr->Length());
                else
                {
                    DEBUG_LOG(_L("BAD_PROPOSAL_SYNTAX (Invalid lifetime type)"));
                    SendNotifyL(BAD_PROPOSAL_SYNTAX);
                    return KErrGeneral;
                }
            }
            break;
        case OAKLEY_ATTR_TYPE_PRF:
            if (!CheckPRF(attr->Value()))
                return KErrNotFound;
            aAttrib->iPRF=attr->Value();
            break;
        case OAKLEY_ATTR_TYPE_KEY_LEN:
            aAttrib->iKeyLength = attr->Value();
            break;
        case OAKLEY_ATTR_TYPE_FIELD_SIZE:
            if (!CheckFieldSize(attr->Value()))
                return KErrNotFound;
            aAttrib->iFieldSize=attr->Value();
            break;
        case OAKLEY_ATTR_TYPE_GROUP_ORDER:
            if (attr->IsBasic())
            {
                val = attr->Value();
                aAttrib->iGroupOrder.Copy((TUint8*)&val, sizeof(val));
            }
            else
                aAttrib->iGroupOrder.Copy(attr->VarValue(),attr->Length());
            break;
        default:
            DEBUG_LOG(_L("ATTRIBUTES_NOT_SUPPORTED (Invalid attribute number)"));
            SendNotifyL(ATTRIBUTES_NOT_SUPPORTED);
            return KErrGeneral;
        }
        attr = attr->Next();
    }
    
    //Done here to ensure both received
    if (aAttrib->iKeyLength !=0)
        if (!CheckKeyLength(aAttrib->iKeyLength, (TUint8)aAttrib->iEncrAlg ,PROTO_ISAKMP))
            return KErrNotFound;

    return KErrNone;
}

//Copies and checks the values of the attributes. The parameter aPayload must be a transform
//aAttrib will contain the sent attributes if the return value is KErrNone, otherwise should be
//ignore because there was an error reading them (KErrGeneral) or the transform was not accepted (KErrNotFound)
TInt CIkev1Negotiation::ProcessAttributes2L(const TPayloadISAKMP *aPayload, TAttrib_II *aAttrib,TUint8 aProtocol)
{
    const TTransformISAKMP *transf = TTransformISAKMP::Ptr(aPayload);
    TInt length= aPayload->GetLength() - sizeof(*transf); //To process the attribs
    
    aAttrib->iTransformNum = transf->GetNum();
    aAttrib->iTransformID = transf->GetID();
    TDataISAKMP *attr= transf->SAAttrib();
    TUint16 lifeType = 0;   //No type assigned yet
    TUint32 lifeValue = 0;
    while (length>0)
    {
        length = length - attr->Size();
        if (length<0)   //Mismatch between lengths!!!
        {
            DEBUG_LOG(_L("BAD_PROPOSAL_SYNTAX (Length mismatch in the attibutes)"));
            SendNotifyL(BAD_PROPOSAL_SYNTAX);
            return KErrGeneral;
        }
        switch (attr->Type())
        {
        case DOI_ATTR_TYPE_LIFE_TYPE:
            lifeType=attr->Value();
            if (!CheckLifeType(lifeType))
            {
                DEBUG_LOG(_L("BAD_PROPOSAL_SYNTAX (Invalid lifetime type)"));
                SendNotifyL(BAD_PROPOSAL_SYNTAX);
                return KErrGeneral;
            }
            break;
        case DOI_ATTR_TYPE_LIFE_DUR:
            if (attr->IsBasic())
            {
                lifeValue = ByteOrder::Swap32(attr->Value());
                if (lifeType==SECONDS)
                    aAttrib->iLifeDurationSecs.Copy((TUint8 *)&lifeValue, sizeof(lifeValue));
                else if (lifeType==KBYTES)
                    aAttrib->iLifeDurationKBytes.Copy((TUint8 *)&lifeValue, sizeof(lifeValue));
                else
                {
                    DEBUG_LOG(_L("BAD_PROPOSAL_SYNTAX (No lifetime type received)"));
                    SendNotifyL(BAD_PROPOSAL_SYNTAX);
                    return KErrGeneral;
                }
            }
            else
            {
                if (lifeType==SECONDS)
                    aAttrib->iLifeDurationSecs.Copy(attr->VarValue(),attr->Length());
                else if (lifeType==KBYTES)
                    aAttrib->iLifeDurationKBytes.Copy(attr->VarValue(),attr->Length());
                else
                {
                    DEBUG_LOG(_L("BAD_PROPOSAL_SYNTAX (No lifetime type received)"));
                    SendNotifyL(BAD_PROPOSAL_SYNTAX);
                    return KErrGeneral;
                }
            }
            lifeType = 0;   //Cannot received another lifetime without setting the type again
            break;
        case DOI_ATTR_TYPE_GROUP_DESC:
            if (!CheckGroupDesc(attr->Value()))
                return KErrNotFound;
            aAttrib->iGroupDesc=attr->Value();
            break;
        case DOI_ATTR_TYPE_ENC_MODE:    //Encapsulation Mode
            if (!CheckEncMode(attr->Value()))
                return KErrNotFound;
            aAttrib->iEncMode=attr->Value();
            if ( aAttrib->iEncMode == UDP_ENC_TUNNEL || aAttrib->iEncMode == UDP_RFC_ENC_TUNNEL  )
               aAttrib->iEncMode = DOI_TUNNEL;
            if ( aAttrib->iEncMode == UDP_ENC_TRANSPORT || aAttrib->iEncMode == UDP_RFC_ENC_TRANSPORT)
               aAttrib->iEncMode = DOI_TRANSPORT;
            break;
        case DOI_ATTR_TYPE_AUTH_ALG:
            if (!CheckAuthAlg(attr->Value()))
                return KErrNotFound;
            aAttrib->iAuthAlg=attr->Value();
            break;
        case DOI_ATTR_TYPE_KEY_LEN:
            aAttrib->iKeyLength = attr->Value();
            break;  
        case DOI_ATTR_TYPE_KEY_ROUNDS:
            aAttrib->iKeyRounds=attr->Value();
            break;
/*          
        case DOI_ATTR_TYPE_COMP_DIC_SIZE:   //Compress Dictionary size
            aAttrib->iComprDicSize=attr->Value();
            break;  
        case DOI_ATTR_TYPE_COMP_PRIV_ALG:   //Compress Dictionary size
            aAttrib->iComprPrivAlg=attr->Value();
            break;
*/          
        default:
            DEBUG_LOG(_L("ATTRIBUTES_NOT_SUPPORTED (Invalid attribute number)"));
            SendNotifyL(ATTRIBUTES_NOT_SUPPORTED);
            return KErrGeneral;
        }
        attr = attr->Next();
    }

    if (lifeType != 0)  //Type set but not sent
    {
        DEBUG_LOG(_L("Lifetime type set but value not sent!"));
        return KErrNotFound;
    }

    if (aAttrib->iKeyLength !=0)
        if (!CheckKeyLength(aAttrib->iKeyLength,transf->GetID(),aProtocol)) //Check key length correct
            return KErrNotFound;

    return KErrNone;
}


//returns KErrNone if OK, otherwise error already treated.
TBool CIkev1Negotiation::ProcessKeyL(const TKeyISAKMP *aKey)
{
    //const TKeyISAKMP *key = TKeyISAKMP::Ptr(aPayload);
    
    //payload not present
    if (iPhase==PHASE_I)
    {
        if (!aKey)
        {
            DEBUG_LOG(_L("NO KEY Payload"));
            SetErrorStatus( KKmdIkeNegotFailed );
            SendNotifyL(PAYLOAD_MALFORMED);
            return EFalse;
        }
        //Doesn't return yet because it needs to copy the received key
    }
    else    //PHASE_II
    {
        if (!aKey)
        {
            if (iPFS)
            {
                DEBUG_LOG(_L("KEY Payload Expected (PFS is enabled)"));
                return EFalse;
            }

            return ETrue;
            
        }
        else    // Key present
        {
            if (!iPFS)
            {
                DEBUG_LOG(_L("KEY Payload NOT Expected (PFS is disabled)"));
                return EFalse;
            }
            //Doesn't return yet because it needs to copy the received key
        }
    }
        
    //stores the public key sent by the other peer. Only if key received and PFS enabled (PHASE II only)
    iPeerPublicKey.Copy(aKey->KeyData(), aKey->GetLength() - sizeof(*aKey));
    return ETrue;
}


TBool CIkev1Negotiation::ProcessNonceL(const TPayloadISAKMP *aPayload)
{
    const TNonceISAKMP *nonce = TNonceISAKMP::Ptr(aPayload);

    //payload not present
    if (!nonce)
    {
        DEBUG_LOG(_L("NO NONCE PAYLOAD"));
        SetErrorStatus( KKmdIkeNegotFailed );
        SendNotifyL(PAYLOAD_MALFORMED);
        return EFalse;
    }

    //stores the nonce sent by the other peer
    if (iRole==RESPONDER)
        iNONCE_I.Copy(nonce->NonceData(),nonce->NonceDataLen());
    else
        iNONCE_R.Copy(nonce->NonceData(),nonce->NonceDataLen());

    return ETrue;

}

TBool CIkev1Negotiation::ProcessStage1_II_IDsL(const TIdentISAKMP *aInit_ID_payload,const TIdentISAKMP *aResp_ID_payload, CProposal_IIList *aRecv_proposals)
{
    //IDci
    //First we check the received IDs to be able to build the proposals for phase_II
    TInt32 addr;        //Contains a numeric IPv4 addr to be sent
    TBuf<40> addr_buf;  //Contains a text IPv4/IPv6 addr to be sent

    TIp6Addr ip6addr;   //IPV6 raw address

    //We receive the peer proxy address or gateway client
    TAttrib_II *attr_II = aRecv_proposals->At(0)->iAttrList->At(0);
    if (aInit_ID_payload)   //ID Payload received
    {
        iIDReceived = ETrue;
        if (!CheckIdentL(aInit_ID_payload))  
            return EFalse;

        iIDRemotePort = aInit_ID_payload->GetPort();
        iIDProtocol = aInit_ID_payload->GetProtocol();
        iRemoteIDType_II = aInit_ID_payload->GetIDType();

        switch (aInit_ID_payload->GetIDType())
        {
            case ID_IPV4_ADDR:
                Mem::Copy((TUint8 *)&addr, aInit_ID_payload->IDData(),sizeof(TInt32));
                iRemoteAddr1_ID_II.SetAddress(ByteOrder::Swap32(addr));
                DEBUG_LOG(_L("Remote ID received"));
                DEBUG_LOG(_L("Setting Remote ID to:"));
                iRemoteAddr1_ID_II.OutputWithScope(addr_buf);
                DEBUG_LOG(addr_buf);
                if (iRemoteAddr.Match(iRemoteAddr1_ID_II))
                    iDefaultRemoteID = ETrue;   //Must be sent but won't be used when updating the SAD
                else if (attr_II->iEncMode == DOI_TRANSPORT)
                {
                    DEBUG_LOG(_L("ADDRESS_NOTIFICATION (Received ID MUST match the Remote addr in Transport mode)"));
                    SendNotifyL(ADDRESS_NOTIFICATION);
                    return EFalse;
                }
                break;
            case ID_IPV4_ADDR_SUBNET:
                Mem::Copy((TUint8 *)&addr, aInit_ID_payload->IDData(),sizeof(TInt32));  //Address
                iRemoteAddr1_ID_II.SetAddress(ByteOrder::Swap32(addr));
                Mem::Copy((TUint8 *)&addr, aInit_ID_payload->IDData() + sizeof(TInt32),sizeof(TInt32)); //Mask
                iRemoteAddr2_ID_II.SetAddress(ByteOrder::Swap32(addr));
                iRemoteAddr1_ID_II.OutputWithScope(addr_buf);
                DEBUG_LOG(_L("Setting Remote ID to: addr = "));
                DEBUG_LOG(addr_buf);
                iRemoteAddr2_ID_II.OutputWithScope(addr_buf);
                DEBUG_LOG(_L(" mask = "));
                DEBUG_LOG(addr_buf);
                if (PrefixLen(iRemoteAddr2_ID_II) < KErrNone)   //Invalid Mask  (can't be > 32 bec. we get only 4 bytes)
                {
                    DEBUG_LOG(_L("INVALID_ID_INFORMATION (Invalid peer proxy mask for type ID_IPV4_ADDR_SUBNET)"));
                    SetErrorStatus( KKmdIkeNegotFailed );
                    SendNotifyL(INVALID_ID_INFORMATION);
                    return EFalse;
                }
                if (attr_II->iEncMode == DOI_TRANSPORT)
                {
                    if (!iRemoteAddr.Match(iRemoteAddr1_ID_II, iRemoteAddr2_ID_II))
                    {
                        DEBUG_LOG(_L("ADDRESS_NOTIFICATION (Remote ID MUST match the net & mask received)"));
                        SetErrorStatus( KKmdIkeNegotFailed );
                        SendNotifyL(ADDRESS_NOTIFICATION);
                        return EFalse;
                    }
                }
                break;
            case ID_IPV6_ADDR:
                Mem::Copy(&ip6addr.u.iAddr8, aInit_ID_payload->IDData(), sizeof(ip6addr.u.iAddr8));
                iRemoteAddr1_ID_II.SetAddress(ip6addr);
                DEBUG_LOG(_L("Remote ID received"));
                DEBUG_LOG(_L("Setting Remote ID to:"));
                iRemoteAddr1_ID_II.OutputWithScope(addr_buf);
                DEBUG_LOG(addr_buf);
                if (iRemoteAddr.Match(iRemoteAddr1_ID_II))
                    iDefaultRemoteID = ETrue;   //Must be sent but won't be used when updating the SAD
                else if (attr_II->iEncMode == DOI_TRANSPORT)
                {
                    DEBUG_LOG(_L("ADDRESS_NOTIFICATION (Remote ID doesn't match received IDi in Transport mode)"));
                    SendNotifyL(ADDRESS_NOTIFICATION);
                    return EFalse;
                }
                break;
            case ID_IPV6_ADDR_SUBNET:
                Mem::Copy(&ip6addr.u.iAddr8, aInit_ID_payload->IDData(), sizeof(ip6addr.u.iAddr8)); //Address               
                iRemoteAddr1_ID_II.SetAddress(ip6addr);
                Mem::Copy(&ip6addr.u.iAddr8, aInit_ID_payload->IDData() + sizeof(ip6addr.u.iAddr8), sizeof(ip6addr.u.iAddr8));  //Mask
                iRemoteAddr2_ID_II.SetAddress(ip6addr);
                DEBUG_LOG(_L("Remote ID (subnet) received"));
                DEBUG_LOG(_L("Setting Remote ID to: addr = "));
                iRemoteAddr1_ID_II.OutputWithScope(addr_buf);
                DEBUG_LOG(addr_buf);
                DEBUG_LOG(_L(" mask = "));
                iRemoteAddr2_ID_II.OutputWithScope(addr_buf);
                DEBUG_LOG(addr_buf);
                if (PrefixLen(iRemoteAddr2_ID_II) < 0)  //Invalid Mask
                {
                    DEBUG_LOG(_L("INVALID_ID_INFORMATION (Invalid peer proxy mask for type ID_IPV6_ADDR_SUBNET)"));
                    SetErrorStatus( KKmdIkeNegotFailed );
                    SendNotifyL(INVALID_ID_INFORMATION);
                    return EFalse;
                }
                if (attr_II->iEncMode == DOI_TRANSPORT)
                {
                    if (!iRemoteAddr.Match(iRemoteAddr1_ID_II, iRemoteAddr2_ID_II))
                    {
                        DEBUG_LOG(_L("ADDRESS_NOTIFICATION (Remote ID MUST match the net & mask received)"));
                        SetErrorStatus( KKmdIkeNegotFailed );
                        SendNotifyL(ADDRESS_NOTIFICATION);
                        return EFalse;
                    }
                }
                break;
            default:    //redundant. Detected in CheckIdentL()
                DEBUG_LOG(_L("INVALID_ID_INFORMATION (ID Type not supported)"));
                SetErrorStatus( KKmdIkeNegotFailed );
                SendNotifyL(INVALID_ID_INFORMATION);
                return EFalse;
        }//switch
    }
    else    //No id received (That means we're negotiating directly with the end host) (RFC 2409 5.5)
    {
        //For TRANSPORT we don't need to do anything
        iIDLocalPort = 0;
        iIDRemotePort = 0;
        iIDProtocol = 0;
        return ETrue;   //No need to check the Responder ID if no Initiator ID received
    }

    //IDcr
    //Receive our proxy. We don't know it because we are responders so the other peer tells us who does it
    //want to communicate with
    if (aResp_ID_payload)   //ID Payload received
    {
        if (!CheckIdentL(aResp_ID_payload))
            return EFalse;

        iIDLocalPort = aResp_ID_payload->GetPort();
        iLocalIDType_II = aResp_ID_payload->GetIDType();
        if (iIDProtocol != aResp_ID_payload->GetProtocol()) //Must be the same sent in the IDCi
        {
            DEBUG_LOG(_L("INVALID_ID_INFORMATION (Local ID Protocol different from Remote ID Protocol. Must be the same)"));
            SetErrorStatus( KKmdIkeNegotFailed );
            SendNotifyL(INVALID_ID_INFORMATION);
            return EFalse;
        }

        switch (aResp_ID_payload->GetIDType())
        {
            case ID_IPV4_ADDR:
                Mem::Copy((TUint8 *)&addr, aResp_ID_payload->IDData(),sizeof(TInt32));
                iLocalAddr1_ID_II.SetAddress(ByteOrder::Swap32(addr));
                DEBUG_LOG(_L("Local ID received"));
                DEBUG_LOG(_L("Setting Local ID to:"));
                iLocalAddr1_ID_II.OutputWithScope(addr_buf);
                DEBUG_LOG(addr_buf);
                if ( iInternalAddr ) {
                   //
                   // Check ID against internal address instead of local address
                   //
                   if (iInternalAddr->iClientIntAddr.Match(iLocalAddr1_ID_II))
                       iDefaultLocalID = ETrue;  //Must be sent but won't be used when updating the SAD
                }
                else {
                   if (iLocalAddr.Match(iLocalAddr1_ID_II))
                        iDefaultLocalID = ETrue; //Must be sent but won't be used when updating the SAD
                   else if (attr_II->iEncMode == DOI_TRANSPORT)
                   {
                       DEBUG_LOG(_L("ADDRESS_NOTIFICATION (Local ID MUST match the net & mask received)"));
                       SetErrorStatus( KKmdIkeNegotFailed );
                       SendNotifyL(ADDRESS_NOTIFICATION);
                       return EFalse;
                   }   
                }
                break;
            case ID_IPV4_ADDR_SUBNET:
                Mem::Copy((TUint8 *)&addr, aResp_ID_payload->IDData(),sizeof(TInt32));  //Address
                iLocalAddr1_ID_II.SetAddress(ByteOrder::Swap32(addr));
                Mem::Copy((TUint8 *)&addr, aResp_ID_payload->IDData() + sizeof(TInt32),sizeof(TInt32)); //Mask
                iLocalAddr2_ID_II.SetAddress(ByteOrder::Swap32(addr));
                DEBUG_LOG(_L("Setting Local ID to: addr = "));
                iLocalAddr1_ID_II.OutputWithScope(addr_buf);
                DEBUG_LOG(addr_buf);
                DEBUG_LOG(_L(" mask = "));
                iLocalAddr2_ID_II.OutputWithScope(addr_buf);
                DEBUG_LOG(addr_buf);
                if (PrefixLen(iLocalAddr2_ID_II) < 0)   //Invalid Mask
                {
                    DEBUG_LOG(_L("INVALID_ID_INFORMATION (Invalid Remote ID mask for type ID_IPV4_ADDR_SUBNET)"));
                    SetErrorStatus( KKmdIkeNegotFailed );
                    SendNotifyL(INVALID_ID_INFORMATION);
                    return EFalse;
                }
                if (attr_II->iEncMode == DOI_TRANSPORT)
                {
                    if (!iLocalAddr.Match(iLocalAddr1_ID_II, iLocalAddr2_ID_II))
                    {
                        DEBUG_LOG(_L("ADDRESS_NOTIFICATION (Local ID MUST match the net & mask received)"));
                        SetErrorStatus( KKmdIkeNegotFailed );
                        SendNotifyL(ADDRESS_NOTIFICATION);
                        return EFalse;
                    }
                }
                break;
            case ID_IPV6_ADDR:
                Mem::Copy(&ip6addr.u.iAddr8, aResp_ID_payload->IDData(), sizeof(ip6addr.u.iAddr8));
                //iOwnProxyAddr.SetAddress(ip6addr);
                iLocalAddr1_ID_II.SetAddress(ip6addr);
                DEBUG_LOG(_L("Own Proxy received"));
                DEBUG_LOG(_L("Setting Own Proxy address to:"));
                iLocalAddr1_ID_II.OutputWithScope(addr_buf);
                DEBUG_LOG(addr_buf);
                if (iLocalAddr.Match(iLocalAddr1_ID_II))
                    iDefaultLocalID = ETrue;    //Must be sent but won't be used when updating the SAD
                else if (attr_II->iEncMode == DOI_TRANSPORT)
                {
                    DEBUG_LOG(_L("ADDRESS_NOTIFICATION (Local ID MUST match the net & mask received)"));
                    SendNotifyL(ADDRESS_NOTIFICATION);
                    return EFalse;
                }
                break;
            case ID_IPV6_ADDR_SUBNET:
                Mem::Copy(&ip6addr.u.iAddr8, aResp_ID_payload->IDData(), sizeof(ip6addr.u.iAddr8)); //Address
                iLocalAddr1_ID_II.SetAddress(ip6addr);
                Mem::Copy(&ip6addr.u.iAddr8, aResp_ID_payload->IDData() + sizeof(ip6addr.u.iAddr8), sizeof(ip6addr.u.iAddr8));  //Mask
                iLocalAddr2_ID_II.SetAddress(ip6addr);
                DEBUG_LOG(_L("Setting Own Proxy to: addr = "));
                iLocalAddr1_ID_II.OutputWithScope(addr_buf);
                DEBUG_LOG(addr_buf);
                DEBUG_LOG(_L(" mask = "));
                iLocalAddr2_ID_II.OutputWithScope(addr_buf);
                DEBUG_LOG(addr_buf);
                if (PrefixLen(iLocalAddr2_ID_II) < 0)   //Invalid Mask
                {
                    DEBUG_LOG(_L("INVALID_ID_INFORMATION (Invalid Remote ID mask for type ID_IPV6_ADDR_SUBNET)"));
                    SetErrorStatus( KKmdIkeNegotFailed );
                    SendNotifyL(INVALID_ID_INFORMATION);
                    return EFalse;
                }
                if (attr_II->iEncMode == DOI_TRANSPORT)
                {
                    if (!iLocalAddr.Match(iLocalAddr1_ID_II, iLocalAddr2_ID_II))
                    {
                        DEBUG_LOG(_L("ADDRESS_NOTIFICATION (Local ID MUST match the net & mask received)"));
                        SetErrorStatus( KKmdIkeNegotFailed );
                        SendNotifyL(ADDRESS_NOTIFICATION);
                        return EFalse;
                    }
                }
                break;
            default:
                DEBUG_LOG(_L("INVALID_ID_INFORMATION (ID Type not supported)"));
                SetErrorStatus( KKmdIkeNegotFailed );
                SendNotifyL(INVALID_ID_INFORMATION);
                return EFalse;
        }//switch
    }

    return ETrue;
}


TBool CIkev1Negotiation::ProcessStage2_II_IDsL(const TIdentISAKMP *aInit_ID_payload,const TIdentISAKMP *aResp_ID_payload)//, CProposal_IIList *aRecv_proposals)
{
    TInt32 addr4_int;       //Contains a numeric IPv4 addr to be sent
    TIp6Addr ip6addr;   //IPV6 raw address
    TInetAddr tmp_addr;
    //Here we check the initator proxy (Our client) sent by us has been received correctly
    if (aInit_ID_payload)   //ID Payload received
    {
        if (!CheckIdentL(aInit_ID_payload))
            return EFalse;

        if (aInit_ID_payload->GetPort() != iIDLocalPort)
        {
            DEBUG_LOG(_L("INVALID_ID_INFORMATION (Local ID Port different from the one sent)"));
            SetErrorStatus( KKmdIkeNegotFailed );
            SendNotifyL(INVALID_ID_INFORMATION);
            return EFalse;
        }

        if (aInit_ID_payload->GetProtocol() != iIDProtocol)
        {
            DEBUG_LOG(_L("INVALID_ID_INFORMATION (Local ID Protocol different from the one sent)"));
            SetErrorStatus( KKmdIkeNegotFailed );
            SendNotifyL(INVALID_ID_INFORMATION);
            return EFalse;
        }

        switch (aInit_ID_payload->GetIDType())
        {
        case ID_IPV4_ADDR:
            Mem::Copy((TUint8 *)&addr4_int,aInit_ID_payload->IDData(),sizeof(TInt32));
            tmp_addr.SetAddress(ByteOrder::Swap32(addr4_int));
            if (!tmp_addr.Match(iLocalAddr1_ID_II))
            {
                DEBUG_LOG(_L("Wrong Own ID received (Different from the one sent)"));
                SetErrorStatus( KKmdIkeNegotFailed );
                SendNotifyL(INVALID_ID_INFORMATION);
                return EFalse;
            }
            break;
        case ID_IPV4_ADDR_SUBNET:
            //Subnet
            Mem::Copy((TUint8 *)&addr4_int,aInit_ID_payload->IDData(),sizeof(TInt32));
            tmp_addr.SetAddress(ByteOrder::Swap32(addr4_int));
            if (!tmp_addr.Match(iLocalAddr1_ID_II))
            {
                //The ID subnet is not the one we sent!
                DEBUG_LOG(_L("Wrong Own ID subnet received (Different from the one sent)"));
                SetErrorStatus( KKmdIkeNegotFailed );
                SendNotifyL(INVALID_ID_INFORMATION);
                return EFalse;
            }
            //Mask
            Mem::Copy((TUint8 *)&addr4_int,aInit_ID_payload->IDData() + sizeof(TInt32),sizeof(TInt32));
            tmp_addr.SetAddress(ByteOrder::Swap32(addr4_int));          
            if (!tmp_addr.Match(iLocalAddr2_ID_II))
            {
                //The ID mask is not the one we sent!
                DEBUG_LOG(_L("Wrong Own ID mask received (Different from the one sent)"));
                SetErrorStatus( KKmdIkeNegotFailed );
                SendNotifyL(INVALID_ID_INFORMATION);
                return EFalse;
            }
            break;
        case ID_IPV6_ADDR:
            Mem::Copy(&ip6addr.u.iAddr8, aInit_ID_payload->IDData(), sizeof(ip6addr.u.iAddr8));
            tmp_addr.SetAddress(ip6addr);
            if (!tmp_addr.Match(iLocalAddr1_ID_II))
            {
                //The ID is not the one we sent!
                DEBUG_LOG(_L("Wrong Local ID received (Different from the one sent)"));
                SetErrorStatus( KKmdIkeNegotFailed );
                SendNotifyL(INVALID_ID_INFORMATION);
                return EFalse;
            }
            break;
        case ID_IPV6_ADDR_SUBNET:
            //subnet
            Mem::Copy(&ip6addr.u.iAddr8, aInit_ID_payload->IDData(), sizeof(ip6addr.u.iAddr8));
            tmp_addr.SetAddress(ip6addr);
            if (!tmp_addr.Match(iLocalAddr1_ID_II))
            {
                //The ID is not the one we sent!
                DEBUG_LOG(_L("Wrong Local ID subnet received (Different from the one sent)"));
                SetErrorStatus( KKmdIkeNegotFailed );
                SendNotifyL(INVALID_ID_INFORMATION);
                return EFalse;
            }
            //mask
            Mem::Copy(&ip6addr.u.iAddr8, aInit_ID_payload->IDData() + sizeof(ip6addr.u.iAddr8), sizeof(ip6addr.u.iAddr8));
            tmp_addr.SetAddress(ip6addr);
            if (!tmp_addr.Match(iLocalAddr2_ID_II))
            {
                //The ID is not the one we sent!
                DEBUG_LOG(_L("Wrong Local ID mask received (Different from the one sent)"));
                SetErrorStatus( KKmdIkeNegotFailed );
                SendNotifyL(INVALID_ID_INFORMATION);
                return EFalse;
            }
            break;
        default:
            DEBUG_LOG(_L("INVALID_ID_INFORMATION (ID Type not supported)"));
            SetErrorStatus( KKmdIkeNegotFailed );
            SendNotifyL(INVALID_ID_INFORMATION);
            return EFalse;
        }//switch
    }
    else    //No id sent (That means we're negotiating directly with the end host
    {
        if (!iLocalAddr1_ID_II.IsUnspecified())
        {
            DEBUG_LOG(_L("IDci expected and not received!"));
            SetErrorStatus( KKmdIkeNegotFailed );
            SendNotifyL(INVALID_ID_INFORMATION);
            return EFalse;
        }
    }

    //We receive the peer (responder) proxy address or gateway client
    if (aResp_ID_payload)   //ID Payload received
    {
        if (!CheckIdentL(aResp_ID_payload))
            return EFalse;

        if (aResp_ID_payload->GetPort() != iIDRemotePort)
        {
            DEBUG_LOG(_L("INVALID_ID_INFORMATION (Remote Port different from the one sent)"));
            SetErrorStatus( KKmdIkeNegotFailed );
            SendNotifyL(INVALID_ID_INFORMATION);
            return EFalse;
        }

        if (aResp_ID_payload->GetProtocol() != iIDProtocol)
        {
            DEBUG_LOG(_L("INVALID_ID_INFORMATION (Responder ID Protocol different from the one sent)"));
            SetErrorStatus( KKmdIkeNegotFailed );
            SendNotifyL(INVALID_ID_INFORMATION);
            return EFalse;
        }

        switch (aResp_ID_payload->GetIDType())
        {
            case ID_IPV4_ADDR:
                Mem::Copy((TUint8 *)&addr4_int,aResp_ID_payload->IDData(),sizeof(TInt32));
                tmp_addr.SetAddress(ByteOrder::Swap32(addr4_int));
                DEBUG_LOG(_L("IDcr received"));
                if (!iRemoteAddr1_ID_II.Match(tmp_addr))
                {
                    DEBUG_LOG(_L("INVALID_ID_INFORMATION (Wrong Remote ID, doesn't match sent one)"));
                    SetErrorStatus( KKmdIkeNegotFailed );
                    SendNotifyL(INVALID_ID_INFORMATION);
                    return EFalse;
                }
                break;
            case ID_IPV4_ADDR_SUBNET:
                //subnet address
                Mem::Copy((TUint8 *)&addr4_int,aResp_ID_payload->IDData(),sizeof(TInt32));
                tmp_addr.SetAddress(ByteOrder::Swap32(addr4_int));
                if (!tmp_addr.Match(iRemoteAddr1_ID_II))
                {
                    //The ID subnet is not the one we sent!
                    DEBUG_LOG(_L("Wrong Remote ID subnet received (Different from the one sent)"));
                    SetErrorStatus( KKmdIkeNegotFailed );
                    SendNotifyL(INVALID_ID_INFORMATION);
                    return EFalse;
                }
                //Mask address
                Mem::Copy((TUint8 *)&addr4_int,aResp_ID_payload->IDData() + sizeof(TInt32),sizeof(TInt32));
                tmp_addr.SetAddress(ByteOrder::Swap32(addr4_int));          
                if (!tmp_addr.Match(iRemoteAddr2_ID_II))
                {
                    //The ID mask is not the one we sent!
                    DEBUG_LOG(_L("Wrong Remote ID mask received (Different from the one sent)"));
                    SetErrorStatus( KKmdIkeNegotFailed );
                    SendNotifyL(INVALID_ID_INFORMATION);
                    return EFalse;
                }
                break;
            case ID_IPV6_ADDR:
                Mem::Copy(&ip6addr.u.iAddr8,aResp_ID_payload->IDData(), sizeof(ip6addr.u.iAddr8));
                tmp_addr.SetAddress(ip6addr);
                DEBUG_LOG(_L("IDcr received"));
                if (!iRemoteAddr1_ID_II.Match(tmp_addr))
                {
                    DEBUG_LOG(_L("INVALID_ID_INFORMATION (Wrong ID, doesn't match sent proxy)"));
                    SetErrorStatus( KKmdIkeNegotFailed );
                    SendNotifyL(INVALID_ID_INFORMATION);
                    return EFalse;
                }
                break;
            case ID_IPV6_ADDR_SUBNET:
                //subnet
                Mem::Copy(&ip6addr.u.iAddr8, aResp_ID_payload->IDData(), sizeof(ip6addr.u.iAddr8));
                tmp_addr.SetAddress(ip6addr);
                if (!tmp_addr.Match(iRemoteAddr1_ID_II))
                {
                    //The ID is not the one we sent!
                    DEBUG_LOG(_L("Wrong Remote ID subnet received (Different from the one sent)"));
                    SetErrorStatus( KKmdIkeNegotFailed );
                    SendNotifyL(INVALID_ID_INFORMATION);
                    return EFalse;
                }
                //mask
                Mem::Copy(&ip6addr.u.iAddr8, aResp_ID_payload->IDData() + sizeof(ip6addr.u.iAddr8), sizeof(ip6addr.u.iAddr8));
                tmp_addr.SetAddress(ip6addr);
                if (!tmp_addr.Match(iRemoteAddr2_ID_II))
                {
                    //The ID is not the one we sent!
                    DEBUG_LOG(_L("Wrong Remote ID mask received (Different from the one sent)"));
                    SetErrorStatus( KKmdIkeNegotFailed );
                    SendNotifyL(INVALID_ID_INFORMATION);
                    return EFalse;
                }
                break;
            default:    //Only these 2 modes make sense no reason for subnets or range
                DEBUG_LOG(_L("INVALID_ID_INFORMATION (Remote ID Type not supported)"));
                SetErrorStatus( KKmdIkeNegotFailed );
                SendNotifyL(INVALID_ID_INFORMATION);
                return EFalse;
        }//switch
    }
    else    //No id sent (That means we're negotiating directly with the end host. We check it's TRUE!
    {
        if (!iRemoteAddr1_ID_II.IsUnspecified())
        {
            DEBUG_LOG(_L("IDcr expected and not received!"));
            SetErrorStatus( KKmdIkeNegotFailed );
            SendNotifyL(INVALID_ID_INFORMATION);
            return EFalse;
        }
    }

    return ETrue;
}


TBool CIkev1Negotiation::CheckIdentL(const TPayloadISAKMP *aPayload)
{
    const TIdentISAKMP *ident = TIdentISAKMP::Ptr(aPayload);
    
    //payload not present
    if (!ident)
    {
        DEBUG_LOG(_L("NO ID PAYLOAD"));
        SetErrorStatus( KKmdIkeNegotFailed );
        SendNotifyL(PAYLOAD_MALFORMED);
        return EFalse;
    }

    if (iPhase  == PHASE_I)
    {
        TUint8 protocol = ident->GetProtocol();
        if ((protocol != KProtocolInetUdp) && (protocol != 0))
        {
            DEBUG_LOG(_L("INVALID_ID_INFORMATION: Bad Phase I Protocol (Only UDP(17) or 0 accepted)"));
            SetErrorStatus( KKmdIkeNegotFailed );
            SendNotifyL(INVALID_ID_INFORMATION);
            return EFalse;
        }
        TUint16 port = ident->GetPort();
        if ((port != 0) && (port != IKE_PORT) && (port != FLOATED_IKE_PORT) )
        {
            DEBUG_LOG(_L("INVALID_ID_INFORMATION: Invalid Bad Phase I Port. (Only 0, 500 or 4500 accepted)"));
            SetErrorStatus( KKmdIkeNegotFailed );
            SendNotifyL(INVALID_ID_INFORMATION);
            return EFalse;
        }
        switch (ident->GetIDType())
        {
        case ID_IPV4_ADDR:
        case ID_IPV6_ADDR:
        case ID_FQDN:                       
        case ID_USER_FQDN:
        case ID_DER_ASN1_DN:
            break;
        default:
            DEBUG_LOG(_L("INVALID_ID_INFORMATION: Invalid Type (Only IPV4/IPV6/User FQDN and DER ASN1 DN accepted in PHASE I)"));
            SetErrorStatus( KKmdIkeNegotFailed );
            SendNotifyL(INVALID_ID_INFORMATION);
            return EFalse;
        }
    }

    return ETrue;
}


//Certificate Request Payload processing (all modes).
TBool CIkev1Negotiation::ProcessCertificateReqL(const TCertificateReqISAKMP *aCertReq)
{
    if (iChosenProposal_I.iAttrList->iAuthMethod == IKE_A_CRACK)
    {
        DEBUG_LOG(_L("CR ignored when CRACK auth !)"));
        return ETrue;
    }

    TInt ret = CheckEncodingL(aCertReq->GetEncoding());
    switch (ret)
    {
    case KErrGeneral:
        return EFalse;
    case KErrNotSupported:  //Not supported but not an error, just ignored
        return ETrue;   
    }

	if ( !iPkiService )
	   return EFalse;
	TBool Status = EFalse;
	    //
        // No specific CA asked. Find a certificate using own trusted CA list
        //
    if ( ReadOwnCertL())
        {
         Status = ETrue; 
        }
   
    return Status;

}


//Certificate Request Payload(s) processing (all modes).
TBool CIkev1Negotiation::ProcessCertificateReqArrayL(const CArrayFixFlat<const TCertificateReqISAKMP *> *aCRPayloadArray)
{
	
    TInt count = aCRPayloadArray->Count();
    if ( count == 0 )
	{
       return ETrue;   // No Certificate requests   
    }
    
    if ( ProcessCertificateReqL(aCRPayloadArray->At(0)) )
       {
        DEBUG_LOG(_L("User Certificate required by peer found"));           
        iSendCert = ETrue;  //Requires sending our cert in next interchange where allowed/expected, otherwise not sent
        return ETrue;
       }
    HBufC8* CAName = NULL;
    CIkeCaList* trustedCaList = iPkiService->CaList();
    
    TInt Status=0;
    
    for (TInt i=0; i < trustedCaList->Count(); i++)
        {
        CIkeCaElem* CaElem = (*trustedCaList)[i];                     
                
        CAName = IkeCert::GetCertificateFieldDERL(CaElem->Certificate(), KSubjectName);
        CleanupStack::PushL(CAName);
        
        TRAP_IGNORE(Status=iPkiService->ReadChainL(iHostData, CAName)); 
        
        CleanupStack::PopAndDestroy(CAName);
        CAName=NULL;
        
        if ( Status == KErrNone )
            {
             delete iOwnCert;
             iOwnCert = iPkiService->GetCertificate();
             
             iICA1 = iPkiService->GetTrustedICA1();
                    
             iICA2 = iPkiService->GetTrustedICA2();
             
             iPeerTrustedCA = iPkiService->GetTrustedCA();
             
             iSendCert = ETrue;  //Requires sending our cert in next interchange where allowed/expected, otherwise not sent
             
             DEBUG_LOG(_L("Certificate chain Found!"));
             return ETrue;
             }
        }
    
   
    if ( Status == KVpnErrInvalidCaCertFile)
        {
         SetErrorStatus(KVpnErrInvalidCaCertFile);  
         
         SendNotifyL(CERTIFICATE_UNAVAILABLE);
         
         DEBUG_LOG(_L("Certificate chain read failed!"));
           
         return EFalse;
        }
	
    SetErrorStatus(KKmdIkeNoCertFoundErr);  
    SendNotifyL(CERTIFICATE_UNAVAILABLE);
    DEBUG_LOG(_L("Certificate Chain r!"));
    
    return EFalse;
}

//Certificate Payload(s) processing (all modes).
TBool CIkev1Negotiation::ProcessCertificateArrayL(CArrayFixFlat<const TCertificateISAKMP *>* aCertArray)
{
	TBool Status;	
	if ( iCertRequested )
		 Status = EFalse;
	else Status = ETrue;

	if ( iPkiService && aCertArray->Count() )
	{	        
        const CIkeCaList* trustedCaList = iPkiService->CaList();
    	CX509Certificate* PeerCert = IkePkiUtils::VerifyCertificateL(*aCertArray,
    	                                                             *trustedCaList);
    	if ( PeerCert )
	    {
		   delete iPeerX509Cert;
		   iPeerX509Cert = PeerCert;
	       DEBUG_LOG(_L("Peer Certificate is OK"));
		   Status = ETrue; 		
	    }
		else
		{
		   Status = EFalse;					
	       DEBUG_LOG(_L("Peer Certificate is rejected"));
		}			   
	}			
			
	return Status; 
}

//Checks the signature sent by the peer host
TBool CIkev1Negotiation::ProcessSignatureL(const TSignatureISAKMP *aSigPayload)
{
    TBool ret;
    //payload not present
    if (!aSigPayload || !iPeerX509Cert )
    {
        DEBUG_LOG(_L("NO SIG PAYLOAD"));
		SetErrorStatus(KKmdIkePeerAuthFailed);  				
        SendNotifyL(PAYLOAD_MALFORMED);
        return EFalse;
    }

    //DSS only allows SHA1 as hash
    TUint16 tmp = iChosenProposal_I.iAttrList->iHashAlg;
    if (iChosenProposal_I.iAttrList->iAuthMethod==DSS_SIG)
        iChosenProposal_I.iAttrList->iHashAlg = HASH_SHA1;

    TBuf8<ISAKMP_HASH_SIZE> hash;
    //Verify the peer signature
    if (iRole==RESPONDER)
    {
        ComputeHash1L(hash);   //Computes the value of iHASH_I the signature checking
                            //Nothing else to compute.
    }
    else    //Initiator
    {
        ComputeHashrL(hash); //Computes the value of CRACK digest for signature checking
    }

    ret = VerifySignatureL(iPeerX509Cert, (TUint8 *)hash.Ptr(), hash.Length(), aSigPayload->SigData(),aSigPayload->GetDataLength());

    //restores the value of the Hash alg.
    iChosenProposal_I.iAttrList->iHashAlg = tmp;

    if (!ret)
    {
        DEBUG_LOG(_L("INVALID_SIGNATURE 2"));
		SetErrorStatus(KKmdIkePeerAuthFailed);				
        SendNotifyL(INVALID_SIGNATURE);
        return EFalse;
    }

	DEBUG_LOG(_L("Peer Signature is OK"));           	
    return ETrue;
}


TBool CIkev1Negotiation::ProcessHashL(const THashISAKMP *aHashPayload)
{
	TBool Status = EFalse;
	if (aHashPayload)
	{
        //Compute peer's hash							
		TBuf8<ISAKMP_HASH_SIZE> hash;
		if ( (iStage == 6) || ((iStage == 2) && (iExchange == ISAKMP_EXCHANGE_AGGR)))
			 ComputeHashrL(hash);
		else ComputeHash1L(hash);
		Status = (Mem::Compare((TUint8 *)hash.Ptr(), hash.Length(), aHashPayload->Data(), aHashPayload->DataLen()) == 0 );
		if ( !Status )
		{
			DEBUG_LOG(_L("INVALID_HASH_INFORMATION"));
			SendNotifyL(INVALID_HASH_INFORMATION);
		}
	}	
	
    return Status;
}


TBool CIkev1Negotiation::ProcessHash2L(const ThdrISAKMP &aHdr, const THashISAKMP *aHashPayload, TUint aPadding)
{
	TBool Status = EFalse;
    if ( aHashPayload )
	{
	   TUint8* hashMsg = (TUint8*)aHashPayload->Next();
	   TInt hashMsgLen = aHdr.GetLength() - sizeof(aHdr) - aHashPayload->GetLength() - aPadding;
	   Status = VerifyHash2L(aHashPayload, hashMsg, hashMsgLen);
	   if (!Status)
	   {
		   DEBUG_LOG(_L("INVALID_HASH_INFORMATION"));
		   SendNotifyL(INVALID_HASH_INFORMATION);
       }
	}   
	else   
    {
       DEBUG_LOG(_L("PAYLOAD_MALFORMED"));
       SendNotifyL(PAYLOAD_MALFORMED);
    }

    return Status;
}


//Check a notification Payload inserted in a normal exchange (MAIN , AGGR, QUICK)
TBool CIkev1Negotiation::ProcessNotificationL(const TNotificationISAKMP *aNotifPayload)
{
    if (!aNotifPayload)
        return ETrue;   //optional so noting happens

    if (!CheckDOI(aNotifPayload->GetDOI()))
    {
        DEBUG_LOG(_L("Bad DOI in the NOT payload"));
        SetErrorStatus( KKmdIkeNegotFailed );
        SendNotifyL(DOI_NOT_SUPPORTED);  //send the informational exchange
        return EFalse;
    }
    
    switch(aNotifPayload->GetMsgType())
    {
    case DOI_RESPONDER_LIFETIME:
        return ProcessResponderLifetimeL(aNotifPayload);
    case DOI_REPLAY_STATUS:
        return ProcessReplayStatus(aNotifPayload);
    case DOI_INITIAL_CONTACT:
        return ProcessInitialContactL(aNotifPayload);
    default:
        DEBUG_LOG(_L("INVALID MESSAGE TYPE in NOT payload"));
        SetErrorStatus( KKmdIkeNegotFailed );
        SendNotifyL(PAYLOAD_MALFORMED);
        return EFalse;
    }

}


//Processes a RESPONDER-LIFETIME NOT payload
TBool CIkev1Negotiation::ProcessResponderLifetimeL(const TNotificationISAKMP *aNotifPayload)
{
    TBuf8<2 * ISAKMP_COOKIE_SIZE> spi, own_neg_spi;
    DEBUG_LOG(_L("Processing RESPONDER-LIFETIME"));

			
    if (!((iPhase == PHASE_II) && (iStage == 2)))
    {
		if ( iPhase == PHASE_I ) {
		   DEBUG_LOG(_L("RESPONDER-LIFETIME payload in phase 1, ignored !!"));
		   return ETrue;		
		}
		else {
  		   DEBUG_LOG(_L("Unexpected RESPONDER-LIFETIME payload (Bad stage)"));
  		   SetErrorStatus( KKmdIkeNegotFailed );
		   SendNotifyL(INVALID_PAYLOAD_TYPE);
    	   return EFalse;
		}	
    }

    TUint8 protocol = aNotifPayload->GetProtocol();
    if ((protocol != PROTO_IPSEC_AH) && (protocol != PROTO_IPSEC_ESP) &&
        (protocol != PROTO_ISAKMP)   && (protocol != 0))
    {
        DEBUG_LOG(_L("Bad protocol in the RESPONDER-LIFETIME payload"));
        SetErrorStatus( KKmdIkeNegotFailed );
        SendNotifyL(INVALID_PROTOCOL_ID);
        return EFalse;
    }

    TUint8 spi_size = aNotifPayload->GetSPISize();
    CProposal_II *prop;
    TInt i;
    //If SPI sent
    switch (spi_size)
    {
    case 2 * ISAKMP_COOKIE_SIZE:    //ISAKMP spi
        spi.Copy(aNotifPayload->GetSPI(), aNotifPayload->GetSPISize());
        own_neg_spi.Copy(iCookie_I);
        own_neg_spi.Append(iCookie_R);

        if (spi.Compare(own_neg_spi) != 0)
        {
            DEBUG_LOG(_L("Invalid SPI size in the RESPONDER-LIFETIME payload. Payload ignored"));
            SetErrorStatus( KKmdIkeNegotFailed );
            SendNotifyL(INVALID_SPI);
            return EFalse;
        }
        if (iChosenProp_IIList->Count() > 1)    
        {
            DEBUG_LOG(_L("RESPONDER-LIFETIME ignored. More than one SA (need a IPsec SPI to know which one to use) "));
            return EFalse;
        }
        prop = iChosenProp_IIList->At(0);       //Only one proposal
        break;
    case 0: //Compatibility with Alchemy cc500
        if (iChosenProp_IIList->Count() > 1)    
        {
            DEBUG_LOG(_L("RESPONDER-LIFETIME ignored. More than one SA (need a IPsec SPI to know which one to use) "));
            return EFalse;
        }

        prop = iChosenProp_IIList->At(0);       //Only one proposal
        break;
    case sizeof(TUint32):           //IPSEC SPI
        spi.Copy(aNotifPayload->GetSPI(), aNotifPayload->GetSPISize());
        prop = NULL;    //Only for the debugger, the loop will have at least one proposal
        for (i = 0; i < iChosenProp_IIList->Count(); i++)
        {
            prop = iChosenProp_IIList->At(i);       //Only one proposal
            if (((prop->iSPI.Compare(spi) == 0) && prop->iProtocol == protocol)) //right prop   
                break;
        }

        if (i == iChosenProp_IIList->Count())   //No prop matches
        {
            DEBUG_LOG(_L("RESPONDER-LIFETIME ignored. IPsec SPI doesn't match any chosen proposal"));
            return EFalse;
        }
        break;
    default:
        DEBUG_LOG(_L("Bad SPI size in the RESPONDER-LIFETIME payload"));
        return EFalse;
    }
    TAttrib_II *transform = prop->iAttrList->At(0); //Only one transform
    TInt data_len = aNotifPayload->GetNotifDataSize();
    TUint8 *data_ptr = aNotifPayload->GetNotifData();
    TUint16 lifeType = 0;   //No type assigned yet
    TInt64 lifeValue = 0;   //No type assigned yet
    TInt64 lifeValue32;
    TInt64 curr_lifeValue = 0;
    TInt32 duration;

    TDataISAKMP *attr = (TDataISAKMP*)data_ptr;
    while (data_len > 0)
    {
        data_len = data_len - attr->Size();
        if (data_len < 0)   //Mismatch between lengths!!!
        {
            DEBUG_LOG(_L("RESPONDER-LIFETIME (Length mismatch in the attibutes)"));
            return EFalse;
        }
        switch (attr->Type())
        {
        case DOI_ATTR_TYPE_LIFE_TYPE:           
        case OAKLEY_ATTR_TYPE_LIFE_TYPE:
            lifeType = attr->Value();
            if (!CheckLifeType(lifeType))
            {
                DEBUG_LOG(_L("RESPONDER-LIFETIME (Invalid lifetime type)"));
                return EFalse;
            }
            break;
        case DOI_ATTR_TYPE_LIFE_DUR:                        
        case OAKLEY_ATTR_TYPE_LIFE_DUR:
            if (attr->IsBasic())
            {
                duration = ByteOrder::Swap32(attr->Value());
                lifeValue = MAKE_TINT64(0, duration);
                lifeValue32 = I64LOW(lifeValue);                
                if (lifeType == SECONDS)
                {
                    Desc8ToTInt64(transform->iLifeDurationSecs, curr_lifeValue);        //can't fail
                    if (lifeValue < curr_lifeValue)
                        transform->iLifeDurationSecs.Copy((TUint8 *)&lifeValue32, sizeof(lifeValue32));
                }
                else if (lifeType == KBYTES)
                {
                    Desc8ToTInt64(transform->iLifeDurationKBytes, curr_lifeValue);  //can't fail
                    if (lifeValue < curr_lifeValue)
                        transform->iLifeDurationKBytes.Copy((TUint8 *)&lifeValue32, sizeof(lifeValue32));
                }
                else
                {
                    DEBUG_LOG(_L("RESPONDER-LIFETIME (Invalid lifetime type)"));
                    return EFalse;
                }
            }
            else    //Not basic
            {
                TPtrC8 ptr(attr->VarValue(),attr->Length());

                if (lifeType == SECONDS)
                {
                    if (Desc8ToTInt64(ptr, lifeValue) != KErrNone)
                        {
                        DEBUG_LOG(_L("RESPONDER-LIFETIME Lifetime(Sec) Overflowed Setting to maximum value"));
                        }
                    Desc8ToTInt64(transform->iLifeDurationSecs, curr_lifeValue);        //can't fail
                    if (lifeValue < curr_lifeValue)
                        transform->iLifeDurationSecs.Copy(attr->VarValue(),attr->Length());
                }
                else if (lifeType == KBYTES)
                {
                    if (Desc8ToTInt64(ptr, lifeValue) != KErrNone)
                        {
                        DEBUG_LOG(_L("RESPONDER-LIFETIME Lifetime(KBytes) Overflowed Setting to maximum value"));
                        }
                    Desc8ToTInt64(transform->iLifeDurationKBytes, curr_lifeValue);  //can't fail
                    if (lifeValue < curr_lifeValue)
                        transform->iLifeDurationKBytes.Copy(attr->VarValue(),attr->Length());
                }
                else
                {
                    DEBUG_LOG(_L("RESPONDER-LIFETIME (Invalid lifetime type)"));
                    return EFalse;
                }
            }
            break;
        default:
            DEBUG_LOG1(_L("RESPONDER-LIFETIME (Invalid attribute (%d) received)"), attr->Type());
            return EFalse;
        }//switch
        attr = attr->Next();
    }//while

    return ETrue;
}

//Processes a REPLAY-STATUS NOT payload
TBool CIkev1Negotiation::ProcessReplayStatus(const TNotificationISAKMP *aNotifPayload)
{
    TBuf8<2 * ISAKMP_COOKIE_SIZE> spi, own_neg_spi;

    DEBUG_LOG(_L("Processing REPLAY-STATUS"));

    if (!((iPhase == PHASE_II) && ((iStage == 1) || (iStage == 2))))
    {
        DEBUG_LOG(_L("Unexpected REPLAY-STATUS payload (Bad stage)"));
        return EFalse;
    }

    TUint8 protocol = aNotifPayload->GetProtocol();
    if ((protocol != PROTO_IPSEC_AH) && (protocol != PROTO_IPSEC_ESP) &&
        (protocol != PROTO_ISAKMP)   && (protocol != 0))
    {
        DEBUG_LOG(_L("Bad protocol in the REPLAY-STATUS payload"));
        return EFalse;
    }
    
    TInt i;
    TUint8 spi_size = aNotifPayload->GetSPISize();
    CProposal_II *prop;
    //If SPI sent
    switch (spi_size)
    {
    case 2 * ISAKMP_COOKIE_SIZE:    //ISAKMP spi
        spi.Copy(aNotifPayload->GetSPI(), aNotifPayload->GetSPISize());
        own_neg_spi.Copy(iCookie_I);
        own_neg_spi.Append(iCookie_R);
        if (spi.Compare(own_neg_spi) != 0)
        {
            DEBUG_LOG(_L("Invalid SPI size in the REPLAY-STATUS payload. Payload ignored"));
            return EFalse;
        }
        if (iChosenProp_IIList->Count() > 1)    
        {
            DEBUG_LOG(_L("REPLAY-STATUS ignored. More than one IPsec SA (need an IPsec SPI to know which one to use) "));
            return EFalse;
        }
        break;
    case 0: //Compatibility with Alchemy cc500
        if (iChosenProp_IIList->Count() > 1)
        {
            DEBUG_LOG(_L("RESPONDER-LIFETIME ignored. More than one SA (need a IPsec SPI to know which one to use) "));
            return EFalse;
        }
        prop = iChosenProp_IIList->At(0);       //Only one proposal
        break;
    case sizeof(TUint32):           //IPSEC SPI
        spi.Copy(aNotifPayload->GetSPI(), aNotifPayload->GetSPISize());
        for (i = 0; i < iChosenProp_IIList->Count(); i++)
        {
            prop = iChosenProp_IIList->At(i);       //Only one proposal
            if (((prop->iSPI.Compare(spi) == 0) && prop->iProtocol == protocol)) //right prop
                break;
        }
        if (i == iChosenProp_IIList->Count())   //No prop matches
        {
            DEBUG_LOG(_L("REPLAY-STATUS ignored. IPsec SPI doesn't match any chosen proposal"));
            return EFalse;
        }
        break;
    default:
        DEBUG_LOG(_L("Bad SPI size in the REPLAY-STATUS payload"));
        return EFalse;
    }

    TInt data_len = aNotifPayload->GetNotifDataSize();
    TUint32 *data = (TUint32 *)aNotifPayload->GetNotifData();
    if (STATIC_CAST(TUint, data_len) < sizeof(*data))
    {
        DEBUG_LOG(_L("REPLAY-STATUS (Length mismatch in the attibutes)"));
        return EFalse;
    }

#ifdef _DEBUG
    if (ByteOrder::Swap32(*data) == 0)
        DEBUG_LOG(_L("Anti-Replay Disabled on Peer Host"));
    else
        DEBUG_LOG(_L("Anti-Replay Enabled on Peer Host"));
#endif        
    return ETrue;
}


TBool CIkev1Negotiation::ProcessInitialContactL(const TNotificationISAKMP *aNotifPayload)
{
    TBuf8<2 * ISAKMP_COOKIE_SIZE> spi, neg_spi;

    DEBUG_LOG(_L("Processing INITIAL-CONTACT"));
                                                              // 7 = CRACK 
    if (!(iPhase == PHASE_I && (iStage == 5 || iStage == 6 || iStage == 7)))
    {
        DEBUG_LOG(_L("Unexpected INITIAL-CONTACT payload (Bad stage)"));
        SetErrorStatus( KKmdIkeNegotFailed );
        SendNotifyL(INVALID_PAYLOAD_TYPE);
        return EFalse;
    }

    if (aNotifPayload->GetProtocol() != PROTO_ISAKMP &&
        aNotifPayload->GetProtocol() != 0 )
        
    {
        DEBUG_LOG(_L("Bad protocol in the INITIAL_CONTACT payload"));
        SetErrorStatus( KKmdIkeNegotFailed );
        SendNotifyL(INVALID_PROTOCOL_ID);
        return EFalse;
    }

    if (aNotifPayload->GetSPISize() != 2 * ISAKMP_COOKIE_SIZE)
    {
        DEBUG_LOG(_L("Bad SPI size in the INITIAL_CONTACT payload"));
        SetErrorStatus( KKmdIkeNegotFailed );
        SendNotifyL(INVALID_SPI);
        return EFalse;
    }
    spi.Copy(aNotifPayload->GetSPI(), aNotifPayload->GetSPISize());
    neg_spi.Copy(iCookie_I);
    neg_spi.Append(iCookie_R);

    if (spi.Compare(neg_spi) != 0)
    {
        DEBUG_LOG(_L("Invalid SPI size in the INITIAL_CONTACT payload"));
        SetErrorStatus( KKmdIkeNegotFailed );
        SendNotifyL(INVALID_SPI);
        return EFalse;
    }
   
    if ( iRole == RESPONDER )
        {        
        // Expired SAs are not returned.
        TIkev1SAData* sa = iPluginSession->FindIkev1SADataWithAddr( iRemoteAddr );	
        while ( sa != NULL )
            {
            iPluginSession->UpdateIkev1SAL( sa->iSAId, ETrue );
            sa = iPluginSession->FindIkev1SADataWithAddr( iRemoteAddr );
            }
        
        // Delete other ongoing negotiations.
        CIkev1Negotiation* next = iPluginSession->FirstNegotiation();    
        while ( next != NULL )
            {
            CIkev1Negotiation* current = next;
            next = current->iNext;
            if ( current != this )
                {
                delete current;
                }
            }
        }

    return ETrue;
}



void CIkev1Negotiation::ProcessVendorL(CArrayFixFlat<const TVendorISAKMP*>* aVids)
{
	TBool result;	
    TInt i = 0;

	while ( i < aVids->Count() )
	{
    	TVendorISAKMP* VendorPayload = (TVendorISAKMP*)aVids->At(i);
        DEBUG_LOG(_L("Vendor ID received!\nHex: "));
        DEBUG_LOG_ARRAY(VendorPayload->VIDData(), VendorPayload->GetLength() - sizeof(*VendorPayload));
				
        if ( iLocalAddr.Family() == KAFUnspec )
            User::LeaveIfError( iPluginSession->GetLocalAddress( iLocalAddr ) ); // No local address info, get it !

		result = EFalse;
        iNAT_T_Required = ProcessVendorId(&result,
                                          (TUint8*)iCookie_I.Ptr(),
                                          (TUint8*)iCookie_R.Ptr(),
                                          iLocalAddr,
                                          VendorPayload);
        if ( result )
		{
           iFamiliarPeer = result;
#ifdef _DEBUG           
           DEBUG_LOG(_L("Nokia VPN gateway in peer!"));
		   if ( iNAT_T_Required  ) { 
	  	      DEBUG_LOG(_L("NAT Traversal needed!"));
		      if ( !iHostData->iUseNatProbing )
		      DEBUG_LOG(_L(" NAT probe not requested! NAT-T not used!"));    
		   }
#endif // _DEBUG	   
		   iNAT_T_Required = iNAT_T_Required & iHostData->iUseNatProbing;				   
        }   
        else
		{
		   if ( CheckDPDVendorId(VendorPayload) )
		   {
			  DEBUG_LOG(_L("Peer supports IETF Dead Peer Detection!"));			   
			  iDPDSupported = ETrue;
		   }	  
		   else if ( iNatDiscovery )
		   {
              result = iNatDiscovery->CheckNatVendorId(VendorPayload);
              if ( result )
              {
              	DEBUG_LOG(_L("Peer supports IETF (draft-03) NAT Traversal!"));              	
              }
              else
              {
              	result = iNatDiscovery->CheckRfcNatVendorId(VendorPayload);
              	if ( result )
              	    {
              		iVendorIDRfc=ETrue;
              	    DEBUG_LOG(_L("Peer supports IETF NAT Traversal!"));
              	    }
              }	
		   }		  
		}

		i ++;		
	}

}

//
//Process Internal address payload received
//
void CIkev1Negotiation::ProcessIntAddrL(const TINTNETISAKMP *aIntnetPayload)
{
    if ( aIntnetPayload && iFamiliarPeer && iHostData->iUseInternalAddr ) {
       delete iInternalAddr; //delete if already exists (old)
	   iInternalAddr = NULL;
       iInternalAddr = ProcessIntNetL((TINTNETISAKMP*) aIntnetPayload);
#ifdef _DEBUG       
       if ( iInternalAddr) {  
          TBuf<80> buf;
          TBuf<40> txt_addr;
          iInternalAddr->iClientIntAddr.OutputWithScope(txt_addr);
          DEBUG_LOG1(_L("Internal address received: %S"),&txt_addr);
       }
#endif       
    }   
}

//Computes the hash for phase II
void CIkev1Negotiation::ComputeHash2L(TDes8& aHash, TInt aStage, const TUint8 *aHashMsg, TInt aHashMsgLen)
{
	
	HBufC8* prf_data =
    HBufC8::NewLC(((aHashMsgLen + iNONCE_I.Length() + iNONCE_R.Length() + (2*sizeof(TUint32))) | 0x3) + 1);

    if (aStage == 3)
        prf_data->Des().Append(0);

    TUint32 id = ByteOrder::Swap32(iMessageId);
    prf_data->Des().Append((TUint8*)&id,sizeof(iMessageId));
    DEBUG_LOG(_L("ID"));
    switch (aStage)
    {
    case 2:
        prf_data->Des().Append(iNONCE_I);
        //No break is intended
    case 1:
        prf_data->Des().Append(aHashMsg,aHashMsgLen);
        break;
    case 3:
        
        prf_data->Des().Append(iNONCE_I);
        DEBUG_LOG(_L("iNONCE_I"));
        prf_data->Des().Append(iNONCE_R);
        DEBUG_LOG(_L("iNONCE_R"));
        break;
    default:
		CleanupStack::PopAndDestroy();  //prf_data		
        return;
    }

    DEBUG_LOG1(_L("Hash_II(%d) prf"),aStage);

    ComputePRFL(aHash, iSKEYID_a, prf_data->Des());
    
    DEBUG_LOG(_L("HASH"));

	CleanupStack::PopAndDestroy();  //prf_data

}

//Computes the hash for a protected informational exchange
void CIkev1Negotiation::ComputeHashInfL(TDes8& aHash, const TUint8 *aHashMsg, TInt aHashMsgLen)
{
	
	HBufC8* prf_data =
    HBufC8::NewLC(((aHashMsgLen + sizeof(iMessageId)) | 0x3) + 1);

    //prf(SKEYID_a, M_ID | N/D)
    TUint32 id = ByteOrder::Swap32(iMessageId);
    prf_data->Des().Append((TUint8*)&id, sizeof(iMessageId));

    prf_data->Des().Append(aHashMsg, aHashMsgLen);
    
    DEBUG_LOG(_L("Hash_NOT prf"));

    ComputePRFL(aHash, iSKEYID_a, prf_data->Des());

    DEBUG_LOG(_L("HASH"));

	CleanupStack::PopAndDestroy();  //prf_data			

}

//Verifies that aHash is correct
TBool CIkev1Negotiation::VerifyHash2L(const THashISAKMP *aHash,const TUint8 *aHashMsg, TInt aHashMsgLen)
{
	TBuf8<ISAKMP_HASH_SIZE> tmp_hash;	
	
    ComputeHash2L(tmp_hash, iStage, aHashMsg, aHashMsgLen);    //Computes the specified phase II hash

    TBool Status = (Mem::Compare((TUint8*)tmp_hash.Ptr(), tmp_hash.Length(), aHash->Data(), aHash->DataLen()) == 0);

	return Status;
}

//Verifies the hash of a Notification or Delete payload
// Used also to verify the hash of Transaction exchange (Attribute payload) 
TBool CIkev1Negotiation::VerifyInformationalHashL(const THashISAKMP *aHash,const TPayloadISAKMP *aPayload, TUint32 aMessageId)
{
    TBuf8<MAX_PRF_LENGTH> tmp_hash;

    TUint32 tmp_id = ByteOrder::Swap32(aMessageId);
    HBufC8 *prf_buf = HBufC8::NewLC(sizeof(tmp_id) + aPayload->GetLength());
    prf_buf->Des().Copy((TUint8 *)&tmp_id , sizeof(tmp_id));
    prf_buf->Des().Append((TUint8 *)aPayload, aPayload->GetLength());

    ComputePRFL(tmp_hash, iSKEYID_a, prf_buf->Des());
    TPtrC8 hash_ptr(aHash->Data(),aHash->DataLen());
    TBool b = (tmp_hash.Compare(hash_ptr)==0);
    CleanupStack::PopAndDestroy();  //prf_buf
    return (b);
}


//Computes Own Nonce using current time a seed
void CIkev1Negotiation::ComputeNonceL()
{
    DEBUG_LOG(_L("Computed NONCE."));
    if (iRole==INITIATOR)
    {
        iNONCE_I.SetLength(OAKLEY_DEFAULT_NONCE_SIZE);
        TRandom::RandomL(iNONCE_I);
    }
    else
    {       
        iNONCE_R.SetLength(OAKLEY_DEFAULT_NONCE_SIZE);
        TRandom::RandomL(iNONCE_R);
    }
}

//Computes HASH_R value
void CIkev1Negotiation::ComputeHashrL(TDes8 &aHash)
{
    TInt    id_size = 0;
    TUint16 auth_method = iChosenProposal_I.iAttrList->iAuthMethod;

    DEBUG_LOG(_L("Computing HASH_R"));

    if ( auth_method != IKE_A_CRACK ) {         
       if (iRole==INITIATOR)   
       {  //peer id. payload
          id_size = iPeerIdentPayloadSize;
          DEBUG_LOG(_L("PeerID"));
       }
       else    
       {  //Own identification payload
          id_size = iOwnIdentPayloadSize;
          DEBUG_LOG(_L("OwnID"));
       }
    }   
            
    DEBUG_LOG(_L("SKEYID"));

    HBufC8 *prf_data;
    if (iRole==INITIATOR)   //peer id. payload
    {
        prf_data = HBufC8::NewLC(iPeerPublicKey.Length() + iOwnPublicKey_ptr.Length() + iCookie_R.Length()
        + iCookie_I.Length() + iSAPayloadSize + id_size);
        prf_data->Des().Copy(iPeerPublicKey);
        prf_data->Des().Append(iOwnPublicKey_ptr);
        prf_data->Des().Append(iCookie_R);
        prf_data->Des().Append(iCookie_I);
        prf_data->Des().Append(iSAPayload,iSAPayloadSize);  //stored at the begining
        if ( auth_method != IKE_A_CRACK )
           prf_data->Des().Append(iPeerIdentPayload, iPeerIdentPayloadSize);
    }
    else    //RESPONDER
    {
        prf_data = HBufC8::NewLC(iOwnPublicKey_ptr.Length() + iPeerPublicKey.Length() + iCookie_R.Length()
        + iCookie_I.Length() + iSAPayloadSize + id_size);
        prf_data->Des().Copy(iOwnPublicKey_ptr);
        prf_data->Des().Append(iPeerPublicKey);
        prf_data->Des().Append(iCookie_R);
        prf_data->Des().Append(iCookie_I);
        prf_data->Des().Append(iSAPayload,iSAPayloadSize);  //stored at the begining
        if ( auth_method != IKE_A_CRACK )
           prf_data->Des().Append(iOwnIdentPayload, iOwnIdentPayloadSize);
    }

    DEBUG_LOG(_L("PRF"));

    ComputePRFL(aHash, iSKEYID, prf_data->Des());

    CleanupStack::PopAndDestroy();  //prf_data

    DEBUG_LOG(_L("HASH_R"));

}


//Computes the value of iHASH_I
void CIkev1Negotiation::ComputeHash1L(TDes8 &aHash)
{
    TInt    id_size = 0;
    TUint16 auth_method = iChosenProposal_I.iAttrList->iAuthMethod; 

    DEBUG_LOG(_L("Computing HASH_I"));

    if ( auth_method != IKE_A_CRACK ) {         
       if (iRole==INITIATOR)   //Own identification payload
       {
          id_size = iOwnIdentPayloadSize;
          DEBUG_LOG(_L("OwnID"));
       }
       else    //peer id. payload
       {
          id_size = iPeerIdentPayloadSize;
          DEBUG_LOG(_L("PeerID"));
       }
    }   
            
            
    DEBUG_LOG(_L("SKEYID"));

    HBufC8 *prf_data;
    if (iRole==INITIATOR)
    {
        prf_data = HBufC8::NewLC(iOwnPublicKey_ptr.Length() + iPeerPublicKey.Length() + iCookie_I.Length()
        + iCookie_R.Length() + iSAPayloadSize + id_size);
        prf_data->Des().Copy(iOwnPublicKey_ptr);
        prf_data->Des().Append(iPeerPublicKey);
        prf_data->Des().Append(iCookie_I);
        prf_data->Des().Append(iCookie_R);
        prf_data->Des().Append(iSAPayload,iSAPayloadSize);  //stored at the begining
        if ( auth_method != IKE_A_CRACK )
           prf_data->Des().Append(iOwnIdentPayload,iOwnIdentPayloadSize);      
    }
    else    //RESPONDER
    {
        prf_data = HBufC8::NewLC(iPeerPublicKey.Length() + iOwnPublicKey_ptr.Length() + iCookie_I.Length()
                                 + iCookie_R.Length() + iSAPayloadSize + id_size);
        prf_data->Des().Copy(iPeerPublicKey);
        prf_data->Des().Append(iOwnPublicKey_ptr);
        prf_data->Des().Append(iCookie_I);
        prf_data->Des().Append(iCookie_R);
        prf_data->Des().Append(iSAPayload,iSAPayloadSize);  //stored at the begining

        if ( auth_method != IKE_A_CRACK )       
           prf_data->Des().Append(iPeerIdentPayload, iPeerIdentPayloadSize);        
    }   

    DEBUG_LOG(_L("PRF"));

    ComputePRFL(aHash, iSKEYID, prf_data->Des());
    CleanupStack::PopAndDestroy();  //prf_buf
    DEBUG_LOG(_L("HASH_I"));
}


//Checks the encryption alg is valid
TBool CIkev1Negotiation::CheckEncrAlg(TUint16 aValue)
{
    switch (aValue)
    {
    case DES_CBC:
    case DES3_CBC:
    case AES_CBC:
        return ETrue;
    case IDEA_CBC:
    case BLOWFISH_CBC:
    case RC5_R16_B64_CBC:
    case CAST_CBC:
        DEBUG_LOG(_L("Not implemented Encr algorithm"));
        return ETrue; // Unknown attribute value NOT a fatal error !            
    }
    DEBUG_LOG(_L("Bad Encr algorithm"));

    return ETrue; // Unknown attribute value NOT a fatal error !
}



TBool CIkev1Negotiation::CheckHashAlg(TUint16 aValue)
{
    switch (aValue)
    {
    case HASH_MD5:
    case HASH_SHA1:
        return ETrue;
    case HASH_TIGER:
        DEBUG_LOG(_L("Not implemented Hash algorithm"));
        return ETrue; // Unknown attribute value NOT a fatal error !                            
    }
    DEBUG_LOG(_L("Bad Hash algorithm"));
        return ETrue; // Unknown attribute value NOT a fatal error !                        

}


TBool CIkev1Negotiation::CheckAuthMethod(TUint16 aValue)
{
    switch (aValue)
    {   
    case PRE_SHARED:
        if (iHostData->iPresharedKey.iKey.Length()==0)  //No preshared key defined
        {
            DEBUG_LOG(_L("Authentication method error (No Preshared key available"));
            return EFalse;
        }
        return ETrue;
    case RSA_SIG:
    case DSS_SIG:
    case IKE_A_CRACK:       
        return ETrue;
    }
    DEBUG_LOG(_L("Bad Authentication method"));
    return ETrue; // Unknown attribute value NOT a fatal error !

}

TBool CIkev1Negotiation::CheckGroupDesc(TUint16 aValue)
{
    switch (aValue)
    {
    case MODP_768:
    case MODP_1024:
    case MODP_1536:     
    case MODP_2048:
        return ETrue;
    case EC2N_155:
    case EC2N_185:
        break;
    }

    return ETrue; // Unknown attribute value NOT a fatal error !    
}


TBool CIkev1Negotiation::CheckGroupType(TUint16 aValue)
{
    switch(aValue)
    {
    case MODP:
        return ETrue;
    case ECP:
    case EC2N:
        break;      
    }

    return ETrue; // Unknown attribute value NOT a fatal error !        

}

TBool CIkev1Negotiation::CheckGroupPrime(const TUint8* /* aValue */, TUint16 /* length */)
{
    return ETrue;
}

TBool CIkev1Negotiation::CheckGroupGen(const TUint8* /* aValue */, TUint16 /* length */)
{
    return ETrue;
}

TBool CIkev1Negotiation::CheckGroupCurve(const TUint8* /* aValue */, TUint16 /* length */)
{
    return ETrue;
}

//Used for Phase I and II
TBool CIkev1Negotiation::CheckLifeType(TUint16 aValue)
{
    switch(aValue)
    {
    case SECONDS:
    case KBYTES:
        return ETrue;
    }
    return EFalse;
}

TBool CIkev1Negotiation::CheckLifeDuration(const TUint8* /* aValue */, TUint16 /* length */)
{
    return ETrue;
}

TBool CIkev1Negotiation::CheckPRF(TUint16 aValue)
{
    if (aValue!=OAKLEY_PRF_3DES_CBC_MAC)
    {
        DEBUG_LOG(_L("Bad PRF"));    
        return EFalse;
    }
    return ETrue;
}

TBool CIkev1Negotiation::CheckKeyLength(TUint16 /*aValue*/,TUint8 aID,TUint8 aProtocol)
{
    TBool Status = ETrue;
    switch (aProtocol)
    {
    case PROTO_ISAKMP:
        if ( aID != AES_CBC )
		{	
           Status = EFalse; //all other supported algs have fixed size
		   DEBUG_LOG(_L("Key length specified with fixed ISAKMP encryption algorithm"));
		}
        break;
    case PROTO_IPSEC_AH:
        Status = EFalse;      //Supported algorithms have fixed key length
		DEBUG_LOG(_L("Key length specified with fixed AH integrity algorithm"));
        break;
    case PROTO_IPSEC_ESP:
		if ( aID != ESP_AES_CBC )
		{	
		   Status = EFalse;
		   DEBUG_LOG(_L("Key length specified with fixed ESP encryption algorithm"));		   
		}   
        break;
    default:    //Unsupported SA type
		Status = EFalse;      //Supported algorithms have fixed key length
		break;
    }

	return Status;
}

TBool CIkev1Negotiation::CheckFieldSize(TUint16 /* aValue */)
{
    DEBUG_LOG(_L("Field size not supported"));
    return EFalse;
}

TBool CIkev1Negotiation::CheckGroupOrder(const TUint8* /* aValue */, TUint16 /* length */)
{
    DEBUG_LOG(_L("Group Order not supported "));
    return ETrue; // Unknown attribute value NOT a fatal error !                    
}

//Encapsulation mode
TBool CIkev1Negotiation::CheckEncMode(TUint16 aValue)
{
    switch (aValue)
    {
    case DOI_TUNNEL:
    case DOI_TRANSPORT:
        return ETrue;
    case UDP_ENC_TUNNEL:
    case UDP_RFC_ENC_TUNNEL:
//  case UDP_ENC_TRANSPORT:  
        if ( iNAT_D_Flags )
           return ETrue;
        break;
    }

    DEBUG_LOG(_L("Bad Encapsulation mode"));
    return EFalse;
    
}

//defined authentication algorithm types. Other are invalid.
TBool CIkev1Negotiation::CheckAuthAlg(TUint16 aValue)
{
    switch (aValue)
    {
    case DOI_HMAC_MD5:
    case DOI_HMAC_SHA:
        return ETrue;
	case DOI_DES_MAC:		
    case DOI_KPDK:
        DEBUG_LOG(_L("Unimplemented Auhentication Algorithm"));
    }
    DEBUG_LOG(_L("Bad Auhentication Algorithm"));
    return ETrue; // Unknown attribute value NOT a fatal error !                        
}

//By now only X509_CERT_SIG. Tristate error codes:
//KErrNone -> accepted
//KErrNotSupported ignored but no error Notification.
//KErrGeneral: NOT accepted 
TInt CIkev1Negotiation::CheckEncodingL(TUint8 aEncoding)
{
    switch (aEncoding)
    {
    case X509_CERT_SIG://X.509 Certificate - Signature
        break;
    case CRL://Certificate Revocation List (CRL)
        DEBUG_LOG(_L("WARNING: CRL ignored because not supported"));
        return KErrNotSupported;    //No notification, just ignored!
    case PKCS://PKCS #7 wrapped X.509 certificate
    case PGP://PGP Certificate
    case DNS ://DNS Signed Key
    case X509_CERT_KE://X.509 Certificate - Key Exchange
    case KERBEROS://Kerberos Tokens
    case ARL://Authority Revocation List (ARL)
    case SPKI://SPKI Certificate
    case X509_CERT_ATTR://X.509 Certificate - Attribute
        DEBUG_LOG(_L("CERT_TYPE_UNSUPPORTED (not supported CERT type)"));
        SendNotifyL(CERT_TYPE_UNSUPPORTED);
        return KErrNotSupported;    // No notification, just ignored!        

    default://Invalid encoding type
        DEBUG_LOG(_L("INVALID_CERT_ENCODING (not existent CERT type)"));
        SendNotifyL(INVALID_CERT_ENCODING);
        return KErrNotSupported;    // No notification, just ignored!                

    }

    return KErrNone;
}


//Provisional (Uses as a seed time, Address and port)
TCookie CIkev1Negotiation::CreateCookieL() const
{
    TCookie c;
    //Cookie generation is Random no longer uses known data like addr
    //or port (wrong?)
    c.SetLength(ISAKMP_COOKIE_SIZE);
	TRandom::RandomL(c);
    return c;
}

TInt32 CIkev1Negotiation::RandomMessageId()
{
    TTime tmp_time;
    tmp_time.UniversalTime();
    TInt64 seed = tmp_time.Int64();
    TInt32 rand = Math::Rand(seed);
    return rand;
}

TBool CIkev1Negotiation::CheckCookies(const TCookie& aInit, const TCookie& aResp)
    {
    TCookie NULL_COOKIE;
    NULL_COOKIE.FillZ(ISAKMP_COOKIE_SIZE);

    if ( iCookie_I.Compare(NULL_COOKIE) != 0 &&
         iCookie_I.Compare(aInit) != 0 )
        {
        DEBUG_LOG(_L("Initiator COOKIE incorrect"));
        return EFalse;
        }
    if ( iCookie_R.Compare(NULL_COOKIE) != 0 &&
         iCookie_R.Compare(aResp) != 0 )
        {
        DEBUG_LOG(_L("Responder COOKIE incorrect"));
        return EFalse;
        }

    return ETrue;    
    }

//Checks if the payload value is correct
TBool CIkev1Negotiation::CheckPayloadCode(TUint8 aPayload)
{
    switch (aPayload)
    {
    case ISAKMP_PAYLOAD_NONE:
    case ISAKMP_PAYLOAD_SA:
    case ISAKMP_PAYLOAD_P:
    case ISAKMP_PAYLOAD_T:
    case ISAKMP_PAYLOAD_KE:
    case ISAKMP_PAYLOAD_ID:
    case ISAKMP_PAYLOAD_CERT:
    case ISAKMP_PAYLOAD_CR:
    case ISAKMP_PAYLOAD_HASH:
    case ISAKMP_PAYLOAD_SIG:
    case ISAKMP_PAYLOAD_NONCE:
    case ISAKMP_PAYLOAD_NOTIF:
    case ISAKMP_PAYLOAD_D:
    case ISAKMP_PAYLOAD_VID:
    case ISAKMP_PAYLOAD_ATTRIBUTES:                         
    case ISAKMP_PAYLOAD_CHRE:
    case ISAKMP_INT_NETWORK:
    case IETF_NAT_DISCOVERY:
    case IETF_RFC_NAT_DISCOVERY:
    case IETF_NAT_ORIG_ADDR:
    case IETF_RFC_NAT_ORIG_ADDR:
        return ETrue;   //supported payload type
    }
    DEBUG_LOG1(_L("INVALID_PAYLOAD_TYPE (%x)"),aPayload);
    return EFalse;

}


//Checks if the version (major,minor) is supported
TBool CIkev1Negotiation::CheckVersionL(TUint8 aVersion)
{
    if (aVersion >> 4  > MAJOR)
    {
        DEBUG_LOG(_L("INVALID_MAJOR_VERSION"));
        SendNotifyL(INVALID_MAJOR_VERSION);
        return EFalse;
    }

    if (aVersion & (0x0f) > MINOR)
    {
        DEBUG_LOG(_L("INVALID_MINOR_VERSION"));
        SendNotifyL(INVALID_MINOR_VERSION);
        return EFalse;
    }

    return ETrue;   //version correct
}

//Checks if the exchange type is valid and the same as in the negotiation
TBool CIkev1Negotiation::CheckExchangeTypeL(TUint8 aType)
{
    switch (aType)
    {
    case ISAKMP_EXCHANGE_ID:    //Main
    case ISAKMP_EXCHANGE_AGGR:  // Agressive
    case ISAKMP_EXCHANGE_INFO:
    case IKE_QUICK_MODE:
        //invalid Exchange Type Not the same being used and not an error notification
        if (aType != iExchange)
        {
            DEBUG_LOG(_L("INVALID_EXCHANGE_TYPE"));  //send the informational exchange
            SendNotifyL(INVALID_EXCHANGE_TYPE);  //send the informational exchange
            return EFalse;  //invalid Exchange Type
        }
        break;
    case ISAKMP_EXCHANGE_BASE:  // Base
    case ISAKMP_EXCHANGE_NONE:  // Identity Protection (Main mode in IKE)
    case ISAKMP_EXCHANGE_AUTH:  // Authentication Only
    case IKE_NEW_GROUP_MODE:    // New Group Mode
        DEBUG_LOG(_L("INVALID_EXCHANGE_TYPE"));  //send the informational exchange
        SendNotifyL(UNSUPPORTED_EXCHANGE_TYPE);  //send the informational exchange
        return EFalse;
    }

    return ETrue;
}

//Checks the non-relevant bits are 0. Other comprovations are done when needed
TBool CIkev1Negotiation::CheckFlagsL(TUint8 aFlags)
{
    if (aFlags >> 3 != 0)
    {
        DEBUG_LOG(_L("INVALID_FLAGS"));  //send the informational exchange
        SendNotifyL(INVALID_FLAGS);  //send the informational exchange
        return EFalse;
    }

    return ETrue;
}

//Checks the Id has a correct value. 0 in Phase I, the correct one in Phase II
TBool CIkev1Negotiation::CheckMessageIdL(TUint32 aId)
{
    if (aId != iMessageId)    //iMessageId will be 0 during Phase I
    {
        DEBUG_LOG2(_L("INVALID_MESSAGE_ID %u (neg=%u)"),aId, iMessageId);
        SendNotifyL(INVALID_MESSAGE_ID); //send the informational exchange
        return EFalse;
    }
    return ETrue;
}

//Checks the DOI is valid
TBool CIkev1Negotiation::CheckDOI(TUint32 aDOI)
{
    if (aDOI > IPSEC_DOI)    //Not IPSEC nor ISAKMP DOI
        return EFalse;

    return ETrue;
}

//Checks the SIT is valid
TBool CIkev1Negotiation::CheckSituationL(TUint32 aSIT)
{
    //Secrecy and integrity not yet supported
    if ((aSIT & IPSEC_SIT_SECRECY) || (aSIT & IPSEC_SIT_INTEGRITY))
    {
        DEBUG_LOG(_L("SITUATION_NOT_SUPPORTED"));    //send the informational exchange
        SendNotifyL(SITUATION_NOT_SUPPORTED);    //send the informational exchange
        return EFalse;
    }

    return ETrue;
}
//check the generic payload is OK. Correct payload + Reserved==0
TBool CIkev1Negotiation::CheckGenericPayloadL(const TPayloadISAKMP *aPayload)
{
    if (!CheckPayloadCode(aPayload->GetPayload()))
        return EFalse;

    if (aPayload->GetReserved() != 0)   //Must be always 0
    {
        DEBUG_LOG(_L("INVALID RESERVED FIELD"));
        SendNotifyL(PAYLOAD_MALFORMED);
        return EFalse;
    }

    if ((aPayload->GetLength() < MIN_ISAKMP_PAYLOAD_SIZE) || (aPayload->GetLength() > iLengthLeft))
    {
        DEBUG_LOG(_L("BAD PAYLOAD SIZE"));
        SendNotifyL(PAYLOAD_MALFORMED);
        return EFalse;
    }

    iLengthLeft -= aPayload->GetLength();   //Updates the length left in the buffer

    return ETrue;

}

//checks if protocol supported
TBool CIkev1Negotiation::CheckProtocolL(TUint8 aProtocol)
{
    switch (aProtocol)
    {
        //PHASE_I Protocol
        case PROTO_ISAKMP:  //Only when establishing own SA??
            if (iPhase != PHASE_I)
            {
                DEBUG_LOG(_L("INVALID_PROTOCOL_ID (ISAKMP only allowed in Phase I)"));
                SendNotifyL(INVALID_PROTOCOL_ID);
                return EFalse;
            }
            return ETrue;
			
        //PHASE_II Protocols
        case PROTO_IPSEC_AH:
        case PROTO_IPSEC_ESP:
            if (iPhase != PHASE_II)
            {
                DEBUG_LOG1(_L("INVALID_PROTOCOL_ID (Prot (%u) only allowed in Phase II)"),aProtocol);
                SendNotifyL(INVALID_PROTOCOL_ID);
                return EFalse;
            }
            return ETrue;
    }
    DEBUG_LOG1(_L("INVALID_PROTOCOL_ID (Unknown Protocol (%u))"),aProtocol);
    SendNotifyL(INVALID_PROTOCOL_ID);
    return EFalse;
}

TBool CIkev1Negotiation::CheckSPIL(const TProposalISAKMP *aProposal)
{

    TUint size=aProposal->GetSPISize();
    if (iPhase == PHASE_I)
    {
        if (size > MAX_SPI_SIZE)
        {
            DEBUG_LOG(_L("INVALID_SPI (Bad Size)"));
            SendNotifyL(INVALID_SPI);
            return EFalse;
        }
    }
    else    //Phase II
    {
        TUint32 spi = 0;
        if (aProposal->GetSPISize() > sizeof(TUint32))
        {
            DEBUG_LOG(_L("INVALID_SPI (Too big. Max. is 32 bits)"));
            SendNotifyL(INVALID_SPI);
            return EFalse;
        }
        Mem::Copy((TUint8 *)&spi, ((TProposalISAKMP *)aProposal)->SPI(), aProposal->GetSPISize());
        spi = ByteOrder::Swap32(spi);
        if (spi < 256)  //The first 256 are reserved
        {
            DEBUG_LOG(_L("INVALID_SPI (spi's < 256 are RESERVED)"));
            SendNotifyL(INVALID_SPI);
            return EFalse;
        }
    }
    return ETrue;
}

//Checks for transform payloads. MUST NOT abort processing, just discard the payload
TBool CIkev1Negotiation::CheckTransformID(TUint8 aProtocol,TUint8 aID)
{
    switch (aProtocol)
    {
    case PROTO_ISAKMP:
        if (aID != KEY_IKE)
            return EFalse;
        break;
		
    case PROTO_IPSEC_AH:
		if ( (aID != AH_MD5) && (aID != AH_SHA))
		{
			DEBUG_LOG(_L("Unsupported Authentication Algorithm"));					
			return EFalse;
		}
        break;
    case PROTO_IPSEC_ESP:
		switch ( aID )
		{
			case ESP_DES_CBC:
			case ESP_3DES_CBC:
			case ESP_NULL:
			case ESP_AES_CBC:				
				break;
			default:
				DEBUG_LOG(_L("Unsupported Encryption Algorithm"));
				return EFalse;
		}	
        break;
		
    default:
        return EFalse;
    }

    return ETrue;
}


//Diffie-Hellman key exchange
//The info in iNegotiation MUST be correct
TBool CIkev1Negotiation::ComputeDHPublicValueL()
{
    TUint desc;
    if (iPhase == PHASE_I)
    {
        //If aggressive sends the SA and KE at the same time b4 knowing the chosen group.
        //The group in the first proposed transform is chosen. Shouldn't be transforms with 
        //different groups sent. Checked when using the configuration tool?
        if ((iExchange == ISAKMP_EXCHANGE_AGGR) && (iRole == INITIATOR))
            desc = iProposal_I.iAttrList->iGroupDesc;
        else
            desc = iChosenProposal_I.iAttrList->iGroupDesc;
    }
    else
    {
        if (iRole == INITIATOR) //We have to use one of the proposals because we don't have the reply yet
                                //Anyay only one group can be specified for phase II so it's fine
            desc = iProposal_IIList->At(0)->iAttrList->At(0)->iGroupDesc;
        else    //RESPONDER
            desc = iChosenProp_IIList->At(0)->iAttrList->At(0)->iGroupDesc;
    }

    delete iOwnKeys;    //Happens in phase II because we may recalculate the DH value.
	iOwnKeys = NULL;
    delete iOwnPublicKey;   // Happens in phase II because we may recalculate the DH value.
    iOwnPublicKey = NULL;

    iOwnKeys = GeneratePubPrivKeysL(desc);
    if (!iOwnKeys)
    {
        DEBUG_LOG(_L("Error generating DH public and private keys"));
        return EFalse;
    }
    iOwnPublicKey = iOwnKeys->GetPubKey();    //save the public key in a buffer to have easy access
    iOwnPublicKey_ptr.Set(iOwnPublicKey->Des());
    return ETrue;
}


//Initial IV computation
//Pre:Requires correct Public keys alredy stored!!!
TBool CIkev1Negotiation::InitIVL()
{
	HBufC8* prf_data =
    HBufC8::NewLC(((iOwnPublicKey_ptr.Length() + iPeerPublicKey.Length()) | 0x3) + 1);
	
    if (iRole == INITIATOR)
    {
        prf_data->Des().Copy(iOwnPublicKey_ptr);
        prf_data->Des().Append(iPeerPublicKey);

    }
    else    //RESPONDER
    {
        prf_data->Des().Copy(iPeerPublicKey);
        prf_data->Des().Append(iOwnPublicKey_ptr);
    }
    if (iChosenProposal_I.iAttrList->iHashAlg == HASH_MD5)
         MD5HashL(prf_data->Des(), iIV);
    else SHA1HashL(prf_data->Des(), iIV);

    if (iChosenProposal_I.iAttrList->iEncrAlg == AES_CBC )
         iIVSize = 16;
    else iIVSize = 8;
    iIV.SetLength(iIVSize); 
    DEBUG_LOG(_L("Init"));

	CleanupStack::PopAndDestroy();  //prf_data
    
    return ETrue;
}



//subsequent IV computations. Like when send notifications or beginning of Phase II
TBool CIkev1Negotiation::ComputeIVL(TDes8 &aIV, TInt32 aMessageId)
{ 
	HBufC8* prf_data =
    HBufC8::NewLC(((aIV.Length() + sizeof(aMessageId)) | 0x3) + 1);
	
    if ((iChosenProposal_I.iAttrList->iEncrAlg != DES3_CBC) &&
	    (iChosenProposal_I.iAttrList->iEncrAlg != AES_CBC)  &&
	    (iChosenProposal_I.iAttrList->iEncrAlg != DES_CBC))
    {
        return EFalse;
    }
    //former IV
    prf_data->Des().Copy(aIV);
    //Message ID
    TInt32 id = ByteOrder::Swap32(aMessageId);  //Needed to add it

    prf_data->Des().Append((TUint8 *)&id, sizeof(id));
    
    DEBUG_LOG(_L("prf"));
    if (iChosenProposal_I.iAttrList->iHashAlg == HASH_MD5)
		 MD5HashL(prf_data->Des(), aIV);
    else SHA1HashL(prf_data->Des(), aIV);
		
    DEBUG_LOG(_L("Computed IV"));
			
    CleanupStack::PopAndDestroy();  //prf_data			
    return ETrue;
}

//Generates all the keying material SKEYID,SKEYID_d,SKEYID_a,SKEYID_e
TBool CIkev1Negotiation::ComputeKeysL()
{
	TUint desc;			
    //If aggressive sends the SA and KE at the same time b4 knowing the chosen group.
    //The group in the first proposed transform is chosen. Shouldn't be transforms with 
    //different groups sent
    if ((iExchange == ISAKMP_EXCHANGE_AGGR) && (iRole == INITIATOR))
        desc = iProposal_I.iAttrList->iGroupDesc;
    else
        desc = iChosenProposal_I.iAttrList->iGroupDesc;
	//
	//Computes agreed key 
	//
	HBufC8* agreedKey = ComputeAgreedKeyL(desc, iPeerPublicKey, iOwnKeys);  //(gxy)
	if ( !agreedKey ) {
		DEBUG_LOG(_L("DH secret creation failed (ComputeAgreedKeyL)"));
		SetErrorStatus( KKmdIkeNegotFailed );
		SendNotifyL(INVALID_KEY_INFORMATION);				
		return EFalse;
	}
	CleanupStack::PushL(agreedKey);  //agreedKey

	// Use prf and agreed DH-key to generate keying material
	HBufC8* prf_data =
    HBufC8::NewLC(((iNONCE_I.Length() + iNONCE_R.Length() + 2*ISAKMP_COOKIE_SIZE) | 0x3) + 1);
	HBufC8* prf_key  =
    HBufC8::NewLC(((iNONCE_I.Length() + iNONCE_R.Length() ) | 0x3) + 1);

    DEBUG_LOG(_L("Agreed Key."));

    switch(iChosenProposal_I.iAttrList->iAuthMethod)
    {
    case RSA_SIG:
    case DSS_SIG:
    case IKE_A_CRACK:       
        //For signatures:
        //SKEYID = prf(Ni_b|Nr_b, g^xy)
        //key
        prf_key->Des().Copy(iNONCE_I);
        prf_key->Des().Append(iNONCE_R);
        ComputePRFL(iSKEYID, prf_key->Des(), agreedKey->Des());
        break;
    case PRE_SHARED:
        {
        //pre-shared keys:
        //SKEYID=prf(pre_shared key, Ni_b | Nr_b);
        //data
        prf_data->Des().Copy(iNONCE_I.Ptr(),iNONCE_I.Length());
        prf_data->Des().Append(iNONCE_R.Ptr(),iNONCE_R.Length());
        DEBUG_LOG(_L("Pre-shared Key"));

        HBufC8 *preshared_key_buf = GetPskFromPolicyL();
        CleanupStack::PushL(preshared_key_buf);        
        TPtrC8 preshared_key_ptr(preshared_key_buf->Des());
        ComputePRFL(iSKEYID, preshared_key_ptr, prf_data->Des());
        CleanupStack::PopAndDestroy(preshared_key_buf);
        }
        break;
    default://method not implemented
        DEBUG_LOG1(_L("ATTRIBUTES_NOT_SUPPORTED:Auth Method %d not supported "),iChosenProposal_I.iAttrList->iAuthMethod);
        SetErrorStatus( KKmdIkeNegotFailed );
        SendNotifyL(ATTRIBUTES_NOT_SUPPORTED);
        return EFalse;
    }
	
	CleanupStack::PopAndDestroy(2);  //prf_data and prf_key
	prf_data =
	HBufC8::NewLC(((agreedKey->Length() + iSKEYID.Length() + 2*ISAKMP_COOKIE_SIZE + 4) | 0x3) + 1);
	
    prf_data->Des().Copy(agreedKey->Des());
    prf_data->Des().Append(iCookie_I);
    prf_data->Des().Append(iCookie_R);
    prf_data->Des().Append(0);
    ComputePRFL(iSKEYID_d, iSKEYID, prf_data->Des());

    prf_data->Des().Copy(iSKEYID_d);
    prf_data->Des().Append(agreedKey->Des());
    prf_data->Des().Append(iCookie_I);
    prf_data->Des().Append(iCookie_R);
    prf_data->Des().Append(1);
    ComputePRFL(iSKEYID_a, iSKEYID, prf_data->Des());

    prf_data->Des().Copy(iSKEYID_a);
    prf_data->Des().Append(agreedKey->Des());
    prf_data->Des().Append(iCookie_I);
    prf_data->Des().Append(iCookie_R);
    prf_data->Des().Append(2);
    ComputePRFL(iSKEYID_e, iSKEYID, prf_data->Des());

	agreedKey->Des().FillZ();  // Zeroe DH secret g^xy

    //Builds the IV 
    if (!InitIVL())
    {
        DEBUG_LOG(_L("Error Computing IV"));
        return EFalse;
    }
    if (iExchange == ISAKMP_EXCHANGE_AGGR)
    {
        iLastIV.Copy(iIV);      //Saves last IV in Phase 1
        iLastIV.SetLength(iIVSize);   
        DEBUG_LOG(_L("Last IV Saved!"));
    }

    //if key extension required:
    TUint8 *key;
    TInt key_len=0;
    TInt total_key_len = ISAKMPEncrKeyLength((TUint8)iChosenProposal_I.iAttrList->iEncrAlg);
    if (iSKEYID_e.Length() < total_key_len)
    {
        DEBUG_LOG(_L("Extending encrytion key..."));
        key = new (ELeave) TUint8[total_key_len*2];
        CleanupStack::PushL(key);
        key[0] = 0;
        TPtr8 kx0_ptr(key, 1, 1);
        TPtr8 kx1_ptr(key, 0, total_key_len * 2);
        ComputePRFL(kx1_ptr, iSKEYID_e, kx0_ptr); //K1=prf(SKEYID_e,0)
        key_len += kx1_ptr.Length();
        
        while (key_len < total_key_len)
        {
            kx0_ptr.Set(&key[key_len - kx1_ptr.Length()], kx1_ptr.Length(), total_key_len);
            kx1_ptr.Set(&key[key_len], 0, total_key_len);
            ComputePRFL(kx1_ptr, iSKEYID_e, kx0_ptr); //Kx=prf(SKEYID_e,K<x-1>)
            key_len += kx1_ptr.Length();
        }
        iSKEYID_e.Copy(key, total_key_len);
        CleanupStack::PopAndDestroy();  //key
        DEBUG_LOG(_L(" SKEYID_e (EXTENDED)"));
    }
	else
	{
		iSKEYID_e.SetLength(total_key_len);		
		DEBUG_LOG(_L(" SKEYID_e"));
	}
    
    DEBUG_LOG(_L(" Init IV"));

	CleanupStack::PopAndDestroy(2);  //prf_data and agreedKey
	
    return ETrue;
}

//Computes the IPSEC keys needed for each IPSEC SA
//KEYMAT = prf(SKEY_ID, protocol | SPI | Ni_b | Nr_b)
//if PFS:
//KEYMAT = prf(SKEY_ID, g(qm)^xy | protocol | SPI | Ni_b | Nr_b)
void CIkev1Negotiation::ComputeKeys2L(const CProposal_II *aProp, TInt aKeyLen, TSPINode &aInboundSpiNode, TDes8& aOutboundKey_II, TDes8& aInboundKey_II)
{
    DEBUG_LOG(_L("Computing PHASE II keys "));
    
    aOutboundKey_II.SetLength(0);
    aInboundKey_II.SetLength(0);
    DEBUG_LOG(_L("Total Computed Key Length="));
    DEBUG_LOG_NUM(aKeyLen);

	HBufC8* agreedKey = NULL; //g(qm)^xy
	TInt  prf_len = iSKEYID_d.Length() + iNONCE_I.Length() + iNONCE_R.Length() + 8; // 8 for protocol and SPI
	
    if (iPFS)
    {
        agreedKey = ComputeAgreedKeyL(iChosenProp_IIList->At(0)->iAttrList->At(0)->iGroupDesc, iPeerPublicKey, iOwnKeys);
		if ( !agreedKey )
			User::Leave(KErrGeneral);
		CleanupStack::PushL(agreedKey);
		prf_len += agreedKey->Length(); 
        DEBUG_LOG(_L(" Agreed Key"));
    }
#ifdef _DEBUG    
	else DEBUG_LOG(_L("(NO PFS)"));
#endif // _DEBUG	

    DEBUG_LOG(_L("Protocol:"));
    DEBUG_LOG_NUM(aProp->iProtocol);
  
    TUint32 in_spi = aInboundSpiNode.iSPI;  //inbound spi in Network Order
    DEBUG_LOG(_L("InSPI:"));
    DEBUG_LOG_NUM(ByteOrder::Swap32(in_spi));
    TUint32 out_spi;
    Mem::Copy((TUint8 *)&out_spi, aProp->iSPI.Ptr(), aProp->iSPI.Length());
    DEBUG_LOG1(_L("OutSPI: %x"), out_spi);
    //Inbound and outbound calculations only differ in the SPI
    //If the key is not long enough we need to extend it
    TPtr8 key_ptr((TUint8 *)aOutboundKey_II.Ptr() + aOutboundKey_II.Length(), 0, aOutboundKey_II.MaxLength());
	
	HBufC8* prf_data = HBufC8::NewLC((prf_len | 0x3) + 1);
	
    while ((aOutboundKey_II.Length() * 8) < aKeyLen)  //include the key extension algorithm
    {
        prf_data->Des().Copy(key_ptr);
        if (agreedKey)//Only used if PFS
        {
            prf_data->Des().Append(agreedKey->Des());
        }
        prf_data->Des().Append(&aProp->iProtocol, sizeof(aProp->iProtocol));
        prf_data->Des().Append(aProp->iSPI.Ptr(),aProp->iSPI.Length());
        prf_data->Des().Append(iNONCE_I);
        prf_data->Des().Append(iNONCE_R);
        key_ptr.Set((TUint8 *)aOutboundKey_II.Ptr() + aOutboundKey_II.Length(), 0, aOutboundKey_II.MaxLength() - aOutboundKey_II.Length());
        ComputePRFL(key_ptr, iSKEYID_d, prf_data->Des());
        aOutboundKey_II.SetLength(aOutboundKey_II.Length() + key_ptr.Length());
    }

    key_ptr.Set((TUint8 *)aInboundKey_II.Ptr() + aInboundKey_II.Length(), 0, aOutboundKey_II.MaxLength());
    while ((aInboundKey_II.Length() * 8) < aKeyLen)  //include the key extension algorithm
    {
        prf_data->Des().Copy(key_ptr);
        if (agreedKey)//Only used if PFS
        {
            prf_data->Des().Append(agreedKey->Des());
        }
        prf_data->Des().Append(&aProp->iProtocol,sizeof(aProp->iProtocol));
        prf_data->Des().Append((TUint8 *)&in_spi, sizeof(TUint32));
        prf_data->Des().Append(iNONCE_I);
        prf_data->Des().Append(iNONCE_R);
        
        key_ptr.Set((TUint8 *)aInboundKey_II.Ptr() + aInboundKey_II.Length(), 0, aInboundKey_II.MaxLength() - aInboundKey_II.Length());
        ComputePRFL(key_ptr, iSKEYID_d, prf_data->Des());
        aInboundKey_II.SetLength(aInboundKey_II.Length() + key_ptr.Length());
    }

	if ( agreedKey )		
	     CleanupStack::PopAndDestroy(2);  //prf_data and agreedKey
	else CleanupStack::PopAndDestroy();   //prf_data
			
}


/* This function is called by oakley module to do pseudo random computation for
   given data */
//IMPORTANT: If some func added or modified check MAX_PRF_LENGTH is correct in ike.h

void CIkev1Negotiation::ComputePRFL(TDes8 &prf_output, const TDesC8 &prf_key, const TDesC8 &prf_data)
{
// All actions taken are moved into the crypto module IKE_CRYPTO.CPP
    
    if ( iChosenProposal_I.iAttrList->iPRF == OAKLEY_PRF_3DES_CBC_MAC)
    {
       DEBUG_LOG(_L("PRF (3DES_CBC_MAC)"));        
       Hmac3DesCbcL(prf_data, prf_output, prf_key);  
    }
    else
    {
      // Use HMAC version of the negotiated hash function as default PRF
        if ( iChosenProposal_I.iAttrList->iHashAlg == HASH_MD5 ) {
           DEBUG_LOG(_L("PRF (MD5)"));           
           MD5HmacL(prf_data, prf_output, prf_key);
        }   
        else {
           if ( iChosenProposal_I.iAttrList->iHashAlg == HASH_SHA1 ) {
              DEBUG_LOG(_L("PRF (SHA1)"));              
              SHA1HmacL(prf_data, prf_output, prf_key);
           }
        }   
    }

}


//NOTE: Must be called after ProcessCertificate() !!!
TBool CIkev1Negotiation::CertifyRemoteIdentityL(const TIdentISAKMP *aIdPayload)
{
  TBool Status = EFalse;
  TInt id_len  = aIdPayload->IDDataLen();

  if ( id_len && iPkiService && iPeerX509Cert )
  {
    TPtrC8 IdData((TUint8 *)aIdPayload->IDData(), id_len);

    // Leave is trapped and handled here because the event log is easily accessible
    // and in the upper layers it would not be possible to identify the
    // cause of the error based on the generic leave code.
    TRAPD(err, Status = IkePkiUtils::CertifyIdentityL(iPeerX509Cert, IdData, (TInt)aIdPayload->GetIDType()));

    if( KErrNotSupported == err )
    {
      LOG_KMD_EVENT( MKmdEventLoggerIf::KLogError,
                     R_VPN_MSG_CERT_ERROR,
                     KErrNotSupported,
                     iPluginSession->VpnIapId(),
                     &iRemoteAddr );
    }

#ifdef _DEBUG    
    if (Status)
        DEBUG_LOG(_L("Remote identity has been certified"));
#endif // _DEBUG    
  }
       
  return Status;
}


//Digests aHash with the key in aCert and compares it to the stored value aSig
//Needs to be check after the Signature payload has been processed
TBool CIkev1Negotiation::VerifySignatureL(CX509Certificate *aCert,TUint8 *aHash, TInt aHashLength,TUint8 *aSig, TUint aSigLength)
{

	TBool ret = EFalse;
	if ( iPkiService )
	{
		TPtrC8 Signature(aSig, aSigLength);
		TPtrC8 RefHash(aHash, aHashLength);	   							   			
	    ret = IkePkiUtils::VerifyIkev1SignatureL(Signature, RefHash, *aCert);
	}   
	
    return ret;


}

//NOTE!!! The func sets iFinished to TRUE always so after detecting an error the communication finishes.
//If it doesn't have to iFinished should be set outside the function.
void CIkev1Negotiation::SendNotifyL(TUint16 aError)
{
	SetFinished();  //Ends the negotiation (error condition detected) If CONNECTED also should end
	
#ifdef _DEBUG	
    TBuf<60> buf;
    DEBUG_LOG(_L("SendNotifyL(), Reason: "));
    buf.Append(TextNotifyType(aError));
    DEBUG_LOG(buf);
#endif // _DEBUG    
    TUint8 protocol = PROTO_ISAKMP;
    TUint8 tmp_exchange = iExchange;
    TUint32 tmp_msg_id = iMessageId;
	TIkev1IsakmpStream* msg = SaveIkeMsgBfr( new (ELeave) TIkev1IsakmpStream(iDebug) );			
    //If configured to send notification payloads for errors
    if (iHostData->iNotify)
    {
        iExchange = ISAKMP_EXCHANGE_INFO;
        iMessageId = RandomMessageId();
        msg->IsakmpInit(this);
		
        //HASH Payload only if payload protected with encyption
        if (iFlags & ISAKMP_HDR_EFLAG)
      		msg->IsakmpHashL();		

        if (iPhase == PHASE_I)
            protocol = iChosenProposal_I.iProtocol;
        else
        {
            if (iChosenProp_IIList) //May be the begining of PHASE_II when still not decided
                protocol = iChosenProp_IIList->At(0)->iProtocol;
        }
        msg->IsakmpNotification(aError, protocol);

        if (iFlags & ISAKMP_HDR_EFLAG)
        {
            msg->IsakmpHashContL();
        }

        iExchange  = tmp_exchange;
        iMessageId = tmp_msg_id;

		SendL(*msg);

  		LOG_KMD_EVENT( MKmdEventLoggerIf::KLogError,
  		               R_VPN_MSG_SENT_ERROR_RESPONSE,
  		               aError,
  		               iPluginSession->VpnIapId(),
  		               &iRemoteAddr );
    }

}


//Hash function key length (in bytes)
TInt CIkev1Negotiation::HashLength()
{
    if (iChosenProposal_I.iAttrList->iPRF == OAKLEY_PRF_3DES_CBC_MAC)
        return 24;
        
    //If no PRF hash alg used instead.
    if (iChosenProposal_I.iAttrList->iPRF==0)
    {
        if (iChosenProposal_I.iAttrList->iHashAlg==HASH_MD5)
            return HMAC_MD5_SIZE/8; //16 bytes
            //return MD5_DIGEST_LENGTH;
        else if (iChosenProposal_I.iAttrList->iHashAlg==HASH_SHA1)
            return HMAC_SHA1_SIZE/8;
            //return SHA_DIGEST_LENGTH;
    }
    
    return 0;
}


//Could be done using directly iEncrAlg from the chosen transform (in bytes)
TUint32 CIkev1Negotiation::ISAKMPEncrKeyLength(TUint8 aAlgId) const
{
    TUint32 KeyLth = 0;
    switch (aAlgId) {
        case DES_CBC:
            KeyLth = 8;
            break;
        case DES3_CBC:
            KeyLth = 24;
            break;
        case AES_CBC:
            if ( iChosenProposal_I.iAttrList->iKeyLength )
                 KeyLth = (TUint32)(iChosenProposal_I.iAttrList->iKeyLength/8);
            else KeyLth = 16;  // default
            break;
        default:
            break;
    }           
        
    return KeyLth;
}

TIkev1IsakmpStream* CIkev1Negotiation::SaveIkeMsgBfr(TIkev1IsakmpStream* aMsg)
{
	delete iSavedIkeMsgBfr;
    iSavedIkeMsgBfr = aMsg;
	return aMsg;
}	

//Returns the key length (in bits) of an algorithm for iAuth (TAttrib_II) field when protocol is AH
TUint32 CIkev1Negotiation::HMAC_KeyLength(TUint8 aId) const
{
    if (aId == SADB_AALG_MD5HMAC)
        return (HMAC_MD5_SIZE);
    else if (aId == SADB_AALG_SHA1HMAC)
        return (HMAC_SHA1_SIZE);

  return (0); // Error 
}

//Exchange Type
TPtrC CIkev1Negotiation::TextNotifyType(TUint16 aNotif)
{
#ifndef _DEBUG
    (void)aNotif;
#endif

#ifdef _DEBUG
    //TBuf<35> err;
    switch(aNotif)
    {
    case INVALID_PAYLOAD_TYPE:
        return _L("INVALID_PAYLOAD_TYPE");
    case DOI_NOT_SUPPORTED:
        return _L("DOI_NOT_SUPPORTED");
    case SITUATION_NOT_SUPPORTED:
        return _L("SITUATION_NOT_SUPPORTED");
    case INVALID_COOKIE:
        return _L("INVALID_COOKIE");
    case INVALID_MAJOR_VERSION:
        return _L("INVALID_MAJOR_VERSION");
    case INVALID_MINOR_VERSION:
        return _L("INVALID_MINOR_VERSION");
    case INVALID_EXCHANGE_TYPE:
        return _L("INVALID_EXCHANGE_TYPE");
    case INVALID_FLAGS:
        return _L("INVALID_FLAGS");
    case INVALID_MESSAGE_ID:
        return _L("INVALID_MESSAGE_ID");
    case INVALID_PROTOCOL_ID:
        return _L("INVALID_PROTOCOL_ID");
    case INVALID_SPI:
        return _L("INVALID_SPI");
    case INVALID_TRANSFORM_ID:
        return _L("INVALID_TRANSFORM_ID");
    case ATTRIBUTES_NOT_SUPPORTED:
        return _L("ATTRIBUTES_NOT_SUPPORTED");
    case NO_PROPOSAL_CHOSEN:
        return _L("NO_PROPOSAL_CHOSEN");
    case BAD_PROPOSAL_SYNTAX:
        return _L("BAD_PROPOSAL_SYNTAX");
    case PAYLOAD_MALFORMED:
        return _L("PAYLOAD_MALFORMED");
    case INVALID_KEY_INFORMATION:
        return _L("INVALID_KEY_INFORMATION");
    case INVALID_ID_INFORMATION:
        return _L("INVALID_ID_INFORMATION");
    case INVALID_CERT_ENCODING:
        return _L("INVALID_CERT_ENCODING");
    case INVALID_CERTIFICATE:
        return _L("INVALID_CERTIFICATE");
    case CERT_TYPE_UNSUPPORTED:
        return _L("CERT_TYPE_UNSUPPORTED");
    case INVALID_CERT_AUTHORITY:
        return _L("INVALID_CERT_AUTHORITY");
    case INVALID_HASH_INFORMATION:
        return _L("INVALID_HASH_INFORMATION");
    case AUTHENTICATION_FAILED:
        return _L("AUTHENTICATION_FAILED");
    case INVALID_SIGNATURE:
        return _L("INVALID_SIGNATURE");
    case ADDRESS_NOTIFICATION:
        return _L("ADDRESS_NOTIFICATION");
    case NOTIFY_SA_LIFETIME:
        return _L("NOTIFY_SA_LIFETIME");
    case CERTIFICATE_UNAVAILABLE:
        return _L("CERTIFICATE_UNAVAILABLE");
    case UNSUPPORTED_EXCHANGE_TYPE:
        return _L("UNSUPPORTED_EXCHANGE_TYPE");
    case UNEQUAL_PAYLOAD_LENGTHS:
        return _L("UNEQUAL_PAYLOAD_LENGTHS");
    case CONNECTED:
        return _L("CONNECTED");
	case DOI_RESPONDER_LIFETIME:
		return _L("RESPONDER_LIFETIME");
	case DOI_REPLAY_STATUS:
		return _L("REPLAY_STATUS");
	case DOI_INITIAL_CONTACT:
		return _L("INITIAL_CONTACT");
    }
    return _L("Unknown ");
#else
    return NULL;
#endif          
}


void CIkev1Negotiation::TextPayload(TDes &aBuf, TUint8 aPayload)
{
#ifndef _DEBUG
    (void)aBuf;
    (void)aPayload;
#endif

#ifdef _DEBUG
    switch(aPayload)
    {
    case ISAKMP_PAYLOAD_NONE:// (Terminator)
        aBuf = _L("ISAKMP_PAYLOAD_NONE");
        break;
    case ISAKMP_PAYLOAD_SA:// Security Association
        aBuf = _L("ISAKMP_PAYLOAD_SA");
        break;
    case ISAKMP_PAYLOAD_P:// Proposal
        aBuf = _L("ISAKMP_PAYLOAD_P");
        break;
    case ISAKMP_PAYLOAD_T:// Transform
        aBuf = _L("ISAKMP_PAYLOAD_T");
        break;
    case ISAKMP_PAYLOAD_KE:// Key Exchange
        aBuf = _L("ISAKMP_PAYLOAD_KE");
        break;
    case ISAKMP_PAYLOAD_ID:// Identification
        aBuf = _L("ISAKMP_PAYLOAD_ID");
        break;
    case ISAKMP_PAYLOAD_CERT:// Certificate
        aBuf = _L("ISAKMP_PAYLOAD_CERT");
        break;
    case ISAKMP_PAYLOAD_CR:// Certificate Request
        aBuf = _L("ISAKMP_PAYLOAD_CR");
        break;
    case ISAKMP_PAYLOAD_HASH:// Hash
        aBuf = _L("ISAKMP_PAYLOAD_HASH");
        break;
    case ISAKMP_PAYLOAD_SIG:// Signature
        aBuf = _L("ISAKMP_PAYLOAD_SIG");
        break;
    case ISAKMP_PAYLOAD_NONCE:// Nonce
        aBuf = _L("ISAKMP_PAYLOAD_NONCE");
        break;
    case ISAKMP_PAYLOAD_NOTIF:// Notification
        aBuf = _L("ISAKMP_PAYLOAD_NOTIF");
        break;
    case ISAKMP_PAYLOAD_D:// Delete
        aBuf = _L("ISAKMP_PAYLOAD_D");
        break;
    case ISAKMP_PAYLOAD_VID:// Vendor ID
        aBuf = _L("ISAKMP_PAYLOAD_VID");
        break;
    case ISAKMP_PAYLOAD_PRIVATE:// Private use (up to 255)
        aBuf = _L("ISAKMP_PAYLOAD_PRIVATE");
        break;
    default:
        aBuf.Format(_L("Unknown (%d) "),aPayload);
    }
#endif                  
}

//Sends the built message through the socket
void CIkev1Negotiation::SendL(TIkev1IsakmpStream &aMsg)
{
    if (aMsg.iError)
    {
        DEBUG_LOG(_L("Error Building message"));
        return;
    }
    TBuf8<IKEV1_MAX_IV_SIZE> tmp_IV;
    
    ThdrISAKMP *hdr = (ThdrISAKMP *)aMsg.iBuf.Ptr();
    hdr->SetLength(aMsg.iBuf.Length());
    DEBUG_LOG(_L("Sending (clear)..."));
    
    TBool FloatedPort = EFalse; 
    if ( iNAT_D_Flags & (REMOTE_END_NAT + LOCAL_END_NAT) )
        FloatedPort = ETrue;
    
#ifdef _DEBUG
    const TPtrC8 ikeMsgPtr( aMsg.iBuf.Ptr(), aMsg.iBuf.Length() );
    TInetAddr localAddr;
    iPluginSession->GetLocalAddress( localAddr );    
    TInt port = ( FloatedPort ? IkeSocket::KIkePort4500 : IkeSocket::KIkePort500 );
    localAddr.SetPort( port );
    TRACE_MSG_IKEV1( ikeMsgPtr, localAddr, iLastRemoteAddr );
#endif // _DEBUG
    
    TPtr8 lastMsg(iLastMsg->Des());   

    if (hdr->GetFlags() & ISAKMP_HDR_EFLAG)
    {
        DEBUG_LOG(_L("Encrypting..."));

        if (hdr->GetExchange()==ISAKMP_EXCHANGE_INFO || hdr->GetExchange()==ISAKMP_EXCHANGE_TRANSACT )
        {
            if ( hdr->GetExchange()==ISAKMP_EXCHANGE_TRANSACT )
            {
               //
               // Get current IV via CTransNegotiation object linked into
               // CIkev1Negotiation  
               //
               if ( !iTransactionNeg ||
                    !iTransactionNeg->GetIV(hdr->GetMessageId(), tmp_IV) )
               {
                   DEBUG_LOG(_L("Send error ! Cannot get Transaction IV !"));
                   return;
               }       
               DEBUG_LOG(_L("Transaction IV"));                     
               EncryptL(aMsg.iBuf, lastMsg, tmp_IV, iSKEYID_e, iChosenProposal_I.iAttrList->iEncrAlg);
               iTransactionNeg->SetIV(hdr->GetMessageId(), tmp_IV);
            }   
            else
            {
               if (iLastIV.Length() != 0)
                    tmp_IV.Copy(iLastIV);
               else    //iLastIV not yet computed so current iIV is used
                    tmp_IV.Copy(iIV);
               ComputeIVL(tmp_IV, hdr->GetMessageId());
               DEBUG_LOG(_L("Notif IV"));
               EncryptL(aMsg.iBuf, lastMsg, tmp_IV, iSKEYID_e, iChosenProposal_I.iAttrList->iEncrAlg);
            }   

        }
        else    //Normal exchange MAIN, AGGR or QUICK
        {
            DEBUG_LOG(_L("IV"));
            EncryptL(aMsg.iBuf, lastMsg, iIV, iSKEYID_e, iChosenProposal_I.iAttrList->iEncrAlg);
            DEBUG_LOG(_L("New IV (dec)"));
            //Saves last iIV in Phase 1
            if (((iStage==6) && (iExchange == ISAKMP_EXCHANGE_ID)) ||
                ((iStage==3) && (iExchange == ISAKMP_EXCHANGE_AGGR)))
            {
                iLastIV.Copy(iIV);  
                DEBUG_LOG(_L("Last IV Saved!"));
            }
        }

        DEBUG_LOG(_L("Sending ..."));
        DEBUG_LOG1(_L("EncrLen = %d"), lastMsg.Length());
    }
    else
        {
        lastMsg.Copy(aMsg.iBuf);
        }

    hdr = (ThdrISAKMP *)lastMsg.Ptr();
    hdr->SetLength(lastMsg.Length());  //Set the total length!!!		

    if (hdr->GetExchange() == ISAKMP_EXCHANGE_INFO)
    {
        SendAndSaveIkeMsgL(lastMsg, iLastRemoteAddr, FloatedPort);   //No timers!
    }
    else    //Normal msg.
    {
        iTimer->Cancel();   //Cancel previous timer because reply received & processed
        DEBUG_LOG(_L("Timer Cancelled!"));
        iRetryNum = 0;
		SendAndSaveIkeMsgL(lastMsg, iLastRemoteAddr, FloatedPort);
        iTimer->IssueRequest(MAX_RETRANS_TIMER * 1000000); // 1000000 = 1 second
    }
    
}


void CIkev1Negotiation::ReSendL()
{
    //Will resend a packet in the interval (MAX_RETRANS_TIMER/2 , MAX_RETRANS_TIMER)
    if ( iRetryNum < MAX_RETRANS_COUNT ) 
    {
        DEBUG_LOG2(_L("---------- Phase %d - Stage %d ----------"),iPhase, iStage - 1);
        DEBUG_LOG1(_L("ReSending(%d)..."), iRetryNum);
		TBool FloatedPort = EFalse;	
		if ( iNAT_D_Flags & (REMOTE_END_NAT + LOCAL_END_NAT) )
			FloatedPort = ETrue;
		TPtr8 lastMsg(iLastMsg->Des());
		iPluginSession->SendIkeMsgL(lastMsg, iLastRemoteAddr, FloatedPort);  
        //next retransmission between MAX_RETRANS_TIMER/2 and MAX_RETRANS_TIMER seconds
        TTime tmp_time;
        TReal secs = 0;
        tmp_time.UniversalTime();
        TInt64 seed = tmp_time.Int64();
        TInt rand = Math::Rand(seed);
        TInt err = Math::Round(secs, rand / (KMaxTInt / MAX_RETRANS_TIMER/2), 0);
        secs = Math::Round(secs, secs + MAX_RETRANS_TIMER/2, 0);
        if ((!secs) || (err != KErrNone))
            secs = MAX_RETRANS_TIMER/2;
        iTimer->IssueRequest((TInt)secs * 1000000);  // 1000000 = 1 second
        iRetryNum++;
    }
    else
    {
		SendDeleteL(PROTO_ISAKMP);
		if ( iPhase == PHASE_I )
		{	
           DEBUG_LOG(_L("Max num retries reached!!!"));
		}		
		else
		{		    
		    DEBUG_LOG(_L("Quick mode failed, Max num retries reached!!!"));
		}
	
		if ( iPhase == PHASE_I &&
		     iPluginSession->FindIkev1SA() == NULL )
		{
            // Set error status in Phase 1, if there are no IKE SAs.
            if ( GetNotifyStatus() )
                 SetErrorStatus(KKmdIkeNoProposalErr);                                           
            else SetErrorStatus(KKmdIkeNoResponseErr);
		}
		
        LOG_KMD_EVENT( MKmdEventLoggerIf::KLogError,
                       R_VPN_MSG_VPN_GW_NO_RESP,
                       ErrorStatus(),
                       iPluginSession->VpnIapId(),
                       &iRemoteAddr );								
        iPluginSession->DeleteNegotiation( this );
    }
}

void CIkev1Negotiation::SendDeleteL(TUint8 aProtocol, TUint32 aIpsecSPI)
{
	TIkev1IsakmpStream* msg = SaveIkeMsgBfr( new (ELeave) TIkev1IsakmpStream(iDebug) );							
	
    TBuf8<MAX_SPI_SIZE> SPI;

    DEBUG_LOG(_L("Sending Delete payload..."));
    
    //Creates a DUMMY negotiation with the info to send the packet
    TUint8 tmp_exchange = iExchange;
    iExchange=ISAKMP_EXCHANGE_INFO; //Set the exchange type as info

    msg->IsakmpInit(this);

    //HASH Payload only if payload protected with encyption
    if (iFlags & ISAKMP_HDR_EFLAG)
	    msg->IsakmpHashL();	

    if (aProtocol == PROTO_ISAKMP)  //ISAKMP SPI are the cookies
    {
        SPI.Copy(iCookie_I);
        SPI.Append(iCookie_R);
    }
    else    //IPSECSPI
    {
        SPI.Copy((TUint8 *)&aIpsecSPI, sizeof(TUint32));
    }
    msg->IsakmpDelete(SPI, aProtocol);

    if (iFlags & ISAKMP_HDR_EFLAG)
    {
        msg->IsakmpHashContL();
    }
    DEBUG_LOG(_L("Sending Delete Payload..."));
	SendL(*msg);

    iExchange = tmp_exchange;
}

void CIkev1Negotiation::CheckSendResponderLifetime(TIkev1IsakmpStream &aMsg)
{
    TInt count = iChosenProp_IIList->Count();
    CProposal_II *prop;
    TChosenAttrib_II *attr_II;
    TSPINode inboundspi_node;
    TInt j;
    for (j = 0 ; j < count; j++)    //Check all the chosen proposals (Probably one)
    {
        prop = iChosenProp_IIList->At(j);
        inboundspi_node = iInboundSPIList->At(j);
        attr_II = (TChosenAttrib_II *)prop->iAttrList->At(0);   //only 1 transform is chosen no matter how many there are

        if ((attr_II->iReducedLifeSecs.Length() != 0) || (attr_II->iReducedLifeKBytes.Length() != 0))   //Any lifetime to update
            aMsg.IsakmpResponderLifetime(prop->iProtocol,
                                         inboundspi_node.iSPI,
                                         attr_II->iResponderLifetimeSecs,
                                         attr_II->iResponderLifetimeKBytes);

    }
}


/**--------------------------------------------------------------------
 *
 *  The following methods are used to implement the Dead Peer Detection
 *  protocol defined in <draft-ietf-ipsec-dpd-04.txt>
 *  When timeout expires the R-U-THERE notify message is transmitted
 *  if there has not been any activity during the last timeout
 *
 *--------------------------------------------------------------------*/
void CIkev1Negotiation::DpdNotifyMessageReceivedL(TIkev1SAData* aSa, TUint16 aMsgType, TUint32 aSequence)
{

	if ( aMsgType == DPD_R_U_THERE )
	{
	  //
	  // -- Assure that sequence number in notify data is what expected
	  // -- If ok, transmit a R-U-THERE-ACK
	  //
		DEBUG_LOG(_L("DPD R-U-THERE Notify received"));	  
		if ( (aSa->iExpectedDPDSequence == aSequence) || (aSa->iExpectedDPDSequence == 0) )
		{
			aSa->iExpectedDPDSequence = GetNextSequence(aSequence);
			DEBUG_LOG(_L("Sending DPD R-U-THERE_ACK notify"));
			SendDpdNotifyMessageL(DPD_R_U_THERE_ACK, aSequence);
			iPluginSession->UpdateIkev1SAL(aSa->iSAId, EFalse, aSa);			
		}
#ifdef _DEBUG		
		else DEBUG_LOG(_L("Wrong sequence number in DPD notify message"));
#endif // _DEBUG		
	}
	else if ( aMsgType == DPD_R_U_THERE_ACK )
	{
	  //
	  // -- Assure that sequence number in notify data is corresponds
	  //    current pending sequence
	  //
		DEBUG_LOG(_L("DPD R-U-THERE-ACK Notify received"));
		if ( aSa->iPendingDPDSequence == aSequence )
		{
			aSa->iPendingDPDSequence = 0;
			aSa->iDPDRetry           = 0;
			iPluginSession->UpdateIkev1SAL(aSa->iSAId, EFalse, aSa);
		}
#ifdef _DEBUG		
		else DEBUG_LOG(_L("Wrong sequence number in DPD notify ack message"));
#endif // _DEBUG		
	}	
}

void CIkev1Negotiation::SendKeepAliveMsgL(TIkev1SAData* aSa)
{
	if ( aSa->iDPDRetry > KMaxDpdRetryCount )
	{
		//
		//  DPD Retry count exhausted, current IKE SA in interpreted to
		//  be closed
		//
        LOG_KMD_EVENT( MKmdEventLoggerIf::KLogError,
                       R_VPN_MSG_VPN_GW_NO_RESP,
                       KKmdIkeNoResponseErr,
                       iPluginSession->VpnIapId(),
                       &iRemoteAddr );      	
        
		iPluginSession->DeleteIpsecSAs(aSa->iSAId);		
		iPluginSession->UpdateIkev1SAL(aSa->iSAId, ETrue);
	}
	else
	{
	    //
	    //  Send DPD R-U-THERE notify message 
	    //
		if ( aSa->iPendingDPDSequence == 0 )
		{	
			aSa->iPendingDPDSequence = aSa->iDPDSequence;
			aSa->iDPDSequence = GetNextSequence(aSa->iDPDSequence);			
		}	
		else
		{
		    aSa->iDPDRetry ++;
		}
		DEBUG_LOG(_L("Sending DPD R-U-THERE notify"));		
		SendDpdNotifyMessageL(DPD_R_U_THERE, aSa->iPendingDPDSequence);
	}
	SetFinished();
}

MKmdEventLoggerIf& CIkev1Negotiation::EventLogger()
{
    return iPluginSession->EventLogger();
}

void CIkev1Negotiation::IpsecSaSpiRetrieved(TUint32 aSpiRequestId, 
                                            TInt aStatus, 
                                            TUint32 aSpi) 
{
    DEBUG_LOG3(_L("IPsec SA SPI retrieved, seq=%d, SPI=%d, status=%d"),
            aSpiRequestId, aSpi, aStatus);
    if ( aStatus == KErrNone )
        {
        TRAP( aStatus, ReceiveSPIL( aSpi, aSpiRequestId ) );
        }
    else
        {
        iPluginSession->HandleError( aStatus );
        }
}

TBool CIkev1Negotiation::IsRekeyingIkeSa()
{
    return ( iSARekeyInfo != NULL );
}

void CIkev1Negotiation::PreparePhase2L(const TPfkeyMessage &aReq)
{
    DEBUG_LOG(_L("Prepare for Phase II"));
    GetAcquireDataL(aReq);
    iPhaseIIAfterIkeSaRekey = ETrue;
}

TUint32 CIkev1Negotiation::GetNextSequence(TUint32 aSequence)
{
	aSequence ++;
	if ( aSequence == 0 )
		aSequence = 1;
	return aSequence;
}

void CIkev1Negotiation::SendDpdNotifyMessageL(TUint16 aMsgType, TUint32 aSequence)
{
	iExchange  = ISAKMP_EXCHANGE_INFO;
	iMessageId = RandomMessageId();
	TIkev1IsakmpStream* Msg = SaveIkeMsgBfr( new (ELeave) TIkev1IsakmpStream(iDebug) );
	Msg->IsakmpInit(this);
	Msg->IsakmpHashL();
	TUint32 NotifData = ByteOrder::Swap32(aSequence);
	Msg->IsakmpNotification(aMsgType, PROTO_ISAKMP, (TUint8*)&NotifData, sizeof(NotifData));
	Msg->IsakmpHashContL();
	SendL(*Msg);
}

TInt CIkev1Negotiation::ErrorStatus()
{
    TInt ret( KErrNone );
    if ( iPluginSession )
    {
        ret = iPluginSession->ErrorStatus();
    }
    return ret;
}

void CIkev1Negotiation::SetErrorStatus(TInt aStatus)
{
    SetFinished();
    iPluginSession->SetErrorStatus(aStatus);
}

void CIkev1Negotiation::SendAndSaveIkeMsgL( const TDesC8& aIkeMsg,
                                            TInetAddr& aDestAddr,
                                            TBool aUseNatPort )
{
    iPluginSession->SendIkeMsgL( aIkeMsg, aDestAddr, aUseNatPort );
    SaveLastMsgL();
}


TBool CIkev1Negotiation::IsRetransmit(TLastIKEMsg& aRef)
{       
    TBool isRetransmit(EFalse);
    if (iLastIKEMsgInfo.IsUninitialized())
        {
        TIkev1SAData* ikev1SAData = iPluginSession->FindIkev1SAData(iSAId);
        if (ikev1SAData && ikev1SAData->iLastIKEMsgInfo.IsReTransmit(aRef))
            {
            isRetransmit = ETrue;
            }
        }
    else
        {
        isRetransmit = iLastIKEMsgInfo.IsReTransmit(aRef);
        }
    return isRetransmit;
}

void CIkev1Negotiation::SaveRetransmitInfo(TLastIKEMsg& aRef)
{
    aRef.Store(iLastIKEMsgInfo);
    TIkev1SAData* ikev1SAData = iPluginSession->FindIkev1SAData(iSAId);
    if (ikev1SAData != NULL)
        {
        aRef.Store(ikev1SAData->iLastIKEMsgInfo);
        }
}

void CIkev1Negotiation::SaveLastMsgL()
{
    if ( iLastMsg != NULL )
        {
        TIkev1SAData* ikev1SAData = iPluginSession->FindIkev1SAData(iSAId);
        if ( ikev1SAData != NULL )
            {
            delete ikev1SAData->iLastMsg;
            ikev1SAData->iLastMsg = iLastMsg->AllocL(); 
            }                
        }    
}

HBufC8* CIkev1Negotiation::GetPskFromPolicyL()
{
    ASSERT(iHostData);
    //
    // Get Preshared Key from IKE policy and return in to caller in
    // HBufc8.
    //
    HBufC8* psk = NULL;
    if ( iHostData->iPresharedKey.iFormat ==  STRING_KEY )
    {
        psk = HBufC8::NewL(iHostData->iPresharedKey.iKey.Length());
        psk->Des().Copy(iHostData->iPresharedKey.iKey);
    }
    else if ( iHostData->iPresharedKey.iFormat == HEX_KEY ) 
    {
        psk = HBufC8::NewL(iHostData->iPresharedKey.iKey.Length() / 2);        
        
        for(TInt i = 0; i < iHostData->iPresharedKey.iKey.Length(); i += 2)
        {        
            TPtrC hexByte(iHostData->iPresharedKey.iKey.Mid(i, 2));
            TLex lex(hexByte);
            TUint8 value;
            User::LeaveIfError(lex.Val(value, EHex));
            
            psk->Des().Append(&value, 1);
        }
        
    }

    return psk;
}

void CIkev1Negotiation::SetPhase2LifeDurations( const TInt64 aSoftAddTime,
                                                const TInt64 aHardAddTime,
                                                const TInt64 aSoftBytes,
                                                const TInt64 aHardBytes,
                                                TAttrib_II& aAttr_II,
                                                TBool aResponder )    
{
    TInt64 lifetime64 = aSoftAddTime;
    iHardLifetime = aHardAddTime;
    
    if ( lifetime64 == 0 ||
         aResponder )
        {
        lifetime64 = iHardLifetime;
        }
    
    TUint high = 0;
    TUint low = 0;
    if ( lifetime64 != 0 )
        {
        high = ByteOrder::Swap32(I64HIGH(lifetime64));
        if (high > 0)
            {
            aAttr_II.iLifeDurationSecs.Copy((TUint8 *)&high, sizeof(high));
            }
        low = ByteOrder::Swap32(I64LOW(lifetime64));
        aAttr_II.iLifeDurationSecs.Append((TUint8 *)&low, sizeof(low));
        }
    
    if ( aResponder )
        {
        // Set responder lifetime.
        lifetime64 = aSoftAddTime;
        if ( lifetime64 == 0 )
            {
            lifetime64 = iHardLifetime;
            }    
        
        if ( lifetime64 != 0 )
            {
            high = ByteOrder::Swap32(I64HIGH(lifetime64));
            if (high > 0)
                {
                aAttr_II.iResponderLifetimeSecs.Copy((TUint8 *)&high, sizeof(high));            
                }
            low = ByteOrder::Swap32(I64LOW(lifetime64));
            aAttr_II.iResponderLifetimeSecs.Append((TUint8 *)&low, sizeof(low));
            }        
        }

    //Bytes lifetime
    lifetime64 = aSoftBytes;
    
    if ( lifetime64 == 0 ||
         aResponder )
        {
        lifetime64 = aHardBytes;
        }
        
    lifetime64 = (lifetime64/1024); //Bytes to KB
    if ( lifetime64 != 0 )
        {
        high = ByteOrder::Swap32(I64HIGH(lifetime64));      
        if (high > 0)
            aAttr_II.iLifeDurationKBytes.Copy((TUint8 *)&high, sizeof(high));
        low = ByteOrder::Swap32(I64LOW(lifetime64));        
        aAttr_II.iLifeDurationKBytes.Append((TUint8 *)&low, sizeof(low));
        }    
    
    if ( aResponder )
        {
        // Set responder lifetime.
        if ( lifetime64 == 0 )
            {
            lifetime64 = iHardLifetime;
            }    
        lifetime64 = (lifetime64/1024); //Bytes to KB
        
        if ( lifetime64 != 0 )
            {
            high = ByteOrder::Swap32(I64HIGH(lifetime64));
            if (high > 0)
                {
                aAttr_II.iResponderLifetimeKBytes.Copy((TUint8 *)&high, sizeof(high));            
                }
            low = ByteOrder::Swap32(I64LOW(lifetime64));
            aAttr_II.iResponderLifetimeKBytes.Append((TUint8 *)&low, sizeof(low));
            }        
        }    
}

TInt CIkev1Negotiation::ProcessUserResponseL(CAuthDialogInfo *aDialogInfo )
{
    delete iDialog;  /* delete dialog object */
    iDialog = NULL;
    
    iCRACKLAMUserName = aDialogInfo->iUsername->AllocL();
    iCRACKLAMPassword = aDialogInfo->iSecret->AllocL();
    
    delete aDialogInfo;  /* release dialog info object */
    iDialogInfo = NULL;  /* reset dialog info pointer  */
    DEBUG_LOG(_L("Continue negotiation from begining"));
    InitNegotiationL();
    
    return KErrNone;

}
//
// The implementation for class MIkeDialogComplete virtual function
//
TInt CIkev1Negotiation::DialogCompleteL(CIkev1Dialog* /*aDialog*/, TAny* aUserInfo,
                                            HBufC8* aUsername, HBufC8* aSecret, HBufC8* aDomain)
{
/*---------------------------------------------------------------------------
 *  
 *  A response received from client user (through asynchronous dialog)
 *  This method is introduced as a TUserCallback for CGetIKEPassword dialog
 *  object is created. When the dialog is completed this callback function
 *  is called 
 *  
 *-------------------------------------------------------------------------*/
    TUint32 obj_id = 1;
     CAuthDialogInfo* info = (CAuthDialogInfo*)aUserInfo;
     DEBUG_LOG1(_L("CIkev1Negotiation::DialogCompleteL(), aUserInfo =  %x"), aUserInfo);
             
     if ( info )
     {
        obj_id = info->GetObjId();
        DEBUG_LOG1(_L("Preparing to call AuthDialogCompletedL(), ObjId = %x"), obj_id);
        if ( obj_id == DIALOG_INFO_ID )
        {
           info->iUsername = aUsername;
           info->iSecret   = aSecret;
           info->iDomain   = aDomain;
           obj_id = info->PluginSession()->AuthDialogCompletedL(info);
        }   
     }

     return obj_id;
    
}


