// SoftEther VPN Source Code
// Cedar Communication Module
// 
// SoftEther VPN Server, Client and Bridge are free software under GPLv2.
// 
// Copyright (c) 2012-2015 Daiyuu Nobori.
// Copyright (c) 2012-2015 SoftEther VPN Project, University of Tsukuba, Japan.
// Copyright (c) 2012-2015 SoftEther Corporation.
// 
// All Rights Reserved.
// 
// http://www.softether.org/
// 
// Author: Daiyuu Nobori
// Comments: Tetsuo Sugiyama, Ph.D.
// 
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// version 2 as published by the Free Software Foundation.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License version 2
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// 
// THE LICENSE AGREEMENT IS ATTACHED ON THE SOURCE-CODE PACKAGE
// AS "LICENSE.TXT" FILE. READ THE TEXT FILE IN ADVANCE TO USE THE SOFTWARE.
// 
// 
// THIS SOFTWARE IS DEVELOPED IN JAPAN, AND DISTRIBUTED FROM JAPAN,
// UNDER JAPANESE LAWS. YOU MUST AGREE IN ADVANCE TO USE, COPY, MODIFY,
// MERGE, PUBLISH, DISTRIBUTE, SUBLICENSE, AND/OR SELL COPIES OF THIS
// SOFTWARE, THAT ANY JURIDICAL DISPUTES WHICH ARE CONCERNED TO THIS
// SOFTWARE OR ITS CONTENTS, AGAINST US (SOFTETHER PROJECT, SOFTETHER
// CORPORATION, DAIYUU NOBORI OR OTHER SUPPLIERS), OR ANY JURIDICAL
// DISPUTES AGAINST US WHICH ARE CAUSED BY ANY KIND OF USING, COPYING,
// MODIFYING, MERGING, PUBLISHING, DISTRIBUTING, SUBLICENSING, AND/OR
// SELLING COPIES OF THIS SOFTWARE SHALL BE REGARDED AS BE CONSTRUED AND
// CONTROLLED BY JAPANESE LAWS, AND YOU MUST FURTHER CONSENT TO
// EXCLUSIVE JURISDICTION AND VENUE IN THE COURTS SITTING IN TOKYO,
// JAPAN. YOU MUST WAIVE ALL DEFENSES OF LACK OF PERSONAL JURISDICTION
// AND FORUM NON CONVENIENS. PROCESS MAY BE SERVED ON EITHER PARTY IN
// THE MANNER AUTHORIZED BY APPLICABLE LAW OR COURT RULE.
// 
// USE ONLY IN JAPAN. DO NOT USE THIS SOFTWARE IN ANOTHER COUNTRY UNLESS
// YOU HAVE A CONFIRMATION THAT THIS SOFTWARE DOES NOT VIOLATE ANY
// CRIMINAL LAWS OR CIVIL RIGHTS IN THAT PARTICULAR COUNTRY. USING THIS
// SOFTWARE IN OTHER COUNTRIES IS COMPLETELY AT YOUR OWN RISK. THE
// SOFTETHER VPN PROJECT HAS DEVELOPED AND DISTRIBUTED THIS SOFTWARE TO
// COMPLY ONLY WITH THE JAPANESE LAWS AND EXISTING CIVIL RIGHTS INCLUDING
// PATENTS WHICH ARE SUBJECTS APPLY IN JAPAN. OTHER COUNTRIES' LAWS OR
// CIVIL RIGHTS ARE NONE OF OUR CONCERNS NOR RESPONSIBILITIES. WE HAVE
// NEVER INVESTIGATED ANY CRIMINAL REGULATIONS, CIVIL LAWS OR
// INTELLECTUAL PROPERTY RIGHTS INCLUDING PATENTS IN ANY OF OTHER 200+
// COUNTRIES AND TERRITORIES. BY NATURE, THERE ARE 200+ REGIONS IN THE
// WORLD, WITH DIFFERENT LAWS. IT IS IMPOSSIBLE TO VERIFY EVERY
// COUNTRIES' LAWS, REGULATIONS AND CIVIL RIGHTS TO MAKE THE SOFTWARE
// COMPLY WITH ALL COUNTRIES' LAWS BY THE PROJECT. EVEN IF YOU WILL BE
// SUED BY A PRIVATE ENTITY OR BE DAMAGED BY A PUBLIC SERVANT IN YOUR
// COUNTRY, THE DEVELOPERS OF THIS SOFTWARE WILL NEVER BE LIABLE TO
// RECOVER OR COMPENSATE SUCH DAMAGES, CRIMINAL OR CIVIL
// RESPONSIBILITIES. NOTE THAT THIS LINE IS NOT LICENSE RESTRICTION BUT
// JUST A STATEMENT FOR WARNING AND DISCLAIMER.
// 
// 
// SOURCE CODE CONTRIBUTION
// ------------------------
// 
// Your contribution to SoftEther VPN Project is much appreciated.
// Please send patches to us through GitHub.
// Read the SoftEther VPN Patch Acceptance Policy in advance:
// http://www.softether.org/5-download/src/9.patch
// 
// 
// DEAR SECURITY EXPERTS
// ---------------------
// 
// If you find a bug or a security vulnerability please kindly inform us
// about the problem immediately so that we can fix the security problem
// to protect a lot of users around the world as soon as possible.
// 
// Our e-mail address for security reports is:
// softether-vpn-security [at] softether.org
// 
// Please note that the above e-mail address is not a technical support
// inquiry address. If you need technical assistance, please visit
// http://www.softether.org/ and ask your question on the users forum.
// 
// Thank you for your cooperation.
// 
// 
// NO MEMORY OR RESOURCE LEAKS
// ---------------------------
// 
// The memory-leaks and resource-leaks verification under the stress
// test has been passed before release this source code.


// IPsec_IkePacket.c
// IKE (ISAKMP) packet processing

#include "CedarPch.h"

// Convert the string to a password
BUF *IkeStrToPassword(char *str)
{
	BUF *b;
	// Validate arguments
	if (str == NULL)
	{
		return NewBuf();
	}

	if (StartWith(str, "0x") == false)
	{
		// Accept the string as is
		b = NewBuf();
		WriteBuf(b, str, StrLen(str));
	}
	else
	{
		// Interpret as a hexadecimal value
		b = StrToBin(str + 2);
	}

	return b;
}

// Phase 1: Convert the encryption algorithm name to key size
UINT IkePhase1CryptIdToKeySize(UCHAR id)
{
	switch (id)
	{
	case IKE_P1_CRYPTO_3DES_CBC:
		return DES3_KEY_SIZE;

	case IKE_P1_CRYPTO_DES_CBC:
		return DES_KEY_SIZE;
	}

	return 0;
}

// Phase 2: Convert the encryption algorithm name to key size
UINT IkePhase2CryptIdToKeySize(UCHAR id)
{
	switch (id)
	{
	case IKE_TRANSFORM_ID_P2_ESP_3DES:
		return DES3_KEY_SIZE;

	case IKE_TRANSFORM_ID_P2_ESP_DES:
		return DES_KEY_SIZE;
	}

	return 0;
}

// Convert a string to an algorithm name
UCHAR IkeStrToPhase1CryptId(char *name)
{
	if (StartWith(name, "3DES") || StartWith("3DES", name))
	{
		return IKE_P1_CRYPTO_3DES_CBC;
	}
	else if (StartWith(name, "DES") || StartWith("DES", name))
	{
		return IKE_P1_CRYPTO_DES_CBC;
	}
	else
	{
		return 0;
	}
}
UCHAR IkeStrToPhase1HashId(char *name)
{
	if (StartWith(name, "SHA-1") || StartWith("SHA-1", name))
	{
		return IKE_P1_HASH_SHA1;
	}

	return 0;
}
UCHAR IkeStrToPhase2CryptId(char *name)
{
	if (StartWith(name, "3DES") || StartWith("3DES", name))
	{
		return IKE_TRANSFORM_ID_P2_ESP_3DES;
	}
	else if (StartWith(name, "DES") || StartWith("DES", name))
	{
		return IKE_TRANSFORM_ID_P2_ESP_DES;
	}
	else
	{
		return 0;
	}
}
UCHAR IkeStrToPhase2HashId(char *name)
{
	if (StartWith(name, "SHA-1") || StartWith("SHA-1", name))
	{
		return IKE_P2_HMAC_SHA1_96;
	}

	return 0;
}

// Build a data payload
BUF *IkeBuildDataPayload(IKE_PACKET_DATA_PAYLOAD *t)
{
	BUF *b;
	// Validate arguments
	if (t == NULL)
	{
		return NULL;
	}

	b = NewBuf();
	WriteBuf(b, t->Data->Buf, t->Data->Size);

	return b;
}

// Build a SA payload
BUF *IkeBuildSaPayload(IKE_PACKET_SA_PAYLOAD *t)
{
	IKE_SA_HEADER h;
	BUF *ret;
	BUF *b;
	// Validate arguments
	if (t == NULL)
	{
		return NULL;
	}

	Zero(&h, sizeof(h));
	h.DoI = Endian32(IKE_SA_DOI_IPSEC);
	h.Situation = Endian32(IKE_SA_SITUATION_IDENTITY);

	ret = NewBuf();

	WriteBuf(ret, &h, sizeof(h));

	b = IkeBuildPayloadList(t->PayloadList);
	WriteBufBuf(ret, b);

	FreeBuf(b);

	return ret;
}

// Build a proposal payload
BUF *IkeBuildProposalPayload(IKE_PACKET_PROPOSAL_PAYLOAD *t)
{
	IKE_PROPOSAL_HEADER h;
	BUF *ret, *b;
	// Validate arguments
	if (t == NULL)
	{
		return NULL;
	}

	Zero(&h, sizeof(h));
	h.Number = t->Number;
	h.NumTransforms = LIST_NUM(t->PayloadList);
	h.ProtocolId = t->ProtocolId;
	h.SpiSize = t->Spi->Size;

	ret = NewBuf();
	WriteBuf(ret, &h, sizeof(h));
	WriteBufBuf(ret, t->Spi);

	b = IkeBuildPayloadList(t->PayloadList);
	WriteBufBuf(ret, b);

	FreeBuf(b);

	return ret;
}

// Build the transform value list
BUF *IkeBuildTransformValueList(LIST *o)
{
	BUF *b;
	UINT i;
	// Validate arguments
	if (o == NULL)
	{
		return NULL;
	}

	b = NewBuf();

	for (i = 0;i < LIST_NUM(o);i++)
	{
		IKE_PACKET_TRANSFORM_VALUE *v = LIST_DATA(o, i);
		BUF *tmp = IkeBuildTransformValue(v);

		WriteBufBuf(b, tmp);

		FreeBuf(tmp);
	}

	return b;
}

