C++程序  |  3354行  |  110.28 KB

/** @file
  The implementation of Payloads Creation.

  (C) Copyright 2015 Hewlett-Packard Development Company, L.P.<BR>
  Copyright (c) 2010 - 2016, 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 "IpSecConfigImpl.h"
#include "IpSecCryptIo.h"

//
// The Constant String of "Key Pad for IKEv2" for Authentication Payload generation.
//
#define CONSTANT_KEY_SIZE     17
GLOBAL_REMOVE_IF_UNREFERENCED CHAR8 mConstantKey[CONSTANT_KEY_SIZE] =
{
  'K', 'e', 'y', ' ', 'P', 'a', 'd', ' ', 'f', 'o', 'r', ' ', 'I', 'K', 'E', 'v', '2'
};

/**
  Generate Ikev2 SA payload according to SessionSaData

  @param[in] SessionSaData   The data used in SA payload.
  @param[in] NextPayload     The payload type presented in NextPayload field of
                             SA Payload header.
  @param[in] Type            The SA type. It MUST be neither (1) for IKE_SA or
                             (2) for CHILD_SA or (3) for INFO.

  @retval a Pointer to SA IKE payload.

**/
IKE_PAYLOAD *
Ikev2GenerateSaPayload (
  IN IKEV2_SA_DATA    *SessionSaData,
  IN UINT8            NextPayload,
  IN IKE_SESSION_TYPE Type
  )
{
  IKE_PAYLOAD   *SaPayload;
  IKEV2_SA_DATA *SaData;
  UINTN         SaDataSize;

  SaPayload = IkePayloadAlloc ();
  if (SaPayload == NULL) {
    return NULL;
  }
  
  //
  // TODO: Get the Proposal Number and Transform Number from IPsec Config,
  // after the Ipsecconfig Application is support it.
  //

  if (Type == IkeSessionTypeIkeSa) {
    SaDataSize = sizeof (IKEV2_SA_DATA) +
                 SessionSaData->NumProposals * sizeof (IKEV2_PROPOSAL_DATA) +
                 sizeof (IKEV2_TRANSFORM_DATA) * SessionSaData->NumProposals * 4;
  } else {
    SaDataSize = sizeof (IKEV2_SA_DATA) +
                 SessionSaData->NumProposals * sizeof (IKEV2_PROPOSAL_DATA) +
                 sizeof (IKEV2_TRANSFORM_DATA) * SessionSaData->NumProposals * 3;

  }

  SaData = AllocateZeroPool (SaDataSize);
  if (SaData == NULL) {
    IkePayloadFree (SaPayload);
    return NULL;
  }

  CopyMem (SaData, SessionSaData, SaDataSize);
  SaData->SaHeader.Header.NextPayload = NextPayload;
  SaPayload->PayloadType              = IKEV2_PAYLOAD_TYPE_SA;
  SaPayload->PayloadBuf               = (UINT8 *) SaData;

  return SaPayload;
}

/**
  Generate a Nonce payload containing the input parameter NonceBuf.

  @param[in]  NonceBuf      The nonce buffer contains the whole Nonce payload block
                            except the payload header.
  @param[in]  NonceSize     The buffer size of the NonceBuf
  @param[in]  NextPayload   The payload type presented in the NextPayload field
                            of Nonce Payload header.

  @retval Pointer to Nonce IKE paload.

**/
IKE_PAYLOAD *
Ikev2GenerateNoncePayload (
  IN UINT8            *NonceBuf,
  IN UINTN            NonceSize,
  IN UINT8            NextPayload
  )
{
  IKE_PAYLOAD *NoncePayload;
  IKEV2_NONCE *Nonce;
  UINTN       Size;
  UINT8       *NonceBlock;

  //                           1                   2                   3
  //     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  //    ! Next Payload  !C!  RESERVED   !         Payload Length        !
  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  //    !                                                               !
  //    ~                            Nonce Data                         ~
  //    !                                                               !
  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  //
  Size        = sizeof (IKEV2_NONCE) + NonceSize;
  NonceBlock  = NonceBuf;

  Nonce       = AllocateZeroPool (Size);
  if (Nonce == NULL) {
    return NULL;
  }
  
  CopyMem (Nonce + 1, NonceBlock, Size - sizeof (IKEV2_NONCE));

  Nonce->Header.NextPayload   = NextPayload;
  Nonce->Header.PayloadLength = (UINT16) Size;
  NoncePayload                = IkePayloadAlloc ();
  if (NoncePayload == NULL) {
    FreePool (Nonce);
    return NULL;
  }
  
  NoncePayload->PayloadType = IKEV2_PAYLOAD_TYPE_NONCE;
  NoncePayload->PayloadBuf  = (UINT8 *) Nonce;
  NoncePayload->PayloadSize = Size;

  return NoncePayload;
}

/**
  Generate a Key Exchange payload according to the DH group type and save the
  public Key into IkeSaSession IkeKey field.

  @param[in, out] IkeSaSession    Pointer of the IKE_SA_SESSION.
  @param[in]      NextPayload     The payload type presented in the NextPayload field of Key
                                  Exchange Payload header.

  @retval Pointer to Key IKE payload.

**/
IKE_PAYLOAD*
Ikev2GenerateKePayload (
  IN OUT IKEV2_SA_SESSION *IkeSaSession,
  IN     UINT8            NextPayload
  )
{
  IKE_PAYLOAD         *KePayload;
  IKEV2_KEY_EXCHANGE  *Ke;
  UINTN               KeSize;
  IKEV2_SESSION_KEYS  *IkeKeys;

  //
  //                        1                   2                   3
  //   0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
  //   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  //   ! Next Payload  !C!  RESERVED   !         Payload Length        !
  //   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  //   !          DH Group #           !           RESERVED            !
  //   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  //   !                                                               !
  //   ~                       Key Exchange Data                       ~
  //   !                                                               !
  //   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  //
  IkeKeys = IkeSaSession->IkeKeys;

  if (IkeSaSession->SessionCommon.IsInitiator) {
    KeSize = sizeof (IKEV2_KEY_EXCHANGE) + IkeKeys->DhBuffer->GxSize;
  } else {
    KeSize = sizeof (IKEV2_KEY_EXCHANGE) + IkeKeys->DhBuffer->GxSize;
  }

  //
  // Allocate buffer for Key Exchange
  //
  Ke = AllocateZeroPool (KeSize);
  if (Ke == NULL) {
    return NULL;
  }

  Ke->Header.NextPayload    = NextPayload;
  Ke->Header.PayloadLength  = (UINT16) KeSize;
  Ke->DhGroup               = IkeSaSession->SessionCommon.PreferDhGroup;

  CopyMem (Ke + 1, IkeKeys->DhBuffer->GxBuffer, IkeKeys->DhBuffer->GxSize);

  //
  // Create IKE_PAYLOAD to point to Key Exchange payload
  //
  KePayload = IkePayloadAlloc ();
  if (KePayload == NULL) {
    FreePool (Ke);
    return NULL;
  }

  KePayload->PayloadType = IKEV2_PAYLOAD_TYPE_KE;
  KePayload->PayloadBuf  = (UINT8 *) Ke;
  KePayload->PayloadSize = KeSize;
  return KePayload;
}

/**
  Generate a ID payload.

  @param[in] CommonSession   Pointer to IKEV2_SESSION_COMMON related to ID payload.
  @param[in] NextPayload     The payload type presented in the NextPayload field
                             of ID Payload header.

  @retval Pointer to ID IKE payload.

**/
IKE_PAYLOAD *
Ikev2GenerateIdPayload (
  IN IKEV2_SESSION_COMMON *CommonSession,
  IN UINT8                NextPayload
  )
{
  IKE_PAYLOAD    *IdPayload;
  IKEV2_ID       *Id;
  UINTN          IdSize;
  UINT8          IpVersion;
  UINT8          AddrSize;

  //
  // ID payload
  //    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
  //   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  //   ! Next Payload  !   RESERVED    !         Payload Length        !
  //   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  //   !   ID Type     !             RESERVED                          !
  //   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  //   !                                                               !
  //   ~                   Identification Data                         ~
  //   !                                                               !
  //   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  //

  IpVersion = CommonSession->UdpService->IpVersion;
  AddrSize  = (UINT8) ((IpVersion == IP_VERSION_4) ? sizeof(EFI_IPv4_ADDRESS) : sizeof(EFI_IPv6_ADDRESS));
  IdSize    = sizeof (IKEV2_ID) + AddrSize;

  Id = (IKEV2_ID *) AllocateZeroPool (IdSize);
  if (Id == NULL) {
    return NULL;
  }

  IdPayload = IkePayloadAlloc ();
  if (IdPayload == NULL) {
    FreePool (Id);
    return NULL;
  }

  IdPayload->PayloadType  = (UINT8) ((CommonSession->IsInitiator) ? IKEV2_PAYLOAD_TYPE_ID_INIT : IKEV2_PAYLOAD_TYPE_ID_RSP);
  IdPayload->PayloadBuf   = (UINT8 *) Id;
  IdPayload->PayloadSize  = IdSize;

  //
  // Set generic header of identification payload
  //
  Id->Header.NextPayload    = NextPayload;
  Id->Header.PayloadLength  = (UINT16) IdSize;
  Id->IdType                = (UINT8) ((IpVersion == IP_VERSION_4) ? IKEV2_ID_TYPE_IPV4_ADDR : IKEV2_ID_TYPE_IPV6_ADDR);
  CopyMem (Id + 1, &CommonSession->LocalPeerIp, AddrSize);

  return IdPayload;
}

/**
  Generate a ID payload.

  @param[in] CommonSession   Pointer to IKEV2_SESSION_COMMON related to ID payload.
  @param[in] NextPayload     The payload type presented in the NextPayload field
                             of ID Payload header.
  @param[in] InCert          Pointer to the Certificate which distinguished name
                             will be added into the Id payload.
  @param[in] CertSize        Size of the Certificate.

  @retval Pointer to ID IKE payload.

**/
IKE_PAYLOAD *
Ikev2GenerateCertIdPayload (
  IN IKEV2_SESSION_COMMON *CommonSession,
  IN UINT8                NextPayload,
  IN UINT8                *InCert,
  IN UINTN                CertSize
  )
{
  IKE_PAYLOAD    *IdPayload;
  IKEV2_ID       *Id;
  UINTN          IdSize;
  UINTN          SubjectSize;
  UINT8          *CertSubject;

  //
  // ID payload
  //    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
  //   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  //   ! Next Payload  !   RESERVED    !         Payload Length        !
  //   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  //   !   ID Type     !             RESERVED                          !
  //   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  //   !                                                               !
  //   ~                   Identification Data                         ~
  //   !                                                               !
  //   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  //

  SubjectSize = 0;
  CertSubject = NULL;
  IpSecCryptoIoGetSubjectFromCert (
    InCert,
    CertSize,
    &CertSubject,
    &SubjectSize
    );
  if (SubjectSize != 0) {
    ASSERT (CertSubject != NULL);
  }

  IdSize = sizeof (IKEV2_ID) + SubjectSize;

  Id = (IKEV2_ID *) AllocateZeroPool (IdSize);
  if (Id == NULL) {
    return NULL;
  }

  IdPayload = IkePayloadAlloc ();
  if (IdPayload == NULL) {
    FreePool (Id);
    return NULL;
  }

  IdPayload->PayloadType  = (UINT8) ((CommonSession->IsInitiator) ? IKEV2_PAYLOAD_TYPE_ID_INIT : IKEV2_PAYLOAD_TYPE_ID_RSP);
  IdPayload->PayloadBuf   = (UINT8 *) Id;
  IdPayload->PayloadSize  = IdSize;

  //
  // Set generic header of identification payload
  //
  Id->Header.NextPayload    = NextPayload;
  Id->Header.PayloadLength  = (UINT16) IdSize;
  Id->IdType                = 9;
  CopyMem (Id + 1, CertSubject, SubjectSize);

  if (CertSubject != NULL) {
    FreePool (CertSubject);
  }
  return IdPayload;
}

