/** @file
  The operations for IKEv2 SA.

  Copyright (c) 2010 - 2011, Intel Corporation. All rights reserved.<BR>

  This program and the accompanying materials
  are licensed and made available under the terms and conditions of the BSD License
  which accompanies this distribution.  The full text of the license may be found at
  http://opensource.org/licenses/bsd-license.php.

  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

**/

#include "Utility.h"
#include "IpSecDebug.h"
#include "IkeService.h"
#include "Ikev2.h"

/**
  Generates the DH Key.

  This generates the DH local public key and store it in the IKEv2 SA Session's GxBuffer.
  
  @param[in]  IkeSaSession   Pointer to related IKE SA Session.

  @retval EFI_SUCCESS        The operation succeeded.
  @retval Others             The operation failed.

**/
EFI_STATUS
Ikev2GenerateSaDhPublicKey (
  IN IKEV2_SA_SESSION         *IkeSaSession
  );

/**
  Generates the IKEv2 SA key for the furthure IKEv2 exchange.

  @param[in]  IkeSaSession       Pointer to IKEv2 SA Session.
  @param[in]  KePayload          Pointer to Key payload used to generate the Key.

  @retval EFI_UNSUPPORTED    If the Algorithm Id is not supported.
  @retval EFI_SUCCESS        The operation succeeded.

**/
EFI_STATUS
Ikev2GenerateSaKeys (
  IN IKEV2_SA_SESSION       *IkeSaSession,
  IN IKE_PAYLOAD            *KePayload
  );

/**
  Generates the Keys for the furthure IPsec Protocol.

  @param[in]  ChildSaSession     Pointer to IKE Child SA Session.
  @param[in]  KePayload          Pointer to Key payload used to generate the Key.

  @retval EFI_UNSUPPORTED    If one or more Algorithm Id is unsupported.
  @retval EFI_SUCCESS        The operation succeeded.

**/
EFI_STATUS
Ikev2GenerateChildSaKeys (
  IN IKEV2_CHILD_SA_SESSION     *ChildSaSession,
  IN IKE_PAYLOAD                *KePayload
  );

/**
  Gernerates IKEv2 packet for IKE_SA_INIT exchange.

  @param[in] SaSession  Pointer to IKEV2_SA_SESSION related to the exchange.
  @param[in] Context    Context Data passed by caller.

  @retval EFI_SUCCESS   The IKEv2 packet generation succeeded.
  @retval Others        The IKEv2 packet generation failed.

**/
IKE_PACKET *
Ikev2InitPskGenerator (
  IN UINT8           *SaSession,
  IN VOID            *Context
  )
{
  IKE_PACKET         *IkePacket;
  IKEV2_SA_SESSION   *IkeSaSession;
  IKE_PAYLOAD        *SaPayload;
  IKE_PAYLOAD        *KePayload;
  IKE_PAYLOAD        *NoncePayload;
  IKE_PAYLOAD        *NotifyPayload;
  EFI_STATUS         Status;

  SaPayload      = NULL;
  KePayload      = NULL;
  NoncePayload   = NULL;
  NotifyPayload  = NULL;

  IkeSaSession = (IKEV2_SA_SESSION *) SaSession;

  //
  // 1. Allocate IKE packet
  //
  IkePacket = IkePacketAlloc ();
  ASSERT (IkePacket != NULL);

  //
  // 1.a Fill the IkePacket->Hdr
  //
  IkePacket->Header->ExchangeType    = IKEV2_EXCHANGE_TYPE_INIT;
  IkePacket->Header->InitiatorCookie = IkeSaSession->InitiatorCookie;
  IkePacket->Header->ResponderCookie = IkeSaSession->ResponderCookie;
  IkePacket->Header->Version         = (UINT8) (2 << 4);
  IkePacket->Header->MessageId       = 0;

  if (IkeSaSession->SessionCommon.IsInitiator) {
    IkePacket->Header->Flags = IKE_HEADER_FLAGS_INIT;
  } else {
    IkePacket->Header->Flags = IKE_HEADER_FLAGS_RESPOND;
  }

  //
  // If the NCookie is not NULL, this IKE_SA_INIT packet is resent by the NCookie
  // and the NCookie payload should be the first payload in this packet.
  //
  if (IkeSaSession->NCookie != NULL) {
    IkePacket->Header->NextPayload = IKEV2_PAYLOAD_TYPE_NOTIFY;
    NotifyPayload = Ikev2GenerateNotifyPayload (
                      IPSEC_PROTO_ISAKMP,
                      IKEV2_PAYLOAD_TYPE_SA,
                      0,
                      IKEV2_NOTIFICATION_COOKIE,
                      NULL,
                      IkeSaSession->NCookie,
                      IkeSaSession->NCookieSize
                      );
  } else {
    IkePacket->Header->NextPayload = IKEV2_PAYLOAD_TYPE_SA;
  }

  //
  // 2. Generate SA Payload according to the SaData & SaParams
  //
  SaPayload = Ikev2GenerateSaPayload (
                IkeSaSession->SaData,
                IKEV2_PAYLOAD_TYPE_KE,
                IkeSessionTypeIkeSa
                );

  //
  // 3. Generate DH public key.
  //    The DhPrivate Key has been generated in Ikev2InitPskParser, if the
  //    IkeSaSession is responder. If resending IKE_SA_INIT with Cookie Notify
  //    No need to recompute the Public key.
  //
  if ((IkeSaSession->SessionCommon.IsInitiator) && (IkeSaSession->NCookie == NULL)) {    
    Status = Ikev2GenerateSaDhPublicKey (IkeSaSession);
    if (EFI_ERROR (Status)) {
      goto CheckError;
    }
  }

  //
  // 4. Generate KE Payload according to SaParams->DhGroup
  //
  KePayload = Ikev2GenerateKePayload (
                IkeSaSession, 
                IKEV2_PAYLOAD_TYPE_NONCE
                );

  //
  // 5. Generate Nonce Payload
  //    If resending IKE_SA_INIT with Cookie Notify paylaod, no need to regenerate
  //    the Nonce Payload.
  //
  if ((IkeSaSession->SessionCommon.IsInitiator) && (IkeSaSession->NCookie == NULL)) {
    IkeSaSession->NiBlkSize = IKE_NONCE_SIZE;
    IkeSaSession->NiBlock   = IkeGenerateNonce (IKE_NONCE_SIZE);
    ASSERT (IkeSaSession->NiBlock != NULL);
  }

  if (IkeSaSession->SessionCommon.IsInitiator) {
    NoncePayload = Ikev2GenerateNoncePayload (
                     IkeSaSession->NiBlock,
                     IkeSaSession->NiBlkSize,
                     IKEV2_PAYLOAD_TYPE_NONE
                     );
  } else {
    //
    // The Nonce Payload has been created in Ikev2PskParser if the IkeSaSession is
    // responder.
    //
    NoncePayload = Ikev2GenerateNoncePayload (
                     IkeSaSession->NrBlock,
                     IkeSaSession->NrBlkSize,
                     IKEV2_PAYLOAD_TYPE_NONE
                     );
  }

  if (NotifyPayload != NULL) {
    IKE_PACKET_APPEND_PAYLOAD (IkePacket, NotifyPayload);
  }
  if (SaPayload != NULL) {
    IKE_PACKET_APPEND_PAYLOAD (IkePacket, SaPayload);
  }
  if (KePayload != NULL) {
    IKE_PACKET_APPEND_PAYLOAD (IkePacket, KePayload);
  }
  if (NoncePayload != NULL) {
    IKE_PACKET_APPEND_PAYLOAD (IkePacket, NoncePayload);
  }

  return IkePacket;

CheckError:
  if (IkePacket != NULL) {
    IkePacketFree (IkePacket);
  }
  if (SaPayload != NULL) {
    IkePayloadFree (SaPayload);
  }
  return NULL;    
}