// Build a transform value
BUF *IkeBuildTransformValue(IKE_PACKET_TRANSFORM_VALUE *v)
{
	BUF *b;
	UCHAR af_bit, type;
	USHORT size_or_value;
	// Validate arguments
	if (v == NULL)
	{
		return NULL;
	}

	type = v->Type;

	if (v->Value >= 65536)
	{
		// 32 bit
		af_bit = 0;
		size_or_value = Endian16(sizeof(UINT));
	}
	else
	{
		// 16 bit
		af_bit = 0x80;
		size_or_value = Endian16((USHORT)v->Value);
	}

	b = NewBuf();
	WriteBuf(b, &af_bit, sizeof(af_bit));
	WriteBuf(b, &type, sizeof(type));
	WriteBuf(b, &size_or_value, sizeof(size_or_value));

	if (af_bit == 0)
	{
		UINT value = Endian32(v->Value);
		WriteBuf(b, &value, sizeof(UINT));
	}

	return b;
}

// Build a transform payload
BUF *IkeBuildTransformPayload(IKE_PACKET_TRANSFORM_PAYLOAD *t)
{
	IKE_TRANSFORM_HEADER h;
	BUF *ret, *b;
	// Validate arguments
	if (t == NULL)
	{
		return NULL;
	}

	Zero(&h, sizeof(h));
	h.Number = t->Number;
	h.TransformId = t->TransformId;

	ret = NewBuf();
	WriteBuf(ret, &h, sizeof(h));

	b = IkeBuildTransformValueList(t->ValueList);
	WriteBufBuf(ret, b);

	FreeBuf(b);

	return ret;
}

// Get the value from the transform payload
UINT IkeGetTransformValue(IKE_PACKET_TRANSFORM_PAYLOAD *t, UINT type, UINT index)
{
	UINT i;
	UINT num;
	// Validate arguments
	if (t == NULL)
	{
		return 0;
	}

	num = 0;

	for (i = 0;i < LIST_NUM(t->ValueList);i++)
	{
		IKE_PACKET_TRANSFORM_VALUE *v = LIST_DATA(t->ValueList, i);

		if (v->Type == type)
		{
			if (num == index)
			{
				return v->Value;
			}

			num++;
		}
	}

	return 0;
}

// Get the number of values from the transform payload
UINT IkeGetTransformValueNum(IKE_PACKET_TRANSFORM_PAYLOAD *t, UINT type)
{
	UINT i;
	UINT num;
	// Validate arguments
	if (t == NULL)
	{
		return 0;
	}

	num = 0;

	for (i = 0;i < LIST_NUM(t->ValueList);i++)
	{
		IKE_PACKET_TRANSFORM_VALUE *v = LIST_DATA(t->ValueList, i);

		if (v->Type == type)
		{
			num++;
		}
	}

	return num;
}

// Build the ID payload
BUF *IkeBuildIdPayload(IKE_PACKET_ID_PAYLOAD *t)
{
	IKE_ID_HEADER h;
	BUF *ret;
	// Validate arguments
	if (t == NULL)
	{
		return NULL;
	}

	Zero(&h, sizeof(h));
	h.IdType = t->Type;
	h.Port = Endian16(t->Port);
	h.ProtocolId = t->ProtocolId;

	ret = NewBuf();
	WriteBuf(ret, &h, sizeof(h));

	WriteBufBuf(ret, t->IdData);

	return ret;
}

// Build a certificate payload
BUF *IkeBuildCertPayload(IKE_PACKET_CERT_PAYLOAD *t)
{
	IKE_CERT_HEADER h;
	BUF *ret;
	// Validate arguments
	if (t == NULL)
	{
		return NULL;
	}

	Zero(&h, sizeof(h));
	h.CertType = t->CertType;

	ret = NewBuf();
	WriteBuf(ret, &h, sizeof(h));
	WriteBufBuf(ret, t->CertData);

	return ret;
}

// Build a certificate request payload
BUF *IkeBuildCertRequestPayload(IKE_PACKET_CERT_REQUEST_PAYLOAD *t)
{
	IKE_CERT_REQUEST_HEADER h;
	BUF *ret;
	// Validate arguments
	if (t == NULL)
	{
		return NULL;
	}

	Zero(&h, sizeof(h));
	h.CertType = t->CertType;

	ret = NewBuf();
	WriteBuf(ret, &h, sizeof(h));
	WriteBufBuf(ret, t->Data);

	return ret;
}

// Build a notification payload
BUF *IkeBuildNoticePayload(IKE_PACKET_NOTICE_PAYLOAD *t)
{
	IKE_NOTICE_HEADER h;
	BUF *ret;
	// Validate arguments
	if (t == NULL)
	{
		return NULL;
	}

	Zero(&h, sizeof(h));
	h.DoI = Endian32(IKE_SA_DOI_IPSEC);
	h.MessageType = Endian16(t->MessageType);
	h.ProtocolId = t->ProtocolId;
	h.SpiSize = t->Spi->Size;

	ret = NewBuf();
	WriteBuf(ret, &h, sizeof(h));
	WriteBuf(ret, t->Spi->Buf, t->Spi->Size);

	if (t->MessageData != NULL)
	{
		WriteBuf(ret, t->MessageData->Buf, t->MessageData->Size);
	}

	return ret;
}

// Build a NAT-OA payload
BUF *IkeBuildNatOaPayload(IKE_PACKET_NAT_OA_PAYLOAD *t)
{
	IKE_NAT_OA_HEADER h;
	BUF *ret;
	// Validate arguments
	if (t == NULL)
	{
		return NULL;
	}

	Zero(&h, sizeof(h));

	if (IsIP6(&t->IpAddress))
	{
		h.IdType = IKE_ID_IPV6_ADDR;
	}
	else
	{
		h.IdType = IKE_ID_IPV4_ADDR;
	}

	ret = NewBuf();

	WriteBuf(ret, &h, sizeof(h));

	if (IsIP6(&t->IpAddress))
	{
		WriteBuf(ret, t->IpAddress.ipv6_addr, 16);
	}
	else
	{
		WriteBuf(ret, t->IpAddress.addr, 4);
	}

	return ret;
}

// Build a deletion payload
BUF *IkeBuildDeletePayload(IKE_PACKET_DELETE_PAYLOAD *t)
{
	IKE_DELETE_HEADER h;
	BUF *ret;
	UINT i;
	// Validate arguments
	if (t == NULL)
	{
		return NULL;
	}

	Zero(&h, sizeof(h));
	h.DoI = Endian32(IKE_SA_DOI_IPSEC);
	h.NumSpis = Endian16(LIST_NUM(t->SpiList));
	h.ProtocolId = t->ProtocolId;

	if (LIST_NUM(t->SpiList) >= 1)
	{
		BUF *b = LIST_DATA(t->SpiList, 0);

		h.SpiSize = b->Size;
	}

	ret = NewBuf();
	WriteBuf(ret, &h, sizeof(h));

	for (i = 0;i < LIST_NUM(t->SpiList);i++)
	{
		BUF *b = LIST_DATA(t->SpiList, i);

		WriteBuf(ret, b->Buf, b->Size);
	}

	return ret;
}

// Build a bit array from the payload
BUF *IkeBuildPayload(IKE_PACKET_PAYLOAD *p)
{
	BUF *b = NULL;
	// Validate arguments
	if (p == NULL)
	{
		return NULL;
	}

	switch (p->PayloadType)
	{
	case IKE_PAYLOAD_SA:					// SA payload
		b = IkeBuildSaPayload(&p->Payload.Sa);
		break;

	case IKE_PAYLOAD_PROPOSAL:			// Proposal payload
		b = IkeBuildProposalPayload(&p->Payload.Proposal);
		break;

	case IKE_PAYLOAD_TRANSFORM:			// Transform payload
		b = IkeBuildTransformPayload(&p->Payload.Transform);
		break;

	case IKE_PAYLOAD_ID:					// ID payload
		b = IkeBuildIdPayload(&p->Payload.Id);
		break;

	case IKE_PAYLOAD_CERT:				// Certificate payload
		b = IkeBuildCertPayload(&p->Payload.Cert);
		break;

	case IKE_PAYLOAD_CERT_REQUEST:		// Certificate request payload
		b = IkeBuildCertRequestPayload(&p->Payload.CertRequest);
		break;

	case IKE_PAYLOAD_NOTICE:			// Notification Payload
		b = IkeBuildNoticePayload(&p->Payload.Notice);
		break;

	case IKE_PAYLOAD_DELETE:			// Deletion payload
		b = IkeBuildDeletePayload(&p->Payload.Delete);
		break;

	case IKE_PAYLOAD_NAT_OA:			// NAT-OA payload
	case IKE_PAYLOAD_NAT_OA_DRAFT:
	case IKE_PAYLOAD_NAT_OA_DRAFT_2:
		b = IkeBuildNatOaPayload(&p->Payload.NatOa);
		break;

	case IKE_PAYLOAD_KEY_EXCHANGE:		// Key exchange payload
	case IKE_PAYLOAD_HASH:				// Hash payload
	case IKE_PAYLOAD_SIGN:				// Signature payload
	case IKE_PAYLOAD_RAND:				// Random number payload
	case IKE_PAYLOAD_VENDOR_ID:			// Vendor ID payload
	case IKE_PAYLOAD_NAT_D:				// NAT-D payload
	case IKE_PAYLOAD_NAT_D_DRAFT:		// NAT-D payload (draft)
	default:
		b = IkeBuildDataPayload(&p->Payload.GeneralData);
		break;
	}

	if (b != NULL)
	{
		if (p->BitArray != NULL)
		{
			FreeBuf(p->BitArray);
		}
		p->BitArray = CloneBuf(b);
	}

	return b;
}

// Get the payload type of the first item
UCHAR IkeGetFirstPayloadType(LIST *o)
{
	IKE_PACKET_PAYLOAD *p;
	// Validate arguments
	if (o == NULL)
	{
		return IKE_PAYLOAD_NONE;
	}

	if (LIST_NUM(o) == 0)
	{
		return IKE_PAYLOAD_NONE;
	}

	p = (IKE_PACKET_PAYLOAD *)LIST_DATA(o, 0);

	return p->PayloadType;
}

// Build a bit array from the payload list
BUF *IkeBuildPayloadList(LIST *o)
{
	BUF *b;
	UINT i;
	// Validate arguments
	if (o == NULL)
	{
		return NULL;
	}

	b = NewBuf();

	for (i = 0;i < LIST_NUM(o);i++)
	{
		IKE_PACKET_PAYLOAD *p = LIST_DATA(o, i);
		IKE_PACKET_PAYLOAD *next = NULL;
		IKE_COMMON_HEADER h;
		BUF *tmp;

		if (i < (LIST_NUM(o) - 1))
		{
			next = LIST_DATA(o, i + 1);
		}

		Zero(&h, sizeof(h));
		if (next != NULL)
		{
			h.NextPayload = next->PayloadType;
		}
		else
		{
			h.NextPayload = IKE_PAYLOAD_NONE;
		}

		tmp = IkeBuildPayload(p);
		if (tmp != NULL)
		{
			h.PayloadSize = Endian16(tmp->Size + (USHORT)sizeof(h));

			WriteBuf(b, &h, sizeof(h));
			WriteBuf(b, tmp->Buf, tmp->Size);

			FreeBuf(tmp);
		}
	}

	SeekBuf(b, 0, 0);

	return b;
}