/**
  Generate a Authentication Payload.

  This function is used for both Authentication generation and verification. When the
  IsVerify is TRUE, it create a Auth Data for verification. This function choose the
  related IKE_SA_INIT Message for Auth data creation according to the IKE Session's type
  and the value of IsVerify parameter.

  @param[in]  IkeSaSession  Pointer to IKEV2_SA_SESSION related to.
  @param[in]  IdPayload     Pointer to the ID payload to be used for Authentication
                            payload generation.
  @param[in]  NextPayload   The type filled into the Authentication Payload next
                            payload field.
  @param[in]  IsVerify      If it is TURE, the Authentication payload is used for
                            verification.

  @return pointer to IKE Authentication payload for Pre-shared key method.

**/
IKE_PAYLOAD *
Ikev2PskGenerateAuthPayload (
  IN IKEV2_SA_SESSION *IkeSaSession,
  IN IKE_PAYLOAD      *IdPayload,
  IN UINT8            NextPayload,
  IN BOOLEAN          IsVerify
  )
{
  UINT8              *Digest;
  UINTN              DigestSize;
  PRF_DATA_FRAGMENT  Fragments[3];
  UINT8              *KeyBuf;
  UINTN              KeySize;
  IKE_PAYLOAD        *AuthPayload;
  IKEV2_AUTH         *PayloadBuf;
  EFI_STATUS         Status;

  //
  // Auth = Prf(Prf(Secret,"Key Pad for IKEv2),IKE_SA_INIi/r|Ni/r|Prf(SK_Pr, IDi/r))
  //
  //                           1                   2                   3
  //     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  //    ! Next Payload  !C!  RESERVED   !         Payload Length        !
  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  //    ! Auth Method   !                RESERVED                       !
  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  //    !                                                               !
  //    ~                      Authentication Data                      ~
  //    !                                                               !
  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  //

  KeyBuf      = NULL;
  AuthPayload = NULL;
  Digest      = NULL;

  DigestSize = IpSecGetHmacDigestLength ((UINT8)IkeSaSession->SessionCommon.SaParams->Prf);
  Digest     = AllocateZeroPool (DigestSize);
  if (Digest == NULL) {
    return NULL;
  }
  
  if (IdPayload == NULL) {
    return NULL;
  }
  
  //
  // Calcualte Prf(Seceret, "Key Pad for IKEv2");
  //
  Fragments[0].Data     = (UINT8 *) mConstantKey;
  Fragments[0].DataSize = CONSTANT_KEY_SIZE;

  Status = IpSecCryptoIoHmac (
             (UINT8)IkeSaSession->SessionCommon.SaParams->Prf,
             IkeSaSession->Pad->Data->AuthData,
             IkeSaSession->Pad->Data->AuthDataSize,
             (HASH_DATA_FRAGMENT *)Fragments,
             1,
             Digest,
             DigestSize
             );
  if (EFI_ERROR (Status)) {
    goto EXIT;
  }

  //
  // Store the AuthKey into KeyBuf
  //
  KeyBuf = AllocateZeroPool (DigestSize);
  if (KeyBuf == NULL) {
    Status = EFI_OUT_OF_RESOURCES;
    goto EXIT;
  }
  
  CopyMem (KeyBuf, Digest, DigestSize);
  KeySize = DigestSize;

  //
  // Calculate Prf(SK_Pi/r, IDi/r)
  //
  Fragments[0].Data     = IdPayload->PayloadBuf + sizeof (IKEV2_COMMON_PAYLOAD_HEADER);
  Fragments[0].DataSize = IdPayload->PayloadSize - sizeof (IKEV2_COMMON_PAYLOAD_HEADER);

  if ((IkeSaSession->SessionCommon.IsInitiator && IsVerify) ||
      (!IkeSaSession->SessionCommon.IsInitiator && !IsVerify)
     ) {
     Status = IpSecCryptoIoHmac (
                (UINT8)IkeSaSession->SessionCommon.SaParams->Prf,
                IkeSaSession->IkeKeys->SkPrKey,
                IkeSaSession->IkeKeys->SkPrKeySize,
                (HASH_DATA_FRAGMENT *) Fragments,
                1,
                Digest,
                DigestSize
                );
  } else {
    Status = IpSecCryptoIoHmac (
               (UINT8)IkeSaSession->SessionCommon.SaParams->Prf,
               IkeSaSession->IkeKeys->SkPiKey,
               IkeSaSession->IkeKeys->SkPiKeySize,
               (HASH_DATA_FRAGMENT *) Fragments,
               1,
               Digest,
               DigestSize
               );
  }
  if (EFI_ERROR (Status)) {
    goto EXIT;
  }

  //
  // Copy data to Fragments.
  //
  if ((IkeSaSession->SessionCommon.IsInitiator && IsVerify) ||
      (!IkeSaSession->SessionCommon.IsInitiator && !IsVerify)
     )  {
    Fragments[0].Data     = IkeSaSession->RespPacket;
    Fragments[0].DataSize = IkeSaSession->RespPacketSize;
    Fragments[1].Data     = IkeSaSession->NiBlock;
    Fragments[1].DataSize = IkeSaSession->NiBlkSize;
  } else {
    Fragments[0].Data     = IkeSaSession->InitPacket;
    Fragments[0].DataSize = IkeSaSession->InitPacketSize;
    Fragments[1].Data     = IkeSaSession->NrBlock;
    Fragments[1].DataSize = IkeSaSession->NrBlkSize;
  }

  //
  // Copy the result of Prf(SK_Pr, IDi/r) to Fragments[2].
  //
  Fragments[2].Data     = AllocateZeroPool (DigestSize);
  if (Fragments[2].Data == NULL) {
    Status = EFI_OUT_OF_RESOURCES;
    goto EXIT;
  }
  
  Fragments[2].DataSize = DigestSize;
  CopyMem (Fragments[2].Data, Digest, DigestSize);

  //
  // Calculate Prf(Key,IKE_SA_INIi/r|Ni/r|Prf(SK_Pr, IDi/r))
  //
  Status = IpSecCryptoIoHmac (
             (UINT8)IkeSaSession->SessionCommon.SaParams->Prf,
             KeyBuf,
             KeySize,
             (HASH_DATA_FRAGMENT *) Fragments,
             3,
             Digest,
             DigestSize
             );
  if (EFI_ERROR (Status)) {
    goto EXIT;
  }

  //
  // Allocate buffer for Auth Payload
  //
  AuthPayload               = IkePayloadAlloc ();
  if (AuthPayload == NULL) {
    Status = EFI_OUT_OF_RESOURCES;
    goto EXIT;
  }

  AuthPayload->PayloadSize  = sizeof (IKEV2_AUTH) + DigestSize;
  PayloadBuf                = (IKEV2_AUTH *) AllocateZeroPool (AuthPayload->PayloadSize);
  if (PayloadBuf == NULL) {
    Status = EFI_OUT_OF_RESOURCES;
    goto EXIT;
  }
  
  //
  // Fill in Auth payload.
  //
  PayloadBuf->Header.NextPayload   = NextPayload;
  PayloadBuf->Header.PayloadLength = (UINT16) (AuthPayload->PayloadSize);
  if (IkeSaSession->Pad->Data->AuthMethod == EfiIPsecAuthMethodPreSharedSecret) {
    //
    // Only support Shared Key Message Integrity
    //
    PayloadBuf->AuthMethod = IKEV2_AUTH_METHOD_SKMI;
  } else {
    //
    // Not support other Auth method.
    //
    Status = EFI_UNSUPPORTED;
    goto EXIT;
  }

  //
  // Copy the result of Prf(Key,IKE_SA_INIi/r|Ni/r|Prf(SK_Pr, IDi/r)) to Auth
  // payload block.
  //
  CopyMem (
    PayloadBuf + 1,
    Digest,
    DigestSize
    );

  //
  // Fill in IKE_PACKET
  //
  AuthPayload->PayloadBuf   = (UINT8 *) PayloadBuf;
  AuthPayload->PayloadType  = IKEV2_PAYLOAD_TYPE_AUTH;

EXIT:
  if (KeyBuf != NULL) {
    FreePool (KeyBuf);
  }
  if (Digest != NULL) {
    FreePool (Digest);
  }
  if (Fragments[2].Data != NULL) {
    //
    // Free the buffer which contains the result of Prf(SK_Pr, IDi/r)
    //
    FreePool (Fragments[2].Data);
  }

  if (EFI_ERROR (Status)) {
    if (AuthPayload != NULL) {
      IkePayloadFree (AuthPayload);
    }
    return NULL;
  } else {
    return AuthPayload;
  }
}

/**
  Generate a Authentication Payload for Certificate Auth method.

  This function has two functions. One is creating a local Authentication
  Payload for sending and other is creating the remote Authentication data
  for verification when the IsVerify is TURE.

  @param[in]  IkeSaSession      Pointer to IKEV2_SA_SESSION related to.
  @param[in]  IdPayload         Pointer to the ID payload to be used for Authentication
                                payload generation.
  @param[in]  NextPayload       The type filled into the Authentication Payload
                                next payload field.
  @param[in]  IsVerify          If it is TURE, the Authentication payload is used
                                for verification.
  @param[in]  UefiPrivateKey    Pointer to the UEFI private key. Ignore it when
                                verify the authenticate payload.
  @param[in]  UefiPrivateKeyLen The size of UefiPrivateKey in bytes. Ignore it
                                when verify the authenticate payload.
  @param[in]  UefiKeyPwd        Pointer to the password of UEFI private key.
                                Ignore it when verify the authenticate payload.
  @param[in]  UefiKeyPwdLen     The size of UefiKeyPwd in bytes.Ignore it when
                                verify the authenticate payload.

  @return pointer to IKE Authentication payload for Cerifitcation method.

**/
IKE_PAYLOAD *
Ikev2CertGenerateAuthPayload (
  IN IKEV2_SA_SESSION *IkeSaSession,
  IN IKE_PAYLOAD      *IdPayload,
  IN UINT8            NextPayload,
  IN BOOLEAN          IsVerify,
  IN UINT8            *UefiPrivateKey,
  IN UINTN            UefiPrivateKeyLen,
  IN UINT8            *UefiKeyPwd,
  IN UINTN            UefiKeyPwdLen
  )
{
  UINT8              *Digest;
  UINTN              DigestSize;
  PRF_DATA_FRAGMENT  Fragments[3];
  IKE_PAYLOAD        *AuthPayload;
  IKEV2_AUTH         *PayloadBuf;
  EFI_STATUS         Status;
  UINT8              *Signature;
  UINTN              SigSize;

  //
  // Auth = Prf(Scert,IKE_SA_INIi/r|Ni/r|Prf(SK_Pr, IDi/r))
  //
  //                           1                   2                   3
  //     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  //    ! Next Payload  !C!  RESERVED   !         Payload Length        !
  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  //    ! Auth Method   !                RESERVED                       !
  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  //    !                                                               !
  //    ~                      Authentication Data                      ~
  //    !                                                               !
  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  //
  //
  // Initial point
  //
  AuthPayload = NULL;
  Digest      = NULL;
  Signature   = NULL;
  SigSize     = 0;

  if (IdPayload == NULL) {
    return NULL;
  }
  DigestSize = IpSecGetHmacDigestLength ((UINT8)IkeSaSession->SessionCommon.SaParams->Prf);
  Digest     = AllocateZeroPool (DigestSize);
  if (Digest == NULL) {
    return NULL;
  }

  //
  // Calculate Prf(SK_Pi/r, IDi/r)
  //
  Fragments[0].Data     = IdPayload->PayloadBuf + sizeof (IKEV2_COMMON_PAYLOAD_HEADER);
  Fragments[0].DataSize = IdPayload->PayloadSize - sizeof (IKEV2_COMMON_PAYLOAD_HEADER);

  IpSecDumpBuf ("RestofIDPayload", Fragments[0].Data, Fragments[0].DataSize);

  if ((IkeSaSession->SessionCommon.IsInitiator && IsVerify) ||
      (!IkeSaSession->SessionCommon.IsInitiator && !IsVerify)
     ) {
     Status = IpSecCryptoIoHmac(
                (UINT8)IkeSaSession->SessionCommon.SaParams->Prf,
                IkeSaSession->IkeKeys->SkPrKey,
                IkeSaSession->IkeKeys->SkPrKeySize,
                (HASH_DATA_FRAGMENT *) Fragments,
                1,
                Digest,
                DigestSize
                );
    IpSecDumpBuf ("MACedIDForR", Digest, DigestSize);
  } else {
    Status = IpSecCryptoIoHmac (
               (UINT8)IkeSaSession->SessionCommon.SaParams->Prf,
               IkeSaSession->IkeKeys->SkPiKey,
               IkeSaSession->IkeKeys->SkPiKeySize,
               (HASH_DATA_FRAGMENT *) Fragments,
               1,
               Digest,
               DigestSize
               );
    IpSecDumpBuf ("MACedIDForI", Digest, DigestSize);
  }
  if (EFI_ERROR (Status)) {
    goto EXIT;
  }

  //
  // Copy data to Fragments.
  //
  if ((IkeSaSession->SessionCommon.IsInitiator && IsVerify) ||
      (!IkeSaSession->SessionCommon.IsInitiator && !IsVerify)
     )  {
    Fragments[0].Data     = IkeSaSession->RespPacket;
    Fragments[0].DataSize = IkeSaSession->RespPacketSize;
    Fragments[1].Data     = IkeSaSession->NiBlock;
    Fragments[1].DataSize = IkeSaSession->NiBlkSize;
    IpSecDumpBuf ("RealMessage2", Fragments[0].Data, Fragments[0].DataSize);
    IpSecDumpBuf ("NonceIDdata", Fragments[1].Data, Fragments[1].DataSize);
  } else {
    Fragments[0].Data     = IkeSaSession->InitPacket;
    Fragments[0].DataSize = IkeSaSession->InitPacketSize;
    Fragments[1].Data     = IkeSaSession->NrBlock;
    Fragments[1].DataSize = IkeSaSession->NrBlkSize;
    IpSecDumpBuf ("RealMessage1", Fragments[0].Data, Fragments[0].DataSize);
    IpSecDumpBuf ("NonceRDdata", Fragments[1].Data, Fragments[1].DataSize);
  }

  //
  // Copy the result of Prf(SK_Pr, IDi/r) to Fragments[2].
  //
  Fragments[2].Data     = AllocateZeroPool (DigestSize);
  if (Fragments[2].Data == NULL) {
    Status = EFI_OUT_OF_RESOURCES;
    goto EXIT;
  }
  
  Fragments[2].DataSize = DigestSize;
  CopyMem (Fragments[2].Data, Digest, DigestSize);

  //
  // Calculate Prf(Key,IKE_SA_INIi/r|Ni/r|Prf(SK_Pr, IDi/r))
  //
  Status = IpSecCryptoIoHash (
             (UINT8)IkeSaSession->SessionCommon.SaParams->Prf,
             (HASH_DATA_FRAGMENT *) Fragments,
             3,
             Digest,
             DigestSize
             );
  if (EFI_ERROR (Status)) {
    goto EXIT;
  }

  IpSecDumpBuf ("HashSignedOctects", Digest, DigestSize);
  //
  // Sign the data by the private Key
  //
  if (!IsVerify) {
    IpSecCryptoIoAuthDataWithCertificate (
      Digest,
      DigestSize,
      UefiPrivateKey,
      UefiPrivateKeyLen,
      UefiKeyPwd,
      UefiKeyPwdLen,
      &Signature,
      &SigSize
      );

    if (SigSize == 0 || Signature == NULL) {
      goto EXIT;
    }
  }

  //
  // Allocate buffer for Auth Payload
  //
  AuthPayload = IkePayloadAlloc ();
  if (AuthPayload == NULL) {
    Status = EFI_OUT_OF_RESOURCES;
    goto EXIT;
  }

  if (!IsVerify) {
    AuthPayload->PayloadSize  = sizeof (IKEV2_AUTH) + SigSize;
  } else {
    AuthPayload->PayloadSize  = sizeof (IKEV2_AUTH) + DigestSize;
  }

  PayloadBuf = (IKEV2_AUTH *) AllocateZeroPool (AuthPayload->PayloadSize);
  if (PayloadBuf == NULL) {
    Status = EFI_OUT_OF_RESOURCES;
    goto EXIT;
  }
  
  //
  // Fill in Auth payload.
  //
  PayloadBuf->Header.NextPayload   = NextPayload;
  PayloadBuf->Header.PayloadLength = (UINT16) (AuthPayload->PayloadSize);
  if (IkeSaSession->Pad->Data->AuthMethod == EfiIPsecAuthMethodCertificates) {
      PayloadBuf->AuthMethod = IKEV2_AUTH_METHOD_RSA;
  } else {
    Status = EFI_INVALID_PARAMETER;
    goto EXIT;
  }

  //
  // Copy the result of Prf(Key,IKE_SA_INIi/r|Ni/r|Prf(SK_Pr, IDi/r)) to Auth
  // payload block.
  //
  if (!IsVerify) {
    CopyMem (PayloadBuf + 1, Signature, SigSize);
  } else {
    CopyMem (PayloadBuf + 1, Digest, DigestSize);
  }

  //
  // Fill in IKE_PACKET
  //
  AuthPayload->PayloadBuf   = (UINT8 *) PayloadBuf;
  AuthPayload->PayloadType  = IKEV2_PAYLOAD_TYPE_AUTH;

EXIT:
  if (Digest != NULL) {
    FreePool (Digest);
  }
  if (Signature != NULL) {
    FreePool (Signature);
  }
  if (Fragments[2].Data != NULL) {
    //
    // Free the buffer which contains the result of Prf(SK_Pr, IDi/r)
    //
    FreePool (Fragments[2].Data);
  }

  if (EFI_ERROR (Status)) {
    if (AuthPayload != NULL) {
      IkePayloadFree (AuthPayload);
    }
    return NULL;
  } else {
    return AuthPayload;
  }
}