/**
  Parses the IKEv2 packet for IKE_SA_INIT exchange.

  @param[in] SaSession  Pointer to IKEV2_SA_SESSION related to the exchange.
  @param[in] IkePacket  The received IKE packet to be parsed.

  @retval EFI_SUCCESS            The IKEv2 packet is acceptable and the relative data is
                                 saved for furthure communication.
  @retval EFI_INVALID_PARAMETER  The IKEv2 packet is malformed or the SA proposal is unacceptable.

**/
EFI_STATUS
Ikev2InitPskParser (
  IN UINT8            *SaSession,
  IN IKE_PACKET       *IkePacket
  ) 
{
  IKEV2_SA_SESSION     *IkeSaSession;
  IKE_PAYLOAD          *SaPayload;
  IKE_PAYLOAD          *KeyPayload;
  IKE_PAYLOAD          *IkePayload;
  IKE_PAYLOAD          *NoncePayload;
  IKE_PAYLOAD          *NotifyPayload;
  UINT8                *NonceBuffer;
  UINTN                NonceSize;
  LIST_ENTRY           *Entry;
  EFI_STATUS           Status;

  IkeSaSession   = (IKEV2_SA_SESSION *) SaSession;
  KeyPayload     = NULL;
  SaPayload      = NULL;
  NoncePayload   = NULL;
  IkePayload     = NULL;
  NotifyPayload  = NULL;

  //
  // Iterate payloads to find the SaPayload and KeyPayload.
  //
  NET_LIST_FOR_EACH (Entry, &(IkePacket)->PayloadList) {
    IkePayload  = IKE_PAYLOAD_BY_PACKET (Entry);
    if (IkePayload->PayloadType == IKEV2_PAYLOAD_TYPE_SA) {
      SaPayload = IkePayload;
    }
    if (IkePayload->PayloadType == IKEV2_PAYLOAD_TYPE_KE) {
      KeyPayload = IkePayload;
    }
    if (IkePayload->PayloadType == IKEV2_PAYLOAD_TYPE_NONCE) {
      NoncePayload = IkePayload;
    }
    if (IkePayload->PayloadType == IKEV2_PAYLOAD_TYPE_NOTIFY) {
      NotifyPayload = IkePayload;
    }
  }

  //
  // According to RFC 4306 - 2.6. If the responder responds with the COOKIE Notify
  // payload with the cookie data, initiator MUST retry the IKE_SA_INIT with a
  // Notify payload of type COOKIE containing the responder suppplied cookie data
  // as first payload and all other payloads unchanged.
  //
  if (IkeSaSession->SessionCommon.IsInitiator) {
    if (NotifyPayload != NULL) {
      Status = Ikev2ParserNotifyCookiePayload (NotifyPayload, IkeSaSession);
      return Status;
    }
  }

  if ((KeyPayload == NULL) || (SaPayload == NULL) || (NoncePayload == NULL)) {
    return EFI_INVALID_PARAMETER;
  }

  //
  // Store NoncePayload for SKEYID computing.
  //
  NonceSize   = NoncePayload->PayloadSize - sizeof (IKEV2_COMMON_PAYLOAD_HEADER);
  NonceBuffer = (UINT8 *) AllocatePool (NonceSize);
  ASSERT (NonceBuffer != NULL);
  CopyMem (
    NonceBuffer,
    NoncePayload->PayloadBuf + sizeof (IKEV2_COMMON_PAYLOAD_HEADER),
    NonceSize
    );

  //
  // Check if IkePacket Header matches the state
  //
  if (IkeSaSession->SessionCommon.IsInitiator) {
    //
    // 1. Check the IkePacket->Hdr == IKE_HEADER_FLAGS_RESPOND
    //
    if (IkePacket->Header->Flags != IKE_HEADER_FLAGS_RESPOND) {
      Status = EFI_INVALID_PARAMETER;
      goto CheckError;
    }

    //
    // 2. Parse the SA Payload and Key Payload to find out the cryptographic
    //    suite and fill in the Sa paramse into CommonSession->SaParams
    //
    if (!Ikev2SaParseSaPayload (IkeSaSession, SaPayload, IkePacket->Header->Flags)) {
      Status = EFI_INVALID_PARAMETER;
      goto CheckError;
    }

    //
    // 3. If Initiator, the NoncePayload is Nr_b.
    //
    IKEV2_DUMP_STATE (IkeSaSession->SessionCommon.State, IkeStateAuth);
    IkeSaSession->NrBlock             = NonceBuffer;
    IkeSaSession->NrBlkSize           = NonceSize;
    IkeSaSession->SessionCommon.State = IkeStateAuth;
    IkeSaSession->ResponderCookie     = IkePacket->Header->ResponderCookie;

    //
    // 4. Change the state of IkeSaSession
    //
    IkeSaSession->SessionCommon.State = IkeStateAuth;
  } else {
    //
    // 1. Check the IkePacket->Hdr == IKE_HEADER_FLAGS_INIT
    //
    if (IkePacket->Header->Flags != IKE_HEADER_FLAGS_INIT) {
      Status = EFI_INVALID_PARAMETER;
      goto CheckError;
    }

    //
    // 2. Parse the SA payload and find out the perfered one
    //    and fill in the SA parameters into CommonSession->SaParams and SaData into
    //    IkeSaSession for the responder SA payload generation.
    //
    if (!Ikev2SaParseSaPayload (IkeSaSession, SaPayload, IkePacket->Header->Flags)) {
      Status = EFI_INVALID_PARAMETER;
      goto CheckError;
    }

    //
    // 3. Generat Dh Y parivate Key
    //
    Status = Ikev2GenerateSaDhPublicKey (IkeSaSession);
    if (EFI_ERROR (Status)) {
      goto CheckError;
    }

    //
    // 4. If Responder, the NoncePayload is Ni_b and go to generate Nr_b.
    //
    IkeSaSession->NiBlock   = NonceBuffer;
    IkeSaSession->NiBlkSize = NonceSize;

    //
    // 5. Generate Nr_b
    //
    IkeSaSession->NrBlock   = IkeGenerateNonce (IKE_NONCE_SIZE);
    ASSERT_EFI_ERROR (IkeSaSession->NrBlock != NULL);
    IkeSaSession->NrBlkSize = IKE_NONCE_SIZE;

    //
    // 6. Save the Cookies
    //
    IkeSaSession->InitiatorCookie = IkePacket->Header->InitiatorCookie;
    IkeSaSession->ResponderCookie = IkeGenerateCookie ();
  }

  if (IkeSaSession->SessionCommon.PreferDhGroup != ((IKEV2_KEY_EXCHANGE *)KeyPayload->PayloadBuf)->DhGroup) {
    Status = EFI_INVALID_PARAMETER;
    goto CheckError;
  }
  //
  // Call Ikev2GenerateSaKeys to create SKEYID, SKEYID_d, SKEYID_a, SKEYID_e.
  //
  Status = Ikev2GenerateSaKeys (IkeSaSession, KeyPayload);
  if (EFI_ERROR(Status)) {
    goto CheckError;
  }
  return EFI_SUCCESS;

CheckError:
  if (NonceBuffer != NULL) {
    FreePool (NonceBuffer);
  }
  
  return Status;
}

/**
  Generates the IKEv2 packet for IKE_AUTH exchange.

  @param[in] SaSession  Pointer to IKEV2_SA_SESSION.
  @param[in] Context    Context data passed by caller.

  @retval   Pointer to IKE Packet to be sent out.

**/
IKE_PACKET *
Ikev2AuthPskGenerator (
  IN UINT8         *SaSession,
  IN VOID          *Context
  )
{
  IKE_PACKET             *IkePacket;
  IKEV2_SA_SESSION       *IkeSaSession;
  IKE_PAYLOAD            *IdPayload;
  IKE_PAYLOAD            *AuthPayload;
  IKE_PAYLOAD            *SaPayload;
  IKE_PAYLOAD            *TsiPayload;
  IKE_PAYLOAD            *TsrPayload;
  IKE_PAYLOAD            *NotifyPayload;
  IKE_PAYLOAD            *CpPayload;
  IKEV2_CHILD_SA_SESSION *ChildSaSession;
  

  IkeSaSession   = (IKEV2_SA_SESSION *) SaSession;
  ChildSaSession = IKEV2_CHILD_SA_SESSION_BY_IKE_SA (GetFirstNode (&IkeSaSession->ChildSaSessionList));

  CpPayload      = NULL;
  NotifyPayload  = NULL;
  
  //
  // 1. Allocate IKE Packet
  //
  IkePacket= IkePacketAlloc ();
  ASSERT (IkePacket != NULL);

  //
  // 1.a Fill the IkePacket Header.
  //
  IkePacket->Header->ExchangeType    = IKEV2_EXCHANGE_TYPE_AUTH;
  IkePacket->Header->InitiatorCookie = IkeSaSession->InitiatorCookie;
  IkePacket->Header->ResponderCookie = IkeSaSession->ResponderCookie;
  IkePacket->Header->Version         = (UINT8)(2 << 4);
  if (ChildSaSession->SessionCommon.IsInitiator) {
    IkePacket->Header->NextPayload   = IKEV2_PAYLOAD_TYPE_ID_INIT;
  } else {
    IkePacket->Header->NextPayload   = IKEV2_PAYLOAD_TYPE_ID_RSP;
  }

  //
  // According to RFC4306_2.2, For the IKE_SA_INIT message the MessageID should 
  // be always number 0 and 1;
  //
  IkePacket->Header->MessageId = 1;

  if (IkeSaSession->SessionCommon.IsInitiator) {
    IkePacket->Header->Flags = IKE_HEADER_FLAGS_INIT;
  } else {
    IkePacket->Header->Flags = IKE_HEADER_FLAGS_RESPOND;
  }

  //
  // 2. Generate ID Payload according to IP version and address.
  //
  IdPayload = Ikev2GenerateIdPayload (
                &IkeSaSession->SessionCommon,
                IKEV2_PAYLOAD_TYPE_AUTH
                );

  //
  // 3. Generate Auth Payload
  //    If it is tunnel mode, should create the configuration payload after the
  //    Auth payload.
  //
  if (IkeSaSession->Spd->Data->ProcessingPolicy->Mode == EfiIPsecTransport) {

    AuthPayload = Ikev2PskGenerateAuthPayload (
                    ChildSaSession->IkeSaSession,
                    IdPayload,
                    IKEV2_PAYLOAD_TYPE_SA,
                    FALSE
                    );
  } else {
    AuthPayload = Ikev2PskGenerateAuthPayload (
                    ChildSaSession->IkeSaSession,
                    IdPayload,
                    IKEV2_PAYLOAD_TYPE_CP,
                    FALSE
                    );
    if (IkeSaSession->SessionCommon.UdpService->IpVersion == IP_VERSION_4) {
      CpPayload = Ikev2GenerateCpPayload (
                    ChildSaSession->IkeSaSession,
                    IKEV2_PAYLOAD_TYPE_SA,
                    IKEV2_CFG_ATTR_INTERNAL_IP4_ADDRESS
                    );
    } else {
      CpPayload = Ikev2GenerateCpPayload (
                    ChildSaSession->IkeSaSession,
                    IKEV2_PAYLOAD_TYPE_SA,
                    IKEV2_CFG_ATTR_INTERNAL_IP6_ADDRESS
                    );
    }
  }

  //
  // 4. Generate SA Payload according to the SA Data in ChildSaSession
  //
  SaPayload = Ikev2GenerateSaPayload (
                ChildSaSession->SaData,
                IKEV2_PAYLOAD_TYPE_TS_INIT,
                IkeSessionTypeChildSa
                );

  if (IkeSaSession->Spd->Data->ProcessingPolicy->Mode == EfiIPsecTransport) {
    //
    // Generate Tsi and Tsr.
    //
    TsiPayload = Ikev2GenerateTsPayload (
                   ChildSaSession,
                   IKEV2_PAYLOAD_TYPE_TS_RSP,
                   FALSE
                   );

    TsrPayload = Ikev2GenerateTsPayload (
                   ChildSaSession,
                   IKEV2_PAYLOAD_TYPE_NOTIFY,
                   FALSE
                   );

    //
    // Generate Notify Payload. If transport mode, there should have Notify
    // payload with TRANSPORT_MODE notification.
    //
    NotifyPayload = Ikev2GenerateNotifyPayload (
                      0,
                      IKEV2_PAYLOAD_TYPE_NONE,
                      0,
                      IKEV2_NOTIFICATION_USE_TRANSPORT_MODE,
                      NULL,
                      NULL,
                      0
                      );
  } else {
    //
    // Generate Tsr for Tunnel mode.
    //
    TsiPayload = Ikev2GenerateTsPayload (
                   ChildSaSession,
                   IKEV2_PAYLOAD_TYPE_TS_RSP,
                   TRUE
                   );
    TsrPayload = Ikev2GenerateTsPayload (
                   ChildSaSession,
                   IKEV2_PAYLOAD_TYPE_NONE,
                   FALSE
                   );
  }

  IKE_PACKET_APPEND_PAYLOAD (IkePacket, IdPayload);
  IKE_PACKET_APPEND_PAYLOAD (IkePacket, AuthPayload);
  if (IkeSaSession->Spd->Data->ProcessingPolicy->Mode == EfiIPsecTunnel) {
    IKE_PACKET_APPEND_PAYLOAD (IkePacket, CpPayload);
  }
  IKE_PACKET_APPEND_PAYLOAD (IkePacket, SaPayload);
  IKE_PACKET_APPEND_PAYLOAD (IkePacket, TsiPayload);
  IKE_PACKET_APPEND_PAYLOAD (IkePacket, TsrPayload);
  if (IkeSaSession->Spd->Data->ProcessingPolicy->Mode == EfiIPsecTransport) {
    IKE_PACKET_APPEND_PAYLOAD (IkePacket, NotifyPayload);
  }

  return IkePacket;
}