// Get the specified payload
IKE_PACKET_PAYLOAD *IkeGetPayload(LIST *o, UINT payload_type, UINT index)
{
	UINT i, num;
	IKE_PACKET_PAYLOAD *ret = NULL;
	// Validate arguments
	if (o == NULL)
	{
		return 0;
	}

	num = 0;

	for (i = 0;i < LIST_NUM(o);i++)
	{
		IKE_PACKET_PAYLOAD *p = LIST_DATA(o, i);

		if (p->PayloadType == payload_type)
		{
			if (num == index)
			{
				ret = p;
				break;
			}

			num++;
		}
	}

	return ret;
}

// Get the number of the payload of the specified type
UINT IkeGetPayloadNum(LIST *o, UINT payload_type)
{
	UINT i, num;
	// Validate arguments
	if (o == NULL)
	{
		return 0;
	}

	num = 0;

	for (i = 0;i < LIST_NUM(o);i++)
	{
		IKE_PACKET_PAYLOAD *p = LIST_DATA(o, i);

		if (p->PayloadType == payload_type)
		{
			num++;
		}
	}

	return num;
}

// Create a deletion payload
IKE_PACKET_PAYLOAD *IkeNewDeletePayload(UCHAR protocol_id, LIST *spi_list)
{
	IKE_PACKET_PAYLOAD *p;
	if (spi_list == NULL)
	{
		return NULL;
	}

	p = IkeNewPayload(IKE_PAYLOAD_DELETE);
	p->Payload.Delete.ProtocolId = protocol_id;
	p->Payload.Delete.SpiList = spi_list;

	return p;
}

// Create a Notification payload
IKE_PACKET_PAYLOAD *IkeNewNoticePayload(UCHAR protocol_id, USHORT message_type,
										void *spi, UINT spi_size,
										void *message, UINT message_size)
{
	IKE_PACKET_PAYLOAD *p;
	if (spi == NULL && spi_size != 0)
	{
		return NULL;
	}
	if (message == NULL && message_size != 0)
	{
		return NULL;
	}

	p = IkeNewPayload(IKE_PAYLOAD_NOTICE);
	p->Payload.Notice.MessageType = message_type;
	p->Payload.Notice.MessageData = MemToBuf(message, message_size);
	p->Payload.Notice.Spi = MemToBuf(spi, spi_size);
	p->Payload.Notice.ProtocolId = protocol_id;

	return p;
}

// Create a Invalid Cookie Payload
IKE_PACKET_PAYLOAD *IkeNewNoticeErrorInvalidCookiePayload(UINT64 init_cookie, UINT64 resp_cookie)
{
	IKE_PACKET_PAYLOAD *ret;
	BUF *b = NewBuf();

	WriteBufInt64(b, init_cookie);
	WriteBufInt64(b, resp_cookie);

	ret = IkeNewNoticePayload(IKE_PROTOCOL_ID_IKE, IKE_NOTICE_ERROR_INVALID_COOKIE, b->Buf, b->Size,
		b->Buf, b->Size);

	FreeBuf(b);

	return ret;
}

// Create an Invalid Exchange Type Payload
IKE_PACKET_PAYLOAD *IkeNewNoticeErrorInvalidExchangeTypePayload(UINT64 init_cookie, UINT64 resp_cookie, UCHAR exchange_type)
{
	IKE_PACKET_PAYLOAD *ret;
	BUF *b = NewBuf();

	WriteBufInt64(b, init_cookie);
	WriteBufInt64(b, resp_cookie);

	ret = IkeNewNoticePayload(IKE_PROTOCOL_ID_IKE, IKE_NOTICE_ERROR_INVALID_EXCHANGE_TYPE, b->Buf, b->Size,
		&exchange_type, 1);

	FreeBuf(b);

	return ret;
}

// Create an Invalid SPI payload
IKE_PACKET_PAYLOAD *IkeNewNoticeErrorInvalidSpiPayload(UINT spi)
{
	IKE_PACKET_PAYLOAD *ret;
	spi = Endian32(spi);

	ret = IkeNewNoticePayload(IKE_PROTOCOL_ID_IPSEC_ESP, IKE_NOTICE_ERROR_INVALID_SPI, &spi, sizeof(UINT),
		&spi, sizeof(UINT));

	return ret;
}

// Create a No Proposal Chosen payload
IKE_PACKET_PAYLOAD *IkeNewNoticeErrorNoProposalChosenPayload(bool quick_mode, UINT64 init_cookie, UINT64 resp_cookie)
{
	BUF *b = NewBuf();
	IKE_PACKET_PAYLOAD *ret;

	WriteBufInt64(b, init_cookie);
	WriteBufInt64(b, resp_cookie);

	ret = IkeNewNoticePayload((quick_mode ? IKE_PROTOCOL_ID_IPSEC_ESP : IKE_PROTOCOL_ID_IKE),
		IKE_NOTICE_ERROR_NO_PROPOSAL_CHOSEN, b->Buf, b->Size,
		NULL, 0);

	FreeBuf(b);

	return ret;
}

// Create a DPD payload
IKE_PACKET_PAYLOAD *IkeNewNoticeDpdPayload(bool ack, UINT64 init_cookie, UINT64 resp_cookie, UINT seq_no)
{
	IKE_PACKET_PAYLOAD *ret;
	BUF *b = NewBuf();

	seq_no = Endian32(seq_no);

	WriteBufInt64(b, init_cookie);
	WriteBufInt64(b, resp_cookie);

	ret = IkeNewNoticePayload(IKE_PROTOCOL_ID_IKE, (ack ? IKE_NOTICE_DPD_RESPONSE : IKE_NOTICE_DPD_REQUEST),
		b->Buf, b->Size,
		&seq_no, sizeof(UINT));

	FreeBuf(b);

	return ret;
}

// Create a Certificate Request Payload
IKE_PACKET_PAYLOAD *IkeNewCertRequestPayload(UCHAR cert_type, void *data, UINT size)
{
	IKE_PACKET_PAYLOAD *p;
	if (data == NULL && size != 0)
	{
		return NULL;
	}

	p = IkeNewPayload(IKE_PAYLOAD_CERT_REQUEST);
	p->Payload.CertRequest.CertType = cert_type;
	p->Payload.CertRequest.Data = MemToBuf(data, size);

	return p;
}

// Create a Certificate payload
IKE_PACKET_PAYLOAD *IkeNewCertPayload(UCHAR cert_type, void *cert_data, UINT cert_size)
{
	IKE_PACKET_PAYLOAD *p;
	if (cert_data == NULL && cert_size != 0)
	{
		return NULL;
	}

	p = IkeNewPayload(IKE_PAYLOAD_CERT);
	p->Payload.Cert.CertType = cert_type;
	p->Payload.Cert.CertData = MemToBuf(cert_data, cert_size);

	return p;
}

// Create an ID payload
IKE_PACKET_PAYLOAD *IkeNewIdPayload(UCHAR id_type, UCHAR protocol_id, USHORT port, void *id_data, UINT id_size)
{
	IKE_PACKET_PAYLOAD *p;
	if (id_data == NULL && id_size != 0)
	{
		return NULL;
	}

	p = IkeNewPayload(IKE_PAYLOAD_ID);
	p->Payload.Id.IdData = MemToBuf(id_data, id_size);
	p->Payload.Id.Port = port;
	p->Payload.Id.ProtocolId = protocol_id;
	p->Payload.Id.Type = id_type;

	return p;
}

// Create a transform payload
IKE_PACKET_PAYLOAD *IkeNewTransformPayload(UCHAR number, UCHAR transform_id, LIST *value_list)
{
	IKE_PACKET_PAYLOAD *p;
	if (value_list == NULL)
	{
		return NULL;
	}

	p = IkeNewPayload(IKE_PAYLOAD_TRANSFORM);
	p->Payload.Transform.Number = number;
	p->Payload.Transform.TransformId = transform_id;
	p->Payload.Transform.ValueList = value_list;

	return p;
}

// Create a proposal payload
IKE_PACKET_PAYLOAD *IkeNewProposalPayload(UCHAR number, UCHAR protocol_id, void *spi, UINT spi_size, LIST *payload_list)
{
	IKE_PACKET_PAYLOAD *p;
	if (payload_list == NULL || (spi == NULL && spi_size != 0))
	{
		return NULL;
	}

	p = IkeNewPayload(IKE_PAYLOAD_PROPOSAL);
	p->Payload.Proposal.Number = number;
	p->Payload.Proposal.ProtocolId = protocol_id;
	p->Payload.Proposal.Spi = MemToBuf(spi, spi_size);
	p->Payload.Proposal.PayloadList = payload_list;

	return p;
}

// Create an SA payload
IKE_PACKET_PAYLOAD *IkeNewSaPayload(LIST *payload_list)
{
	IKE_PACKET_PAYLOAD *p;
	// Validate arguments
	if (payload_list == NULL)
	{
		return NULL;
	}

	p = IkeNewPayload(IKE_PAYLOAD_SA);
	p->Payload.Sa.PayloadList = payload_list;

	return p;
}

// Create a NAT-OA payload
IKE_PACKET_PAYLOAD *IkeNewNatOaPayload(UCHAR payload_type, IP *ip)
{
	IKE_PACKET_PAYLOAD *p;
	// Validate arguments
	if (ip == NULL)
	{
		return NULL;
	}

	p = IkeNewPayload(payload_type);
	Copy(&p->Payload.NatOa.IpAddress, ip, sizeof(IP));
	p->PayloadType = payload_type;

	return p;
}

// Create a data payload
IKE_PACKET_PAYLOAD *IkeNewDataPayload(UCHAR payload_type, void *data, UINT size)
{
	IKE_PACKET_PAYLOAD *p;
	// Validate arguments
	if (data == NULL)
	{
		return NULL;
	}

	p = IkeNewPayload(payload_type);
	p->Payload.GeneralData.Data = MemToBuf(data, size);

	return p;
}

// Create a new payload
IKE_PACKET_PAYLOAD *IkeNewPayload(UINT payload_type)
{
	IKE_PACKET_PAYLOAD *p;

	p = ZeroMalloc(sizeof(IKE_PACKET_PAYLOAD));

	p->PayloadType = payload_type;

	return p;
}