/**
  Generate TS payload.

  This function generates TSi or TSr payload according to type of next payload.
  If the next payload is Responder TS, gereate TSi Payload. Otherwise, generate
  TSr payload.

  @param[in] ChildSa        Pointer to IKEV2_CHILD_SA_SESSION related to this TS payload.
  @param[in] NextPayload    The payload type presented in the NextPayload field
                            of ID Payload header.
  @param[in] IsTunnel       It indicates that if the Ts Payload is after the CP payload.
                            If yes, it means the Tsi and Tsr payload should be with
                            Max port range and address range and protocol is marked
                            as zero.

  @retval Pointer to Ts IKE payload.

**/
IKE_PAYLOAD *
Ikev2GenerateTsPayload (
  IN IKEV2_CHILD_SA_SESSION *ChildSa,
  IN UINT8                  NextPayload,
  IN BOOLEAN                IsTunnel
  )
{
  IKE_PAYLOAD        *TsPayload;
  IKEV2_TS           *TsPayloadBuf;
  TRAFFIC_SELECTOR   *TsSelector;
  UINTN              SelectorSize;
  UINTN              TsPayloadSize;
  UINT8              IpVersion;
  UINT8              AddrSize;

  //
  //                           1                   2                   3
  //     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  //    ! Next Payload  !C!  RESERVED   !         Payload Length        !
  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  //    ! Number of TSs !                 RESERVED                      !
  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  //    !                                                               !
  //    ~                       <Traffic Selectors>                     ~
  //    !                                                               !
  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  //

  TsPayload    = IkePayloadAlloc();
  if (TsPayload == NULL) {
    return NULL;
  }

  IpVersion    = ChildSa->SessionCommon.UdpService->IpVersion;
  //
  // The Starting Address and Ending Address is variable length depends on
  // is IPv4 or IPv6
  //
  AddrSize      = (UINT8)((IpVersion == IP_VERSION_4) ? sizeof (EFI_IPv4_ADDRESS) : sizeof (EFI_IPv6_ADDRESS));
  SelectorSize  = sizeof (TRAFFIC_SELECTOR) + 2 * AddrSize;
  TsPayloadSize = sizeof (IKEV2_TS) + SelectorSize;
  TsPayloadBuf = AllocateZeroPool (TsPayloadSize);
  if (TsPayloadBuf == NULL) {
    goto ON_ERROR;
  }

  TsPayload->PayloadBuf = (UINT8 *) TsPayloadBuf;
  TsSelector            = (TRAFFIC_SELECTOR*)(TsPayloadBuf + 1);

  TsSelector->TSType = (UINT8)((IpVersion == IP_VERSION_4) ? IKEV2_TS_TYPE_IPV4_ADDR_RANGE : IKEV2_TS_TYPS_IPV6_ADDR_RANGE);

  //
  // For tunnel mode
  //
  if (IsTunnel) {
    TsSelector->IpProtocolId = IKEV2_TS_ANY_PROTOCOL;
    TsSelector->SelecorLen   = (UINT16) SelectorSize;
    TsSelector->StartPort    = 0;
    TsSelector->EndPort      = IKEV2_TS_ANY_PORT;
    ZeroMem ((UINT8*)TsSelector + sizeof(TRAFFIC_SELECTOR), AddrSize);
    SetMem  ((UINT8*)TsSelector + sizeof(TRAFFIC_SELECTOR) + AddrSize, AddrSize, 0xff);

  } else {
    //
    // TODO: Support port range and address range
    //
    if (NextPayload == IKEV2_PAYLOAD_TYPE_TS_RSP){
      //
      // Create initiator Traffic Selector
      //
      TsSelector->SelecorLen   = (UINT16)SelectorSize;

      //
      // Currently only support the port range from 0~0xffff. Don't support other
      // port range.
      // TODO: support Port range
      //
      if (ChildSa->SessionCommon.IsInitiator) {
        if (ChildSa->Spd->Selector->LocalPort != 0 &&
            ChildSa->Spd->Selector->LocalPortRange == 0) {
          //
          // For not port range.
          //
          TsSelector->StartPort = ChildSa->Spd->Selector->LocalPort;
          TsSelector->EndPort   = ChildSa->Spd->Selector->LocalPort;
        } else if (ChildSa->Spd->Selector->LocalPort == 0){
          //
          // For port from 0~0xffff
          //
          TsSelector->StartPort = 0;
          TsSelector->EndPort   = IKEV2_TS_ANY_PORT;
        } else {
          //
          // Not support now.
          //
          goto ON_ERROR;
        }
      } else {
        if (ChildSa->Spd->Selector->RemotePort != 0 &&
            ChildSa->Spd->Selector->RemotePortRange == 0) {
          //
          // For not port range.
          //
          TsSelector->StartPort = ChildSa->Spd->Selector->RemotePort;
          TsSelector->EndPort   = ChildSa->Spd->Selector->RemotePort;
        } else if (ChildSa->Spd->Selector->RemotePort == 0) {
          //
          // For port from 0~0xffff
          //
          TsSelector->StartPort = 0;
          TsSelector->EndPort   = IKEV2_TS_ANY_PORT;
        } else {
          //
          // Not support now.
          //
          goto ON_ERROR;
        }
      }
      //
      // Copy Address.Currently the address range is not supported.
      // The Starting address is same as Ending address
      // TODO: Support Address Range.
      //
      CopyMem (
        (UINT8*)TsSelector + sizeof(TRAFFIC_SELECTOR),
        ChildSa->SessionCommon.IsInitiator ?
        ChildSa->Spd->Selector->LocalAddress :
        ChildSa->Spd->Selector->RemoteAddress,
        AddrSize
        );
      CopyMem (
        (UINT8*)TsSelector + sizeof(TRAFFIC_SELECTOR) + AddrSize,
        ChildSa->SessionCommon.IsInitiator ?
        ChildSa->Spd->Selector->LocalAddress :
        ChildSa->Spd->Selector->RemoteAddress,
        AddrSize
        );
      //
      // If the Next Payload is not TS responder, this TS payload type is the TS responder.
      //
      TsPayload->PayloadType             = IKEV2_PAYLOAD_TYPE_TS_INIT;
    }else{
        //
        // Create responder Traffic Selector
        //
        TsSelector->SelecorLen   = (UINT16)SelectorSize;

        //
        // Currently only support the port range from 0~0xffff. Don't support other
        // port range.
        // TODO: support Port range
        //
        if (!ChildSa->SessionCommon.IsInitiator) {
          if (ChildSa->Spd->Selector->LocalPort != 0 &&
              ChildSa->Spd->Selector->LocalPortRange == 0) {
            //
            // For not port range.
            //
            TsSelector->StartPort = ChildSa->Spd->Selector->LocalPort;
            TsSelector->EndPort   = ChildSa->Spd->Selector->LocalPort;
          } else if (ChildSa->Spd->Selector->LocalPort == 0){
            //
            // For port from 0~0xffff
            //
            TsSelector->StartPort = 0;
            TsSelector->EndPort   = IKEV2_TS_ANY_PORT;
          } else {
            //
            // Not support now.
            //
            goto ON_ERROR;
          }
        } else {
          if (ChildSa->Spd->Selector->RemotePort != 0 &&
              ChildSa->Spd->Selector->RemotePortRange == 0) {
            //
            // For not port range.
            //
            TsSelector->StartPort = ChildSa->Spd->Selector->RemotePort;
            TsSelector->EndPort   = ChildSa->Spd->Selector->RemotePort;
          } else if (ChildSa->Spd->Selector->RemotePort == 0){
            //
            // For port from 0~0xffff
            //
            TsSelector->StartPort = 0;
            TsSelector->EndPort   = IKEV2_TS_ANY_PORT;
          } else {
            //
            // Not support now.
            //
            goto ON_ERROR;
          }
        }
        //
        // Copy Address.Currently the address range is not supported.
        // The Starting address is same as Ending address
        // TODO: Support Address Range.
        //
        CopyMem (
          (UINT8*)TsSelector + sizeof(TRAFFIC_SELECTOR),
          ChildSa->SessionCommon.IsInitiator ?
          ChildSa->Spd->Selector->RemoteAddress :
          ChildSa->Spd->Selector->LocalAddress,
          AddrSize
          );
        CopyMem (
          (UINT8*)TsSelector + sizeof(TRAFFIC_SELECTOR) + AddrSize,
          ChildSa->SessionCommon.IsInitiator ?
          ChildSa->Spd->Selector->RemoteAddress :
          ChildSa->Spd->Selector->LocalAddress,
          AddrSize
          );
        //
        // If the Next Payload is not TS responder, this TS payload type is the TS responder.
        //
        TsPayload->PayloadType          = IKEV2_PAYLOAD_TYPE_TS_RSP;
      }
    }

    if (ChildSa->Spd->Selector->NextLayerProtocol != 0xffff) {
      TsSelector->IpProtocolId = (UINT8)ChildSa->Spd->Selector->NextLayerProtocol;
    } else {
      TsSelector->IpProtocolId = IKEV2_TS_ANY_PROTOCOL;
    }

  TsPayloadBuf->Header.NextPayload    = NextPayload;
  TsPayloadBuf->Header.PayloadLength  = (UINT16)TsPayloadSize;
  TsPayloadBuf->TSNumbers             = 1;
  TsPayload->PayloadSize              = TsPayloadSize;
  goto ON_EXIT;

ON_ERROR:
  if (TsPayload != NULL) {
    IkePayloadFree (TsPayload);
    TsPayload = NULL;
  }
ON_EXIT:
  return TsPayload;
}

/**
  Generate the Notify payload.

  Since the structure of Notify payload which defined in RFC 4306 is simple, so
  there is no internal data structure for Notify payload. This function generate
  Notify payload defined in RFC 4306, but all the fields in this payload are still
  in host order and need call Ikev2EncodePayload() to convert those fields from
  the host order to network order beforing sending it.

  @param[in]  ProtocolId        The protocol type ID. For IKE_SA it MUST be one (1).
                                For IPsec SAs it MUST be neither (2) for AH or (3)
                                for ESP.
  @param[in]  NextPayload       The next paylaod type in NextPayload field of
                                the Notify payload.
  @param[in]  SpiSize           Size of the SPI in SPI size field of the Notify Payload.
  @param[in]  MessageType       The message type in NotifyMessageType field of the
                                Notify Payload.
  @param[in]  SpiBuf            Pointer to buffer contains the SPI value.
  @param[in]  NotifyData        Pointer to buffer contains the notification data.
  @param[in]  NotifyDataSize    The size of NotifyData in bytes.


  @retval Pointer to IKE Notify Payload.

**/
IKE_PAYLOAD *
Ikev2GenerateNotifyPayload (
  IN UINT8            ProtocolId,
  IN UINT8            NextPayload,
  IN UINT8            SpiSize,
  IN UINT16           MessageType,
  IN UINT8            *SpiBuf,
  IN UINT8            *NotifyData,
  IN UINTN            NotifyDataSize
  )
{
  IKE_PAYLOAD         *NotifyPayload;
  IKEV2_NOTIFY        *Notify;
  UINT16              NotifyPayloadLen;
  UINT8               *MessageData;

  //                       1                   2                   3
  //  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
  //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  //  ! Next Payload  !C!  RESERVED   !         Payload Length        !
  //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  //  !  Protocol ID  !   SPI Size    !      Notify Message Type      !
  //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  //  !                                                               !
  //  ~                Security Parameter Index (SPI)                 ~
  //  !                                                               !
  //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  //  !                                                               !
  //  ~                       Notification Data                       ~
  //  !                                                               !
  //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  //
  //
  NotifyPayloadLen  = (UINT16) (sizeof (IKEV2_NOTIFY) + NotifyDataSize + SpiSize);
  Notify            = (IKEV2_NOTIFY *) AllocateZeroPool (NotifyPayloadLen);
  if (Notify == NULL) {
    return NULL;
  }

  //
  // Set Delete Payload's Generic Header
  //
  Notify->Header.NextPayload    = NextPayload;
  Notify->Header.PayloadLength  = NotifyPayloadLen;
  Notify->SpiSize               = SpiSize;
  Notify->ProtocolId            = ProtocolId;
  Notify->MessageType           = MessageType;

  //
  // Copy Spi , for Cookie Notify, there is no SPI.
  //
  if (SpiBuf != NULL && SpiSize != 0 ) {
    CopyMem (Notify + 1, SpiBuf, SpiSize);
  }

  MessageData = ((UINT8 *) (Notify + 1)) + SpiSize;

  //
  // Copy Notification Data
  //
  if (NotifyDataSize != 0) {
    CopyMem (MessageData, NotifyData, NotifyDataSize);
  }

  //
  // Create Payload for and set type as IKEV2_PAYLOAD_TYPE_NOTIFY
  //
  NotifyPayload = IkePayloadAlloc ();
  if (NotifyPayload == NULL) {
    FreePool (Notify);
    return NULL;
  }
  
  NotifyPayload->PayloadType  = IKEV2_PAYLOAD_TYPE_NOTIFY;
  NotifyPayload->PayloadBuf   = (UINT8 *) Notify;
  NotifyPayload->PayloadSize  = NotifyPayloadLen;
  return NotifyPayload;
}

/**
  Generate the Delete payload.

  Since the structure of Delete payload which defined in RFC 4306 is simple,
  there is no internal data structure for Delete payload. This function generate
  Delete payload defined in RFC 4306, but all the fields in this payload are still
  in host order and need call Ikev2EncodePayload() to convert those fields from
  the host order to network order beforing sending it.

  @param[in]  IkeSaSession      Pointer to IKE SA Session to be used of Delete payload generation.
  @param[in]  NextPayload       The next paylaod type in NextPayload field of
                                the Delete payload.
  @param[in]  SpiSize           Size of the SPI in SPI size field of the Delete Payload.
  @param[in]  SpiNum            Number of SPI in NumofSPIs field of the Delete Payload.
  @param[in]  SpiBuf            Pointer to buffer contains the SPI value.

  @retval a Pointer of IKE Delete Payload.

**/
IKE_PAYLOAD *
Ikev2GenerateDeletePayload (
  IN IKEV2_SA_SESSION  *IkeSaSession,
  IN UINT8             NextPayload,
  IN UINT8             SpiSize,
  IN UINT16            SpiNum,
  IN UINT8             *SpiBuf

  )
{
  IKE_PAYLOAD  *DelPayload;
  IKEV2_DELETE *Del;
  UINT16       SpiBufSize;
  UINT16       DelPayloadLen;

  //                         1                   2                   3
  //   0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
  //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  //  ! Next Payload  !C!  RESERVED   !         Payload Length        !
  //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  //  ! Protocol ID   !   SPI Size    !           # of SPIs           !
  //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  //  !                                                               !
  //  ~               Security Parameter Index(es) (SPI)              ~
  //  !                                                               !
  //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  //
  SpiBufSize    = (UINT16) (SpiSize * SpiNum);
  if (SpiBufSize != 0 && SpiBuf == NULL) {
    return NULL;
  }

  DelPayloadLen = (UINT16) (sizeof (IKEV2_DELETE) + SpiBufSize);

  Del           = AllocateZeroPool (DelPayloadLen);
  if (Del == NULL) {
    return NULL;
  }

  //
  // Set Delete Payload's Generic Header
  //
  Del->Header.NextPayload   = NextPayload;
  Del->Header.PayloadLength = DelPayloadLen;
  Del->NumSpis              = SpiNum;
  Del->SpiSize              = SpiSize;

  if (SpiSize == 4) {
    //
    // TODO: should consider the AH if needs to support.
    //
    Del->ProtocolId = IPSEC_PROTO_IPSEC_ESP;
  } else {
    Del->ProtocolId = IPSEC_PROTO_ISAKMP;
  }

  //
  // Set Del Payload's Idntification Data
  //
  CopyMem (Del + 1, SpiBuf, SpiBufSize);
  DelPayload = IkePayloadAlloc ();
  if (DelPayload == NULL) {
    FreePool (Del);
    return NULL;
  }
  
  DelPayload->PayloadType = IKEV2_PAYLOAD_TYPE_DELETE;
  DelPayload->PayloadBuf  = (UINT8 *) Del;
  DelPayload->PayloadSize = DelPayloadLen;
  return DelPayload;
}