/**
  Parses IKE_AUTH packet.

  @param[in]  SaSession   Pointer to the IKE_SA_SESSION related to this packet.
  @param[in]  IkePacket   Pointer to the IKE_AUTH packet to be parsered.

  @retval     EFI_INVALID_PARAMETER   The IKE packet is malformed or the SA 
                                      proposal is unacceptable.
  @retval     EFI_SUCCESS             The IKE packet is acceptable and the
                                      relative data is saved for furthure communication.

**/
EFI_STATUS 
Ikev2AuthPskParser (
  IN UINT8             *SaSession,
  IN IKE_PACKET        *IkePacket
  )
{
  IKEV2_CHILD_SA_SESSION *ChildSaSession;
  IKEV2_SA_SESSION       *IkeSaSession;
  IKE_PAYLOAD            *IkePayload;
  IKE_PAYLOAD            *SaPayload;
  IKE_PAYLOAD            *IdiPayload;
  IKE_PAYLOAD            *IdrPayload;
  IKE_PAYLOAD            *AuthPayload;
  IKE_PAYLOAD            *TsiPayload;
  IKE_PAYLOAD            *TsrPayload;
  IKE_PAYLOAD            *VerifiedAuthPayload;
  LIST_ENTRY             *Entry;
  EFI_STATUS             Status;

  IkeSaSession   = (IKEV2_SA_SESSION *) SaSession;
  ChildSaSession = IKEV2_CHILD_SA_SESSION_BY_IKE_SA (GetFirstNode (&IkeSaSession->ChildSaSessionList));

  SaPayload   = NULL;
  IdiPayload  = NULL;
  IdrPayload  = NULL;
  AuthPayload = NULL;
  TsiPayload  = NULL;
  TsrPayload  = NULL;

  //
  // Iterate payloads to find the SaPayload/ID/AUTH/TS Payload.
  //
  NET_LIST_FOR_EACH (Entry, &(IkePacket)->PayloadList) {
    IkePayload  = IKE_PAYLOAD_BY_PACKET (Entry);

    if (IkePayload->PayloadType == IKEV2_PAYLOAD_TYPE_ID_INIT) {
      IdiPayload = IkePayload;
    }
    if (IkePayload->PayloadType == IKEV2_PAYLOAD_TYPE_ID_RSP) {
      IdrPayload = IkePayload;
    }
    if (IkePayload->PayloadType == IKEV2_PAYLOAD_TYPE_SA) {
      SaPayload = IkePayload;
    }
    if (IkePayload->PayloadType == IKEV2_PAYLOAD_TYPE_AUTH) {
      AuthPayload = IkePayload;
    }
    if (IkePayload->PayloadType == IKEV2_PAYLOAD_TYPE_TS_INIT) {
      TsiPayload = IkePayload;
    }
    if (IkePayload->PayloadType == IKEV2_PAYLOAD_TYPE_TS_RSP) {
      TsrPayload = IkePayload;
    }
  }

  if ((SaPayload == NULL) || (AuthPayload == NULL) || (TsiPayload == NULL) || (TsrPayload == NULL)) {
    return EFI_INVALID_PARAMETER;
  }
  if ((IdiPayload == NULL) && (IdrPayload == NULL)) {
    return EFI_INVALID_PARAMETER;
  }

  //
  // Check IkePacket Header is match the state
  //
  if (IkeSaSession->SessionCommon.IsInitiator) {
    
    //
    // 1. Check the IkePacket->Hdr == IKE_HEADER_FLAGS_RESPOND
    //
    if ((IkePacket->Header->Flags != IKE_HEADER_FLAGS_RESPOND) ||
        (IkePacket->Header->ExchangeType != IKEV2_EXCHANGE_TYPE_AUTH)
        ) {
      return EFI_INVALID_PARAMETER;
    }

  } else {
    //
    // 1. Check the IkePacket->Hdr == IKE_HEADER_FLAGS_INIT
    //
    if ((IkePacket->Header->Flags != IKE_HEADER_FLAGS_INIT) ||
        (IkePacket->Header->ExchangeType != IKEV2_EXCHANGE_TYPE_AUTH)
        ) {
      return EFI_INVALID_PARAMETER;
    }

    //
    // 2. Parse the SA payload and Key Payload and find out the perferable one
    //    and fill in the Sa paramse into CommonSession->SaParams and SaData into
    //    IkeSaSession for the responder SA payload generation.
    //
  }

  //
  // Verify the Auth Payload.
  //
  VerifiedAuthPayload = Ikev2PskGenerateAuthPayload (
                          IkeSaSession,
                          IkeSaSession->SessionCommon.IsInitiator ? IdrPayload : IdiPayload,
                          IKEV2_PAYLOAD_TYPE_SA,
                          TRUE
                          );
  if ((VerifiedAuthPayload != NULL) &&
      (0 != CompareMem (
              VerifiedAuthPayload->PayloadBuf + sizeof (IKEV2_COMMON_PAYLOAD_HEADER),
              AuthPayload->PayloadBuf + sizeof (IKEV2_COMMON_PAYLOAD_HEADER),
              VerifiedAuthPayload->PayloadSize - sizeof (IKEV2_COMMON_PAYLOAD_HEADER)
              ))) {
    return EFI_INVALID_PARAMETER;
  };

  //
  // 3. Parse the SA Payload to find out the cryptographic suite
  //    and fill in the Sa paramse into CommonSession->SaParams. If no acceptable
  //    porposal found, return EFI_INVALID_PARAMETER.
  //
  if (!Ikev2ChildSaParseSaPayload (ChildSaSession, SaPayload, IkePacket->Header->Flags)) {
    return EFI_INVALID_PARAMETER;
  }

  //
  // 4. Parse TSi, TSr payloads.
  //
  if ((((TRAFFIC_SELECTOR *)(TsiPayload->PayloadBuf + sizeof (IKEV2_TS)))->IpProtocolId !=
       ((TRAFFIC_SELECTOR *)(TsrPayload->PayloadBuf + sizeof (IKEV2_TS)))->IpProtocolId) &&
      (((TRAFFIC_SELECTOR *)(TsiPayload->PayloadBuf + sizeof (IKEV2_TS)))->IpProtocolId != 0) &&
      (((TRAFFIC_SELECTOR *)(TsrPayload->PayloadBuf + sizeof (IKEV2_TS)))->IpProtocolId != 0)
      ) {
    return EFI_INVALID_PARAMETER;
  }

  if (!IkeSaSession->SessionCommon.IsInitiator) {
    //
    //TODO:check the Port range. Only support any port and one certain port here.
    //
    ChildSaSession->ProtoId    = ((TRAFFIC_SELECTOR *)(TsrPayload->PayloadBuf + sizeof (IKEV2_TS)))->IpProtocolId;
    ChildSaSession->LocalPort  = ((TRAFFIC_SELECTOR *)(TsrPayload->PayloadBuf + sizeof (IKEV2_TS)))->StartPort;
    ChildSaSession->RemotePort = ((TRAFFIC_SELECTOR *)(TsiPayload->PayloadBuf + sizeof (IKEV2_TS)))->StartPort;
    //
    // Association a SPD with this SA.
    //
    Status = Ikev2ChildSaAssociateSpdEntry (ChildSaSession);
    if (EFI_ERROR (Status)) {
      return EFI_INVALID_PARAMETER;
    }
    //
    // Associate the IkeSaSession's SPD to the first ChildSaSession's SPD.
    //
    if (ChildSaSession->IkeSaSession->Spd == NULL) {
      ChildSaSession->IkeSaSession->Spd = ChildSaSession->Spd;
      Ikev2ChildSaSessionSpdSelectorCreate (ChildSaSession);
    }
  } else {
    //
    //TODO:check the Port range.
    //
    if ((((TRAFFIC_SELECTOR *)(TsrPayload->PayloadBuf + sizeof (IKEV2_TS)))->StartPort != 0) &&
        (((TRAFFIC_SELECTOR *)(TsrPayload->PayloadBuf + sizeof (IKEV2_TS)))->StartPort != ChildSaSession->RemotePort)
        ) {
      return EFI_INVALID_PARAMETER;
    } 
    if ((((TRAFFIC_SELECTOR *)(TsiPayload->PayloadBuf + sizeof (IKEV2_TS)))->StartPort != 0) &&
        (((TRAFFIC_SELECTOR *)(TsiPayload->PayloadBuf + sizeof (IKEV2_TS)))->StartPort != ChildSaSession->LocalPort)
        ) {
      return EFI_INVALID_PARAMETER;
    }
    //
    // For the tunnel mode, it should add the vitual IP address into the SA's SPD Selector.
    //
    if (ChildSaSession->Spd->Data->ProcessingPolicy->Mode == EfiIPsecTunnel) {
      if (!ChildSaSession->IkeSaSession->SessionCommon.IsInitiator) {
        //
        // If it is tunnel mode, the UEFI part must be the initiator.
        //
        return EFI_INVALID_PARAMETER;
      }
      //
      // Get the Virtual IP address from the Tsi traffic selector. 
      // TODO: check the CFG reply payload
      //
      CopyMem (
        &ChildSaSession->SpdSelector->LocalAddress[0].Address,
        TsiPayload->PayloadBuf + sizeof (IKEV2_TS) + sizeof (TRAFFIC_SELECTOR),
        (ChildSaSession->SessionCommon.UdpService->IpVersion == IP_VERSION_4) ?
        sizeof (EFI_IPv4_ADDRESS) : sizeof (EFI_IPv6_ADDRESS)
        );
      }    
  }

  //
  // 5. Generate keymats for IPsec protocol.
  //
  Ikev2GenerateChildSaKeys (ChildSaSession, NULL);
  if (IkeSaSession->SessionCommon.IsInitiator) {
    //
    // 6. Change the state of IkeSaSession
    //
    IKEV2_DUMP_STATE (IkeSaSession->SessionCommon.State, IkeStateIkeSaEstablished);
    IkeSaSession->SessionCommon.State = IkeStateIkeSaEstablished;
  }
  
  return EFI_SUCCESS;
}