// Analyse the IKE payload body
IKE_PACKET_PAYLOAD *IkeParsePayload(UINT payload_type, BUF *b)
{
	IKE_PACKET_PAYLOAD *p = NULL;
	bool ok = true;
	// Validate arguments
	if (b == NULL)
	{
		return NULL;
	}

	p = ZeroMalloc(sizeof(IKE_PACKET_PAYLOAD));
	p->PayloadType = payload_type;

	switch (p->PayloadType)
	{
	case IKE_PAYLOAD_SA:					// SA payload
		ok = IkeParseSaPayload(&p->Payload.Sa, b);
		break;

	case IKE_PAYLOAD_PROPOSAL:			// Proposal payload
		ok = IkeParseProposalPayload(&p->Payload.Proposal, b);
		break;

	case IKE_PAYLOAD_TRANSFORM:			// Proposal payload
		ok = IkeParseTransformPayload(&p->Payload.Transform, b);
		break;

	case IKE_PAYLOAD_ID:					// ID payload
		ok = IkeParseIdPayload(&p->Payload.Id, b);
		break;

	case IKE_PAYLOAD_CERT:				// Certificate payload
		ok = IkeParseCertPayload(&p->Payload.Cert, b);
		break;

	case IKE_PAYLOAD_CERT_REQUEST:		// Certificate request payload
		ok = IkeParseCertRequestPayload(&p->Payload.CertRequest, b);
		break;

	case IKE_PAYLOAD_NOTICE:				// Notification Payload
		ok = IkeParseNoticePayload(&p->Payload.Notice, b);
		break;

	case IKE_PAYLOAD_DELETE:				// Deletion payload
		ok = IkeParseDeletePayload(&p->Payload.Delete, b);
		break;

	case IKE_PAYLOAD_NAT_OA:
	case IKE_PAYLOAD_NAT_OA_DRAFT:
	case IKE_PAYLOAD_NAT_OA_DRAFT_2:
		ok = IkeParseNatOaPayload(&p->Payload.NatOa, b);
		break;

	case IKE_PAYLOAD_KEY_EXCHANGE:		// Key exchange payload
	case IKE_PAYLOAD_HASH:				// Hash payload
	case IKE_PAYLOAD_SIGN:				// Signature payload
	case IKE_PAYLOAD_RAND:				// Random number payload
	case IKE_PAYLOAD_VENDOR_ID:			// Vendor ID payload
	case IKE_PAYLOAD_NAT_D:				// NAT-D payload
	case IKE_PAYLOAD_NAT_D_DRAFT:		// NAT-D payload (draft)
	default:
		ok = IkeParseDataPayload(&p->Payload.GeneralData, b);
		break;
	}

	if (ok == false)
	{
		Free(p);
		p = NULL;
	}
	else
	{
		p->BitArray = CloneBuf(b);
	}

	return p;
}

// Parse the SA payload
bool IkeParseSaPayload(IKE_PACKET_SA_PAYLOAD *t, BUF *b)
{
	IKE_SA_HEADER *h;
	UCHAR *buf;
	UINT size;
	// Validate arguments
	if (t == NULL || b == NULL)
	{
		return false;
	}

	if (b->Size < sizeof(IKE_SA_HEADER))
	{
		return false;
	}

	h = (IKE_SA_HEADER *)b->Buf;
	buf = (UCHAR *)b->Buf;
	buf += sizeof(IKE_SA_HEADER);
	size = b->Size - sizeof(IKE_SA_HEADER);

	if (Endian32(h->DoI) != IKE_SA_DOI_IPSEC)
	{
		Debug("ISAKMP: Invalid DoI Value: 0x%x\n", Endian32(h->DoI));
		return false;
	}

	if (Endian32(h->Situation) != IKE_SA_SITUATION_IDENTITY)
	{
		Debug("ISAKMP: Invalid Situation Value: 0x%x\n", Endian32(h->Situation));
		return false;
	}

	t->PayloadList = IkeParsePayloadList(buf, size, IKE_PAYLOAD_PROPOSAL);

	return true;
}

// Release the SA payload
void IkeFreeSaPayload(IKE_PACKET_SA_PAYLOAD *t)
{
	// Validate arguments
	if (t == NULL)
	{
		return;
	}

	if (t->PayloadList != NULL)
	{
		IkeFreePayloadList(t->PayloadList);
		t->PayloadList = NULL;
	}
}

// Parse the proposal payload
bool IkeParseProposalPayload(IKE_PACKET_PROPOSAL_PAYLOAD *t, BUF *b)
{
	IKE_PROPOSAL_HEADER *h;
	UCHAR *buf;
	UINT size;
	// Validate arguments
	if (t == NULL || b == NULL)
	{
		return false;
	}

	if (b->Size < sizeof(IKE_PROPOSAL_HEADER))
	{
		return false;
	}

	h = (IKE_PROPOSAL_HEADER *)b->Buf;

	t->Number = h->Number;
	t->ProtocolId = h->ProtocolId;

	buf = (UCHAR *)b->Buf;
	buf += sizeof(IKE_PROPOSAL_HEADER);
	size = b->Size - sizeof(IKE_PROPOSAL_HEADER);

	if (size < (UINT)h->SpiSize)
	{
		return false;
	}

	t->Spi = MemToBuf(buf, h->SpiSize);

	buf += h->SpiSize;
	size -= h->SpiSize;

	t->PayloadList = IkeParsePayloadList(buf, size, IKE_PAYLOAD_TRANSFORM);

	return true;
}

// Release the proposal payload
void IkeFreeProposalPayload(IKE_PACKET_PROPOSAL_PAYLOAD *t)
{
	// Validate arguments
	if (t == NULL)
	{
		return;
	}

	if (t->Spi != NULL)
	{
		FreeBuf(t->Spi);
		t->Spi = NULL;
	}

	if (t->PayloadList != NULL)
	{
		IkeFreePayloadList(t->PayloadList);
		t->PayloadList = NULL;
	}
}

// Parse the transform payload
bool IkeParseTransformPayload(IKE_PACKET_TRANSFORM_PAYLOAD *t, BUF *b)
{
	IKE_TRANSFORM_HEADER h;
	// Validate arguments
	if (t == NULL || b == NULL)
	{
		return false;
	}

	if (ReadBuf(b, &h, sizeof(h)) != sizeof(h))
	{
		return false;
	}

	t->Number = h.Number;
	t->TransformId = h.TransformId;
	t->ValueList = IkeParseTransformValueList(b);

	return true;
}

// Create a new transform value
IKE_PACKET_TRANSFORM_VALUE *IkeNewTransformValue(UCHAR type, UINT value)
{
	IKE_PACKET_TRANSFORM_VALUE *v = ZeroMalloc(sizeof(IKE_PACKET_TRANSFORM_VALUE));

	v->Type = type;
	v->Value = value;

	return v;
}

// Parse the transform value list
LIST *IkeParseTransformValueList(BUF *b)
{
	LIST *o;
	bool ok = true;
	// Validate arguments
	if (b == NULL)
	{
		return NULL;
	}

	o = NewListFast(NULL);

	while (b->Current < b->Size)
	{
		UCHAR af_bit, type;
		USHORT size;
		UINT value = 0;
		IKE_PACKET_TRANSFORM_VALUE *v;

		if (ReadBuf(b, &af_bit, sizeof(af_bit)) != sizeof(af_bit))
		{
			ok = false;
			break;
		}

		if (ReadBuf(b, &type, sizeof(type)) != sizeof(type))
		{
			ok = false;
			break;
		}

		if (ReadBuf(b, &size, sizeof(size)) != sizeof(size))
		{
			ok = false;
		}

		size = Endian16(size);

		if (af_bit == 0)
		{
			UCHAR *tmp = Malloc(size);

			if (ReadBuf(b, tmp, size) != size)
			{
				ok = false;
				Free(tmp);
				break;
			}

			switch (size)
			{
			case sizeof(UINT):
				value = READ_UINT(tmp);
				break;

			case sizeof(USHORT):
				value = READ_USHORT(tmp);
				break;

			case sizeof(UCHAR):
				value = *((UCHAR *)tmp);
				break;
			}

			Free(tmp);
		}
		else
		{
			value = (UINT)size;
		}

		v = ZeroMalloc(sizeof(IKE_PACKET_TRANSFORM_VALUE));
		v->Type = type;
		v->Value = value;

		Add(o, v);
	}

	if (ok == false)
	{
		IkeFreeTransformValueList(o);
		o = NULL;
	}

	return o;
}

// Release the transform value list
void IkeFreeTransformValueList(LIST *o)
{
	UINT i;
	// Validate arguments
	if (o == NULL)
	{
		return;
	}

	for (i = 0;i < LIST_NUM(o);i++)
	{
		IKE_PACKET_TRANSFORM_VALUE *v = LIST_DATA(o, i);

		Free(v);
	}

	ReleaseList(o);
}

// Release the transform payload
void IkeFreeTransformPayload(IKE_PACKET_TRANSFORM_PAYLOAD *t)
{
	// Validate arguments
	if (t == NULL)
	{
		return;
	}

	if (t->ValueList != NULL)
	{
		IkeFreeTransformValueList(t->ValueList);
		t->ValueList = NULL;
	}
}

// Parse the ID payload
bool IkeParseIdPayload(IKE_PACKET_ID_PAYLOAD *t, BUF *b)
{
	IKE_ID_HEADER h;
	IP ip;
	IP subnet;
	// Validate arguments
	if (t == NULL || b == NULL)
	{
		return false;
	}

	if (ReadBuf(b, &h, sizeof(h)) != sizeof(h))
	{
		return false;
	}

	t->Type = h.IdType;
	t->ProtocolId = h.ProtocolId;
	t->Port = Endian16(h.Port);
	t->IdData = ReadRemainBuf(b);
	if (t->IdData == NULL)
	{
		return false;
	}

	Zero(&ip, sizeof(ip));
	Zero(&subnet, sizeof(subnet));

	// Convert to string
	Zero(t->StrData, sizeof(t->StrData));
	switch (t->Type)
	{
	case IKE_ID_FQDN:
	case IKE_ID_USER_FQDN:
	case IKE_ID_KEY_ID:
		Copy(t->StrData, t->IdData->Buf, MIN(t->IdData->Size, sizeof(t->StrData) - 1));
		break;

	case IKE_ID_IPV4_ADDR:
		if (t->IdData->Size == 4)
		{
			Copy(ip.addr, t->IdData->Buf, 4);

			IPToStr(t->StrData, sizeof(t->StrData), &ip);
		}
		break;

	case IKE_ID_IPV6_ADDR:
		if (t->IdData->Size == 16)
		{
			SetIP6(&ip, t->IdData->Buf);

			IPToStr(t->StrData, sizeof(t->StrData), &ip);
		}
		break;

	case IKE_ID_IPV4_ADDR_SUBNET:
		if (t->IdData->Size == 8)
		{
			char ipstr[MAX_SIZE];
			char subnetstr[MAX_SIZE];
			Copy(ip.addr, t->IdData->Buf, 4);
			Copy(subnet.addr, ((UCHAR *)t->IdData->Buf) + 4, 4);

			IPToStr(ipstr, sizeof(ipstr), &ip);
			MaskToStr(subnetstr, sizeof(subnetstr), &subnet);

			Format(t->StrData, sizeof(t->StrData), "%s/%s", ipstr, subnetstr);
		}
		break;

	case IKE_ID_IPV6_ADDR_SUBNET:
		if (t->IdData->Size == 32)
		{
			char ipstr[MAX_SIZE];
			char subnetstr[MAX_SIZE];
			SetIP6(&ip, t->IdData->Buf);
			SetIP6(&subnet, ((UCHAR *)t->IdData->Buf) + 16);

			IPToStr(ipstr, sizeof(ipstr), &ip);
			MaskToStr(subnetstr, sizeof(subnetstr), &subnet);

			Format(t->StrData, sizeof(t->StrData), "%s/%s", ipstr, subnetstr);
		}
		break;
	}

	return true;
}