/**
  Generate the Configuration payload.

  This function generate configuration payload defined in RFC 4306, but all the
  fields in this payload are still in host order and need call Ikev2EncodePayload()
  to convert those fields from the host order to network order beforing sending it.

  @param[in]  IkeSaSession      Pointer to IKE SA Session to be used for Delete payload
                                generation.
  @param[in]  NextPayload       The next paylaod type in NextPayload field of
                                the Delete payload.
  @param[in]  CfgType           The attribute type in the Configuration attribute.

  @retval Pointer to IKE CP Payload.

**/
IKE_PAYLOAD *
Ikev2GenerateCpPayload (
  IN IKEV2_SA_SESSION  *IkeSaSession,
  IN UINT8             NextPayload,
  IN UINT8             CfgType
  )
{
  IKE_PAYLOAD           *CpPayload;
  IKEV2_CFG             *Cfg;
  UINT16                PayloadLen;
  IKEV2_CFG_ATTRIBUTES  *CfgAttributes;

  //
  //     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  //    ! Next Payload  !C! RESERVED    !         Payload Length        !
  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  //    !   CFG Type    !                    RESERVED                   !
  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  //    !                                                               !
  //    ~                   Configuration Attributes                    ~
  //    !                                                               !
  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  //

  PayloadLen = (UINT16) (sizeof (IKEV2_CFG) + sizeof (IKEV2_CFG_ATTRIBUTES));
  Cfg        = (IKEV2_CFG *) AllocateZeroPool (PayloadLen);

  if (Cfg == NULL) {
    return NULL;
  }

  CfgAttributes = (IKEV2_CFG_ATTRIBUTES *)((UINT8 *)Cfg + sizeof (IKEV2_CFG));

  //
  // Only generate the configuration payload with an empty INTERNAL_IP4_ADDRESS
  // or INTERNAL_IP6_ADDRESS.
  //

  Cfg->Header.NextPayload   = NextPayload;
  Cfg->Header.PayloadLength = PayloadLen;
  Cfg->CfgType              = IKEV2_CFG_TYPE_REQUEST;

  CfgAttributes->AttritType  = CfgType;
  CfgAttributes->ValueLength = 0;

  CpPayload = IkePayloadAlloc ();
  if (CpPayload == NULL) {
    if (Cfg != NULL) {
      FreePool (Cfg);
    }
    return NULL;
  }

  CpPayload->PayloadType = IKEV2_PAYLOAD_TYPE_CP;
  CpPayload->PayloadBuf  = (UINT8 *) Cfg;
  CpPayload->PayloadSize = PayloadLen;
  return CpPayload;
}

/**
  Parser the Notify Cookie payload.

  This function parses the Notify Cookie payload.If the Notify ProtocolId is not
  IPSEC_PROTO_ISAKMP or if the SpiSize is not zero or if the MessageType is not
  the COOKIE, return EFI_INVALID_PARAMETER.

  @param[in]      IkeNCookie    Pointer to the IKE_PAYLOAD which contians the
                                Notify Cookie payload.
                                the Notify payload.
  @param[in, out] IkeSaSession  Pointer to the relevant IKE SA Session.

  @retval EFI_SUCCESS           The Notify Cookie Payload is valid.
  @retval EFI_INVALID_PARAMETER The Notify Cookie Payload is invalid.
  @retval EFI_OUT_OF_RESOURCE   The required resource can't be allocated.

**/
EFI_STATUS
Ikev2ParserNotifyCookiePayload (
  IN     IKE_PAYLOAD      *IkeNCookie,
  IN OUT IKEV2_SA_SESSION *IkeSaSession
  )
{
  IKEV2_NOTIFY      *NotifyPayload;
  UINTN             NotifyDataSize;

  NotifyPayload = (IKEV2_NOTIFY *)IkeNCookie->PayloadBuf;

  if ((NotifyPayload->ProtocolId != IPSEC_PROTO_ISAKMP) ||
      (NotifyPayload->SpiSize != 0) ||
      (NotifyPayload->MessageType != IKEV2_NOTIFICATION_COOKIE)
      ) {
    return EFI_INVALID_PARAMETER;
  }

  NotifyDataSize        = NotifyPayload->Header.PayloadLength - sizeof (IKEV2_NOTIFY);
  IkeSaSession->NCookie = AllocateZeroPool (NotifyDataSize);
  if (IkeSaSession->NCookie == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }

  IkeSaSession->NCookieSize = NotifyDataSize;

  CopyMem (
    IkeSaSession->NCookie,
    (UINT8 *)NotifyPayload + sizeof (IKEV2_NOTIFY),
    NotifyDataSize
    );

  return EFI_SUCCESS;
}


/**
  Generate the Certificate payload or Certificate Request Payload.

  Since the Certificate Payload structure is same with Certificate Request Payload,
  the only difference is that one contains the Certificate Data, other contains
  the acceptable certificateion CA. This function generate Certificate payload
  or Certificate Request Payload defined in RFC 4306, but all the fields
  in the payload are still in host order and need call Ikev2EncodePayload()
  to convert those fields from the host order to network order beforing sending it.

  @param[in]  IkeSaSession      Pointer to IKE SA Session to be used of Delete payload
                                generation.
  @param[in]  NextPayload       The next paylaod type in NextPayload field of
                                the Delete payload.
  @param[in]  Certificate       Pointer of buffer contains the certification data.
  @param[in]  CertificateLen    The length of Certificate in byte.
  @param[in]  EncodeType        Specified the Certificate Encodeing which is defined
                                in RFC 4306.
  @param[in]  IsRequest         To indicate create Certificate Payload or Certificate
                                Request Payload. If it is TURE, create Certificate
                                Request Payload. Otherwise, create Certificate Payload.

  @retval  a Pointer to IKE Payload whose payload buffer containing the Certificate
           payload or Certificated Request payload.

**/
IKE_PAYLOAD *
Ikev2GenerateCertificatePayload (
  IN IKEV2_SA_SESSION  *IkeSaSession,
  IN UINT8             NextPayload,
  IN UINT8             *Certificate,
  IN UINTN             CertificateLen,
  IN UINT8             EncodeType,
  IN BOOLEAN           IsRequest
  )
{
  IKE_PAYLOAD           *CertPayload;
  IKEV2_CERT            *Cert;
  UINT16                PayloadLen;
  UINT8                 *PublicKey;
  UINTN                 PublicKeyLen;
  HASH_DATA_FRAGMENT    Fragment[1];
  UINT8                 *HashData;
  UINTN                 HashDataSize;
  EFI_STATUS            Status;

  //
  //                         1                   2                   3
  //     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  //    ! Next Payload  !C!  RESERVED   !         Payload Length        !
  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  //    ! Cert Encoding !                                               !
  //    +-+-+-+-+-+-+-+-+                                               !
  //    ~                       Certificate Data/Authority              ~
  //    !                                                               !
  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  //

  Status       = EFI_SUCCESS;
  PublicKey    = NULL;
  PublicKeyLen = 0;

  if (!IsRequest) {
    PayloadLen = (UINT16) (sizeof (IKEV2_CERT) + CertificateLen);
  } else {
    //
    // SHA1 Hash length is 20.
    //
    PayloadLen = (UINT16) (sizeof (IKEV2_CERT) + 20);
  }

  Cert = AllocateZeroPool (PayloadLen);
  if (Cert == NULL) {
    return NULL;
  }

  //
  // Generate Certificate Payload or Certificate Request Payload.
  //
  Cert->Header.NextPayload   = NextPayload;
  Cert->Header.PayloadLength = PayloadLen;
  Cert->CertEncoding         = EncodeType;
  if (!IsRequest) {
    CopyMem (
      ((UINT8 *)Cert) + sizeof (IKEV2_CERT),
      Certificate,
      CertificateLen
      );
  } else {
    Status = IpSecCryptoIoGetPublicKeyFromCert (
               Certificate,
               CertificateLen,
               &PublicKey,
               &PublicKeyLen
               );
    if (EFI_ERROR (Status)) {
      goto ON_EXIT;
    }

    Fragment[0].Data     = PublicKey;
    Fragment[0].DataSize = PublicKeyLen;
    HashDataSize      = IpSecGetHmacDigestLength (IKE_AALG_SHA1HMAC);
    HashData          = AllocateZeroPool (HashDataSize);
    if (HashData == NULL) {
      goto ON_EXIT;
    }

    Status = IpSecCryptoIoHash (
               IKE_AALG_SHA1HMAC,
               Fragment,
               1,
               HashData,
               HashDataSize
               );
    if (EFI_ERROR (Status)) {
      goto ON_EXIT;
    }

    CopyMem (
      ((UINT8 *)Cert) + sizeof (IKEV2_CERT),
      HashData,
      HashDataSize
      );
  }

  CertPayload = IkePayloadAlloc ();
  if (CertPayload == NULL) {
    goto ON_EXIT;
  }

  if (!IsRequest) {
    CertPayload->PayloadType = IKEV2_PAYLOAD_TYPE_CERT;
  } else {
    CertPayload->PayloadType = IKEV2_PAYLOAD_TYPE_CERTREQ;
  }

  CertPayload->PayloadBuf  = (UINT8 *) Cert;
  CertPayload->PayloadSize = PayloadLen;
  return CertPayload;

ON_EXIT:
  if (Cert != NULL) {
    FreePool (Cert);
  }
  if (PublicKey != NULL) {
    FreePool (PublicKey);
  }
  return NULL;
}

/**
  Remove and free all IkePayloads in the specified IkePacket.

  @param[in] IkePacket   The pointer of IKE_PACKET.

**/
VOID
ClearAllPayloads (
  IN IKE_PACKET     *IkePacket
  )
{
  LIST_ENTRY      *PayloadEntry;
  IKE_PAYLOAD     *IkePayload;
  //
  // remove all payloads from list and free each payload.
  //
  while (!IsListEmpty (&IkePacket->PayloadList)) {
    PayloadEntry  = IkePacket->PayloadList.ForwardLink;
    IkePayload    = IKE_PAYLOAD_BY_PACKET (PayloadEntry);
    IKE_PACKET_REMOVE_PAYLOAD (IkePacket, IkePayload);
    IkePayloadFree (IkePayload);
  }
}

/**
  Transfer the intrnal data structure IKEV2_SA_DATA to IKEV2_SA structure defined in RFC.

  @param[in] SessionCommon Pointer to IKEV2_SESSION_COMMON related to the SA Session.
  @param[in] SaData        Pointer to IKEV2_SA_DATA to be transfered.

  @retval  return the pointer of IKEV2_SA.

**/
IKEV2_SA*
Ikev2EncodeSa (
  IN IKEV2_SESSION_COMMON *SessionCommon,
  IN IKEV2_SA_DATA        *SaData
  )
{
  IKEV2_SA              *Sa;
  UINTN                 SaSize;
  IKEV2_PROPOSAL_DATA   *ProposalData;
  IKEV2_TRANSFORM_DATA  *TransformData;
  UINTN                 TotalTransforms;
  UINTN                 SaAttrsSize;
  UINTN                 TransformsSize;
  UINTN                 TransformSize;
  UINTN                 ProposalsSize;
  UINTN                 ProposalSize;
  UINTN                 ProposalIndex;
  UINTN                 TransformIndex;
  IKE_SA_ATTRIBUTE      *SaAttribute;
  IKEV2_PROPOSAL        *Proposal;
  IKEV2_TRANSFORM       *Transform;

  //
  // Transform IKE_SA_DATA structure to IKE_SA Payload.
  // Header length is host order.
  // The returned IKE_SA struct should be freed by caller.
  //
  TotalTransforms = 0;
  //
  // Calculate the Proposal numbers and Transform numbers.
  //
  for (ProposalIndex = 0; ProposalIndex < SaData->NumProposals; ProposalIndex++) {

    ProposalData     = (IKEV2_PROPOSAL_DATA *) (SaData + 1) + ProposalIndex;
    TotalTransforms += ProposalData->NumTransforms;

  }
  SaSize = sizeof (IKEV2_SA) +
           SaData->NumProposals * sizeof (IKEV2_PROPOSAL) +
           TotalTransforms * (sizeof (IKEV2_TRANSFORM) + MAX_SA_ATTRS_SIZE);
  //
  // Allocate buffer for IKE_SA.
  //
  Sa = AllocateZeroPool (SaSize);
  if (Sa == NULL) {
    return NULL;
  }
  
  CopyMem (Sa, SaData, sizeof (IKEV2_SA));
  Sa->Header.PayloadLength  = (UINT16) sizeof (IKEV2_SA);
  ProposalsSize             = 0;
  Proposal                  = (IKEV2_PROPOSAL *) (Sa + 1);

  //
  // Set IKE_PROPOSAL
  //
  ProposalData  = (IKEV2_PROPOSAL_DATA *) (SaData + 1);
  for (ProposalIndex = 0; ProposalIndex < SaData->NumProposals; ProposalIndex++) {
    Proposal->ProposalIndex   = ProposalData->ProposalIndex;
    Proposal->ProtocolId      = ProposalData->ProtocolId;
    Proposal->NumTransforms   = ProposalData->NumTransforms;

    if (ProposalData->Spi == 0) {
      Proposal->SpiSize = 0;
    } else {
      Proposal->SpiSize           = 4;
      *(UINT32 *) (Proposal + 1)  = HTONL (*((UINT32*)ProposalData->Spi));
    }

    TransformsSize  = 0;
    Transform       = (IKEV2_TRANSFORM *) ((UINT8 *) (Proposal + 1) + Proposal->SpiSize);

    //
    // Set IKE_TRANSFORM
    //
    for (TransformIndex = 0; TransformIndex < ProposalData->NumTransforms; TransformIndex++) {
      TransformData               = (IKEV2_TRANSFORM_DATA *) (ProposalData + 1) + TransformIndex;
      Transform->TransformType    = TransformData->TransformType;
      Transform->TransformId      = HTONS (TransformData->TransformId);
      SaAttrsSize                 = 0;

      //
      // If the Encryption Algorithm is variable key length set the key length in attribute.
      // Note that only a single attribute type (Key Length) is defined and it is fixed length.
      //
      if (Transform->TransformType == IKEV2_TRANSFORM_TYPE_ENCR && TransformData->Attribute.Attr.AttrValue != 0) {
        SaAttribute                 = (IKE_SA_ATTRIBUTE *) (Transform + 1);
        SaAttribute->AttrType       = HTONS (IKEV2_ATTRIBUTE_TYPE_KEYLEN | SA_ATTR_FORMAT_BIT);
        SaAttribute->Attr.AttrValue = HTONS (TransformData->Attribute.Attr.AttrValue);
        SaAttrsSize                 = sizeof (IKE_SA_ATTRIBUTE);
      }

      //
      // If the Integrity Algorithm is variable key length set the key length in attribute.
      //
      if (Transform->TransformType == IKEV2_TRANSFORM_TYPE_INTEG && TransformData->Attribute.Attr.AttrValue != 0) {
        SaAttribute                 = (IKE_SA_ATTRIBUTE *) (Transform + 1);
        SaAttribute->AttrType       = HTONS (IKEV2_ATTRIBUTE_TYPE_KEYLEN | SA_ATTR_FORMAT_BIT);
        SaAttribute->Attr.AttrValue = HTONS (TransformData->Attribute.Attr.AttrValue);
        SaAttrsSize                 = sizeof (IKE_SA_ATTRIBUTE);
      }

      TransformSize                 = sizeof (IKEV2_TRANSFORM) + SaAttrsSize;
      TransformsSize               += TransformSize;

      Transform->Header.NextPayload   = IKE_TRANSFORM_NEXT_PAYLOAD_MORE;
      Transform->Header.PayloadLength = HTONS ((UINT16)TransformSize);

      if (TransformIndex == (UINTN)(ProposalData->NumTransforms - 1)) {
        Transform->Header.NextPayload = IKE_TRANSFORM_NEXT_PAYLOAD_NONE;
      }

      Transform     = (IKEV2_TRANSFORM *)((UINT8 *) Transform + TransformSize);
    }

    //
    // Set Proposal's Generic Header.
    //
    ProposalSize                   = sizeof (IKEV2_PROPOSAL) + Proposal->SpiSize + TransformsSize;
    ProposalsSize                 += ProposalSize;
    Proposal->Header.NextPayload   = IKE_PROPOSAL_NEXT_PAYLOAD_MORE;
    Proposal->Header.PayloadLength = HTONS ((UINT16)ProposalSize);

    if (ProposalIndex == (UINTN)(SaData->NumProposals - 1)) {
      Proposal->Header.NextPayload = IKE_PROPOSAL_NEXT_PAYLOAD_NONE;
    }

    //
    // Point to next Proposal Payload
    //
    Proposal     = (IKEV2_PROPOSAL *) ((UINT8 *) Proposal + ProposalSize);
    ProposalData = (IKEV2_PROPOSAL_DATA *)(((UINT8 *)ProposalData) + sizeof (IKEV2_PROPOSAL_DATA) + (TransformIndex * sizeof (IKEV2_TRANSFORM_DATA)));
  }
  //
  // Set SA's Generic Header.
  //
  Sa->Header.PayloadLength = (UINT16) (Sa->Header.PayloadLength + ProposalsSize);
  return Sa;
}