/**
  Gernerates IKEv2 packet for IKE_SA_INIT exchange.

  @param[in] SaSession  Pointer to IKEV2_SA_SESSION related to the exchange.
  @param[in] Context    Context Data passed by caller.

  @retval EFI_SUCCESS   The IKE packet generation succeeded.
  @retval Others        The IKE packet generation failed.

**/
IKE_PACKET*
Ikev2InitCertGenerator (
  IN UINT8           *SaSession,
  IN VOID            *Context
  ) 
{
  IKE_PACKET         *IkePacket;
  IKE_PAYLOAD        *CertReqPayload;
  LIST_ENTRY         *Node;
  IKE_PAYLOAD        *NoncePayload;

  if (!FeaturePcdGet (PcdIpsecCertificateEnabled)) {
    return NULL;
  }

  //
  // The first two messages exchange is same between PSK and Cert.
  //
  IkePacket = Ikev2InitPskGenerator (SaSession, Context);

  if ((IkePacket != NULL) && (!((IKEV2_SA_SESSION *)SaSession)->SessionCommon.IsInitiator)) {
    //
    // Add the Certification Request Payload
    //
    CertReqPayload = Ikev2GenerateCertificatePayload (
                       (IKEV2_SA_SESSION *)SaSession,
                       IKEV2_PAYLOAD_TYPE_NONE,
                       (UINT8*)PcdGetPtr(PcdIpsecUefiCaFile),
                       PcdGet32(PcdIpsecUefiCaFileSize),
                       IKEV2_CERT_ENCODEING_HASH_AND_URL_OF_X509_CERT,
                       TRUE
                       );
    //
    // Change Nonce Payload Next payload type.
    //
    IKE_PACKET_END_PAYLOAD (IkePacket, Node);
    NoncePayload = IKE_PAYLOAD_BY_PACKET (Node);
    ((IKEV2_NONCE *)NoncePayload->PayloadBuf)->Header.NextPayload = IKEV2_PAYLOAD_TYPE_CERTREQ;

    //
    // Add Certification Request Payload
    //
    IKE_PACKET_APPEND_PAYLOAD (IkePacket, CertReqPayload);
  }

  return IkePacket;
}

/**
  Parses the IKEv2 packet for IKE_SA_INIT exchange.

  @param[in] SaSession  Pointer to IKEV2_SA_SESSION related to the exchange.
  @param[in] IkePacket  The received IKEv2 packet to be parsed.

  @retval EFI_SUCCESS            The IKEv2 packet is acceptable and the relative data is
                                 saved for furthure communication.
  @retval EFI_INVALID_PARAMETER  The IKE packet is malformed or the SA proposal is unacceptable.                        
  @retval EFI_UNSUPPORTED        The certificate authentication is not supported.

**/
EFI_STATUS
Ikev2InitCertParser (
  IN UINT8            *SaSession,
  IN IKE_PACKET       *IkePacket
  )
{
  if (!FeaturePcdGet (PcdIpsecCertificateEnabled)) {
    return EFI_UNSUPPORTED;
  } 
  
  //
  // The first two messages exchange is same between PSK and Cert.
  // Todo: Parse Certificate Request from responder Initial Exchange. 
  //
  return Ikev2InitPskParser (SaSession, IkePacket);
}

/**
  Generates the IKEv2 packet for IKE_AUTH exchange.

  @param[in] SaSession  Pointer to IKEV2_SA_SESSION.
  @param[in] Context    Context data passed by caller.

  @retval Pointer to IKEv2 Packet to be sent out.

**/
IKE_PACKET *
Ikev2AuthCertGenerator (
  IN UINT8         *SaSession,
  IN VOID          *Context
  )
{
  IKE_PACKET             *IkePacket;
  IKEV2_SA_SESSION       *IkeSaSession;
  IKE_PAYLOAD            *IdPayload;
  IKE_PAYLOAD            *AuthPayload;
  IKE_PAYLOAD            *SaPayload;
  IKE_PAYLOAD            *TsiPayload;
  IKE_PAYLOAD            *TsrPayload;
  IKE_PAYLOAD            *NotifyPayload;
  IKE_PAYLOAD            *CpPayload;
  IKE_PAYLOAD            *CertPayload;
  IKE_PAYLOAD            *CertReqPayload;
  IKEV2_CHILD_SA_SESSION *ChildSaSession;

  if (!FeaturePcdGet (PcdIpsecCertificateEnabled)) {
    return NULL;
  }

  IkeSaSession   = (IKEV2_SA_SESSION *) SaSession;
  ChildSaSession = IKEV2_CHILD_SA_SESSION_BY_IKE_SA (GetFirstNode (&IkeSaSession->ChildSaSessionList));

  CpPayload      = NULL;
  NotifyPayload  = NULL;
  CertPayload    = NULL;
  CertReqPayload = NULL;

  //
  // 1. Allocate IKE Packet
  //
  IkePacket= IkePacketAlloc ();
  ASSERT (IkePacket != NULL);

  //
  // 1.a Fill the IkePacket Header.
  //
  IkePacket->Header->ExchangeType    = IKEV2_EXCHANGE_TYPE_AUTH;
  IkePacket->Header->InitiatorCookie = IkeSaSession->InitiatorCookie;
  IkePacket->Header->ResponderCookie = IkeSaSession->ResponderCookie;
  IkePacket->Header->Version         = (UINT8)(2 << 4);
  if (ChildSaSession->SessionCommon.IsInitiator) {
    IkePacket->Header->NextPayload   = IKEV2_PAYLOAD_TYPE_ID_INIT;
  } else {
    IkePacket->Header->NextPayload   = IKEV2_PAYLOAD_TYPE_ID_RSP;
  }

  //
  // According to RFC4306_2.2, For the IKE_SA_INIT message the MessageID should
  // be always number 0 and 1;
  //
  IkePacket->Header->MessageId = 1;

  if (IkeSaSession->SessionCommon.IsInitiator) {
    IkePacket->Header->Flags = IKE_HEADER_FLAGS_INIT;
  } else {
    IkePacket->Header->Flags = IKE_HEADER_FLAGS_RESPOND;
  }

  //
  // 2. Generate ID Payload according to IP version and address.
  //
  IdPayload = Ikev2GenerateCertIdPayload (
                &IkeSaSession->SessionCommon,
                IKEV2_PAYLOAD_TYPE_CERT,
                (UINT8 *)PcdGetPtr (PcdIpsecUefiCertificate),
                PcdGet32 (PcdIpsecUefiCertificateSize)
                );

  //
  // 3. Generate Certificate Payload
  //
  CertPayload = Ikev2GenerateCertificatePayload (
                  IkeSaSession,
                  (UINT8)(IkeSaSession->SessionCommon.IsInitiator ? IKEV2_PAYLOAD_TYPE_CERTREQ : IKEV2_PAYLOAD_TYPE_AUTH),
                  (UINT8 *)PcdGetPtr (PcdIpsecUefiCertificate),
                  PcdGet32 (PcdIpsecUefiCertificateSize),
                  IKEV2_CERT_ENCODEING_X509_CERT_SIGN,
                  FALSE
                  );
  if (IkeSaSession->SessionCommon.IsInitiator) {
    CertReqPayload = Ikev2GenerateCertificatePayload (
                       IkeSaSession,
                       IKEV2_PAYLOAD_TYPE_AUTH,
                       (UINT8 *)PcdGetPtr (PcdIpsecUefiCertificate),
                       PcdGet32 (PcdIpsecUefiCertificateSize),
                       IKEV2_CERT_ENCODEING_HASH_AND_URL_OF_X509_CERT,
                       TRUE
                       );
  }

  //
  // 4. Generate Auth Payload
  //    If it is tunnel mode, should create the configuration payload after the
  //    Auth payload.
  //
  if (IkeSaSession->Spd->Data->ProcessingPolicy->Mode == EfiIPsecTransport) {
    AuthPayload = Ikev2CertGenerateAuthPayload (
                    ChildSaSession->IkeSaSession,
                    IdPayload,
                    IKEV2_PAYLOAD_TYPE_SA,
                    FALSE,
                    (UINT8 *)PcdGetPtr (PcdIpsecUefiCertificateKey),
                    PcdGet32 (PcdIpsecUefiCertificateKeySize),
                    ChildSaSession->IkeSaSession->Pad->Data->AuthData,
                    ChildSaSession->IkeSaSession->Pad->Data->AuthDataSize
                    );
  } else {
    AuthPayload = Ikev2CertGenerateAuthPayload (
                    ChildSaSession->IkeSaSession,
                    IdPayload,
                    IKEV2_PAYLOAD_TYPE_CP,
                    FALSE,
                    (UINT8 *)PcdGetPtr (PcdIpsecUefiCertificateKey),
                    PcdGet32 (PcdIpsecUefiCertificateKeySize),
                    ChildSaSession->IkeSaSession->Pad->Data->AuthData,
                    ChildSaSession->IkeSaSession->Pad->Data->AuthDataSize
                    );
    if (IkeSaSession->SessionCommon.UdpService->IpVersion == IP_VERSION_4) {
      CpPayload = Ikev2GenerateCpPayload (
                    ChildSaSession->IkeSaSession,
                    IKEV2_PAYLOAD_TYPE_SA,
                    IKEV2_CFG_ATTR_INTERNAL_IP4_ADDRESS
                    );
    } else {
      CpPayload = Ikev2GenerateCpPayload (
                    ChildSaSession->IkeSaSession,
                    IKEV2_PAYLOAD_TYPE_SA,
                    IKEV2_CFG_ATTR_INTERNAL_IP6_ADDRESS
                    );
    }
  }

  //
  // 5. Generate SA Payload according to the Sa Data in ChildSaSession
  //
  SaPayload = Ikev2GenerateSaPayload (
                ChildSaSession->SaData,
                IKEV2_PAYLOAD_TYPE_TS_INIT,
                IkeSessionTypeChildSa
                );

  if (IkeSaSession->Spd->Data->ProcessingPolicy->Mode == EfiIPsecTransport) {
    //
    // Generate Tsi and Tsr.
    //
    TsiPayload = Ikev2GenerateTsPayload (
                   ChildSaSession,
                   IKEV2_PAYLOAD_TYPE_TS_RSP,
                   FALSE
                   );

    TsrPayload = Ikev2GenerateTsPayload (
                   ChildSaSession,
                   IKEV2_PAYLOAD_TYPE_NOTIFY,
                   FALSE
                   );

    //
    // Generate Notify Payload. If transport mode, there should have Notify 
    // payload with TRANSPORT_MODE notification.
    //
    NotifyPayload = Ikev2GenerateNotifyPayload (
                      0,
                      IKEV2_PAYLOAD_TYPE_NONE,
                      0,
                      IKEV2_NOTIFICATION_USE_TRANSPORT_MODE,
                      NULL,
                      NULL,
                      0
                      );
  } else {
    //
    // Generate Tsr for Tunnel mode.
    //
    TsiPayload = Ikev2GenerateTsPayload (
                   ChildSaSession,
                   IKEV2_PAYLOAD_TYPE_TS_RSP,
                   TRUE
                   );
    TsrPayload = Ikev2GenerateTsPayload (
                   ChildSaSession,
                   IKEV2_PAYLOAD_TYPE_NONE,
                   FALSE
                   );
  }

  IKE_PACKET_APPEND_PAYLOAD (IkePacket, IdPayload);
  IKE_PACKET_APPEND_PAYLOAD (IkePacket, CertPayload);
  if (IkeSaSession->SessionCommon.IsInitiator) {
    IKE_PACKET_APPEND_PAYLOAD (IkePacket, CertReqPayload);
  }
  IKE_PACKET_APPEND_PAYLOAD (IkePacket, AuthPayload);
  if (IkeSaSession->Spd->Data->ProcessingPolicy->Mode == EfiIPsecTunnel) {
    IKE_PACKET_APPEND_PAYLOAD (IkePacket, CpPayload);
  }
  IKE_PACKET_APPEND_PAYLOAD (IkePacket, SaPayload);
  IKE_PACKET_APPEND_PAYLOAD (IkePacket, TsiPayload);
  IKE_PACKET_APPEND_PAYLOAD (IkePacket, TsrPayload);
  if (IkeSaSession->Spd->Data->ProcessingPolicy->Mode == EfiIPsecTransport) {
    IKE_PACKET_APPEND_PAYLOAD (IkePacket, NotifyPayload);
  }

  return IkePacket;
}