// Release the ID payload
void IkeFreeIdPayload(IKE_PACKET_ID_PAYLOAD *t)
{
	// Validate arguments
	if (t == NULL)
	{
		return;
	}

	if (t->IdData != NULL)
	{
		FreeBuf(t->IdData);
		t->IdData = NULL;
	}
}

// Parse the certificate payload
bool IkeParseCertPayload(IKE_PACKET_CERT_PAYLOAD *t, BUF *b)
{
	IKE_CERT_HEADER h;
	// Validate arguments
	if (t == NULL || b == NULL)
	{
		return false;
	}

	if (ReadBuf(b, &h, sizeof(h)) != sizeof(h))
	{
		return false;
	}

	t->CertType = h.CertType;
	t->CertData = ReadRemainBuf(b);
	if (t->CertData == NULL)
	{
		return false;
	}

	return true;
}

// Release the certificate payload
void IkeFreeCertPayload(IKE_PACKET_CERT_PAYLOAD *t)
{
	// Validate arguments
	if (t == NULL)
	{
		return;
	}

	if (t->CertData != NULL)
	{
		FreeBuf(t->CertData);
		t->CertData = NULL;
	}
}

// Parse the certificate request payload
bool IkeParseCertRequestPayload(IKE_PACKET_CERT_REQUEST_PAYLOAD *t, BUF *b)
{
	IKE_CERT_REQUEST_HEADER h;
	// Validate arguments
	if (t == NULL || b == NULL)
	{
		return false;
	}

	if (ReadBuf(b, &h, sizeof(h)) != sizeof(h))
	{
		return false;
	}

	t->CertType = h.CertType;
	t->Data = ReadRemainBuf(b);
	if (t->Data == NULL)
	{
		return false;
	}

	return true;
}

// Release the certificate request payload
void IkeFreeCertRequestPayload(IKE_PACKET_CERT_REQUEST_PAYLOAD *t)
{
	// Validate arguments
	if (t == NULL)
	{
		return;
	}

	if (t->Data != NULL)
	{
		FreeBuf(t->Data);
		t->Data = NULL;
	}
}

// Parse the notification payload
bool IkeParseNoticePayload(IKE_PACKET_NOTICE_PAYLOAD *t, BUF *b)
{
	IKE_NOTICE_HEADER h;
	// Validate arguments
	if (t == NULL || b == NULL)
	{
		return false;
	}

	if (ReadBuf(b, &h, sizeof(h)) != sizeof(h))
	{
		return false;
	}

	if (Endian32(h.DoI) != IKE_SA_DOI_IPSEC)
	{
		Debug("ISAKMP: Invalid DoI Value: 0x%x\n", Endian32(h.DoI));
		return false;
	}

	t->MessageType = Endian16(h.MessageType);
	t->ProtocolId = h.ProtocolId;
	t->Spi = ReadBufFromBuf(b, h.SpiSize);
	if (t->Spi == NULL)
	{
		return false;
	}
	t->MessageData = ReadRemainBuf(b);

	return true;
}

// Release the notification payload
void IkeFreeNoticePayload(IKE_PACKET_NOTICE_PAYLOAD *t)
{
	// Validate arguments
	if (t == NULL)
	{
		return;
	}

	if (t->MessageData != NULL)
	{
		FreeBuf(t->MessageData);
		t->MessageData = NULL;
	}

	if (t->Spi != NULL)
	{
		FreeBuf(t->Spi);
		t->Spi = NULL;
	}
}

// Parse the NAT-OA payload
bool IkeParseNatOaPayload(IKE_PACKET_NAT_OA_PAYLOAD *t, BUF *b)
{
	IKE_NAT_OA_HEADER h;
	UCHAR ip4[4];
	UCHAR ip6[16];
	IP ip;
	// Validate arguments
	if (t == NULL || b == NULL)
	{
		return false;
	}

	Zero(&ip, sizeof(ip));

	if (ReadBuf(b, &h, sizeof(h)) != sizeof(h))
	{
		return false;
	}

	if (h.IdType != IKE_ID_IPV4_ADDR && h.IdType != IKE_ID_IPV6_ADDR)
	{
		return false;
	}

	switch (h.IdType)
	{
	case IKE_ID_IPV4_ADDR:	// IPv4
		if (ReadBuf(b, ip4, sizeof(ip4)) != sizeof(ip4))
		{
			return false;
		}

		SetIP(&ip, ip4[0], ip4[1], ip4[2], ip4[3]);

		break;

	case IKE_ID_IPV6_ADDR:	// IPv6
		if (ReadBuf(b, ip6, sizeof(ip6)) != sizeof(ip6))
		{
			return false;
		}

		SetIP6(&ip, ip6);

		break;

	default:
		return false;
	}

	Copy(&t->IpAddress, &ip, sizeof(IP));

	return true;
}

// Parse the deletion payload
bool IkeParseDeletePayload(IKE_PACKET_DELETE_PAYLOAD *t, BUF *b)
{
	IKE_DELETE_HEADER h;
	UINT num_spi;
	UINT spi_size;
	UINT i;
	bool ok = true;
	// Validate arguments
	if (t == NULL || b == NULL)
	{
		return false;
	}

	if (ReadBuf(b, &h, sizeof(h)) != sizeof(h))
	{
		return false;
	}

	if (Endian32(h.DoI) != IKE_SA_DOI_IPSEC)
	{
		Debug("ISAKMP: Invalid DoI Value: 0x%x\n", Endian32(h.DoI));
		return false;
	}

	t->ProtocolId = h.ProtocolId;
	t->SpiList = NewListFast(NULL);
	num_spi = Endian16(h.NumSpis);
	spi_size = h.SpiSize;

	for (i = 0;i < num_spi;i++)
	{
		BUF *spi = ReadBufFromBuf(b, spi_size);

		if (spi == NULL)
		{
			ok = false;
			break;
		}

		Add(t->SpiList, spi);
	}

	if (ok == false)
	{
		IkeFreeDeletePayload(t);
		return false;
	}

	return true;
}

// Release the deletion payload
void IkeFreeDeletePayload(IKE_PACKET_DELETE_PAYLOAD *t)
{
	UINT i;
	// Validate arguments
	if (t == NULL)
	{
		return;
	}

	if (t->SpiList != NULL)
	{
		for (i = 0;i < LIST_NUM(t->SpiList);i++)
		{
			BUF *spi = LIST_DATA(t->SpiList, i);

			FreeBuf(spi);
		}

		ReleaseList(t->SpiList);

		t->SpiList = NULL;
	}
}

// Check whether the hash matches
bool IkeCompareHash(IKE_PACKET_PAYLOAD *hash_payload, void *hash_data, UINT hash_size)
{
	//char tmp1[MAX_SIZE], tmp2[MAX_SIZE];
	// Validate arguments
	if (hash_payload == NULL || hash_data == NULL || hash_size == 0)
	{
		return false;
	}

	if (hash_payload->PayloadType != IKE_PAYLOAD_HASH)
	{
		return false;
	}

	if (hash_payload->Payload.Hash.Data == NULL)
	{
		return false;
	}

	if (hash_payload->Payload.Hash.Data->Size != hash_size)
	{
		return false;
	}

	//BinToStrEx(tmp1, sizeof(tmp1), hash_payload->Payload.Hash.Data->Buf, hash_size);
	//BinToStrEx(tmp2, sizeof(tmp2), hash_data, hash_size);

	//Debug("IkeCompareHash\n  1: %s\n  2: %s\n", tmp1, tmp2);

	if (Cmp(hash_payload->Payload.Hash.Data->Buf, hash_data, hash_size) != 0)
	{
		return false;
	}

	return true;
}

// Parse the data payload
bool IkeParseDataPayload(IKE_PACKET_DATA_PAYLOAD *t, BUF *b)
{
	// Validate arguments
	if (t == NULL || b == NULL)
	{
		return false;
	}

	t->Data = MemToBuf(b->Buf, b->Size);

	return true;
}

// Release the data payload
void IkeFreeDataPayload(IKE_PACKET_DATA_PAYLOAD *t)
{
	// Validate arguments
	if (t == NULL)
	{
		return;
	}

	FreeBuf(t->Data);
}

// Release the IKE payload body
void IkeFreePayload(IKE_PACKET_PAYLOAD *p)
{
	// Validate arguments
	if (p == NULL)
	{
		return;
	}

	switch (p->PayloadType)
	{
	case IKE_PAYLOAD_SA:					// SA payload
		IkeFreeSaPayload(&p->Payload.Sa);
		break;

	case IKE_PAYLOAD_PROPOSAL:			// Proposal payload
		IkeFreeProposalPayload(&p->Payload.Proposal);
		break;

	case IKE_PAYLOAD_TRANSFORM:			// Proposal payload
		IkeFreeTransformPayload(&p->Payload.Transform);
		break;

	case IKE_PAYLOAD_ID:					// ID payload
		IkeFreeIdPayload(&p->Payload.Id);
		break;

	case IKE_PAYLOAD_CERT:				// Certificate payload
		IkeFreeCertPayload(&p->Payload.Cert);
		break;

	case IKE_PAYLOAD_CERT_REQUEST:		// Certificate request payload
		IkeFreeCertRequestPayload(&p->Payload.CertRequest);
		break;

	case IKE_PAYLOAD_NOTICE:				// Notification Payload
		IkeFreeNoticePayload(&p->Payload.Notice);
		break;

	case IKE_PAYLOAD_DELETE:				// Deletion payload
		IkeFreeDeletePayload(&p->Payload.Delete);
		break;

	case IKE_PAYLOAD_NAT_OA:				// NAT-OD payload
	case IKE_PAYLOAD_NAT_OA_DRAFT:
	case IKE_PAYLOAD_NAT_OA_DRAFT_2:
		// Do Nothing
		break;

	case IKE_PAYLOAD_KEY_EXCHANGE:		// Key exchange payload
	case IKE_PAYLOAD_HASH:				// Hash payload
	case IKE_PAYLOAD_SIGN:				// Signature payload
	case IKE_PAYLOAD_RAND:				// Random number payload
	case IKE_PAYLOAD_VENDOR_ID:			// Vendor ID payload
	case IKE_PAYLOAD_NAT_D:				// NAT-D payload
	case IKE_PAYLOAD_NAT_D_DRAFT:		// NAT-D payload (draft)
	default:
		IkeFreeDataPayload(&p->Payload.GeneralData);
		break;
	}

	if (p->BitArray != NULL)
	{
		FreeBuf(p->BitArray);
	}

	Free(p);
}