/**
  Decode SA payload.

  This function converts the received SA payload to internal data structure.

  @param[in]  SessionCommon       Pointer to IKE Common Session used to decode the SA
                                  Payload.
  @param[in]  Sa                  Pointer to SA Payload

  @return a Pointer to internal data structure for SA payload.

**/
IKEV2_SA_DATA *
Ikev2DecodeSa (
  IN IKEV2_SESSION_COMMON *SessionCommon,
  IN IKEV2_SA             *Sa
  )
{
  IKEV2_SA_DATA         *SaData;
  EFI_STATUS            Status;
  IKEV2_PROPOSAL        *Proposal;
  IKEV2_TRANSFORM       *Transform;
  UINTN                 TotalProposals;
  UINTN                 TotalTransforms;
  UINTN                 ProposalNextPayloadSum;
  UINTN                 ProposalIndex;
  UINTN                 TransformIndex;
  UINTN                 SaRemaining;
  UINT16                ProposalSize;
  UINTN                 ProposalRemaining;
  UINT16                TransformSize;
  UINTN                 SaAttrRemaining;
  IKE_SA_ATTRIBUTE      *SaAttribute;
  IKEV2_PROPOSAL_DATA   *ProposalData;
  IKEV2_TRANSFORM_DATA  *TransformData;
  UINT8                 *Spi;

  //
  // Transfrom from IKE_SA payload to IKE_SA_DATA structure.
  // Header length NTOH is already done
  // The returned IKE_SA_DATA should be freed by caller
  //
  SaData    = NULL;
  Status    = EFI_SUCCESS;

  //
  // First round sanity check and size calculae
  //
  TotalProposals         = 0;
  TotalTransforms        = 0;
  ProposalNextPayloadSum = 0;
  SaRemaining            = Sa->Header.PayloadLength - sizeof (IKEV2_SA);// Point to current position in SA
  Proposal               = (IKEV2_PROPOSAL *)((IKEV2_SA *)(Sa)+1);

  //
  // Calculate the number of Proposal payload and the total numbers of
  // Transforms payload (the transforms in all proposal payload).
  //
  while (SaRemaining > sizeof (IKEV2_PROPOSAL)) {
    ProposalSize = NTOHS (Proposal->Header.PayloadLength);
    if (SaRemaining < ProposalSize) {
      Status = EFI_INVALID_PARAMETER;
      goto Exit;
    }

    if (Proposal->SpiSize != 0 && Proposal->SpiSize != 4) {
      Status = EFI_INVALID_PARAMETER;
      goto Exit;
    }

    TotalProposals++;
    TotalTransforms        += Proposal->NumTransforms;
    SaRemaining            -= ProposalSize;
    ProposalNextPayloadSum += Proposal->Header.NextPayload;
    Proposal                = IKEV2_NEXT_PROPOSAL_WITH_SIZE (Proposal, ProposalSize);
  }

  //
  // Check the proposal number.
  // The proposal Substructure, the NextPayLoad field indicates : 0 (last) or 2 (more)
  // which Specifies whether this is the last Proposal Substructure in the SA.
  // Here suming all Proposal NextPayLoad field to check the proposal number is correct
  // or not.
  //
  if (TotalProposals == 0 ||
      (TotalProposals - 1) * IKE_PROPOSAL_NEXT_PAYLOAD_MORE != ProposalNextPayloadSum
      ) {
    Status = EFI_INVALID_PARAMETER;
    goto Exit;
  }

  //
  // Second round sanity check and decode. Transform the SA payload into
  // a IKE_SA_DATA structure.
  //
  SaData = (IKEV2_SA_DATA *) AllocateZeroPool (
                               sizeof (IKEV2_SA_DATA) +
                               TotalProposals * sizeof (IKEV2_PROPOSAL_DATA) +
                               TotalTransforms * sizeof (IKEV2_TRANSFORM_DATA)
                               );
  if (SaData == NULL) {
    Status = EFI_OUT_OF_RESOURCES;
    goto Exit;
  }
  
  CopyMem (SaData, Sa, sizeof (IKEV2_SA));
  SaData->NumProposals        = TotalProposals;
  ProposalData                = (IKEV2_PROPOSAL_DATA *) (SaData + 1);

  //
  // Proposal Payload
  //    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
  //   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  //   ! Next Payload  !   RESERVED    !         Payload Length        !
  //   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  //   !  Proposal #   !  Protocol-Id  !    SPI Size   !# of Transforms!
  //   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  //   !                        SPI (variable)                         !
  //   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  //
  for (ProposalIndex = 0, Proposal = IKEV2_SA_FIRST_PROPOSAL (Sa);
       ProposalIndex < TotalProposals;
       ProposalIndex++
       ) {

    //
    // TODO: check ProposalId
    //
    ProposalData->ProposalIndex   = Proposal->ProposalIndex;
    ProposalData->ProtocolId      = Proposal->ProtocolId;
    if (Proposal->SpiSize == 0) {
      ProposalData->Spi = 0;
    } else {
      //
      // SpiSize == 4
      //
      Spi = AllocateZeroPool (Proposal->SpiSize);
      if (Spi == NULL) {
        Status = EFI_OUT_OF_RESOURCES;
        goto Exit;
      }
      
      CopyMem (Spi, (UINT32 *) (Proposal + 1), Proposal->SpiSize);
      *((UINT32*) Spi) = NTOHL (*((UINT32*) Spi));
      ProposalData->Spi = Spi;
    }

    ProposalData->NumTransforms = Proposal->NumTransforms;
    ProposalSize                = NTOHS (Proposal->Header.PayloadLength);
    ProposalRemaining           = ProposalSize;
    //
    // Transform Payload
    //   0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
    //   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    //   ! Next Payload  !   RESERVED    !         Payload Length        !
    //   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    //   !Transform Type !   RESERVED    !         Transform ID          !
    //   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    //   !                                                               !
    //   ~                        SA Attributes                          ~
    //   !                                                               !
    //   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    //
    Transform = IKEV2_PROPOSAL_FIRST_TRANSFORM (Proposal);
    for (TransformIndex = 0; TransformIndex < Proposal->NumTransforms; TransformIndex++) {

      //
      // Transfer the IKEV2_TRANSFORM structure into internal IKEV2_TRANSFORM_DATA struture.
      //
      TransformData                   = (IKEV2_TRANSFORM_DATA *) (ProposalData + 1) + TransformIndex;
      TransformData->TransformId      = NTOHS (Transform->TransformId);
      TransformData->TransformType    = Transform->TransformType;
      TransformSize                   = NTOHS (Transform->Header.PayloadLength);
      //
      // Check the Proposal Data is correct.
      //
      if (ProposalRemaining < TransformSize) {
        Status = EFI_INVALID_PARAMETER;
        goto Exit;
      }

      //
      // Check if the Transform payload includes Attribution.
      //
      SaAttrRemaining = TransformSize - sizeof (IKEV2_TRANSFORM);

      //
      // According to RFC 4603, currently only the Key length attribute type is
      // supported. For each Transform, there is only one attributeion.
      //
      if (SaAttrRemaining > 0) {
        if (SaAttrRemaining != sizeof (IKE_SA_ATTRIBUTE)) {
          Status = EFI_INVALID_PARAMETER;
          goto Exit;
        }
        SaAttribute                             = (IKE_SA_ATTRIBUTE *) ((IKEV2_TRANSFORM *)(Transform) + 1);
        TransformData->Attribute.AttrType       = (UINT16)((NTOHS (SaAttribute->AttrType))  & ~SA_ATTR_FORMAT_BIT);
        TransformData->Attribute.Attr.AttrValue = NTOHS (SaAttribute->Attr.AttrValue);

        //
        // Currently, only supports the Key Length Attribution.
        //
        if (TransformData->Attribute.AttrType != IKEV2_ATTRIBUTE_TYPE_KEYLEN) {
          Status = EFI_INVALID_PARAMETER;
          goto Exit;
        }
      }

      //
      // Move to next Transform
      //
      Transform = IKEV2_NEXT_TRANSFORM_WITH_SIZE (Transform, TransformSize);
    }
    Proposal     = IKEV2_NEXT_PROPOSAL_WITH_SIZE (Proposal, ProposalSize);
    ProposalData = (IKEV2_PROPOSAL_DATA *) ((UINT8 *)(ProposalData + 1) +
                                                ProposalData->NumTransforms *
                                                sizeof (IKEV2_TRANSFORM_DATA));
  }

Exit:
  if (EFI_ERROR (Status) && SaData != NULL) {
    FreePool (SaData);
    SaData = NULL;
  }
  return SaData;
}

/**
  General interface of payload encoding.

  This function encodes the internal data structure into payload which
  is defined in RFC 4306. The IkePayload->PayloadBuf is used to store both the input
  payload and converted payload. Only the SA payload use the interal structure
  to store the attribute. Other payload use structure which is same with the RFC
  defined, for this kind payloads just do host order to network order change of
  some fields.

  @param[in]      SessionCommon       Pointer to IKE Session Common used to encode the payload.
  @param[in, out] IkePayload          Pointer to IKE payload to be encoded as input, and
                                      store the encoded result as output.

  @retval EFI_INVALID_PARAMETER  Meet error when encoding the SA payload.
  @retval EFI_SUCCESS            Encoded successfully.

**/
EFI_STATUS
Ikev2EncodePayload (
  IN     UINT8               *SessionCommon,
  IN OUT IKE_PAYLOAD         *IkePayload
  )
{
  IKEV2_SA_DATA               *SaData;
  IKEV2_SA                    *SaPayload;
  IKEV2_COMMON_PAYLOAD_HEADER *PayloadHdr;
  IKEV2_NOTIFY                *NotifyPayload;
  IKEV2_DELETE                *DeletePayload;
  IKEV2_KEY_EXCHANGE          *KeyPayload;
  IKEV2_TS                    *TsPayload;
  IKEV2_CFG_ATTRIBUTES        *CfgAttribute;
  UINT8                       *TsBuffer;
  UINT8                       Index;
  TRAFFIC_SELECTOR            *TrafficSelector;

  //
  // Transform the Internal IKE structure to IKE payload.
  // Only the SA payload use the interal structure to store the attribute.
  // Other payload use structure which same with the RFC defined, so there is
  // no need to tranform them to IKE payload.
  //
  switch (IkePayload->PayloadType) {
  case IKEV2_PAYLOAD_TYPE_SA:
    //
    // Transform IKE_SA_DATA to IK_SA payload
    //
    SaData    = (IKEV2_SA_DATA *) IkePayload->PayloadBuf;
    SaPayload = Ikev2EncodeSa ((IKEV2_SESSION_COMMON *) SessionCommon, SaData);

    if (SaPayload == NULL) {
      return EFI_INVALID_PARAMETER;
    }
    if (!IkePayload->IsPayloadBufExt) {
      FreePool (IkePayload->PayloadBuf);
    }
    IkePayload->PayloadBuf      = (UINT8 *) SaPayload;
    IkePayload->IsPayloadBufExt = FALSE;
    break;

  case IKEV2_PAYLOAD_TYPE_NOTIFY:
    NotifyPayload               = (IKEV2_NOTIFY *) IkePayload->PayloadBuf;
    NotifyPayload->MessageType  = HTONS (NotifyPayload->MessageType);
    break;

  case IKEV2_PAYLOAD_TYPE_DELETE:
    DeletePayload           = (IKEV2_DELETE *) IkePayload->PayloadBuf;
    DeletePayload->NumSpis  = HTONS (DeletePayload->NumSpis);
    break;

  case IKEV2_PAYLOAD_TYPE_KE:
    KeyPayload              = (IKEV2_KEY_EXCHANGE *) IkePayload->PayloadBuf;
    KeyPayload->DhGroup     = HTONS (KeyPayload->DhGroup);
    break;

  case IKEV2_PAYLOAD_TYPE_TS_INIT:
  case IKEV2_PAYLOAD_TYPE_TS_RSP:
    TsPayload = (IKEV2_TS *) IkePayload->PayloadBuf;
    TsBuffer  = IkePayload->PayloadBuf + sizeof (IKEV2_TS);

    for (Index = 0; Index < TsPayload->TSNumbers; Index++) {
      TrafficSelector = (TRAFFIC_SELECTOR *) TsBuffer;
      TsBuffer        = TsBuffer + TrafficSelector->SelecorLen;
      //
      // Host order to network order
      //
      TrafficSelector->SelecorLen = HTONS (TrafficSelector->SelecorLen);
      TrafficSelector->StartPort  = HTONS (TrafficSelector->StartPort);
      TrafficSelector->EndPort    = HTONS (TrafficSelector->EndPort);

    }

    break;

  case IKEV2_PAYLOAD_TYPE_CP:
    CfgAttribute = (IKEV2_CFG_ATTRIBUTES *)(((IKEV2_CFG *) IkePayload->PayloadBuf) + 1);
    CfgAttribute->AttritType  = HTONS (CfgAttribute->AttritType);
    CfgAttribute->ValueLength = HTONS (CfgAttribute->ValueLength);

  case IKEV2_PAYLOAD_TYPE_ID_INIT:
  case IKEV2_PAYLOAD_TYPE_ID_RSP:
  case IKEV2_PAYLOAD_TYPE_AUTH:
  default:
    break;
  }

  PayloadHdr  = (IKEV2_COMMON_PAYLOAD_HEADER *) IkePayload->PayloadBuf;
  IkePayload->PayloadSize = PayloadHdr->PayloadLength;
  PayloadHdr->PayloadLength = HTONS (PayloadHdr->PayloadLength);
  IKEV2_DUMP_PAYLOAD (IkePayload);
  return EFI_SUCCESS;
}