/**
  Parses IKE_AUTH packet.

  @param[in]  SaSession   Pointer to the IKE_SA_SESSION related to this packet.
  @param[in]  IkePacket   Pointer to the IKE_AUTH packet to be parsered.

  @retval     EFI_INVALID_PARAMETER   The IKEv2 packet is malformed or the SA
                                      proposal is unacceptable.
  @retval     EFI_SUCCESS             The IKE packet is acceptable and the
                                      relative data is saved for furthure communication.
  @retval     EFI_UNSUPPORTED         The certificate authentication is not supported.

**/
EFI_STATUS
Ikev2AuthCertParser (
  IN UINT8             *SaSession,
  IN IKE_PACKET        *IkePacket
  )
{
  IKEV2_CHILD_SA_SESSION *ChildSaSession;
  IKEV2_SA_SESSION       *IkeSaSession;
  IKE_PAYLOAD            *IkePayload;
  IKE_PAYLOAD            *SaPayload;
  IKE_PAYLOAD            *IdiPayload;
  IKE_PAYLOAD            *IdrPayload;
  IKE_PAYLOAD            *AuthPayload;
  IKE_PAYLOAD            *TsiPayload;
  IKE_PAYLOAD            *TsrPayload;
  IKE_PAYLOAD            *CertPayload;
  IKE_PAYLOAD            *CertReqPayload;
  IKE_PAYLOAD            *VerifiedAuthPayload;
  LIST_ENTRY             *Entry;
  EFI_STATUS             Status;

  if (!FeaturePcdGet (PcdIpsecCertificateEnabled)) {
    return EFI_UNSUPPORTED;
  }

  IkeSaSession   = (IKEV2_SA_SESSION *) SaSession;
  ChildSaSession = IKEV2_CHILD_SA_SESSION_BY_IKE_SA (GetFirstNode (&IkeSaSession->ChildSaSessionList));

  SaPayload           = NULL;
  IdiPayload          = NULL;
  IdrPayload          = NULL;
  AuthPayload         = NULL;
  TsiPayload          = NULL;
  TsrPayload          = NULL;
  CertPayload         = NULL;
  CertReqPayload      = NULL;
  VerifiedAuthPayload = NULL;
  Status              = EFI_INVALID_PARAMETER;

  //
  // Iterate payloads to find the SaPayload/ID/AUTH/TS Payload.
  //
  NET_LIST_FOR_EACH (Entry, &(IkePacket)->PayloadList) {
    IkePayload  = IKE_PAYLOAD_BY_PACKET (Entry);

    if (IkePayload->PayloadType == IKEV2_PAYLOAD_TYPE_ID_INIT) {
      IdiPayload = IkePayload;
    }
    if (IkePayload->PayloadType == IKEV2_PAYLOAD_TYPE_ID_RSP) {
      IdrPayload = IkePayload;
    }

    if (IkePayload->PayloadType == IKEV2_PAYLOAD_TYPE_SA) {
      SaPayload = IkePayload;
    }
    if (IkePayload->PayloadType == IKEV2_PAYLOAD_TYPE_AUTH) {
      AuthPayload = IkePayload;
    }
    if (IkePayload->PayloadType == IKEV2_PAYLOAD_TYPE_TS_INIT) {
      TsiPayload = IkePayload;
    }
    if (IkePayload->PayloadType == IKEV2_PAYLOAD_TYPE_TS_RSP) {
      TsrPayload = IkePayload;
    }
    if (IkePayload->PayloadType == IKEV2_PAYLOAD_TYPE_CERT) {
      CertPayload = IkePayload;
    }
    if (IkePayload->PayloadType == IKEV2_PAYLOAD_TYPE_CERTREQ) {
      CertReqPayload = IkePayload;
    }
  }

  if ((SaPayload == NULL) || (AuthPayload == NULL) || (TsiPayload == NULL) || 
      (TsrPayload == NULL) || (CertPayload == NULL)) {
    goto Exit;
  }
  if ((IdiPayload == NULL) && (IdrPayload == NULL)) {
    goto Exit;
  }

  //
  // Check IkePacket Header is match the state
  //
  if (IkeSaSession->SessionCommon.IsInitiator) {
    
    //
    // 1. Check the IkePacket->Hdr == IKE_HEADER_FLAGS_RESPOND
    //
    if ((IkePacket->Header->Flags != IKE_HEADER_FLAGS_RESPOND) ||
        (IkePacket->Header->ExchangeType != IKEV2_EXCHANGE_TYPE_AUTH)) {
      goto Exit;
    }
  } else {
    //
    // 1. Check the IkePacket->Hdr == IKE_HEADER_FLAGS_INIT
    //
    if ((IkePacket->Header->Flags != IKE_HEADER_FLAGS_INIT) ||
        (IkePacket->Header->ExchangeType != IKEV2_EXCHANGE_TYPE_AUTH)) {
      goto Exit;
    }
  }

  //
  // Verify the Auth Payload.
  //
  VerifiedAuthPayload = Ikev2CertGenerateAuthPayload (
                          IkeSaSession,
                          IkeSaSession->SessionCommon.IsInitiator ? IdrPayload:IdiPayload,
                          IKEV2_PAYLOAD_TYPE_SA,
                          TRUE,
                          NULL,
                          0,
                          NULL,
                          0
                          );

  if ((VerifiedAuthPayload != NULL) &&
      (!IpSecCryptoIoVerifySignDataByCertificate (
          CertPayload->PayloadBuf + sizeof (IKEV2_CERT),
          CertPayload->PayloadSize - sizeof (IKEV2_CERT),
          (UINT8 *)PcdGetPtr (PcdIpsecUefiCaFile),
          PcdGet32 (PcdIpsecUefiCaFileSize),
          VerifiedAuthPayload->PayloadBuf + sizeof (IKEV2_AUTH),
          VerifiedAuthPayload->PayloadSize - sizeof (IKEV2_AUTH),
          AuthPayload->PayloadBuf + sizeof (IKEV2_AUTH),
          AuthPayload->PayloadSize - sizeof (IKEV2_AUTH)
          ))) {
    goto Exit;
  }

  //
  // 3. Parse the SA Payload to find out the cryptographic suite
  //    and fill in the SA paramse into CommonSession->SaParams. If no acceptable
  //    porposal found, return EFI_INVALID_PARAMETER.
  //
  if (!Ikev2ChildSaParseSaPayload (ChildSaSession, SaPayload, IkePacket->Header->Flags)) {
    goto Exit;
  }

  //
  // 4. Parse TSi, TSr payloads.
  //
  if ((((TRAFFIC_SELECTOR *)(TsiPayload->PayloadBuf + sizeof (IKEV2_TS)))->IpProtocolId !=
      ((TRAFFIC_SELECTOR *)(TsrPayload->PayloadBuf + sizeof (IKEV2_TS)))->IpProtocolId) &&
      (((TRAFFIC_SELECTOR *)(TsiPayload->PayloadBuf + sizeof (IKEV2_TS)))->IpProtocolId != 0) &&
      (((TRAFFIC_SELECTOR *)(TsrPayload->PayloadBuf + sizeof (IKEV2_TS)))->IpProtocolId != 0)
      ) {
    goto Exit;
  }

  if (!IkeSaSession->SessionCommon.IsInitiator) {
    //
    //Todo:check the Port range. Only support any port and one certain port here.
    //
    ChildSaSession->ProtoId    = ((TRAFFIC_SELECTOR *)(TsrPayload->PayloadBuf + sizeof (IKEV2_TS)))->IpProtocolId;
    ChildSaSession->LocalPort  = ((TRAFFIC_SELECTOR *)(TsrPayload->PayloadBuf + sizeof (IKEV2_TS)))->StartPort;
    ChildSaSession->RemotePort = ((TRAFFIC_SELECTOR *)(TsiPayload->PayloadBuf + sizeof (IKEV2_TS)))->StartPort;
    //
    // Association a SPD with this SA.
    //
    if (EFI_ERROR (Ikev2ChildSaAssociateSpdEntry (ChildSaSession))) {
      goto Exit;
    }
    //
    // Associate the IkeSaSession's SPD to the first ChildSaSession's SPD.
    //
    if (ChildSaSession->IkeSaSession->Spd == NULL) {
      ChildSaSession->IkeSaSession->Spd = ChildSaSession->Spd;
      Ikev2ChildSaSessionSpdSelectorCreate (ChildSaSession);
    }
  } else {
    //
    // Todo:check the Port range.
    //
    if ((((TRAFFIC_SELECTOR *)(TsrPayload->PayloadBuf + sizeof (IKEV2_TS)))->StartPort != 0) &&
        (((TRAFFIC_SELECTOR *)(TsrPayload->PayloadBuf + sizeof (IKEV2_TS)))->StartPort != ChildSaSession->RemotePort)
        ) {
      goto Exit;
    } 
    if ((((TRAFFIC_SELECTOR *)(TsiPayload->PayloadBuf + sizeof (IKEV2_TS)))->StartPort != 0) &&
        (((TRAFFIC_SELECTOR *)(TsiPayload->PayloadBuf + sizeof (IKEV2_TS)))->StartPort != ChildSaSession->LocalPort)
        ) {
      goto Exit;
    }
    //
    // For the tunnel mode, it should add the vitual IP address into the SA's SPD Selector.
    //
    if (ChildSaSession->Spd->Data->ProcessingPolicy->Mode == EfiIPsecTunnel) {
      if (!ChildSaSession->IkeSaSession->SessionCommon.IsInitiator) {
        //
        // If it is tunnel mode, the UEFI part must be the initiator.
        //
        goto Exit;
      }
      //
      // Get the Virtual IP address from the Tsi traffic selector. 
      // TODO: check the CFG reply payload
      //
      CopyMem (
        &ChildSaSession->SpdSelector->LocalAddress[0].Address,
        TsiPayload->PayloadBuf + sizeof (IKEV2_TS) + sizeof (TRAFFIC_SELECTOR),
        (ChildSaSession->SessionCommon.UdpService->IpVersion == IP_VERSION_4) ?
        sizeof (EFI_IPv4_ADDRESS) : sizeof (EFI_IPv6_ADDRESS)
        );
    }
  }
  
  //
  // 5. Generat keymats for IPsec protocol.
  //
  Ikev2GenerateChildSaKeys (ChildSaSession, NULL);
  if (IkeSaSession->SessionCommon.IsInitiator) {
    //
    // 6. Change the state of IkeSaSession
    //
    IKEV2_DUMP_STATE (IkeSaSession->SessionCommon.State, IkeStateIkeSaEstablished);
    IkeSaSession->SessionCommon.State = IkeStateIkeSaEstablished;
  }

  Status = EFI_SUCCESS;

Exit:
  if (VerifiedAuthPayload != NULL) {
    IkePayloadFree (VerifiedAuthPayload);
  }
  return Status;
}