// Analyse the IKE payload list
LIST *IkeParsePayloadList(void *data, UINT size, UCHAR first_payload)
{
	return IkeParsePayloadListEx(data, size, first_payload, NULL);
}
LIST *IkeParsePayloadListEx(void *data, UINT size, UCHAR first_payload, UINT *total_read_size)
{
	LIST *o;
	BUF *b;
	UCHAR payload_type = first_payload;
	UINT total = 0;
	// Validate arguments
	if (data == NULL)
	{
		return NULL;
	}

	o = NewListFast(NULL);
	b = MemToBuf(data, size);

	while (payload_type != IKE_PAYLOAD_NONE)
	{
		// Read the common header
		IKE_COMMON_HEADER header;
		USHORT payload_size;
		BUF *payload_data;
		IKE_PACKET_PAYLOAD *pay;

		if (ReadBuf(b, &header, sizeof(header)) != sizeof(header))
		{
			Debug("ISAKMP: Broken Packet (Invalid Payload Size)\n");

LABEL_ERROR:
			// Header reading failure
			IkeFreePayloadList(o);
			o = NULL;

			break;
		}

		total += sizeof(header);

		// Get the payload size
		payload_size = Endian16(header.PayloadSize);

		if (payload_size < sizeof(header))
		{
			Debug("ISAKMP: Broken Packet (Invalid Payload Size)\n");
			goto LABEL_ERROR;
		}

		payload_size -= sizeof(header);

		// Read the payload data
		payload_data = ReadBufFromBuf(b, payload_size);
		if (payload_data == NULL)
		{
			// Data read failure
			Debug("ISAKMP: Broken Packet (Invalid Payload Data)\n");
			goto LABEL_ERROR;
		}

		total += payload_size;

		// Analyse the payload body
		if (IKE_IS_SUPPORTED_PAYLOAD_TYPE(payload_type))
		{
			// Supported payload type
			pay = IkeParsePayload(payload_type, payload_data);

			if (pay == NULL)
			{
				FreeBuf(payload_data);
				Debug("ISAKMP: Broken Packet (Payload Data Parse Failed)\n");
				goto LABEL_ERROR;
			}

			Add(o, pay);
		}
		else
		{
			// Unsupported payload type
			Debug("ISAKMP: Ignored Payload Type: %u\n", payload_type);
			pay = IkeParsePayload(payload_type, payload_data);

			if (pay == NULL)
			{
				FreeBuf(payload_data);
				Debug("ISAKMP: Broken Packet (Payload Data Parse Failed)\n");
				goto LABEL_ERROR;
			}

			Add(o, pay);
		}

		payload_type = header.NextPayload;

		FreeBuf(payload_data);
	}

	FreeBuf(b);

	if (total_read_size != NULL)
	{
		*total_read_size = total;
	}

	return o;
}

// Release the IKE payload list
void IkeFreePayloadList(LIST *o)
{
	UINT i;
	// Validate arguments
	if (o == NULL)
	{
		return;
	}

	for (i = 0;i < LIST_NUM(o);i++)
	{
		IKE_PACKET_PAYLOAD *p = LIST_DATA(o, i);

		IkeFreePayload(p);
	}

	ReleaseList(o);
}

// Build an IKE packet
BUF *IkeBuild(IKE_PACKET *p, IKE_CRYPTO_PARAM *cparam)
{
	return IkeBuildEx(p, cparam, false);
}
BUF *IkeBuildEx(IKE_PACKET *p, IKE_CRYPTO_PARAM *cparam, bool use_original_decrypted)
{
	IKE_HEADER h;
	BUF *msg_buf;
	BUF *ret;
	// Validate arguments
	if (p == NULL)
	{
		return NULL;
	}

	if (p->PayloadList == NULL)
	{
		return NULL;
	}

	Zero(&h, sizeof(h));
	h.InitiatorCookie = Endian64(p->InitiatorCookie);
	h.ResponderCookie = Endian64(p->ResponderCookie);
	h.NextPayload = IkeGetFirstPayloadType(p->PayloadList);
	h.Version = IKE_VERSION;
	h.ExchangeType = p->ExchangeType;
	h.Flag = (p->FlagEncrypted ? IKE_HEADER_FLAG_ENCRYPTED : 0) |
		(p->FlagCommit ? IKE_HEADER_FLAG_COMMIT : 0) |
		(p->FlagAuthOnly ? IKE_HEADER_FLAG_AUTH_ONLY : 0);
	h.MessageId = Endian32(p->MessageId);

	if (p->DecryptedPayload != NULL && use_original_decrypted)
	{
		msg_buf = CloneBuf(p->DecryptedPayload);
	}
	else
	{
		msg_buf = IkeBuildPayloadList(p->PayloadList);
	}

	if (p->DecryptedPayload != NULL)
	{
		FreeBuf(p->DecryptedPayload);
	}

	p->DecryptedPayload = CloneBuf(msg_buf);

	if (p->FlagEncrypted)
	{
		BUF *b;
		// Encryption
		b = IkeEncryptWithPadding(msg_buf->Buf, msg_buf->Size, cparam);

		if (b == NULL)
		{
			Debug("ISAKMP: Packet Encrypt Failed\n");
			FreeBuf(msg_buf);
			return NULL;
		}

		FreeBuf(msg_buf);

		msg_buf = b;
	}

	h.MessageSize = Endian32(msg_buf->Size + sizeof(h));

	ret = NewBuf();
	WriteBuf(ret, &h, sizeof(h));
	WriteBufBuf(ret, msg_buf);

	FreeBuf(msg_buf);

	SeekBuf(ret, 0, 0);

	return ret;
}

// Analyse the IKE packet
IKE_PACKET *IkeParseEx(void *data, UINT size, IKE_CRYPTO_PARAM *cparam, bool header_only)
{
	IKE_PACKET *p = NULL;
	BUF *b;
	// Validate arguments
	if (data == NULL)
	{
		return NULL;
	}

	b = MemToBuf(data, size);

	if (b->Size < sizeof(IKE_HEADER))
	{
		Debug("ISAKMP: Invalid Packet Size\n");
	}
	else
	{
		// Header analysis
		IKE_HEADER *h = (IKE_HEADER *)b->Buf;

		p = ZeroMalloc(sizeof(IKE_PACKET));

		p->MessageSize = Endian32(h->MessageSize);
		p->InitiatorCookie = Endian64(h->InitiatorCookie);
		p->ResponderCookie = Endian64(h->ResponderCookie);
		p->ExchangeType = h->ExchangeType;
		p->FlagEncrypted = (h->Flag & IKE_HEADER_FLAG_ENCRYPTED) ? true : false;
		p->FlagCommit = (h->Flag & IKE_HEADER_FLAG_COMMIT) ? true : false;
		p->FlagAuthOnly = (h->Flag & IKE_HEADER_FLAG_AUTH_ONLY) ? true : false;
		p->MessageId = Endian32(h->MessageId);

		if (b->Size < Endian32(h->MessageSize) ||
			Endian32(h->MessageSize) < sizeof(IKE_HEADER))
		{
			Debug("ISAKMP: Invalid Packet Size\n");

			IkeFree(p);
			p = NULL;
		}
		else
		{
			if (header_only == false)
			{
				bool ok = false;
				UCHAR *payload_data;
				UINT payload_size;
				BUF *buf = NULL;

				payload_data = ((UCHAR *)h) + sizeof(IKE_HEADER);
				payload_size = Endian32(h->MessageSize) - sizeof(IKE_HEADER);

				// Decrypt if it is encrypted
				if (p->FlagEncrypted)
				{
					buf = IkeDecrypt(payload_data, payload_size, cparam);

					if (buf != NULL)
					{
						ok = true;

						payload_data = buf->Buf;
						payload_size = buf->Size;

						p->DecryptedPayload = CloneBuf(buf);
					}
				}
				else
				{
					ok = true;
				}

				if (ok == false)
				{
					Debug("ISAKMP: Decrypt Failed\n");

					IkeFree(p);
					p = NULL;
				}
				else
				{
					UINT total_read_size;

					// Payload analysis
					p->PayloadList = IkeParsePayloadListEx(payload_data,
						payload_size,
						h->NextPayload,
						&total_read_size);

					if (p->DecryptedPayload != NULL)
					{
						p->DecryptedPayload->Size = MIN(p->DecryptedPayload->Size, total_read_size);
					}
					else
					{
						p->DecryptedPayload = MemToBuf(payload_data, payload_size);
					}
				}

				if (buf != NULL)
				{
					FreeBuf(buf);
				}
			}
		}
	}

	FreeBuf(b);

	return p;
}
IKE_PACKET *IkeParseHeader(void *data, UINT size, IKE_CRYPTO_PARAM *cparam)
{
	return IkeParseEx(data, size, cparam, true);
}
IKE_PACKET *IkeParse(void *data, UINT size, IKE_CRYPTO_PARAM *cparam)
{
	return IkeParseEx(data, size, cparam, false);
}

// Send packet for debugging by UDP (For debugging with Ethereal)
void IkeDebugUdpSendRawPacket(IKE_PACKET *p)
{
	BUF *b;
	IP ip;
	SOCK *udp;
	// Validate arguments
	if (p == NULL)
	{
		return;
	}

	p->FlagEncrypted = false;

	b = NULL;

	if (b == NULL)
	{
		b = IkeBuildEx(p, NULL, true);
	}

	if (b == NULL)
	{
		return;
	}

	Zero(&ip, sizeof(ip));
	SetIP(&ip, 1, 2, 3, 4);

	udp = NewUDP(0);

	SendTo(udp, &ip, 500, b->Buf, b->Size);

	ReleaseSock(udp);
	FreeBuf(b);
}

// Output the payload list
void IkeDebugPrintPayloads(LIST *o, UINT depth)
{
	UINT i;
	char space[MAX_SIZE];
	// Validate arguments
	if (o == NULL)
	{
		return;
	}

	MakeCharArray2(space, ' ', depth * 2);

	for (i = 0;i < LIST_NUM(o);i++)
	{
		IKE_PACKET_PAYLOAD *payload = LIST_DATA(o, i);

		Debug("%s%u: Type = %u, Size = %u\n", space, i, payload->PayloadType, payload->BitArray->Size);

		switch (payload->PayloadType)
		{
		case IKE_PAYLOAD_SA:
			IkeDebugPrintPayloads(payload->Payload.Sa.PayloadList, depth + 1);
			break;

		case IKE_PAYLOAD_PROPOSAL:
			IkeDebugPrintPayloads(payload->Payload.Proposal.PayloadList, depth + 1);
			break;
		}
	}
}