/**
  The general interface for decoding Payload.

  This function converts the received Payload into internal structure.

  @param[in]      SessionCommon     Pointer to IKE Session Common used for decoding.
  @param[in, out] IkePayload        Pointer to IKE payload to be decoded as input, and
                                    store the decoded result as output.

  @retval EFI_INVALID_PARAMETER  Meet error when decoding the SA payload.
  @retval EFI_SUCCESS            Decoded successfully.

**/
EFI_STATUS
Ikev2DecodePayload (
  IN     UINT8       *SessionCommon,
  IN OUT IKE_PAYLOAD *IkePayload
  )
{
  IKEV2_COMMON_PAYLOAD_HEADER *PayloadHdr;
  UINT16                      PayloadSize;
  UINT8                       PayloadType;
  IKEV2_SA_DATA               *SaData;
  EFI_STATUS                  Status;
  IKEV2_NOTIFY                *NotifyPayload;
  IKEV2_DELETE                *DeletePayload;
  UINT16                      TsTotalSize;
  TRAFFIC_SELECTOR            *TsSelector;
  IKEV2_TS                    *TsPayload;
  IKEV2_KEY_EXCHANGE          *KeyPayload;
  IKEV2_CFG_ATTRIBUTES        *CfgAttribute;
  UINT8                       Index;

  //
  // Transform the IKE payload to Internal IKE structure.
  // Only the SA payload and Hash Payload use the interal
  // structure to store the attribute. Other payloads use
  // structure which is same with the definitions in RFC,
  // so there is no need to tranform them to internal IKE
  // structure.
  //
  Status      = EFI_SUCCESS;
  PayloadSize = (UINT16) IkePayload->PayloadSize;
  PayloadType = IkePayload->PayloadType;
  PayloadHdr  = (IKEV2_COMMON_PAYLOAD_HEADER *) IkePayload->PayloadBuf;
  //
  // The PayloadSize is the size of whole payload.
  // Replace HTONS operation to assignment statements, since the result is same.
  //
  PayloadHdr->PayloadLength = PayloadSize;

  IKEV2_DUMP_PAYLOAD (IkePayload);
  switch (PayloadType) {
  case IKEV2_PAYLOAD_TYPE_SA:
    if (PayloadSize < sizeof (IKEV2_SA)) {
      Status = EFI_INVALID_PARAMETER;
      goto Exit;
    }

    SaData = Ikev2DecodeSa ((IKEV2_SESSION_COMMON *) SessionCommon, (IKEV2_SA *) PayloadHdr);
    if (SaData == NULL) {
      Status = EFI_INVALID_PARAMETER;
      goto Exit;
    }

    if (!IkePayload->IsPayloadBufExt) {
      FreePool (IkePayload->PayloadBuf);
    }

    IkePayload->PayloadBuf      = (UINT8 *) SaData;
    IkePayload->IsPayloadBufExt = FALSE;
    break;

  case IKEV2_PAYLOAD_TYPE_ID_INIT:
  case IKEV2_PAYLOAD_TYPE_ID_RSP :
    if (PayloadSize < sizeof (IKEV2_ID)) {
      Status = EFI_INVALID_PARAMETER;
      goto Exit;
    }
    break;

  case IKEV2_PAYLOAD_TYPE_NOTIFY:
    if (PayloadSize < sizeof (IKEV2_NOTIFY)) {
      Status = EFI_INVALID_PARAMETER;
      goto Exit;
    }

    NotifyPayload               = (IKEV2_NOTIFY *) PayloadHdr;
    NotifyPayload->MessageType  = NTOHS (NotifyPayload->MessageType);
    break;

  case IKEV2_PAYLOAD_TYPE_DELETE:
    if (PayloadSize < sizeof (IKEV2_DELETE)) {
      Status = EFI_INVALID_PARAMETER;
      goto Exit;
    }

    DeletePayload           = (IKEV2_DELETE *) PayloadHdr;
    DeletePayload->NumSpis  = NTOHS (DeletePayload->NumSpis);
    break;

  case IKEV2_PAYLOAD_TYPE_AUTH:
    if (PayloadSize < sizeof (IKEV2_AUTH)) {
      Status = EFI_INVALID_PARAMETER;
      goto Exit;
    }
    break;

  case IKEV2_PAYLOAD_TYPE_KE:
    KeyPayload              = (IKEV2_KEY_EXCHANGE *) IkePayload->PayloadBuf;
    KeyPayload->DhGroup     = HTONS (KeyPayload->DhGroup);
    break;

  case IKEV2_PAYLOAD_TYPE_TS_INIT:
  case IKEV2_PAYLOAD_TYPE_TS_RSP :
    TsTotalSize = 0;
    if (PayloadSize < sizeof (IKEV2_TS)) {
      Status = EFI_INVALID_PARAMETER;
      goto Exit;
    }
    //
    // Parse each traffic selector and transfer network-order to host-order
    //
    TsPayload   = (IKEV2_TS *) IkePayload->PayloadBuf;
    TsSelector  = (TRAFFIC_SELECTOR *) (IkePayload->PayloadBuf + sizeof (IKEV2_TS));

    for (Index = 0; Index < TsPayload->TSNumbers; Index++) {
      TsSelector->SelecorLen  = NTOHS (TsSelector->SelecorLen);
      TsSelector->StartPort   = NTOHS (TsSelector->StartPort);
      TsSelector->EndPort     = NTOHS (TsSelector->EndPort);

      TsTotalSize             = (UINT16) (TsTotalSize + TsSelector->SelecorLen);
      TsSelector              = (TRAFFIC_SELECTOR *) ((UINT8 *) TsSelector + TsSelector->SelecorLen);
    }
    //
    // Check if the total size of Traffic Selectors is correct.
    //
    if (TsTotalSize != PayloadSize - sizeof(IKEV2_TS)) {
      Status = EFI_INVALID_PARAMETER;
    }

  case IKEV2_PAYLOAD_TYPE_CP:
    CfgAttribute = (IKEV2_CFG_ATTRIBUTES *)(((IKEV2_CFG *) IkePayload->PayloadBuf) + 1);
    CfgAttribute->AttritType  = NTOHS (CfgAttribute->AttritType);
    CfgAttribute->ValueLength = NTOHS (CfgAttribute->ValueLength);

  default:
    break;
  }

 Exit:
  return Status;
}

/**
  Decode the IKE packet.

  This function first decrypts the IKE packet if needed , then separates the whole
  IKE packet from the IkePacket->PayloadBuf into IkePacket payload list.

  @param[in]      SessionCommon          Pointer to IKEV1_SESSION_COMMON containing
                                         some parameter used by IKE packet decoding.
  @param[in, out] IkePacket              The IKE Packet to be decoded on input, and
                                         the decoded result on return.
  @param[in]      IkeType                The type of IKE. IKE_SA_TYPE, IKE_INFO_TYPE and
                                         IKE_CHILD_TYPE are supported.

  @retval         EFI_SUCCESS            The IKE packet is decoded successfully.
  @retval         Otherwise              The IKE packet decoding is failed.

**/
EFI_STATUS
Ikev2DecodePacket (
  IN     IKEV2_SESSION_COMMON  *SessionCommon,
  IN OUT IKE_PACKET            *IkePacket,
  IN     UINTN                 IkeType
  )
{
  EFI_STATUS                  Status;
  IKEV2_COMMON_PAYLOAD_HEADER *PayloadHdr;
  UINT8                       PayloadType;
  UINTN                       RemainBytes;
  UINT16                      PayloadSize;
  IKE_PAYLOAD                 *IkePayload;
  IKE_HEADER                  *IkeHeader;
  IKEV2_SA_SESSION            *IkeSaSession;

  IkeHeader = NULL;

  //
  // Check if the IkePacket need decrypt.
  //
  if (SessionCommon->State >= IkeStateAuth) {
    Status = Ikev2DecryptPacket (SessionCommon, IkePacket, IkeType);
    if (EFI_ERROR (Status)) {
      return Status;
    }
  }

  Status = EFI_SUCCESS;

  //
  // If the IkePacket doesn't contain any payload return invalid parameter.
  //
  if (IkePacket->Header->NextPayload == IKEV2_PAYLOAD_TYPE_NONE) {
    if ((SessionCommon->State >= IkeStateAuth) &&
        (IkePacket->Header->ExchangeType == IKEV2_EXCHANGE_TYPE_INFO)
        ) {
      //
      // If it is Liveness check, there will be no payload load in the encrypt payload.
      //
      Status = EFI_SUCCESS;
    } else {
      Status = EFI_INVALID_PARAMETER;
    }
  }

  //
  // If the PayloadTotalSize < Header length, return invalid parameter.
  //
  RemainBytes = IkePacket->PayloadTotalSize;
  if (RemainBytes < sizeof (IKEV2_COMMON_PAYLOAD_HEADER)) {
    Status = EFI_INVALID_PARAMETER;
    goto Exit;
  }

  //
  // If the packet is first or second message, store whole message in
  // IkeSa->InitiPacket or IkeSa->RespPacket for following Auth Payload
  // calculate.
  //
  if (IkePacket->Header->ExchangeType == IKEV2_EXCHANGE_TYPE_INIT) {
    IkeHeader = AllocateZeroPool (sizeof (IKE_HEADER));
    if (IkeHeader == NULL) {
      Status = EFI_OUT_OF_RESOURCES;
      goto Exit;
    }
    
    CopyMem (IkeHeader, IkePacket->Header, sizeof (IKE_HEADER));

    //
    // Before store the whole packet, roll back the host order to network order,
    // since the header order was changed in the IkePacketFromNetbuf.
    //
    IkeHdrNetToHost (IkeHeader);
    IkeSaSession = IKEV2_SA_SESSION_FROM_COMMON (SessionCommon);
    if (SessionCommon->IsInitiator) {
      IkeSaSession->RespPacket     = AllocateZeroPool (IkePacket->Header->Length);
      if (IkeSaSession->RespPacket == NULL) {
        Status = EFI_OUT_OF_RESOURCES;
        goto Exit;
      }
      IkeSaSession->RespPacketSize = IkePacket->Header->Length;
      CopyMem (IkeSaSession->RespPacket, IkeHeader, sizeof (IKE_HEADER));
      CopyMem (
        IkeSaSession->RespPacket + sizeof (IKE_HEADER),
        IkePacket->PayloadsBuf,
        IkePacket->Header->Length - sizeof (IKE_HEADER)
        );
    } else {
      IkeSaSession->InitPacket     = AllocateZeroPool (IkePacket->Header->Length);
      if (IkeSaSession->InitPacket == NULL) {
        Status = EFI_OUT_OF_RESOURCES;
        goto Exit;
      }
      IkeSaSession->InitPacketSize = IkePacket->Header->Length;
      CopyMem (IkeSaSession->InitPacket, IkeHeader, sizeof (IKE_HEADER));
      CopyMem (
        IkeSaSession->InitPacket + sizeof (IKE_HEADER),
        IkePacket->PayloadsBuf,
        IkePacket->Header->Length - sizeof (IKE_HEADER)
        );
    }
  }

  //
  // Point to the first Payload
  //
  PayloadHdr  = (IKEV2_COMMON_PAYLOAD_HEADER *) IkePacket->PayloadsBuf;
  PayloadType = IkePacket->Header->NextPayload;

  //
  // Parse each payload
  //
  while (RemainBytes >= sizeof (IKEV2_COMMON_PAYLOAD_HEADER)) {
    PayloadSize = NTOHS (PayloadHdr->PayloadLength);

    //
    //Check the size of the payload is correct.
    //
    if (RemainBytes < PayloadSize) {
      Status = EFI_INVALID_PARAMETER;
      goto Exit;
    }

    //
    // At certain states, it should save some datas before decoding.
    //
    if (SessionCommon->BeforeDecodePayload != NULL) {
      SessionCommon->BeforeDecodePayload (
                       (UINT8 *) SessionCommon,
                       (UINT8 *) PayloadHdr,
                       PayloadSize,
                       PayloadType
                       );
    }

    //
    // Initial IkePayload
    //
    IkePayload = IkePayloadAlloc ();
    if (IkePayload == NULL) {
      Status = EFI_OUT_OF_RESOURCES;
      goto Exit;
    }

    IkePayload->PayloadType     = PayloadType;
    IkePayload->PayloadBuf      = (UINT8 *) PayloadHdr;
    IkePayload->PayloadSize     = PayloadSize;
    IkePayload->IsPayloadBufExt = TRUE;

    Status = Ikev2DecodePayload ((UINT8 *) SessionCommon, IkePayload);
    if (EFI_ERROR (Status)) {
      goto Exit;
    }

    IPSEC_DUMP_BUF ("After Decoding Payload", IkePayload->PayloadBuf, IkePayload->PayloadSize);
    //
    // Add each payload into packet
    // Notice, the IkePacket->Hdr->Lenght still recode the whole IkePacket length
    // which is before the decoding.
    //
    IKE_PACKET_APPEND_PAYLOAD (IkePacket, IkePayload);

    RemainBytes -= PayloadSize;
    PayloadType  = PayloadHdr->NextPayload;
    if (PayloadType == IKEV2_PAYLOAD_TYPE_NONE) {
      break;
    }

    PayloadHdr = (IKEV2_COMMON_PAYLOAD_HEADER *) ((UINT8 *) PayloadHdr + PayloadSize);
  }

  if (PayloadType != IKEV2_PAYLOAD_TYPE_NONE) {
    Status = EFI_INVALID_PARAMETER;
    goto Exit;
  }

Exit:
  if (EFI_ERROR (Status)) {
    ClearAllPayloads (IkePacket);
  }

  if (IkeHeader != NULL) {
    FreePool (IkeHeader);
  }
  return Status;
}