/**
  Generates the DH Public Key.

  This generates the DH local public key and store it in the IKE SA Session's GxBuffer.

  @param[in]  IkeSaSession   Pointer to related IKE SA Session.

  @retval EFI_SUCCESS        The operation succeeded.
  @retval Others             The operation failed.

**/
EFI_STATUS
Ikev2GenerateSaDhPublicKey (
  IN IKEV2_SA_SESSION         *IkeSaSession
  )
{
  EFI_STATUS         Status;
  IKEV2_SESSION_KEYS *IkeKeys;

  IkeSaSession->IkeKeys = AllocateZeroPool (sizeof (IKEV2_SESSION_KEYS));
  ASSERT (IkeSaSession->IkeKeys != NULL);
  IkeKeys = IkeSaSession->IkeKeys;
  IkeKeys->DhBuffer = AllocateZeroPool (sizeof (IKEV2_DH_BUFFER));
  ASSERT (IkeKeys->DhBuffer != NULL);

  //
  // Init DH with the certain DH Group Description.
  //
  IkeKeys->DhBuffer->GxSize   = OakleyModpGroup[(UINT8)IkeSaSession->SessionCommon.PreferDhGroup].Size >> 3;
  IkeKeys->DhBuffer->GxBuffer = AllocateZeroPool (IkeKeys->DhBuffer->GxSize);
  ASSERT (IkeKeys->DhBuffer->GxBuffer != NULL);

  //
  // Get X PublicKey
  //
  Status = IpSecCryptoIoDhGetPublicKey (
             &IkeKeys->DhBuffer->DhContext,
             OakleyModpGroup[(UINT8)IkeSaSession->SessionCommon.PreferDhGroup].GroupGenerator,
             OakleyModpGroup[(UINT8)IkeSaSession->SessionCommon.PreferDhGroup].Size,
             OakleyModpGroup[(UINT8)IkeSaSession->SessionCommon.PreferDhGroup].Modulus,
             IkeKeys->DhBuffer->GxBuffer,
             &IkeKeys->DhBuffer->GxSize
             );
  if (EFI_ERROR (Status)) {
    DEBUG ((DEBUG_ERROR, "Error CPLKeyManGetKeyParam X public key error Status = %r\n", Status));
    return Status;
  }

  IPSEC_DUMP_BUF ("DH Public Key (g^x) Dump", IkeKeys->DhBuffer->GxBuffer, IkeKeys->DhBuffer->GxSize);

  return EFI_SUCCESS;
}

/**
  Computes the DH Shared/Exchange Key.

  Given peer's public key, this function computes the exchanged common key and
  stores it in the IKEv2 SA Session's GxyBuffer.

  @param[in]  DhBuffer       Pointer to buffer of peer's puliic key.
  @param[in]  KePayload      Pointer to received key payload.
  
  @retval EFI_SUCCESS        The operation succeeded.
  @retval Otherwise          The operation failed.

**/
EFI_STATUS
Ikev2GenerateSaDhComputeKey (
  IN IKEV2_DH_BUFFER       *DhBuffer,
  IN IKE_PAYLOAD            *KePayload
  )
{
  EFI_STATUS          Status;
  IKEV2_KEY_EXCHANGE  *Ke;
  UINT8               *PubKey;
  UINTN               PubKeySize;

  Ke                  = (IKEV2_KEY_EXCHANGE *) KePayload->PayloadBuf;
  PubKey              = (UINT8 *) (Ke + 1);
  PubKeySize          = KePayload->PayloadSize - sizeof (IKEV2_KEY_EXCHANGE);
  DhBuffer->GxySize   = DhBuffer->GxSize;
  DhBuffer->GxyBuffer = AllocateZeroPool (DhBuffer->GxySize);
  ASSERT (DhBuffer->GxyBuffer != NULL);

  //
  // Get GxyBuf
  //
  Status = IpSecCryptoIoDhComputeKey (
             DhBuffer->DhContext,
             PubKey,
             PubKeySize,
             DhBuffer->GxyBuffer,
             &DhBuffer->GxySize
             );
  if (EFI_ERROR (Status)) {
    DEBUG ((DEBUG_ERROR, "Error CPLKeyManGetKeyParam Y session key error Status = %r\n", Status));
    return Status;
  }

  //
  // Create GxyBuf.
  //
  DhBuffer->GySize   = PubKeySize;
  DhBuffer->GyBuffer = AllocateZeroPool (DhBuffer->GySize);
  ASSERT (DhBuffer->GyBuffer != NULL);
  CopyMem (DhBuffer->GyBuffer, PubKey, DhBuffer->GySize);

  IPSEC_DUMP_BUF ("DH Public Key (g^y) Dump", DhBuffer->GyBuffer, DhBuffer->GySize);
  IPSEC_DUMP_BUF ("DH Shared Key (g^xy) Dump", DhBuffer->GxyBuffer, DhBuffer->GxySize);

  return EFI_SUCCESS;
}

