/** @file
The implementation of Payloads Creation.
(C) Copyright 2015 Hewlett-Packard Development Company, L.P.
Copyright (c) 2010 - 2015, Intel Corporation. All rights reserved.
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 ();
ASSERT (SaPayload != 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);
ASSERT (SaData != 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);
ASSERT (Nonce != NULL);
CopyMem (Nonce + 1, NonceBlock, Size - sizeof (IKEV2_NONCE));
Nonce->Header.NextPayload = NextPayload;
Nonce->Header.PayloadLength = (UINT16) Size;
NoncePayload = IkePayloadAlloc ();
ASSERT (NoncePayload != 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);
ASSERT (Ke != 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 ();
ASSERT (KePayload != 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);
ASSERT (Id != NULL);
IdPayload = IkePayloadAlloc ();
ASSERT (IdPayload != 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);
ASSERT (Id != NULL);
IdPayload = IkePayloadAlloc ();
ASSERT (IdPayload != 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);
ASSERT (KeyBuf != NULL);
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);
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 ();
ASSERT (AuthPayload != NULL);
AuthPayload->PayloadSize = sizeof (IKEV2_AUTH) + DigestSize;
PayloadBuf = (IKEV2_AUTH *) AllocateZeroPool (AuthPayload->PayloadSize);
ASSERT (PayloadBuf != NULL);
//
// 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];
UINT8 *KeyBuf;
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
//
KeyBuf = NULL;
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;
}
//
// Store the AuthKey into KeyBuf
//
KeyBuf = AllocateZeroPool (DigestSize);
ASSERT (KeyBuf != NULL);
CopyMem (KeyBuf, Digest, 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);
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);
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 ();
ASSERT (AuthPayload != NULL);
if (!IsVerify) {
AuthPayload->PayloadSize = sizeof (IKEV2_AUTH) + SigSize;
} else {
AuthPayload->PayloadSize = sizeof (IKEV2_AUTH) + DigestSize;
}
PayloadBuf = (IKEV2_AUTH *) AllocateZeroPool (AuthPayload->PayloadSize);
ASSERT (PayloadBuf != NULL);
//
// 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 (KeyBuf != NULL) {
FreePool (KeyBuf);
}
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 !
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// ! !
// ~ ~
// ! !
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//
TsPayload = IkePayloadAlloc();
ASSERT (TsPayload != 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);
ASSERT (TsPayloadBuf != NULL);
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);
ASSERT (Notify != 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 ();
ASSERT (NotifyPayload != 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);
ASSERT (Del != 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 ();
ASSERT (DelPayload != 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
Payload. Otherwise, create Certificate Request 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);
ASSERT (Sa != 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)
);
ASSERT (SaData != NULL);
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);
ASSERT (Spi != NULL);
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));
ASSERT (IkeHeader != NULL);
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 ();
ASSERT (IkePayload != NULL);
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);
} 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);
ASSERT (IkeSaSession->InitPacket != NULL);
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);
ASSERT (IkeSaSession->RespPacket != NULL);
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);
ASSERT (CheckSumData != NULL);
//
// Fill in the Integrity buffer
//
IntegritySize = IkePacket->PayloadTotalSize + sizeof (IKE_HEADER);
IntegrityBuffer = AllocateZeroPool (IntegritySize);
ASSERT (IntegrityBuffer != NULL);
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);
ASSERT (DecryptedBuf != NULL);
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);
ASSERT (EncryptedBuf != NULL);
//
// 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);
ASSERT (EncryptPayloadBuf != NULL);
//
// 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 ();
ASSERT (EncryptPayload != NULL);
//
// 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);
ASSERT (IkePacketNetbuf != NULL);
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;
}