/**
  Encode the IKE packet.

  This function puts all Payloads into one payload then encrypt it if needed.

  @param[in]      SessionCommon      Pointer to IKEV2_SESSION_COMMON containing
                                     some parameter used during IKE packet encoding.
  @param[in, out] IkePacket          Pointer to IKE_PACKET to be encoded as input,
                                     and the encoded result as output.
  @param[in]      IkeType            The type of IKE. IKE_SA_TYPE, IKE_INFO_TYPE and
                                     IKE_CHILD_TYPE are supportted.

  @retval         EFI_SUCCESS        Encode IKE packet successfully.
  @retval         Otherwise          Encode IKE packet failed.

**/
EFI_STATUS
Ikev2EncodePacket (
  IN     IKEV2_SESSION_COMMON *SessionCommon,
  IN OUT IKE_PACKET           *IkePacket,
  IN     UINTN                IkeType
  )
{
  IKE_PAYLOAD       *IkePayload;
  UINTN             PayloadTotalSize;
  LIST_ENTRY        *Entry;
  EFI_STATUS        Status;
  IKEV2_SA_SESSION  *IkeSaSession;

  PayloadTotalSize = 0;
  //
  // Encode each payload
  //
  for (Entry = IkePacket->PayloadList.ForwardLink; Entry != &(IkePacket->PayloadList);) {
    IkePayload  = IKE_PAYLOAD_BY_PACKET (Entry);
    Entry       = Entry->ForwardLink;
    Status      = Ikev2EncodePayload ((UINT8 *) SessionCommon, IkePayload);
    if (EFI_ERROR (Status)) {
      return Status;
    }

    if (SessionCommon->AfterEncodePayload != NULL) {
      //
      // For certain states, save some payload for further calculation
      //
      SessionCommon->AfterEncodePayload (
                      (UINT8 *) SessionCommon,
                      IkePayload->PayloadBuf,
                      IkePayload->PayloadSize,
                      IkePayload->PayloadType
                      );
    }

    PayloadTotalSize += IkePayload->PayloadSize;
  }
  IkePacket->PayloadTotalSize = PayloadTotalSize;

  Status = EFI_SUCCESS;
  if (SessionCommon->State >= IkeStateAuth) {
    //
    // Encrypt all payload and transfer IKE packet header from Host order to Network order.
    //
    Status = Ikev2EncryptPacket (SessionCommon, IkePacket);
    if (EFI_ERROR (Status)) {
      return Status;
    }
  } else {
    //
    // Fill in the lenght into IkePacket header and transfer Host order to Network order.
    //
    IkePacket->Header->Length = (UINT32) (sizeof (IKE_HEADER) + IkePacket->PayloadTotalSize);
    IkeHdrHostToNet (IkePacket->Header);
  }

  //
  // If the packet is first message, store whole message in IkeSa->InitiPacket
  // for following Auth Payload calculation.
  //
  if (IkePacket->Header->ExchangeType == IKEV2_EXCHANGE_TYPE_INIT) {
    IkeSaSession =  IKEV2_SA_SESSION_FROM_COMMON (SessionCommon);
    if (SessionCommon->IsInitiator) {
      IkeSaSession->InitPacketSize = IkePacket->PayloadTotalSize + sizeof (IKE_HEADER);
      IkeSaSession->InitPacket     = AllocateZeroPool (IkeSaSession->InitPacketSize);
      if (IkeSaSession->InitPacket == NULL) {
        return EFI_OUT_OF_RESOURCES;
      }
      
      CopyMem (IkeSaSession->InitPacket, IkePacket->Header, sizeof (IKE_HEADER));
      PayloadTotalSize = 0;
      for (Entry = IkePacket->PayloadList.ForwardLink; Entry != &(IkePacket->PayloadList);) {
        IkePayload  = IKE_PAYLOAD_BY_PACKET (Entry);
        Entry       = Entry->ForwardLink;
        CopyMem (
          IkeSaSession->InitPacket + sizeof (IKE_HEADER) + PayloadTotalSize,
          IkePayload->PayloadBuf,
          IkePayload->PayloadSize
          );
        PayloadTotalSize = PayloadTotalSize + IkePayload->PayloadSize;
      }
    } else {
      IkeSaSession->RespPacketSize = IkePacket->PayloadTotalSize + sizeof(IKE_HEADER);
      IkeSaSession->RespPacket     = AllocateZeroPool (IkeSaSession->RespPacketSize);
      if (IkeSaSession->RespPacket == NULL) {
        return EFI_OUT_OF_RESOURCES;
      }
      
      CopyMem (IkeSaSession->RespPacket, IkePacket->Header, sizeof (IKE_HEADER));
      PayloadTotalSize = 0;
      for (Entry = IkePacket->PayloadList.ForwardLink; Entry != &(IkePacket->PayloadList);) {
        IkePayload  = IKE_PAYLOAD_BY_PACKET (Entry);
        Entry       = Entry->ForwardLink;

        CopyMem (
          IkeSaSession->RespPacket + sizeof (IKE_HEADER) + PayloadTotalSize,
          IkePayload->PayloadBuf,
          IkePayload->PayloadSize
          );
        PayloadTotalSize = PayloadTotalSize + IkePayload->PayloadSize;
      }
    }
  }

  return Status;
}

/**
  Decrypt IKE packet.

  This function decrypts the Encrypted IKE packet and put the result into IkePacket->PayloadBuf.

  @param[in]      SessionCommon       Pointer to IKEV2_SESSION_COMMON containing
                                      some parameter used during decrypting.
  @param[in, out] IkePacket           Pointer to IKE_PACKET to be decrypted as input,
                                      and the decrypted result as output.
  @param[in, out] IkeType             The type of IKE. IKE_SA_TYPE, IKE_INFO_TYPE and
                                      IKE_CHILD_TYPE are supportted.

  @retval EFI_INVALID_PARAMETER      If the IKE packet length is zero or the
                                     IKE packet length is not aligned with Algorithm Block Size
  @retval EFI_SUCCESS                Decrypt IKE packet successfully.

**/
EFI_STATUS
Ikev2DecryptPacket (
  IN     IKEV2_SESSION_COMMON *SessionCommon,
  IN OUT IKE_PACKET           *IkePacket,
  IN OUT UINTN                IkeType
  )
{
  UINT8                  CryptBlockSize;      // Encrypt Block Size
  UINTN                  DecryptedSize;       // Encrypted IKE Payload Size
  UINT8                  *DecryptedBuf;       // Encrypted IKE Payload buffer
  UINTN                  IntegritySize;
  UINT8                  *IntegrityBuffer;
  UINTN                  IvSize;              // Iv Size
  UINT8                  CheckSumSize;        // Integrity Check Sum Size depends on intergrity Auth
  UINT8                  *CheckSumData;       // Check Sum data
  IKEV2_SA_SESSION       *IkeSaSession;
  IKEV2_CHILD_SA_SESSION *ChildSaSession;
  EFI_STATUS             Status;
  UINT8                  PadLen;
  HASH_DATA_FRAGMENT     Fragments[1];

  IvSize         = 0;
  IkeSaSession   = NULL;
  CryptBlockSize = 0;
  CheckSumSize   = 0;

  //
  // Check if the first payload is the Encrypted payload
  //
  if (IkePacket->Header->NextPayload != IKEV2_PAYLOAD_TYPE_ENCRYPT) {
    return EFI_ACCESS_DENIED;
  }
  CheckSumData    = NULL;
  DecryptedBuf    = NULL;
  IntegrityBuffer = NULL;

  //
  // Get the Block Size
  //
  if (SessionCommon->IkeSessionType == IkeSessionTypeIkeSa) {

    CryptBlockSize = (UINT8) IpSecGetEncryptBlockSize ((UINT8) SessionCommon->SaParams->EncAlgId);

    CheckSumSize   = (UINT8) IpSecGetIcvLength ((UINT8) SessionCommon->SaParams->IntegAlgId);
    IkeSaSession   = IKEV2_SA_SESSION_FROM_COMMON (SessionCommon);

  } else if (SessionCommon->IkeSessionType == IkeSessionTypeChildSa) {

    ChildSaSession = IKEV2_CHILD_SA_SESSION_FROM_COMMON (SessionCommon);
    IkeSaSession   = ChildSaSession->IkeSaSession;
    CryptBlockSize = (UINT8) IpSecGetEncryptBlockSize ((UINT8) IkeSaSession->SessionCommon.SaParams->EncAlgId);
    CheckSumSize   = (UINT8) IpSecGetIcvLength ((UINT8) IkeSaSession->SessionCommon.SaParams->IntegAlgId);
  } else {
    //
    // The type of SA Session would either be IkeSa or ChildSa.
    //
    return EFI_INVALID_PARAMETER;
  }

  CheckSumData = AllocateZeroPool (CheckSumSize);
  if (CheckSumData == NULL) {
    Status = EFI_OUT_OF_RESOURCES;
    goto ON_EXIT;
  }

  //
  // Fill in the Integrity buffer
  //
  IntegritySize   = IkePacket->PayloadTotalSize + sizeof (IKE_HEADER);
  IntegrityBuffer = AllocateZeroPool (IntegritySize);
  if (IntegrityBuffer == NULL) {
    Status = EFI_OUT_OF_RESOURCES;
    goto ON_EXIT;
  }
  
  CopyMem (IntegrityBuffer, IkePacket->Header, sizeof(IKE_HEADER));
  CopyMem (IntegrityBuffer + sizeof (IKE_HEADER), IkePacket->PayloadsBuf, IkePacket->PayloadTotalSize);

  //
  // Change Host order to Network order, since the header order was changed
  // in the IkePacketFromNetbuf.
  //
  IkeHdrHostToNet ((IKE_HEADER *)IntegrityBuffer);

  //
  // Calculate the Integrity CheckSum Data
  //
  Fragments[0].Data     = IntegrityBuffer;
  Fragments[0].DataSize = IntegritySize - CheckSumSize;

  if (SessionCommon->IsInitiator) {
    Status = IpSecCryptoIoHmac (
               (UINT8)IkeSaSession->SessionCommon.SaParams->IntegAlgId,
               IkeSaSession->IkeKeys->SkArKey,
               IkeSaSession->IkeKeys->SkArKeySize,
               (HASH_DATA_FRAGMENT *) Fragments,
               1,
               CheckSumData,
               CheckSumSize
               );
  } else {
    Status = IpSecCryptoIoHmac (
               (UINT8)IkeSaSession->SessionCommon.SaParams->IntegAlgId,
               IkeSaSession->IkeKeys->SkAiKey,
               IkeSaSession->IkeKeys->SkAiKeySize,
               (HASH_DATA_FRAGMENT *) Fragments,
               1,
               CheckSumData,
               CheckSumSize
               );
  }

  if (EFI_ERROR (Status)) {
    goto ON_EXIT;
  }
  //
  // Compare the Integrity CheckSum Data with the one in IkePacket
  //
  if (CompareMem (
        IkePacket->PayloadsBuf + IkePacket->PayloadTotalSize - CheckSumSize,
        CheckSumData,
        CheckSumSize
        ) != 0) {
    DEBUG ((DEBUG_ERROR, "Error auth verify payload\n"));
    Status = EFI_ACCESS_DENIED;
    goto ON_EXIT;
  }

  IvSize = CryptBlockSize;

  //
  // Decrypt the payload with the key.
  //
  DecryptedSize = IkePacket->PayloadTotalSize - sizeof (IKEV2_COMMON_PAYLOAD_HEADER) - IvSize - CheckSumSize;
  DecryptedBuf  = AllocateZeroPool (DecryptedSize);
  if (DecryptedBuf == NULL) {
    Status = EFI_OUT_OF_RESOURCES;
    goto ON_EXIT;
  }

  CopyMem (
    DecryptedBuf,
    IkePacket->PayloadsBuf + sizeof (IKEV2_COMMON_PAYLOAD_HEADER) + IvSize,
    DecryptedSize
    );

  if (SessionCommon->IsInitiator) {
   Status = IpSecCryptoIoDecrypt (
              (UINT8) SessionCommon->SaParams->EncAlgId,
              IkeSaSession->IkeKeys->SkErKey,
              IkeSaSession->IkeKeys->SkErKeySize << 3,
              IkePacket->PayloadsBuf + sizeof (IKEV2_COMMON_PAYLOAD_HEADER),
              DecryptedBuf,
              DecryptedSize,
              DecryptedBuf
              );
  } else {
    Status = IpSecCryptoIoDecrypt (
               (UINT8) SessionCommon->SaParams->EncAlgId,
               IkeSaSession->IkeKeys->SkEiKey,
               IkeSaSession->IkeKeys->SkEiKeySize << 3,
               IkePacket->PayloadsBuf + sizeof (IKEV2_COMMON_PAYLOAD_HEADER),
               DecryptedBuf,
               DecryptedSize,
               DecryptedBuf
               );
  }

  if (EFI_ERROR (Status)) {
    DEBUG ((DEBUG_ERROR, "Error decrypt buffer with %r\n", Status));
    goto ON_EXIT;
  }

  //
  // Get the Padding length
  //
  //
  PadLen = (UINT8) (*(DecryptedBuf + DecryptedSize - sizeof (IKEV2_PAD_LEN)));

  //
  // Save the next payload of encrypted payload into IkePacket->Hdr->NextPayload
  //
  IkePacket->Header->NextPayload = ((IKEV2_ENCRYPTED *) IkePacket->PayloadsBuf)->Header.NextPayload;

  //
  // Free old IkePacket->PayloadBuf and point it to decrypted paylaod buffer.
  //
  FreePool (IkePacket->PayloadsBuf);
  IkePacket->PayloadsBuf      = DecryptedBuf;
  IkePacket->PayloadTotalSize = DecryptedSize - PadLen;

  IPSEC_DUMP_BUF ("Decrypted Buffer", DecryptedBuf, DecryptedSize);


ON_EXIT:
  if (CheckSumData != NULL) {
    FreePool (CheckSumData);
  }

  if (EFI_ERROR (Status) && DecryptedBuf != NULL) {
    FreePool (DecryptedBuf);
  }

  if (IntegrityBuffer != NULL) {
    FreePool (IntegrityBuffer);
  }

  return Status;
}