/**
  Generates the IKE SKEYSEED and seven other secrets. SK_d, SK_ai, SK_ar, SK_ei, SK_er,
  SK_pi, SK_pr are keys for the furthure IKE exchange.

  @param[in]  IkeSaSession       Pointer to IKE SA Session.
  @param[in]  KePayload          Pointer to Key payload used to generate the Key.

  @retval EFI_UNSUPPORTED        If one or more Algorithm Id is not supported.
  @retval EFI_OUT_OF_RESOURCES   If there is no enough resource to be allocated to
                                 meet the requirement.
  @retval EFI_SUCCESS            The operation succeeded.

**/
EFI_STATUS
Ikev2GenerateSaKeys (
  IN IKEV2_SA_SESSION       *IkeSaSession,
  IN IKE_PAYLOAD            *KePayload
  )
{
  EFI_STATUS          Status;
  IKEV2_SA_PARAMS     *SaParams;
  IPSEC_PAD_ENTRY     *Pad;
  PRF_DATA_FRAGMENT   Fragments[4];
  UINT64              InitiatorCookieNet;
  UINT64              ResponderCookieNet;
  UINT8               *KeyBuffer;
  UINTN               KeyBufferSize;
  UINTN               AuthAlgKeyLen;
  UINTN               EncryptAlgKeyLen;
  UINTN               IntegrityAlgKeyLen;
  UINTN               PrfAlgKeyLen;
  UINT8               *OutputKey;
  UINTN               OutputKeyLength;
  UINT8               *Digest;
  UINTN               DigestSize;

  Digest    = NULL;
  OutputKey = NULL;
  KeyBuffer = NULL;
  Status = EFI_SUCCESS;

  //
  // Generate Gxy
  //
  Ikev2GenerateSaDhComputeKey (IkeSaSession->IkeKeys->DhBuffer, KePayload);

  Pad = IkeSaSession->Pad;

  //
  // Get the key length of Authenticaion, Encryption, PRF, and Integrity.
  //
  SaParams           = IkeSaSession->SessionCommon.SaParams;
  AuthAlgKeyLen      = IpSecGetHmacDigestLength ((UINT8)SaParams->Prf);
  EncryptAlgKeyLen   = IpSecGetEncryptKeyLength ((UINT8)SaParams->EncAlgId);
  IntegrityAlgKeyLen = IpSecGetHmacDigestLength ((UINT8)SaParams->IntegAlgId);
  PrfAlgKeyLen       = IpSecGetHmacDigestLength ((UINT8)SaParams->Prf);

  //
  // If one or more algorithm is not support, return EFI_UNSUPPORTED.
  //
  if (AuthAlgKeyLen == 0 || 
      EncryptAlgKeyLen == 0 ||
      IntegrityAlgKeyLen == 0 ||
      PrfAlgKeyLen == 0
      ) {
    Status = EFI_UNSUPPORTED;
    goto Exit;
  }

  //
  // Compute SKEYSEED = prf(Ni | Nr, g^ir)
  //
  KeyBufferSize = IkeSaSession->NiBlkSize + IkeSaSession->NrBlkSize;
  KeyBuffer     = AllocateZeroPool (KeyBufferSize);
  ASSERT (KeyBuffer != NULL);

  CopyMem (KeyBuffer, IkeSaSession->NiBlock, IkeSaSession->NiBlkSize);
  CopyMem (KeyBuffer + IkeSaSession->NiBlkSize, IkeSaSession->NrBlock, IkeSaSession->NrBlkSize);

  Fragments[0].Data     = IkeSaSession->IkeKeys->DhBuffer->GxyBuffer;
  Fragments[0].DataSize = IkeSaSession->IkeKeys->DhBuffer->GxySize;

  DigestSize = IpSecGetHmacDigestLength ((UINT8)SaParams->Prf);
  Digest     = AllocateZeroPool (DigestSize);

  if (Digest == NULL) {
    Status = EFI_OUT_OF_RESOURCES;
    goto Exit;
  }

  IpSecCryptoIoHmac (
    (UINT8)SaParams->Prf,
    KeyBuffer,
    KeyBufferSize,
    (HASH_DATA_FRAGMENT *) Fragments,
    1,
    Digest,
    DigestSize
    );

  //
  // {SK_d | SK_ai | SK_ar | SK_ei | SK_er | SK_pi | SK_pr } = prf+
  //               (SKEYSEED, Ni | Nr | SPIi | SPIr )
  //
  Fragments[0].Data     = IkeSaSession->NiBlock;
  Fragments[0].DataSize = IkeSaSession->NiBlkSize;
  Fragments[1].Data     = IkeSaSession->NrBlock;
  Fragments[1].DataSize = IkeSaSession->NrBlkSize;
  InitiatorCookieNet    = HTONLL (IkeSaSession->InitiatorCookie);
  ResponderCookieNet    = HTONLL (IkeSaSession->ResponderCookie);
  Fragments[2].Data     = (UINT8 *)(&InitiatorCookieNet);
  Fragments[2].DataSize = sizeof (IkeSaSession->InitiatorCookie);
  Fragments[3].Data     = (UINT8 *)(&ResponderCookieNet);
  Fragments[3].DataSize = sizeof (IkeSaSession->ResponderCookie);

  IPSEC_DUMP_BUF (">>> NiBlock", IkeSaSession->NiBlock, IkeSaSession->NiBlkSize);
  IPSEC_DUMP_BUF (">>> NrBlock", IkeSaSession->NrBlock, IkeSaSession->NrBlkSize);
  IPSEC_DUMP_BUF (">>> InitiatorCookie", (UINT8 *)&IkeSaSession->InitiatorCookie, sizeof(UINT64));
  IPSEC_DUMP_BUF (">>> ResponderCookie", (UINT8 *)&IkeSaSession->ResponderCookie, sizeof(UINT64));
  
  OutputKeyLength = PrfAlgKeyLen + 
                    2 * EncryptAlgKeyLen +
                    2 * AuthAlgKeyLen +
                    2 * IntegrityAlgKeyLen;
  OutputKey       = AllocateZeroPool (OutputKeyLength);
  if (OutputKey == NULL) {
    Status = EFI_OUT_OF_RESOURCES;
    goto Exit;
  }

  //
  // Generate Seven Keymates.
  //
  Status = Ikev2SaGenerateKey (
             (UINT8)SaParams->Prf,
             Digest,
             DigestSize,
             OutputKey,
             OutputKeyLength,
             Fragments,
             4
             );
  if (EFI_ERROR(Status)) {
    goto Exit;
  }

  //
  // Save the seven keys into KeySession.
  // First, SK_d
  //
  IkeSaSession->IkeKeys->SkdKey     = AllocateZeroPool (PrfAlgKeyLen);
  if (IkeSaSession->IkeKeys->SkdKey == NULL) {
    Status = EFI_OUT_OF_RESOURCES;
    goto Exit;
  }
  IkeSaSession->IkeKeys->SkdKeySize = PrfAlgKeyLen;
  CopyMem (IkeSaSession->IkeKeys->SkdKey, OutputKey, PrfAlgKeyLen);

  IPSEC_DUMP_BUF (">>> SK_D Key", IkeSaSession->IkeKeys->SkdKey, PrfAlgKeyLen);

  //
  // Second, Sk_ai
  //
  IkeSaSession->IkeKeys->SkAiKey     = AllocateZeroPool (IntegrityAlgKeyLen);
  if (IkeSaSession->IkeKeys->SkAiKey == NULL) {
    Status = EFI_OUT_OF_RESOURCES;
    goto Exit;
  }
  IkeSaSession->IkeKeys->SkAiKeySize = IntegrityAlgKeyLen;
  CopyMem (IkeSaSession->IkeKeys->SkAiKey, OutputKey + PrfAlgKeyLen, IntegrityAlgKeyLen);
  
  IPSEC_DUMP_BUF (">>> SK_Ai Key", IkeSaSession->IkeKeys->SkAiKey, IkeSaSession->IkeKeys->SkAiKeySize);

  //
  // Third, Sk_ar
  //
  IkeSaSession->IkeKeys->SkArKey     = AllocateZeroPool (IntegrityAlgKeyLen);
  if (IkeSaSession->IkeKeys->SkArKey == NULL) {
    Status = EFI_OUT_OF_RESOURCES;
    goto Exit;
  }
  IkeSaSession->IkeKeys->SkArKeySize = IntegrityAlgKeyLen;
  CopyMem (
    IkeSaSession->IkeKeys->SkArKey,
    OutputKey + PrfAlgKeyLen + IntegrityAlgKeyLen,
    IntegrityAlgKeyLen
    );
  
  IPSEC_DUMP_BUF (">>> SK_Ar Key", IkeSaSession->IkeKeys->SkArKey, IkeSaSession->IkeKeys->SkArKeySize);

  //
  // Fourth, Sk_ei
  //
  IkeSaSession->IkeKeys->SkEiKey     = AllocateZeroPool (EncryptAlgKeyLen);
  if (IkeSaSession->IkeKeys->SkEiKey == NULL) {
    Status = EFI_OUT_OF_RESOURCES;
    goto Exit;
  }
  IkeSaSession->IkeKeys->SkEiKeySize = EncryptAlgKeyLen;
  
  CopyMem (
    IkeSaSession->IkeKeys->SkEiKey,
    OutputKey + AuthAlgKeyLen + 2 * IntegrityAlgKeyLen,
    EncryptAlgKeyLen
    );
  IPSEC_DUMP_BUF (
    ">>> SK_Ei Key", 
    OutputKey + AuthAlgKeyLen + 2 * IntegrityAlgKeyLen,
    EncryptAlgKeyLen
    );

  //
  // Fifth, Sk_er
  //
  IkeSaSession->IkeKeys->SkErKey     = AllocateZeroPool (EncryptAlgKeyLen);
  if (IkeSaSession->IkeKeys->SkErKey == NULL) {
    Status = EFI_OUT_OF_RESOURCES;
    goto Exit;
  }
  IkeSaSession->IkeKeys->SkErKeySize = EncryptAlgKeyLen;

  CopyMem (
    IkeSaSession->IkeKeys->SkErKey,
    OutputKey + AuthAlgKeyLen + 2 * IntegrityAlgKeyLen + EncryptAlgKeyLen,
    EncryptAlgKeyLen
    );
  IPSEC_DUMP_BUF (
    ">>> SK_Er Key",
    OutputKey + AuthAlgKeyLen + 2 * IntegrityAlgKeyLen + EncryptAlgKeyLen,
    EncryptAlgKeyLen
    );

  //
  // Sixth, Sk_pi
  //
  IkeSaSession->IkeKeys->SkPiKey     = AllocateZeroPool (AuthAlgKeyLen);
  if (IkeSaSession->IkeKeys->SkPiKey == NULL) {
    Status = EFI_OUT_OF_RESOURCES;
    goto Exit;
  }
  IkeSaSession->IkeKeys->SkPiKeySize = AuthAlgKeyLen;

  CopyMem (
    IkeSaSession->IkeKeys->SkPiKey,
    OutputKey + AuthAlgKeyLen + 2 * IntegrityAlgKeyLen +  2 * EncryptAlgKeyLen,
    AuthAlgKeyLen
    );
  IPSEC_DUMP_BUF (
    ">>> SK_Pi Key",
    OutputKey + AuthAlgKeyLen + 2 * IntegrityAlgKeyLen +  2 * EncryptAlgKeyLen,
    AuthAlgKeyLen
    );

  //
  // Seventh, Sk_pr
  //
  IkeSaSession->IkeKeys->SkPrKey     = AllocateZeroPool (AuthAlgKeyLen);
  if (IkeSaSession->IkeKeys->SkPrKey == NULL) {
    Status = EFI_OUT_OF_RESOURCES;
    goto Exit;
  }
  IkeSaSession->IkeKeys->SkPrKeySize = AuthAlgKeyLen;

  CopyMem (
    IkeSaSession->IkeKeys->SkPrKey,
    OutputKey + AuthAlgKeyLen + 2 * IntegrityAlgKeyLen + 2 * EncryptAlgKeyLen + AuthAlgKeyLen,
    AuthAlgKeyLen
    ); 
  IPSEC_DUMP_BUF (
    ">>> SK_Pr Key",
    OutputKey + AuthAlgKeyLen + 2 * IntegrityAlgKeyLen + 2 * EncryptAlgKeyLen + AuthAlgKeyLen,
    AuthAlgKeyLen
    );


Exit:
  if (Digest != NULL) {
    FreePool (Digest);
  }
  if (KeyBuffer != NULL) {
    FreePool (KeyBuffer);
  }
  if (OutputKey != NULL) {
    FreePool (OutputKey);
  }

  if (EFI_ERROR(Status)) {
    if (IkeSaSession->IkeKeys->SkdKey != NULL) {
      FreePool (IkeSaSession->IkeKeys->SkdKey);
    }
    if (IkeSaSession->IkeKeys->SkAiKey != NULL) {
      FreePool (IkeSaSession->IkeKeys->SkAiKey);
    }
    if (IkeSaSession->IkeKeys->SkArKey != NULL) {
      FreePool (IkeSaSession->IkeKeys->SkArKey);
    }
    if (IkeSaSession->IkeKeys->SkEiKey != NULL) {
      FreePool (IkeSaSession->IkeKeys->SkEiKey);
    }
    if (IkeSaSession->IkeKeys->SkErKey != NULL) {
      FreePool (IkeSaSession->IkeKeys->SkErKey);
    }
    if (IkeSaSession->IkeKeys->SkPiKey != NULL) {
      FreePool (IkeSaSession->IkeKeys->SkPiKey);
    }
    if (IkeSaSession->IkeKeys->SkPrKey != NULL) {
      FreePool (IkeSaSession->IkeKeys->SkPrKey);
    }
  }

  
  return Status;
}

