/** @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; | |
} | |