/**
  Encrypt IKE packet.

  This function encrypt IKE packet before sending it. The Encrypted IKE packet
  is put in to IKEV2 Encrypted Payload.

  @param[in]        SessionCommon     Pointer to IKEV2_SESSION_COMMON related to the IKE packet.
  @param[in, out]   IkePacket         Pointer to IKE packet to be encrypted.

  @retval      EFI_SUCCESS       Operation is successful.
  @retval      Others            Operation is failed.

**/
EFI_STATUS
Ikev2EncryptPacket (
  IN IKEV2_SESSION_COMMON *SessionCommon,
  IN OUT IKE_PACKET       *IkePacket
  )
{
  UINT8                  CryptBlockSize;      // Encrypt Block Size
  UINT8                  CryptBlockSizeMask;  // Block Mask
  UINTN                  EncryptedSize;       // Encrypted IKE Payload Size
  UINT8                  *EncryptedBuf;       // Encrypted IKE Payload buffer
  UINT8                  *EncryptPayloadBuf;  // Contain whole Encrypted Payload
  UINTN                  EncryptPayloadSize;  // Total size of the Encrypted payload
  UINT8                  *IntegrityBuf;       // Buffer to be intergity
  UINT8                  *IvBuffer;           // Initialization Vector
  UINT8                  IvSize;              // Iv Size
  UINT8                  CheckSumSize;        // Integrity Check Sum Size depends on intergrity Auth
  UINT8                  *CheckSumData;       // Check Sum data
  UINTN                  Index;
  IKE_PAYLOAD            *EncryptPayload;
  IKEV2_SA_SESSION       *IkeSaSession;
  IKEV2_CHILD_SA_SESSION *ChildSaSession;
  EFI_STATUS             Status;
  LIST_ENTRY             *Entry;
  IKE_PAYLOAD            *IkePayload;
  HASH_DATA_FRAGMENT     Fragments[1];

  Status = EFI_SUCCESS;

  //
  // Initial all buffers to NULL.
  //
  EncryptedBuf      = NULL;
  EncryptPayloadBuf = NULL;
  IvBuffer          = NULL;
  CheckSumData      = NULL;
  IkeSaSession      = NULL;
  CryptBlockSize    = 0;
  CheckSumSize      = 0;
  IntegrityBuf      = NULL;
  //
  // Get the Block Size
  //
  if (SessionCommon->IkeSessionType == IkeSessionTypeIkeSa) {

    CryptBlockSize = (UINT8) IpSecGetEncryptBlockSize ((UINT8) SessionCommon->SaParams->EncAlgId);
    CheckSumSize   = (UINT8) IpSecGetIcvLength ((UINT8) SessionCommon->SaParams->IntegAlgId);
    IkeSaSession   = IKEV2_SA_SESSION_FROM_COMMON (SessionCommon);

  } else if (SessionCommon->IkeSessionType == IkeSessionTypeChildSa) {

    ChildSaSession = IKEV2_CHILD_SA_SESSION_FROM_COMMON (SessionCommon);
    IkeSaSession   = ChildSaSession->IkeSaSession;
    CryptBlockSize = (UINT8) IpSecGetEncryptBlockSize ((UINT8) IkeSaSession->SessionCommon.SaParams->EncAlgId);
    CheckSumSize   = (UINT8) IpSecGetIcvLength ((UINT8) IkeSaSession->SessionCommon.SaParams->IntegAlgId);
  }

  //
  // Calcualte the EncryptPayloadSize and the PAD length
  //
  CryptBlockSizeMask  = (UINT8) (CryptBlockSize - 1);
  EncryptedSize       = (IkePacket->PayloadTotalSize + sizeof (IKEV2_PAD_LEN) + CryptBlockSizeMask) & ~CryptBlockSizeMask;
  EncryptedBuf        = (UINT8 *) AllocateZeroPool (EncryptedSize);
  if (EncryptedBuf == NULL) {
    Status = EFI_OUT_OF_RESOURCES;
    goto ON_EXIT;
  }
  
  //
  // Copy all payload into EncryptedIkePayload
  //
  Index = 0;
  NET_LIST_FOR_EACH (Entry, &(IkePacket)->PayloadList) {
    IkePayload = IKE_PAYLOAD_BY_PACKET (Entry);

    CopyMem (EncryptedBuf + Index, IkePayload->PayloadBuf, IkePayload->PayloadSize);
    Index += IkePayload->PayloadSize;

  };

  //
  // Fill in the Pading Length
  //
  *(EncryptedBuf + EncryptedSize - 1) = (UINT8)(EncryptedSize - IkePacket->PayloadTotalSize - 1);

  //
  // The IV size is equal with block size
  //
  IvSize    = CryptBlockSize;
  IvBuffer  = (UINT8 *) AllocateZeroPool (IvSize);
  if (IvBuffer == NULL) {
    Status = EFI_OUT_OF_RESOURCES;
    goto ON_EXIT;
  }

  //
  // Generate IV
  //
  IkeGenerateIv (IvBuffer, IvSize);

  //
  // Encrypt payload buf
  //
  if (SessionCommon->IsInitiator) {
    Status = IpSecCryptoIoEncrypt (
               (UINT8) IkeSaSession->SessionCommon.SaParams->EncAlgId,
               IkeSaSession->IkeKeys->SkEiKey,
               IkeSaSession->IkeKeys->SkEiKeySize << 3,
               IvBuffer,
               EncryptedBuf,
               EncryptedSize,
               EncryptedBuf
               );
  } else {
    Status = IpSecCryptoIoEncrypt (
               (UINT8) IkeSaSession->SessionCommon.SaParams->EncAlgId,
               IkeSaSession->IkeKeys->SkErKey,
               IkeSaSession->IkeKeys->SkErKeySize << 3,
               IvBuffer,
               EncryptedBuf,
               EncryptedSize,
               EncryptedBuf
               );
  }
  if (EFI_ERROR (Status)) {
    goto ON_EXIT;
  }

  //
  // Allocate the buffer for the whole IKE payload (Encrypted Payload).
  //
  EncryptPayloadSize = sizeof(IKEV2_ENCRYPTED) + IvSize + EncryptedSize + CheckSumSize;
  EncryptPayloadBuf  = AllocateZeroPool (EncryptPayloadSize);
  if (EncryptPayloadBuf == NULL) {
    Status = EFI_OUT_OF_RESOURCES;
    goto ON_EXIT;
  }

  //
  // Fill in Header of  Encrypted Payload
  //
  ((IKEV2_ENCRYPTED *) EncryptPayloadBuf)->Header.NextPayload   = IkePacket->Header->NextPayload;
  ((IKEV2_ENCRYPTED *) EncryptPayloadBuf)->Header.PayloadLength = HTONS ((UINT16)EncryptPayloadSize);

  //
  // Fill in Iv
  //
  CopyMem (EncryptPayloadBuf + sizeof (IKEV2_ENCRYPTED), IvBuffer, IvSize);

  //
  // Fill in encrypted data
  //
  CopyMem (EncryptPayloadBuf + sizeof (IKEV2_ENCRYPTED) + IvSize, EncryptedBuf, EncryptedSize);

  //
  // Fill in the IKE Packet header
  //
  IkePacket->PayloadTotalSize    = EncryptPayloadSize;
  IkePacket->Header->Length      = (UINT32) (sizeof (IKE_HEADER) + IkePacket->PayloadTotalSize);
  IkePacket->Header->NextPayload = IKEV2_PAYLOAD_TYPE_ENCRYPT;

  IntegrityBuf                   = AllocateZeroPool (IkePacket->Header->Length);
  if (IntegrityBuf == NULL) {
    Status = EFI_OUT_OF_RESOURCES;
    goto ON_EXIT;
  }
  IkeHdrHostToNet (IkePacket->Header);

  CopyMem (IntegrityBuf, IkePacket->Header, sizeof (IKE_HEADER));
  CopyMem (IntegrityBuf + sizeof (IKE_HEADER), EncryptPayloadBuf, EncryptPayloadSize);

  //
  // Calcualte Integrity CheckSum
  //
  Fragments[0].Data     = IntegrityBuf;
  Fragments[0].DataSize = EncryptPayloadSize + sizeof (IKE_HEADER) - CheckSumSize;

  CheckSumData = AllocateZeroPool (CheckSumSize);
  if (CheckSumData == NULL) {
    Status = EFI_OUT_OF_RESOURCES;
    goto ON_EXIT;
  }
  if (SessionCommon->IsInitiator) {

    IpSecCryptoIoHmac (
      (UINT8)IkeSaSession->SessionCommon.SaParams->IntegAlgId,
      IkeSaSession->IkeKeys->SkAiKey,
      IkeSaSession->IkeKeys->SkAiKeySize,
      (HASH_DATA_FRAGMENT *) Fragments,
      1,
      CheckSumData,
      CheckSumSize
      );
  } else {

    IpSecCryptoIoHmac (
      (UINT8)IkeSaSession->SessionCommon.SaParams->IntegAlgId,
      IkeSaSession->IkeKeys->SkArKey,
      IkeSaSession->IkeKeys->SkArKeySize,
      (HASH_DATA_FRAGMENT *) Fragments,
      1,
      CheckSumData,
      CheckSumSize
      );
  }

  //
  // Copy CheckSum into Encrypted Payload
  //
  CopyMem (EncryptPayloadBuf + EncryptPayloadSize - CheckSumSize, CheckSumData, CheckSumSize);

  IPSEC_DUMP_BUF ("Encrypted payload buffer", EncryptPayloadBuf, EncryptPayloadSize);
  IPSEC_DUMP_BUF ("Integrith CheckSum Data", CheckSumData, CheckSumSize);

  //
  // Clean all payload under IkePacket->PayloadList.
  //
  ClearAllPayloads (IkePacket);

  //
  // Create Encrypted Payload and add into IkePacket->PayloadList
  //
  EncryptPayload = IkePayloadAlloc ();
  if (EncryptPayload == NULL) {
    Status = EFI_OUT_OF_RESOURCES;
    goto ON_EXIT;
  }

  //
  // Fill the encrypted payload into the IKE_PAYLOAD structure.
  //
  EncryptPayload->PayloadBuf  = EncryptPayloadBuf;
  EncryptPayload->PayloadSize = EncryptPayloadSize;
  EncryptPayload->PayloadType = IKEV2_PAYLOAD_TYPE_ENCRYPT;

  IKE_PACKET_APPEND_PAYLOAD (IkePacket, EncryptPayload);

ON_EXIT:
  if (EncryptedBuf != NULL) {
    FreePool (EncryptedBuf);
  }

  if (EFI_ERROR (Status) && EncryptPayloadBuf != NULL) {
    FreePool (EncryptPayloadBuf);
  }

  if (IvBuffer != NULL) {
    FreePool (IvBuffer);
  }

  if (CheckSumData != NULL) {
    FreePool (CheckSumData);
  }

  if (IntegrityBuf != NULL) {
    FreePool (IntegrityBuf);
  }

  return Status;
}

/**
  Save some useful payloads after accepting the Packet.

  @param[in] SessionCommon   Pointer to IKEV2_SESSION_COMMON related to the operation.
  @param[in] IkePacket       Pointer to received IkePacet.
  @param[in] IkeType         The type used to indicate it is in IkeSa or ChildSa or Info
                             exchange.

**/
VOID
Ikev2OnPacketAccepted (
  IN IKEV2_SESSION_COMMON *SessionCommon,
  IN IKE_PACKET           *IkePacket,
  IN UINT8                IkeType
  )
{
  return;
}

/**

  The notification function. It will be called when the related UDP_TX_TOKEN's event
  is signaled.

  This function frees the Net Buffer pointed to the input Packet.

  @param[in]  Packet           Pointer to Net buffer containing the sending IKE packet.
  @param[in]  EndPoint         Pointer to UDP_END_POINT containing the remote and local
                               address information.
  @param[in]  IoStatus         The Status of the related UDP_TX_TOKEN.
  @param[in]  Context          Pointer to data passed from the caller.

**/
VOID
EFIAPI
Ikev2OnPacketSent (
  IN NET_BUF                   *Packet,
  IN UDP_END_POINT             *EndPoint,
  IN EFI_STATUS                IoStatus,
  IN VOID                      *Context
  )
{
 IKE_PACKET             *IkePacket;
 IKEV2_SA_SESSION       *IkeSaSession;
 IKEV2_CHILD_SA_SESSION *ChildSaSession;
 UINT8                  Value;
 IPSEC_PRIVATE_DATA     *Private;
 EFI_STATUS             Status;

 IkePacket  = (IKE_PACKET *) Context;
 Private    = NULL;

 if (EFI_ERROR (IoStatus)) {
    DEBUG ((DEBUG_ERROR, "Error send the last packet in IkeSessionTypeIkeSa with %r\n", IoStatus));
  }

  NetbufFree (Packet);

  if (IkePacket->IsDeleteInfo) {
    //
    // For each RemotePeerIP, there are only one IKESA.
    //
    IkeSaSession = Ikev2SaSessionLookup (
                     &IkePacket->Private->Ikev2EstablishedList,
                     &IkePacket->RemotePeerIp
                     );
    if (IkeSaSession == NULL) {
      IkePacketFree (IkePacket);
      return;
    }

    Private = IkePacket->Private;
    if (IkePacket->Spi != 0 ) {
      //
      // At that time, the established Child SA still in eht ChildSaEstablishSessionList.
      // And meanwhile, if the Child SA is in the the ChildSa in Delete list,
      // remove it from delete list and delete it direclty.
      //
      ChildSaSession = Ikev2ChildSaSessionLookupBySpi (
                         &IkeSaSession->ChildSaEstablishSessionList,
                         IkePacket->Spi
                         );
      if (ChildSaSession != NULL) {
        Ikev2ChildSaSessionRemove (
          &IkeSaSession->DeleteSaList,
          ChildSaSession->LocalPeerSpi,
          IKEV2_DELET_CHILDSA_LIST
          );

        //
        // Delete the Child SA.
        //
        Ikev2ChildSaSilentDelete (
          IkeSaSession,
          IkePacket->Spi
          );
      }

    } else {
      //
      // Delete the IKE SA
      //
      DEBUG (
        (DEBUG_INFO,
        "\n------ deleted Packet (cookie_i, cookie_r):(0x%lx, 0x%lx)------\n",
        IkeSaSession->InitiatorCookie,
        IkeSaSession->ResponderCookie)
        );

      RemoveEntryList (&IkeSaSession->BySessionTable);
      Ikev2SaSessionFree (IkeSaSession);
    }
  }
  IkePacketFree (IkePacket);

  //
  // when all IKE SAs were disabled by calling "IPsecConfig -disable", the IPsec status
  // should be changed.
  //
  if (Private != NULL && Private->IsIPsecDisabling) {
    //
    // After all IKE SAs were deleted, set the IPSEC_STATUS_DISABLED value in
    // IPsec status variable.
    //
    if (IsListEmpty (&Private->Ikev1EstablishedList) && IsListEmpty (&Private->Ikev2EstablishedList)) {
      Value = IPSEC_STATUS_DISABLED;
      Status = gRT->SetVariable (
                 IPSECCONFIG_STATUS_NAME,
                 &gEfiIpSecConfigProtocolGuid,
                 EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE,
                 sizeof (Value),
                 &Value
                 );
      if (!EFI_ERROR (Status)) {
        //
        // Set the DisabledFlag in Private data.
        //
        Private->IpSec.DisabledFlag = TRUE;
        Private->IsIPsecDisabling   = FALSE;
      }
    }
  }
}

/**
  Send out IKEV2 packet.

  @param[in]  IkeUdpService     Pointer to IKE_UDP_SERVICE used to send the IKE packet.
  @param[in]  SessionCommon     Pointer to IKEV1_SESSION_COMMON related to the IKE packet.
  @param[in]  IkePacket         Pointer to IKE_PACKET to be sent out.
  @param[in]  IkeType           The type of IKE to point what's kind of the IKE
                                packet is to be sent out. IKE_SA_TYPE, IKE_INFO_TYPE
                                and IKE_CHILD_TYPE are supportted.

  @retval     EFI_SUCCESS       The operation complete successfully.
  @retval     Otherwise         The operation is failed.

**/
EFI_STATUS
Ikev2SendIkePacket (
  IN IKE_UDP_SERVICE     *IkeUdpService,
  IN UINT8               *SessionCommon,
  IN IKE_PACKET          *IkePacket,
  IN UINTN               IkeType
  )
{
  EFI_STATUS            Status;
  NET_BUF               *IkePacketNetbuf;
  UDP_END_POINT         EndPoint;
  IKEV2_SESSION_COMMON  *Common;

  Common = (IKEV2_SESSION_COMMON *) SessionCommon;

  //
  // Set the resend interval
  //
  if (Common->TimeoutInterval == 0) {
    Common->TimeoutInterval = IKE_DEFAULT_TIMEOUT_INTERVAL;
  }

  //
  // Retransfer the packet if it is initial packet.
  //
  if (IkePacket->Header->Flags == IKE_HEADER_FLAGS_INIT) {
    //
    // Set timer for next retry, this will cancel previous timer
    //
    Status = gBS->SetTimer (
                    Common->TimeoutEvent,
                    TimerRelative,
                    MultU64x32 (Common->TimeoutInterval, 10000) // ms->100ns
                    );
    if (EFI_ERROR (Status)) {
      return Status;
    }
  }

  IKE_PACKET_REF (IkePacket);
  //
  // If the last sent packet is same with this round packet, the packet is resent packet.
  //
  if (IkePacket != Common->LastSentPacket && Common->LastSentPacket != NULL) {
    IkePacketFree (Common->LastSentPacket);
  }

  Common->LastSentPacket = IkePacket;

  //
  // Transform IkePacke to NetBuf
  //
  IkePacketNetbuf = IkeNetbufFromPacket ((UINT8 *) SessionCommon, IkePacket, IkeType);
  if (IkePacketNetbuf == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }

  ZeroMem (&EndPoint, sizeof (UDP_END_POINT));
  EndPoint.RemotePort = IKE_DEFAULT_PORT;
  CopyMem (&IkePacket->RemotePeerIp, &Common->RemotePeerIp, sizeof (EFI_IP_ADDRESS));
  CopyMem (&EndPoint.RemoteAddr, &Common->RemotePeerIp, sizeof (EFI_IP_ADDRESS));
  CopyMem (&EndPoint.LocalAddr, &Common->LocalPeerIp, sizeof (EFI_IP_ADDRESS));

  IPSEC_DUMP_PACKET (IkePacket, EfiIPsecOutBound, IkeUdpService->IpVersion);

  if (IkeUdpService->IpVersion == IP_VERSION_4) {
    EndPoint.RemoteAddr.Addr[0] = HTONL (EndPoint.RemoteAddr.Addr[0]);
    EndPoint.LocalAddr.Addr[0]  = HTONL (EndPoint.LocalAddr.Addr[0]);
  }

  //
  // Call UDPIO to send out the IKE packet.
  //
  Status = UdpIoSendDatagram (
             IkeUdpService->Output,
             IkePacketNetbuf,
             &EndPoint,
             NULL,
             Ikev2OnPacketSent,
             (VOID*)IkePacket
             );

  if (EFI_ERROR (Status)) {
    DEBUG ((DEBUG_ERROR, "Error send packet with %r\n", Status));
  }

  return Status;
}