// Encryption (also with padding)
BUF *IkeEncryptWithPadding(void *data, UINT size, IKE_CRYPTO_PARAM *cparam)
{
	UINT total_size;
	UINT i;
	UCHAR n = 0;
	UCHAR *tmp;
	BUF *ret;
	UCHAR tmp1600[1600];
	bool no_free = false;
	// Validate arguments
	if (data == NULL || cparam == NULL)
	{
		return NULL;
	}

	total_size = ((size / cparam->Key->Crypto->BlockSize) + ((size % cparam->Key->Crypto->BlockSize) == 0 ? 0 : 1))
		* cparam->Key->Crypto->BlockSize;
	if (total_size == 0)
	{
		total_size = cparam->Key->Crypto->BlockSize;
	}

	if (total_size > sizeof(tmp1600))
	{
		tmp = Malloc(total_size);
	}
	else
	{
		tmp = tmp1600;
		no_free = true;
	}

	Copy(tmp, data, size);

	for (i = size;i < total_size;i++)
	{
		tmp[i] = ++n;
	}

	ret = IkeEncrypt(tmp, total_size, cparam);

	if (no_free == false)
	{
		Free(tmp);
	}

	return ret;
}

// Encryption
BUF *IkeEncrypt(void *data, UINT size, IKE_CRYPTO_PARAM *cparam)
{
	void *tmp;
	BUF *b;
	UCHAR tmp1600[1600];
	bool no_free = false;
	// Validate arguments
	if (data == NULL || cparam == NULL)
	{
		return NULL;
	}

	if ((size % cparam->Key->Crypto->BlockSize) != 0)
	{
		// Not an integral multiple of block size
		return NULL;
	}

	if (size > sizeof(tmp1600))
	{
		tmp = Malloc(size);
	}
	else
	{
		tmp = tmp1600;
		no_free = true;
	}

	IkeCryptoEncrypt(cparam->Key, tmp, data, size, cparam->Iv);

	if (size >= cparam->Key->Crypto->BlockSize)
	{
		Copy(cparam->NextIv, ((UCHAR *)tmp) + (size - cparam->Key->Crypto->BlockSize), cparam->Key->Crypto->BlockSize);
	}
	else
	{
		Zero(cparam->NextIv, cparam->Key->Crypto->BlockSize);
	}

	b = MemToBuf(tmp, size);

	if (no_free == false)
	{
		Free(tmp);
	}

	return b;
}

// Decryption
BUF *IkeDecrypt(void *data, UINT size, IKE_CRYPTO_PARAM *cparam)
{
	void *tmp;
	BUF *b;
	UCHAR tmp1600[1600];
	bool no_free = false;
	// Validate arguments
	if (data == NULL || cparam == NULL)
	{
		return NULL;
	}

	if ((size % cparam->Key->Crypto->BlockSize) != 0)
	{
		// Not an integral multiple of block size
		return NULL;
	}

	if (size > sizeof(tmp1600))
	{
		tmp = Malloc(size);
	}
	else
	{
		tmp = tmp1600;
		no_free = true;
	}

	IkeCryptoDecrypt(cparam->Key, tmp, data, size, cparam->Iv);

	if (size >= cparam->Key->Crypto->BlockSize)
	{
		Copy(cparam->NextIv, ((UCHAR *)data) + (size - cparam->Key->Crypto->BlockSize), cparam->Key->Crypto->BlockSize);
	}
	else
	{
		Zero(cparam->NextIv, cparam->Key->Crypto->BlockSize);
	}

	b = MemToBuf(tmp, size);

	if (no_free == false)
	{
		Free(tmp);
	}

	return b;
}

// Release the IKE packet
void IkeFree(IKE_PACKET *p)
{
	// Validate arguments
	if (p == NULL)
	{
		return;
	}

	if (p->PayloadList != NULL)
	{
		IkeFreePayloadList(p->PayloadList);
	}

	if (p->DecryptedPayload != NULL)
	{
		FreeBuf(p->DecryptedPayload);
	}

	Free(p);
}

// Create an IKE packet
IKE_PACKET *IkeNew(UINT64 init_cookie, UINT64 resp_cookie, UCHAR exchange_type,
				   bool encrypted, bool commit, bool auth_only, UINT msg_id,
				   LIST *payload_list)
{
	IKE_PACKET *p = ZeroMalloc(sizeof(IKE_PACKET));

	p->InitiatorCookie = init_cookie;
	p->ResponderCookie = resp_cookie;
	p->ExchangeType = exchange_type;
	p->FlagEncrypted = encrypted;
	p->FlagCommit = commit;
	p->FlagAuthOnly = auth_only;
	p->MessageId = msg_id;
	p->PayloadList = payload_list;

	return p;
}

// Create a new SPI value
UINT IkeNewSpi()
{
	while (true)
	{
		UINT i = Rand32();

		if (i >= 4096)
		{
			return i;
		}
	}
}


// Create an encryption engine for IKE
IKE_ENGINE *NewIkeEngine()
{
	IKE_ENGINE *e = ZeroMalloc(sizeof(IKE_ENGINE));
	IKE_CRYPTO *des, *des3, *aes;
	IKE_HASH *sha1, *md5;
	IKE_DH *dh1, *dh2, *dh5;
	UINT des_key_sizes[] =
	{
		8,
	};
	UINT des3_key_sizes[] =
	{
		24,
	};
	UINT aes_key_sizes[] =
	{
		16, 24, 32,
	};

	e->CryptosList = NewListFast(NULL);
	e->HashesList = NewListFast(NULL);
	e->DhsList = NewListFast(NULL);

	//// Encryption algorithm
	// DES
	des = NewIkeCrypto(e, IKE_CRYPTO_DES_ID, IKE_CRYPTO_DES_STRING,
		des_key_sizes, sizeof(des_key_sizes) / sizeof(UINT), 8);

	// 3DES
	des3 = NewIkeCrypto(e, IKE_CRYPTO_3DES_ID, IKE_CRYPTO_3DES_STRING,
		des3_key_sizes, sizeof(des3_key_sizes) / sizeof(UINT), 8);

	// AES
	aes = NewIkeCrypto(e, IKE_CRYPTO_AES_ID, IKE_CRYPTO_AES_STRING,
		aes_key_sizes, sizeof(aes_key_sizes) / sizeof(UINT), 16);

	//// Hash algorithm
	// SHA-1
	sha1 = NewIkeHash(e, IKE_HASH_SHA1_ID, IKE_HASH_SHA1_STRING, 20);

	// MD5
	md5 = NewIkeHash(e, IKE_HASH_MD5_ID, IKE_HASH_MD5_STRING, 16);

	//// DH algorithm
	dh1 = NewIkeDh(e, IKE_DH_1_ID, IKE_DH_1_STRING, 96);
	dh2 = NewIkeDh(e, IKE_DH_2_ID, IKE_DH_2_STRING, 128);
	dh5 = NewIkeDh(e, IKE_DH_5_ID, IKE_DH_5_STRING, 192);

	// Define the IKE algorithm
	e->IkeCryptos[IKE_P1_CRYPTO_DES_CBC] = des;
	e->IkeCryptos[IKE_P1_CRYPTO_3DES_CBC] = des3;
	e->IkeCryptos[IKE_P1_CRYPTO_AES_CBC] = aes;
	e->IkeHashes[IKE_P1_HASH_MD5] = md5;
	e->IkeHashes[IKE_P1_HASH_SHA1] = sha1;

	// Definition of ESP algorithm
	e->EspCryptos[IKE_TRANSFORM_ID_P2_ESP_DES] = des;
	e->EspCryptos[IKE_TRANSFORM_ID_P2_ESP_3DES] = des3;
	e->EspCryptos[IKE_TRANSFORM_ID_P2_ESP_AES] = aes;
	e->EspHashes[IKE_P2_HMAC_MD5_96] = md5;
	e->EspHashes[IKE_P2_HMAC_SHA1_96] = sha1;

	// Definition of the DH algorithm
	e->IkeDhs[IKE_P1_DH_GROUP_768_MODP] = e->EspDhs[IKE_P2_DH_GROUP_768_MODP] = dh1;
	e->IkeDhs[IKE_P1_DH_GROUP_1024_MODP] = e->EspDhs[IKE_P2_DH_GROUP_1024_MODP] = dh2;
	e->IkeDhs[IKE_P1_DH_GROUP_1536_MODP] = e->EspDhs[IKE_P2_DH_GROUP_1536_MODP] = dh5;

	return e;
}

// Release the encryption engine for IKE
void FreeIkeEngine(IKE_ENGINE *e)
{
	UINT i;
	// Validate arguments
	if (e == NULL)
	{
		return;
	}

	for (i = 0;i < LIST_NUM(e->CryptosList);i++)
	{
		IKE_CRYPTO *c = LIST_DATA(e->CryptosList, i);

		FreeIkeCrypto(c);
	}

	ReleaseList(e->CryptosList);

	for (i = 0;i < LIST_NUM(e->HashesList);i++)
	{
		IKE_HASH *h = LIST_DATA(e->HashesList, i);

		FreeIkeHash(h);
	}
	ReleaseList(e->HashesList);

	for (i = 0;i < LIST_NUM(e->DhsList);i++)
	{
		IKE_DH *d = LIST_DATA(e->DhsList, i);

		FreeIkeDh(d);
	}
	ReleaseList(e->DhsList);

	Free(e);
}

// Definition of a new DH algorithm for IKE
IKE_DH *NewIkeDh(IKE_ENGINE *e, UINT dh_id, char *name, UINT key_size)
{
	IKE_DH *d;
	// Validate arguments
	if (e == NULL || name == NULL || key_size == 0)
	{
		return NULL;
	}

	d = ZeroMalloc(sizeof(IKE_DH));

	d->DhId = dh_id;
	d->Name = name;
	d->KeySize = key_size;

	Add(e->DhsList, d);

	return d;
}

// Definition of a new encryption algorithm for IKE
IKE_CRYPTO *NewIkeCrypto(IKE_ENGINE *e, UINT crypto_id, char *name, UINT *key_sizes, UINT num_key_sizes, UINT block_size)
{
	IKE_CRYPTO *c;
	UINT i;
	// Validate arguments
	if (e == NULL || name == NULL || key_sizes == NULL)
	{
		return NULL;
	}

	c = ZeroMalloc(sizeof(IKE_CRYPTO));

	c->CryptoId = crypto_id;
	c->Name = name;

	for (i = 0;i < MIN(num_key_sizes, 16);i++)
	{
		c->KeySizes[i] = key_sizes[i];
	}

	if (num_key_sizes >= 2)
	{
		c->VariableKeySize = true;
	}

	c->BlockSize = block_size;

	Add(e->CryptosList, c);

	return c;
}