/**
  Generates the Keys for the furthure IPsec Protocol.

  @param[in]  ChildSaSession     Pointer to IKE Child SA Session.
  @param[in]  KePayload          Pointer to Key payload used to generate the Key.

  @retval EFI_UNSUPPORTED    If one or more Algorithm Id is not supported.
  @retval EFI_SUCCESS        The operation succeeded.

**/
EFI_STATUS
Ikev2GenerateChildSaKeys (
  IN IKEV2_CHILD_SA_SESSION     *ChildSaSession,
  IN IKE_PAYLOAD                *KePayload
  )
{
  EFI_STATUS          Status;
  IKEV2_SA_PARAMS     *SaParams;
  PRF_DATA_FRAGMENT   Fragments[3];
  UINTN               EncryptAlgKeyLen;
  UINTN               IntegrityAlgKeyLen;
  UINT8*              OutputKey;
  UINTN               OutputKeyLength;

  Status = EFI_SUCCESS;
  OutputKey = NULL;
  
  if (KePayload != NULL) {
    //
    // Generate Gxy 
    //
    Ikev2GenerateSaDhComputeKey (ChildSaSession->DhBuffer, KePayload);
    Fragments[0].Data     = ChildSaSession->DhBuffer->GxyBuffer;
    Fragments[0].DataSize = ChildSaSession->DhBuffer->GxySize;
  }

  Fragments[1].Data     = ChildSaSession->NiBlock;
  Fragments[1].DataSize = ChildSaSession->NiBlkSize;
  Fragments[2].Data     = ChildSaSession->NrBlock;
  Fragments[2].DataSize = ChildSaSession->NrBlkSize;

  //
  // Get the key length of Authenticaion, Encryption, PRF, and Integrity.
  //
  SaParams           = ChildSaSession->SessionCommon.SaParams;
  EncryptAlgKeyLen   = IpSecGetEncryptKeyLength ((UINT8)SaParams->EncAlgId);
  IntegrityAlgKeyLen = IpSecGetHmacDigestLength ((UINT8)SaParams->IntegAlgId);
  OutputKeyLength    = 2 * EncryptAlgKeyLen + 2 * IntegrityAlgKeyLen;

  if ((EncryptAlgKeyLen == 0) || (IntegrityAlgKeyLen == 0)) {
    Status = EFI_UNSUPPORTED;
    goto Exit;
  }

  //
  // 
  // If KePayload is not NULL, calculate KEYMAT = prf+(SK_d, g^ir (new) | Ni | Nr ),
  // otherwise, KEYMAT = prf+(SK_d, Ni | Nr )
  //
  OutputKey = AllocateZeroPool (OutputKeyLength);
  if (OutputKey == NULL) {
    Status = EFI_OUT_OF_RESOURCES;
    goto Exit;
  }

  //
  // Derive Key from the SkdKey Buffer.
  //
  Status = Ikev2SaGenerateKey (
             (UINT8)ChildSaSession->IkeSaSession->SessionCommon.SaParams->Prf,
             ChildSaSession->IkeSaSession->IkeKeys->SkdKey,
             ChildSaSession->IkeSaSession->IkeKeys->SkdKeySize,
             OutputKey,
             OutputKeyLength,
             KePayload == NULL ? &Fragments[1] : Fragments,
             KePayload == NULL ? 2 : 3
             );

  if (EFI_ERROR (Status)) {
    goto Exit;  
  }
  
  //
  // Copy KEYMATE (SK_ENCRYPT_i | SK_ENCRYPT_r | SK_INTEG_i | SK_INTEG_r) to
  // ChildKeyMates.
  //  
  if (!ChildSaSession->SessionCommon.IsInitiator) {

    // 
    // Initiator Encryption Key
    //
    ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.EncAlgoId    = (UINT8)SaParams->EncAlgId;
    ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.EncKeyLength = EncryptAlgKeyLen;
    ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.EncKey       = AllocateZeroPool (EncryptAlgKeyLen);
    if (ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.EncKey == NULL) {
      Status = EFI_OUT_OF_RESOURCES;
      goto Exit;
    }

    CopyMem (
      ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.EncKey,
      OutputKey,
      EncryptAlgKeyLen
      );

    //
    // Initiator Authentication Key
    //
    ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.AuthAlgoId    = (UINT8)SaParams->IntegAlgId;
    ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.AuthKeyLength = IntegrityAlgKeyLen;
    ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.AuthKey       = AllocateZeroPool (IntegrityAlgKeyLen);
    if (ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.AuthKey == NULL) {
      Status = EFI_OUT_OF_RESOURCES;
      goto Exit;
    }    
    
    CopyMem (
      ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.AuthKey,
      OutputKey + EncryptAlgKeyLen,
      IntegrityAlgKeyLen
      );

    //
    // Responder Encrypt Key
    //
    ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.EncAlgoId    = (UINT8)SaParams->EncAlgId;
    ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.EncKeyLength = EncryptAlgKeyLen;
    ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.EncKey       = AllocateZeroPool (EncryptAlgKeyLen);
    if (ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.EncKey == NULL) {
      Status = EFI_OUT_OF_RESOURCES;
      goto Exit;
    }   
    
    CopyMem (
      ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.EncKey,
      OutputKey + EncryptAlgKeyLen + IntegrityAlgKeyLen,
      EncryptAlgKeyLen
      );

    //
    // Responder Authentication Key
    //
    ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.AuthAlgoId    = (UINT8)SaParams->IntegAlgId;
    ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.AuthKeyLength = IntegrityAlgKeyLen;
    ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.AuthKey       = AllocateZeroPool (IntegrityAlgKeyLen);
    if (ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.AuthKey == NULL) {
      Status = EFI_OUT_OF_RESOURCES;
      goto Exit;
    }   
    
    CopyMem (
      ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.AuthKey,
      OutputKey + 2 * EncryptAlgKeyLen + IntegrityAlgKeyLen,
      IntegrityAlgKeyLen
      );
  } else {
    //
    // Initiator Encryption Key
    //
    ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.EncAlgoId    = (UINT8)SaParams->EncAlgId;
    ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.EncKeyLength = EncryptAlgKeyLen;
    ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.EncKey       = AllocateZeroPool (EncryptAlgKeyLen);
    if (ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.EncKey == NULL) {
      Status = EFI_OUT_OF_RESOURCES;
      goto Exit;
    }   
    
    CopyMem (
      ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.EncKey,
      OutputKey,
      EncryptAlgKeyLen
      );

    //
    // Initiator Authentication Key
    //
    ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.AuthAlgoId    = (UINT8)SaParams->IntegAlgId;
    ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.AuthKeyLength = IntegrityAlgKeyLen;
    ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.AuthKey       = AllocateZeroPool (IntegrityAlgKeyLen);
    if (ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.AuthKey == NULL) {
      Status = EFI_OUT_OF_RESOURCES;
      goto Exit;
    }   
    
    CopyMem (
      ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.AuthKey,
      OutputKey + EncryptAlgKeyLen,
      IntegrityAlgKeyLen
      );

    //
    // Responder Encryption Key
    //
    ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.EncAlgoId    = (UINT8)SaParams->EncAlgId;
    ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.EncKeyLength = EncryptAlgKeyLen;
    ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.EncKey       = AllocateZeroPool (EncryptAlgKeyLen);
    if (ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.EncKey == NULL) {
      Status = EFI_OUT_OF_RESOURCES;
      goto Exit;
    }  
    
    CopyMem (
      ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.EncKey,
      OutputKey + EncryptAlgKeyLen + IntegrityAlgKeyLen,
      EncryptAlgKeyLen
      );

    //
    // Responder Authentication Key
    //
    ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.AuthAlgoId    = (UINT8)SaParams->IntegAlgId;
    ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.AuthKeyLength = IntegrityAlgKeyLen;
    ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.AuthKey       = AllocateZeroPool (IntegrityAlgKeyLen);
    if (ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.AuthKey == NULL) {
      Status = EFI_OUT_OF_RESOURCES;
      goto Exit;
    }   
    
    CopyMem (
      ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.AuthKey,
      OutputKey + 2 * EncryptAlgKeyLen + IntegrityAlgKeyLen,
      IntegrityAlgKeyLen
      );
  }

  IPSEC_DUMP_BUF (
      " >>> Local Encryption Key",
      ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.EncKey,
      EncryptAlgKeyLen
      );
  IPSEC_DUMP_BUF (
      " >>> Remote Encryption Key",
      ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.EncKey,
      EncryptAlgKeyLen
      );
  IPSEC_DUMP_BUF (
      " >>> Local Authentication Key",
      ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.AuthKey,
      IntegrityAlgKeyLen
      );
  IPSEC_DUMP_BUF (
    " >>> Remote Authentication Key",
    ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.AuthKey,
    IntegrityAlgKeyLen
    );



Exit:
  if (EFI_ERROR (Status)) {
    if (ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.EncKey != NULL) {
      FreePool (ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.EncKey);
    }
    if (ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.AuthKey != NULL) {
      FreePool (ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.AuthKey);
    }
    if (ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.EncKey != NULL) {
      FreePool (ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.EncKey);
    }
    if (ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.AuthKey != NULL) {
      FreePool (ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.AuthKey);
    }
  }

  if (OutputKey != NULL) {
    FreePool (OutputKey);
  }
  
  return EFI_SUCCESS;
}

GLOBAL_REMOVE_IF_UNREFERENCED IKEV2_PACKET_HANDLER mIkev2Initial[][2] = {
  { //PSK
    { // IKEV2_INIT
      Ikev2InitPskParser,
      Ikev2InitPskGenerator
    },
    { //IKEV2_AUTH
      Ikev2AuthPskParser,
      Ikev2AuthPskGenerator
    }
  },
  { // CERT
    { // IKEV2_INIT
      Ikev2InitCertParser,
      Ikev2InitCertGenerator
    },
    { // IKEV2_AUTH
      Ikev2AuthCertParser,
      Ikev2AuthCertGenerator
    },
  },
};