// Release the definition of Encryption algorithm for IKE
void FreeIkeCrypto(IKE_CRYPTO *c)
{
	// Validate arguments
	if (c == NULL)
	{
		return;
	}

	Free(c);
}

// Release the definition of IKE hash algorithm
void FreeIkeHash(IKE_HASH *h)
{
	// Validate arguments
	if (h == NULL)
	{
		return;
	}

	Free(h);
}

// Release the definition of the DH algorithm for IKE
void FreeIkeDh(IKE_DH *d)
{
	// Validate arguments
	if (d == NULL)
	{
		return;
	}

	Free(d);
}

// Definition of a new hash algorithm for IKE
IKE_HASH *NewIkeHash(IKE_ENGINE *e, UINT hash_id, char *name, UINT size)
{
	IKE_HASH *h;
	// Validate arguments
	if (e == NULL || name == NULL || size == 0)
	{
		return NULL;
	}

	h = ZeroMalloc(sizeof(IKE_HASH));

	h->HashId = hash_id;
	h->Name = name;
	h->HashSize = size;

	Add(e->HashesList, h);

	return h;
}

// Get the encryption algorithm that is used in IKE
IKE_CRYPTO *GetIkeCrypto(IKE_ENGINE *e, bool for_esp, UINT i)
{
	// Validate arguments
	if (e == NULL || i == 0 || i >= MAX_IKE_ENGINE_ELEMENTS)
	{
		return NULL;
	}

	if (for_esp)
	{
		return e->EspCryptos[i];
	}
	else
	{
		return e->IkeCryptos[i];
	}
}

// Get the hash algorithm used in the IKE
IKE_HASH *GetIkeHash(IKE_ENGINE *e, bool for_esp, UINT i)
{
	// Validate arguments
	if (e == NULL || i == 0 || i >= MAX_IKE_ENGINE_ELEMENTS)
	{
		return NULL;
	}

	if (for_esp)
	{
		return e->EspHashes[i];
	}
	else
	{
		return e->IkeHashes[i];
	}
}

// Get the DH algorithm used in the IKE
IKE_DH *GetIkeDh(IKE_ENGINE *e, bool for_esp, UINT i)
{
	// Validate arguments
	if (e == NULL || i == 0 || i >= MAX_IKE_ENGINE_ELEMENTS)
	{
		return NULL;
	}

	if (for_esp)
	{
		return e->EspDhs[i];
	}
	else
	{
		return e->IkeDhs[i];
	}
}

// Perform encryption
void IkeCryptoEncrypt(IKE_CRYPTO_KEY *k, void *dst, void *src, UINT size, void *ivec)
{
	// Validate arguments
	if (k == NULL || dst == NULL || src == NULL || size == 0 || ivec == NULL)
	{
		Zero(dst, size);
		return;
	}

	if ((size % k->Crypto->BlockSize) != 0)
	{
		Zero(dst, size);
		return;
	}

	switch (k->Crypto->CryptoId)
	{
	case IKE_CRYPTO_DES_ID:		// DES
		DesEncrypt(dst, src, size, k->DesKey1, ivec);
		break;

	case IKE_CRYPTO_3DES_ID:	// 3DES
		Des3Encrypt2(dst, src, size, k->DesKey1, k->DesKey2, k->DesKey3, ivec);
		break;

	case IKE_CRYPTO_AES_ID:		// AES
		AesEncrypt(dst, src, size, k->AesKey, ivec);
		break;

	default:
		// Unknown
		Zero(dst, size);
		break;
	}
}

// Perform decryption
void IkeCryptoDecrypt(IKE_CRYPTO_KEY *k, void *dst, void *src, UINT size, void *ivec)
{
	// Validate arguments
	if (k == NULL || dst == NULL || src == NULL || size == 0 || ivec == NULL)
	{
		Zero(dst, size);
		return;
	}

	if ((size % k->Crypto->BlockSize) != 0)
	{
		Zero(dst, size);
		return;
	}

	switch (k->Crypto->CryptoId)
	{
	case IKE_CRYPTO_DES_ID:		// DES
		DesDecrypt(dst, src, size, k->DesKey1, ivec);
		break;

	case IKE_CRYPTO_3DES_ID:	// 3DES
		Des3Decrypt2(dst, src, size, k->DesKey1, k->DesKey2, k->DesKey3, ivec);
		break;

	case IKE_CRYPTO_AES_ID:		// AES
		AesDecrypt(dst, src, size, k->AesKey, ivec);
		break;

	default:
		// Unknown
		Zero(dst, size);
		break;
	}
}

// Calculate a hash
void IkeHash(IKE_HASH *h, void *dst, void *src, UINT size)
{
	// Validate arguments
	if (h == NULL || dst == NULL || (size != 0 && src == NULL))
	{
		Zero(dst, size);
		return;
	}

	switch (h->HashId)
	{
	case IKE_HASH_MD5_ID:
		// MD5
		Md5(dst, src, size);
		break;

	case IKE_HASH_SHA1_ID:
		// SHA-1
		Sha1(dst, src, size);
		break;

	default:
		// Unknown
		Zero(dst, size);
		break;
	}
}

// Calculation of HMAC
void IkeHMac(IKE_HASH *h, void *dst, void *key, UINT key_size, void *data, UINT data_size)
{
	UCHAR k[HMAC_BLOCK_SIZE];
	UCHAR *data1;
	UCHAR hash1[IKE_MAX_HASH_SIZE];
	UINT data1_size;
	UCHAR data2[IKE_MAX_HASH_SIZE + HMAC_BLOCK_SIZE];
	UINT data2_size;
	UCHAR tmp1600[1600];
	bool no_free = false;
	UINT i;
	// Validate arguments
	if (h == NULL || dst == NULL || (key == NULL && key_size != 0) || (data == NULL && data_size != 0))
	{
		return;
	}

	if (h->HashId == IKE_HASH_SHA1_ID)
	{
		// Use special function (fast) in the case of SHA-1
		HMacSha1(dst, key, key_size, data, data_size);
		return;
	}
	else if (h->HashId == IKE_HASH_MD5_ID)
	{
		// Use the special function (fast) in the case of MD5
		HMacMd5(dst, key, key_size, data, data_size);
		return;
	}

	// Creating a K
	Zero(k, sizeof(k));
	if (key_size <= HMAC_BLOCK_SIZE)
	{
		Copy(k, key, key_size);
	}
	else
	{
		IkeHash(h, k, key, key_size);
	}

	// Generation of data 1
	data1_size = data_size + HMAC_BLOCK_SIZE;

	if (data1_size > sizeof(tmp1600))
	{
		data1 = Malloc(data1_size);
	}
	else
	{
		data1 = tmp1600;
		no_free = true;
	}

	for (i = 0;i < HMAC_BLOCK_SIZE;i++)
	{
		data1[i] = k[i] ^ 0x36;
	}

	Copy(data1 + HMAC_BLOCK_SIZE, data, data_size);

	// Calculate the hash value
	IkeHash(h, hash1, data1, data1_size);

	if (no_free == false)
	{
		Free(data1);
	}

	// Generation of data 2
	data2_size = h->HashSize + HMAC_BLOCK_SIZE;

	for (i = 0;i < HMAC_BLOCK_SIZE;i++)
	{
		data2[i] = k[i] ^ 0x5c;
	}

	Copy(data2 + HMAC_BLOCK_SIZE, hash1, h->HashSize);

	// Calculate the hash value
	IkeHash(h, dst, data2, data2_size);
}
void IkeHMacBuf(IKE_HASH *h, void *dst, BUF *key, BUF *data)
{
	// Validate arguments
	if (h == NULL || dst == NULL || key == NULL || data == NULL)
	{
		return;
	}

	IkeHMac(h, dst, key->Buf, key->Size, data->Buf, data->Size);
}

// Check whether the key size is valid
bool IkeCheckKeySize(IKE_CRYPTO *c, UINT size)
{
	bool ok = false;
	UINT i;
	// Validate arguments
	if (c == NULL || size == 0)
	{
		return false;
	}

	for (i = 0;i < sizeof(c->KeySizes) / sizeof(UINT);i++)
	{
		if (c->KeySizes[i] == size)
		{
			ok = true;
			break;
		}
	}

	return ok;
}

// Create a key
IKE_CRYPTO_KEY *IkeNewKey(IKE_CRYPTO *c, void *data, UINT size)
{
	IKE_CRYPTO_KEY *k;
	// Validate arguments
	if (c == NULL || data == NULL || size == 0)
	{
		return NULL;
	}

	if (IkeCheckKeySize(c, size) == false)
	{
		return NULL;
	}

	k = ZeroMalloc(sizeof(IKE_CRYPTO_KEY));
	k->Crypto = c;
	k->Data = Clone(data, size);
	k->Size = size;

	switch (k->Crypto->CryptoId)
	{
	case IKE_CRYPTO_DES_ID:
		// DES 64bit key
		k->DesKey1 = DesNewKeyValue(data);
		break;

	case IKE_CRYPTO_3DES_ID:
		// 3DES 192bit key
		k->DesKey1 = DesNewKeyValue(((UCHAR *)data) + DES_KEY_SIZE * 0);
		k->DesKey2 = DesNewKeyValue(((UCHAR *)data) + DES_KEY_SIZE * 1);
		k->DesKey3 = DesNewKeyValue(((UCHAR *)data) + DES_KEY_SIZE * 2);
		break;

	case IKE_CRYPTO_AES_ID:
		// AES variable length key
		k->AesKey = AesNewKey(data, size);
		break;
	}

	return k;
}

// Release the key
void IkeFreeKey(IKE_CRYPTO_KEY *k)
{
	// Validate arguments
	if (k == NULL)
	{
		return;
	}

	DesFreeKeyValue(k->DesKey1);
	DesFreeKeyValue(k->DesKey2);
	DesFreeKeyValue(k->DesKey3);

	AesFreeKey(k->AesKey);

	Free(k->Data);

	Free(k);
}

// Create a DH object
DH_CTX *IkeDhNewCtx(IKE_DH *d)
{
	// Validate arguments
	if (d == NULL)
	{
		return NULL;
	}

	switch (d->DhId)
	{
	case IKE_DH_1_ID:
		return DhNewGroup1();

	case IKE_DH_2_ID:
		return DhNewGroup2();

	case IKE_DH_5_ID:
		return DhNewGroup5();
	}

	return NULL;
}

// Release the DH object
void IkeDhFreeCtx(DH_CTX *dh)
{
	// Validate arguments
	if (dh == NULL)
	{
		return;
	}

	DhFree(dh);
}






// Developed by SoftEther VPN Project at University of Tsukuba in Japan.
// Department of Computer Science has dozens of overly-enthusiastic geeks.
// Join us: http://www.tsukuba.ac.jp/english/admission/
