eapol/eapol_framework/eapol_common/type/tls_peap/tls/src/tls_application_eap_core.cpp
changeset 0 c8830336c852
child 2 1c7bc153c08e
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/eapol/eapol_framework/eapol_common/type/tls_peap/tls/src/tls_application_eap_core.cpp	Thu Dec 17 08:47:43 2009 +0200
@@ -0,0 +1,7779 @@
+/*
+* Copyright (c) 2001-2006 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of the License "Eclipse Public License v1.0"
+* which accompanies this distribution, and is available
+* at the URL "http://www.eclipse.org/legal/epl-v10.html".
+*
+* Initial Contributors:
+* Nokia Corporation - initial contribution.
+*
+* Contributors:
+*
+* Description:  EAP and WLAN authentication protocols.
+*
+*/
+
+
+// This is enumeration of EAPOL source code.
+#if defined(USE_EAP_MINIMUM_RELEASE_TRACES)
+	#undef EAP_FILE_NUMBER_ENUM
+	#define EAP_FILE_NUMBER_ENUM 124 
+	#undef EAP_FILE_NUMBER_DATE 
+	#define EAP_FILE_NUMBER_DATE 1127594498 
+#endif //#if defined(USE_EAP_MINIMUM_RELEASE_TRACES)
+
+
+#include "eap_am_memory.h"
+#include "eap_tools.h"
+#include "eap_am_tools.h"
+#include "eap_core.h"
+#include "eap_type_tls_peap_types.h"
+#include "tls_record_header.h"
+#include "abs_tls_base_application.h"
+#include "tls_application_eap_core.h"
+#include "tls_peap_types.h"
+#include "tls_peap_tlv_header.h"
+#include "eap_diameter_avp_header.h"
+#include "eap_state_notification.h"
+#include "eap_crypto_api.h"
+#include "eap_header_string.h"
+#include "abs_eap_am_mutex.h"
+#include "eap_config.h"
+#include "eapol_header.h"
+#include "eap_network_id_selector.h"
+#include "eap_tlv_message_data.h"
+#include "eap_array_algorithms.h"
+#include "eap_automatic_variable.h"
+#include "eap_base_type.h"
+
+#if defined(EAP_USE_TTLS_PLAIN_MS_CHAP_V2_HACK)
+	#include "eap_type_mschapv2_types.h"
+	#include "eap_type_mschapv2_header.h"
+#endif //#if defined(EAP_USE_TTLS_PLAIN_MS_CHAP_V2_HACK)
+
+const u32_t TLV_NONCE_LENGTH = 32ul;
+const u32_t TLV_MAC_LENGTH = 16ul;
+
+const u32_t TLV_NONCE_OFFSET = 4ul;
+const u32_t TLV_MAC_OFFSET = TLV_NONCE_OFFSET + TLV_NONCE_LENGTH;
+
+const u32_t TLS_PEAP_V2_TK_OFFSET = 32ul;
+const u32_t TLS_PEAP_V2_TK_LENGTH = 32ul;
+
+const u32_t TLS_PEAP_V2_COMPOUND_MAC_KEY_LENGTH = 20ul;
+
+const u32_t TLS_PEAP_V2_COMPOUND_SESSION_KEY_LENGTH = 128ul;
+
+//-----------------------------------------------------------------------------------------
+
+/// This is pseudo Ethernet and EAPOL header.
+/// This is used in trace of tunneled EAP-packet.
+const u8_t EAP_PSEUDO_ETHERNET_HEADER[] =
+{
+	0x50, 0x73, 0x65, 0x75, 0x64, 0x6f, 0x68, 0x65,
+	0x61, 0x64, 0x65, 0x72, 0x88, 0x8e, 0x01, 0x00,
+	0x00, 0x00
+};
+
+const u32_t EAP_PSEUDO_EAPOL_HEADER_OFFSET = 14ul;
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT tls_application_eap_core_c::~tls_application_eap_core_c()
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("%s: function: tls_application_eap_core_c::~tls_application_eap_core_c(): ")
+		 EAPL("this = 0x%08x, m_eap_core")
+		 EAPL(" = 0x%08x (validity %d).\n"),
+		 (m_is_client == true ? "client": "server"),
+		 this,
+		 m_eap_core,
+		 ((m_eap_core != 0) ? m_eap_core->get_is_valid(): true)));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: tls_application_eap_core_c::~tls_application_eap_core_c()");
+
+	EAP_ASSERT(m_shutdown_was_called == true);
+
+	if (m_free_eap_core == true)
+	{
+		delete m_eap_core;
+	}
+	m_eap_core = 0;
+
+	m_application_partner = 0;
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT tls_application_eap_core_c::tls_application_eap_core_c(
+	abs_eap_am_tools_c * const tools,
+	eap_core_c * const eap_core,
+	const bool free_eap_core,
+	const bool is_client_when_true,
+	const eap_type_value_e eap_type,
+	const eap_am_network_id_c * const receive_network_id)
+	: m_am_tools(tools)
+	, m_eap_core(eap_core)
+	, m_free_eap_core(free_eap_core)
+	, m_application_partner(0)
+	, m_receive_network_id(tools)
+	, m_eap_type(eap_type)
+	, m_peap_version(peap_version_none)
+	, m_peap_tunneled_eap_type(eap_type_none)
+	, m_tunneled_eap_type_authentication_state(eap_state_none)
+	, m_peapv2_tlv_payloads(tools)
+	, m_peap_v2_client_nonce(tools)
+	, m_peap_v2_server_nonce(tools)
+	, m_peap_v2_IPMKn(tools)
+	, m_peap_v2_ISKn(tools)
+	, m_peap_v2_CMK_B1_server(tools)
+	, m_peap_v2_CMK_B2_client(tools)
+	, m_peap_v2_CSK(tools, eap_type)
+	, m_accepted_tunneled_eap_types(tools)
+	, m_pseudo_ethernet_header(tools)
+	, m_ttls_received_payloads(tools)
+	, m_ttls_message_type(eap_ttls_tunneled_message_type_none)
+	, m_ttls_sent_eap_packet(tools)
+#if defined(EAP_USE_TTLS_PLAIN_MS_CHAP_V2_HACK)
+	, m_ttls_user_name(tools)
+	, m_ttls_implicit_challenge(tools)
+	, m_ttls_tunneled_message_state(eap_ttls_tunneled_message_state_none)
+#endif //#if defined(EAP_USE_TTLS_PLAIN_MS_CHAP_V2_HACK)
+	, m_error_probability(0UL)
+	, m_received_eap_identifier(0u)
+	, m_enable_random_errors(false)
+	, m_manipulate_only_tunneled_messages(false)
+	, m_is_valid(false)
+	, m_is_client(is_client_when_true)
+	, m_shutdown_was_called(false)
+	, m_tls_session_type(tls_session_type_none)
+	, m_peap_allow_tunneled_session_resumption(true)
+	, m_use_tppd_tls_peap(true)
+	, m_use_tppd_peapv1_acknowledge_hack(false)
+#if defined(USE_EAP_PEAPV1_EXTENSIONS)
+	, m_server_use_peapv1_extensions_request(false)
+	, m_client_send_peapv1_extensions_response(false)
+#endif //#if defined(USE_EAP_PEAPV1_EXTENSIONS)
+#if defined(EAP_USE_TTLS_PLAIN_MS_CHAP_V2_HACK)
+	, m_tunneled_eap_in_ttls(true)
+	, m_ttls_plain_ms_chap_v2_eap_identifier(0ul)
+#endif //#if defined(EAP_USE_TTLS_PLAIN_MS_CHAP_V2_HACK)
+	, m_use_eap_expanded_type(false)
+	, m_wait_plain_eap_success(true)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("PEAP: %s: function: tls_application_eap_core_c::tls_application_eap_core_c(): ")
+		 EAPL("this = 0x%08x\n"),
+		 (m_is_client == true ? "client": "server"),
+		 this));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: tls_application_eap_core_c::tls_application_eap_core_c()");
+
+	if (m_eap_core == 0
+		|| m_eap_core->get_is_valid() == false)
+	{
+		EAP_TRACE_ERROR(
+			m_am_tools,
+			TRACE_FLAGS_TLS_PEAP_ERROR,
+			(EAPL("ERROR: %s: function: tls_application_eap_core_c() failed, ")
+			 EAPL("m_eap_core is invalid.\n"),
+						 (m_is_client == true ? "client": "server")));
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return;
+	}
+
+	m_eap_core->set_partner(this);
+
+	eap_status_e status = m_receive_network_id.set_copy_of_network_id(receive_network_id);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return;
+	}
+
+
+	status = m_pseudo_ethernet_header.set_copy_of_buffer(
+		EAP_PSEUDO_ETHERNET_HEADER,
+		sizeof(EAP_PSEUDO_ETHERNET_HEADER));
+	if (status != eap_status_ok)
+	{
+		// Do not care of this error.
+		// User will check the validity of m_pseudo_ethernet_header.
+	}
+
+	set_is_valid();
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT void tls_application_eap_core_c::set_is_valid()
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	m_is_valid = true;
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT void tls_application_eap_core_c::set_peap_version(
+	const peap_version_e peap_version,
+	const bool use_tppd_tls_peap,
+	const bool use_tppd_peapv1_acknowledge_hack)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("TLS: %s: message_function: tls_application_eap_core_c::set_peap_version()\n"),
+		(m_is_client == true ? "client": "server")));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: tls_application_eap_core_c::set_peap_version()");
+
+	m_peap_version = peap_version;
+
+	m_use_tppd_tls_peap = use_tppd_tls_peap;
+
+	m_use_tppd_peapv1_acknowledge_hack = use_tppd_peapv1_acknowledge_hack;
+
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e tls_application_eap_core_c::configure()
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("PEAP: %s: function: tls_application_eap_core_c::configure(): ")
+		 EAPL("this = 0x%08x\n"),
+		 (m_is_client == true ? "client": "server"),
+		 this));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: tls_application_eap_core_c::configure()");
+
+	eap_status_e status = eap_status_process_general_error;
+
+	//----------------------------------------------------------
+
+	{
+		eap_variable_data_c tunneled_eap_type(m_am_tools);
+
+		if (m_is_client == true)
+		{
+			status = get_application_partner()->read_configure(
+				cf_str_PEAP_tunneled_eap_type_hex_data.get_field(),
+				&tunneled_eap_type);
+			if (status == eap_status_illegal_configure_type)
+			{
+				status = get_application_partner()->read_configure(
+					cf_str_PEAP_tunneled_eap_type_u32_t.get_field(),
+					&tunneled_eap_type);
+			}
+		}
+#if defined(USE_EAP_CORE_SERVER)
+		else
+		{
+			status = get_application_partner()->read_configure(
+				cf_str_PEAP_server_tunneled_eap_type_hex_data.get_field(),
+				&tunneled_eap_type);
+			if (status == eap_status_illegal_configure_type)
+			{
+				status = get_application_partner()->read_configure(
+					cf_str_PEAP_server_tunneled_eap_type_u32_t.get_field(),
+					&tunneled_eap_type);
+			}
+		}
+#endif //#if defined(USE_EAP_CORE_SERVER)
+
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+		else if (tunneled_eap_type.get_data_length() == sizeof(u32_t)
+			&& tunneled_eap_type.get_data(sizeof(u32_t)) != 0)
+		{
+			u32_t *p_eap_type = reinterpret_cast<u32_t *>(
+				tunneled_eap_type.get_data(sizeof(u32_t)));
+			if (p_eap_type != 0)
+			{
+				m_peap_tunneled_eap_type = static_cast<eap_type_ietf_values_e>(*p_eap_type);
+			}
+			else
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_configure_field);
+			}
+		}
+//#if defined(USE_EAP_EXPANDED_TYPES)
+		else if (tunneled_eap_type.get_data_length()
+				 == eap_expanded_type_c::get_eap_expanded_type_size()
+			&& tunneled_eap_type.get_data(tunneled_eap_type.get_data_length()) != 0)
+		{
+			eap_expanded_type_c eap_type(eap_type_none);
+
+			status = eap_type.set_expanded_type_data(
+				m_am_tools,
+				&tunneled_eap_type);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			status = eap_type.get_type_data(
+				m_am_tools,
+				&m_peap_tunneled_eap_type);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+		}
+//#endif //#if defined(USE_EAP_EXPANDED_TYPES)
+		else
+		{
+			EAP_TRACE_DEBUG(
+				m_am_tools, 
+				TRACE_FLAGS_DEFAULT, 
+				(EAPL("ERROR: %s: No tunneled EAP-type configured, %s.\n"),
+				 (m_is_client == true ? "client": "server"),
+				 cf_str_EAP_default_type_hex_data.get_field()->get_field()));
+			
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_configure_field);
+		}
+
+
+#if defined(EAP_USE_TTLS_PLAIN_MS_CHAP_V2_HACK)
+		if (
+#if defined(USE_EAP_EXPANDED_TYPES)
+			m_peap_tunneled_eap_type == eap_expanded_type_ttls_plain_mschapv2.get_type()
+#else
+			m_peap_tunneled_eap_type == eap_type_plain_mschapv2
+#endif //#if defined(USE_EAP_EXPANDED_TYPES)
+			)
+		{
+			// This is special case.
+			// We must tell to inner EAP-stack to use EAP-MsChapv2.
+			m_tunneled_eap_in_ttls = false;
+		}
+#endif //#if defined(EAP_USE_TTLS_PLAIN_MS_CHAP_V2_HACK)
+
+	}
+
+	//----------------------------------------------------------
+
+	{
+		eap_variable_data_c allowed_eap_types(m_am_tools);
+
+		const eap_configuration_field_c * field
+			= cf_str_PEAP_accepted_tunneled_client_types_hex_data.get_field();
+		if (m_is_client == false)
+		{
+			field = cf_str_PEAP_accepted_tunneled_server_types_hex_data.get_field();
+		}
+
+		eap_status_e status = read_configure(
+			field,
+			&allowed_eap_types);
+		if (status == eap_status_ok
+			&& allowed_eap_types.get_is_valid_data() == true)
+		{
+			// OK read.
+			u32_t type_count = allowed_eap_types.get_data_length()
+				/eap_expanded_type_c::get_eap_expanded_type_size();
+			
+			EAP_TRACE_DEBUG(
+				m_am_tools, 
+				TRACE_FLAGS_DEFAULT, 
+				(EAPL("%s: tls_application_eap_core_c::configure(): Tunneled EAP-type count %d, hex data.\n"),
+				 (m_is_client == true ? "client": "server"),
+				 type_count));
+
+			for (u32_t ind = 0ul; ind < type_count; ind++)
+			{
+				eap_type_value_e * const eap_type = new eap_type_value_e(eap_type_none);
+				if (eap_type == 0)
+				{
+					EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+					return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+				}
+
+				status = eap_expanded_type_c::read_type(
+					m_am_tools,
+					ind,
+					allowed_eap_types.get_data(allowed_eap_types.get_data_length()),
+					allowed_eap_types.get_data_length(),
+					eap_type);
+				if (status != eap_status_ok)
+				{
+					delete eap_type;
+					EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+					return EAP_STATUS_RETURN(m_am_tools, status);
+				}
+
+				EAP_TRACE_DEBUG(
+					m_am_tools, 
+					TRACE_FLAGS_DEFAULT, 
+					(EAPL("%s: tls_application_eap_core_c::configure(): allowed EAP-type %d.\n"),
+					 (m_is_client == true ? "client": "server"),
+					 convert_eap_type_to_u32_t(*eap_type)));
+
+				status = m_accepted_tunneled_eap_types.add_object(eap_type, true);
+				if (status != eap_status_ok)
+				{
+					EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+					return EAP_STATUS_RETURN(m_am_tools, status);
+				}
+			} // for()
+		}
+		else
+		{
+			// Try different configuration type.
+
+			const eap_configuration_field_c * field
+				= cf_str_PEAP_accepted_tunneled_client_types_u32array.get_field();
+			if (m_is_client == false)
+			{
+				field = cf_str_PEAP_accepted_tunneled_server_types_u32array.get_field();
+			}
+
+			status = read_configure(
+				field,
+				&allowed_eap_types);
+			if (status == eap_status_ok
+				&& allowed_eap_types.get_is_valid_data() == true)
+			{
+				// OK read.
+				u32_t type_count = allowed_eap_types.get_data_length()
+					/ sizeof(u32_t);
+
+				u32_t * const array = reinterpret_cast<u32_t *>(
+					allowed_eap_types.get_data(type_count*sizeof(u32_t)));
+				
+				EAP_TRACE_DEBUG(
+					m_am_tools, 
+					TRACE_FLAGS_DEFAULT, 
+					(EAPL("%s: tls_application_eap_core_c::configure(): Tunneled EAP-type count %d, u32array.\n"),
+					 (m_is_client == true ? "client": "server"),
+					 type_count));
+
+				for (u32_t ind = 0ul; ind < type_count; ind++)
+				{
+					eap_type_value_e * const eap_type = new eap_type_value_e(eap_type_none);
+					if (eap_type == 0)
+					{
+						EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+						return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+					}
+
+					*eap_type = static_cast<eap_type_ietf_values_e>(array[ind]);
+
+					EAP_TRACE_DEBUG(
+						m_am_tools, 
+						TRACE_FLAGS_DEFAULT, 
+						(EAPL("%s: tls_application_eap_core_c::configure(): allowed EAP-type %d.\n"),
+						 (m_is_client == true ? "client": "server"),
+						 convert_eap_type_to_u32_t(*eap_type)));
+
+					status = m_accepted_tunneled_eap_types.add_object(eap_type, true);
+					if (status != eap_status_ok)
+					{
+						EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+						return EAP_STATUS_RETURN(m_am_tools, status);
+					}
+				} // for()
+			}
+		}
+	}
+
+	//----------------------------------------------------------
+
+	{
+		// Client and server configuration.
+		eap_variable_data_c allow_tunneled_session_resumption(m_am_tools);
+
+		status = read_configure(
+			cf_str_PEAP_allow_tunneled_session_resumption.get_field(),
+			&allow_tunneled_session_resumption);
+		if (status == eap_status_ok
+			&& allow_tunneled_session_resumption.get_is_valid_data() == true
+			&& allow_tunneled_session_resumption.get_data_length() == sizeof(u32_t)
+			&& allow_tunneled_session_resumption.get_data(sizeof(u32_t)) != 0)
+		{
+			// This is optional value.
+			u32_t *flag = reinterpret_cast<u32_t *>(
+				allow_tunneled_session_resumption.get_data(sizeof(u32_t)));
+			if (flag != 0)
+			{
+				if (*flag == 0)
+				{
+					m_peap_allow_tunneled_session_resumption = false;
+				}
+				else
+				{
+					m_peap_allow_tunneled_session_resumption = true;
+				}
+			}
+		}
+
+		status = eap_status_ok;
+	}
+
+
+	if (m_is_client == false)
+	{
+		{
+			// Server only configuration.
+			// This configuration allows different actions to server and client.
+			eap_variable_data_c allow_tunneled_session_resumption(m_am_tools);
+
+			status = read_configure(
+				cf_str_PEAP_server_allow_tunneled_session_resumption.get_field(),
+				&allow_tunneled_session_resumption);
+			if (status == eap_status_ok
+				&& allow_tunneled_session_resumption.get_is_valid_data() == true
+				&& allow_tunneled_session_resumption.get_data_length() == sizeof(u32_t)
+				&& allow_tunneled_session_resumption.get_data(sizeof(u32_t)) != 0)
+			{
+				// This is optional value.
+				u32_t *flag = reinterpret_cast<u32_t *>(
+					allow_tunneled_session_resumption.get_data(sizeof(u32_t)));
+				if (flag != 0)
+				{
+					if (*flag == 0)
+					{
+						m_peap_allow_tunneled_session_resumption = false;
+					}
+					else
+					{
+						m_peap_allow_tunneled_session_resumption = true;
+					}
+				}
+			}
+
+			status = eap_status_ok;
+		}
+
+#if defined(USE_EAP_PEAPV1_EXTENSIONS)
+		{
+			// Server only configuration.
+			eap_variable_data_c server_use_peapv1_extensions_request(m_am_tools);
+
+			status = read_configure(
+				cf_str_PEAPv1_server_use_extensions_request.get_field(),
+				&server_use_peapv1_extensions_request);
+			if (status == eap_status_ok
+				&& server_use_peapv1_extensions_request.get_is_valid_data() == true
+				&& server_use_peapv1_extensions_request.get_data_length() == sizeof(u32_t)
+				&& server_use_peapv1_extensions_request.get_data(sizeof(u32_t)) != 0)
+			{
+				// This is optional value.
+				u32_t *flag = reinterpret_cast<u32_t *>(
+					server_use_peapv1_extensions_request.get_data(sizeof(u32_t)));
+				if (flag != 0)
+				{
+					if (*flag == 0)
+					{
+						m_server_use_peapv1_extensions_request = false;
+					}
+					else
+					{
+						m_server_use_peapv1_extensions_request = true;
+					}
+				}
+			}
+
+			status = eap_status_ok;
+
+		}
+#endif //#if defined(USE_EAP_PEAPV1_EXTENSIONS)
+
+	}
+
+
+	//----------------------------------------------------------
+
+#if defined(USE_EAP_ERROR_TESTS)
+
+	{
+		eap_variable_data_c EAP_ERROR_TEST_enable_random_errors(m_am_tools);
+			
+		status = read_configure(
+			cf_str_EAP_ERROR_TEST_enable_random_errors.get_field(),
+			&EAP_ERROR_TEST_enable_random_errors);
+		if (status == eap_status_ok
+			&& EAP_ERROR_TEST_enable_random_errors.get_is_valid_data() == true)
+		{
+			u32_t *enable_random_errors = reinterpret_cast<u32_t *>(
+				EAP_ERROR_TEST_enable_random_errors.get_data(sizeof(u32_t)));
+			if (enable_random_errors != 0
+				&& *enable_random_errors != 0)
+			{
+				m_enable_random_errors = true;
+			}
+		}
+	}
+	
+	if (m_enable_random_errors == true)
+	{
+		eap_variable_data_c manipulate_only_tunneled_messages(m_am_tools);
+
+		eap_status_e status = read_configure(
+			cf_str_EAP_ERROR_TEST_manipulate_only_tunneled_messages.get_field(),
+			&manipulate_only_tunneled_messages);
+		if (status == eap_status_ok
+			&& manipulate_only_tunneled_messages.get_is_valid_data() == true)
+		{
+			// NOTE this is optional.
+			u32_t * const flag = reinterpret_cast<u32_t *>(
+				manipulate_only_tunneled_messages.get_data(sizeof(u32_t)));
+			if (flag != 0
+				&& *flag != 0)
+			{
+				m_manipulate_only_tunneled_messages = true;
+			}
+		}
+	}
+
+	{
+		eap_variable_data_c EAP_ERROR_TEST_error_probability(m_am_tools);
+
+		status = read_configure(
+			cf_str_EAP_ERROR_TEST_error_probability.get_field(),
+			&EAP_ERROR_TEST_error_probability);
+		if (status == eap_status_ok
+			&& EAP_ERROR_TEST_error_probability.get_is_valid_data() == true)
+		{
+			u32_t *error_probability = reinterpret_cast<u32_t *>(
+				EAP_ERROR_TEST_error_probability.get_data(sizeof(u32_t)));
+			if (error_probability != 0)
+			{
+				m_error_probability = *error_probability;
+			}
+		}
+	}
+
+#endif //#if defined(USE_EAP_ERROR_TESTS)
+
+	//----------------------------------------------------------
+
+#if defined(USE_EAP_EXPANDED_TYPES) && 0
+	{
+		eap_variable_data_c use_eap_expanded_type(m_am_tools);
+
+		eap_status_e status = read_configure(
+			cf_str_EAP_TLS_PEAP_use_eap_expanded_type.get_field(),
+			&use_eap_expanded_type);
+
+		if (status != eap_status_ok)
+		{
+			status = read_configure(
+				cf_str_EAP_CORE_use_eap_expanded_type.get_field(),
+				&use_eap_expanded_type);
+		}
+
+		if (status == eap_status_ok
+			&& use_eap_expanded_type.get_data_length() == sizeof(u32_t)
+			&& use_eap_expanded_type.get_data() != 0)
+		{
+			u32_t *flag = reinterpret_cast<u32_t *>(use_eap_expanded_type.get_data(use_eap_expanded_type.get_data_length()));
+
+			if (flag != 0)
+			{
+				if ((*flag) != 0ul)
+				{
+					m_use_eap_expanded_type = true;
+				}
+				else
+				{
+					m_use_eap_expanded_type = false;
+				}
+			}
+		}
+	}
+#endif //#if defined(USE_EAP_EXPANDED_TYPES)
+
+	//----------------------------------------------------------
+
+	status = m_eap_core->configure();
+
+	if (m_peap_tunneled_eap_type == eap_type_ttls_plain_pap)
+	{
+		if (m_free_eap_core == true)
+		{
+			m_eap_core->ignore_notifications();
+
+			(void) m_eap_core->shutdown();
+
+			delete m_eap_core;
+		}
+		m_eap_core = 0;
+	}
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e tls_application_eap_core_c::shutdown()
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("%s: tls_application_eap_core_c::shutdown(), m_shutdown_was_called=%d\n"),
+		(m_is_client == true) ? "client": "server",
+		m_shutdown_was_called));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: tls_application_eap_core_c::shutdown()");
+
+	if (m_shutdown_was_called == true)
+	{
+		// Shutdown function was called already.
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_ok);
+	}
+	m_shutdown_was_called = true;
+
+	eap_status_e status(eap_status_ok);
+
+	if (m_eap_core != 0)
+	{
+		status = m_eap_core->shutdown();
+	}
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT void tls_application_eap_core_c::trace_tunneled_packet(
+	eap_const_string prefix,
+	const eap_header_wr_c * const eap_packet)
+{
+	if (eap_packet == 0
+		|| eap_packet->get_is_valid() == false)
+	{
+		EAP_UNREFERENCED_PARAMETER(prefix);
+		// ERROR: Cannot trace invalid packet.
+		EAP_TRACE_ALWAYS(
+			m_am_tools,
+			TRACE_FLAGS_DEFAULT,
+			(EAPL("ERROR: %s: %s: Cannot trace invalid packet.\n"),
+			 prefix,
+			 (m_is_client == true) ? "client": "server"));
+		return;
+	}
+
+	if ((m_am_tools->get_trace_mask() & TRACE_FLAGS_DEFAULT)
+		&& m_pseudo_ethernet_header.get_is_valid_data() == true
+		&& m_pseudo_ethernet_header.get_data_length() >= sizeof(EAP_PSEUDO_ETHERNET_HEADER))
+	{
+		m_pseudo_ethernet_header.set_data_length(sizeof(EAP_PSEUDO_ETHERNET_HEADER));
+
+		u32_t eap_packet_length = eap_packet->get_length();
+		if (eap_packet->get_header_buffer_length() < eap_packet_length)
+		{
+			eap_packet_length = eap_packet->get_header_buffer_length();
+		}
+
+		eap_status_e status = m_pseudo_ethernet_header.add_data_to_offset(
+			sizeof(EAP_PSEUDO_ETHERNET_HEADER),
+			eap_packet->get_header_buffer(eap_packet_length),
+			eap_packet_length);
+
+		if (status == eap_status_ok)
+		{
+			m_pseudo_ethernet_header.set_data_length(
+				sizeof(EAP_PSEUDO_ETHERNET_HEADER) + eap_packet_length);
+
+			// Sets the EAPOL packet data length.
+			eapol_header_wr_c eapol(
+				m_am_tools,
+				m_pseudo_ethernet_header.get_data_offset(
+					EAP_PSEUDO_EAPOL_HEADER_OFFSET,
+					m_pseudo_ethernet_header.get_data_length()-EAP_PSEUDO_EAPOL_HEADER_OFFSET),
+				m_pseudo_ethernet_header.get_data_length()-EAP_PSEUDO_EAPOL_HEADER_OFFSET);
+
+			if (eapol.get_is_valid() == true)
+			{
+				eapol.set_data_length(static_cast<u16_t>(eap_packet_length));
+
+				EAP_TRACE_ALWAYS(
+					m_am_tools,
+					TRACE_FLAGS_DEFAULT,
+					(EAPL("%s: %s: type=0x%08x, eap_length 0x%04x\n"),
+					 prefix,
+					 (m_is_client == true) ? "client": "server",
+					 convert_eap_type_to_u32_t(eap_packet->get_type()),
+					 eap_packet->get_length())); // NOTE, this will trace the values from the header of the EAP-packet.
+
+				EAP_TRACE_DATA_ALWAYS(
+					m_am_tools,
+					TRACE_FLAGS_ALWAYS|TRACE_FLAGS_DEFAULT,
+					(prefix,
+					m_pseudo_ethernet_header.get_data(m_pseudo_ethernet_header.get_data_length()),
+					m_pseudo_ethernet_header.get_data_length()));
+			}
+		}
+	}
+}
+
+//--------------------------------------------------
+
+//
+EAP_FUNC_EXPORT eap_status_e tls_application_eap_core_c::parse_generic_payload(
+	const tls_peap_tlv_type_e current_payload,
+	const tls_peap_tlv_header_c * const payload,
+	peap_tlv_payloads_c * const p_peap_tlv_payloads)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("TLS: %s: message_function: tls_application_eap_core_c::parse_generic_payload()\n"),
+		(m_is_client == true ? "client": "server")));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: tls_application_eap_core_c::parse_generic_payload()");
+
+	EAP_TLS_PEAP_TRACE_PAYLOAD("Parsing payload", payload, m_is_client);
+
+	if (payload->get_data_length() == 0)
+	{
+		EAP_TRACE_ERROR(
+			m_am_tools,
+			TRACE_FLAGS_TLS_PEAP_ERROR,
+			(EAPL("ERROR: tls_application_eap_core_c::parse_generic_payload(0x%08x): ")
+			 EAPL("current payload 0x%04x=%s, length is 0x%04x.\n"),
+			payload, current_payload, payload->get_tlv_type_string(), payload->get_data_length()));
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_header_corrupted);		
+	}
+
+
+	if (current_payload == tls_peap_tlv_type_result)
+	{
+		/*  Result TLV:
+		 *  0                   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 
+		 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+		 * |M|R|  TLV Type (AVP Type)      |            Length             |
+		 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+		 * |            Status             |
+		 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+		 */
+		const u32_t PEAP_TLV_STATUS_LENGTH = sizeof(u16_t);
+
+		if (payload->get_data_length() != PEAP_TLV_STATUS_LENGTH)
+		{
+			EAP_TRACE_ERROR(
+				m_am_tools,
+				TRACE_FLAGS_TLS_PEAP_ERROR,
+				(EAPL("ERROR: tls_application_eap_core_c::parse_generic_payload(0x%08x): ")
+				 EAPL("current payload 0x%04x=%s, data length incorrect 0x%04x.\n"),
+				 payload, current_payload, payload->get_tlv_type_string(),
+				 payload->get_data_length()));
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_header_corrupted);
+		}
+
+		u8_t * buffer
+			= static_cast<u8_t *>(payload->get_data(PEAP_TLV_STATUS_LENGTH));
+
+		if (buffer == 0)
+		{
+			EAP_TRACE_ERROR(
+				m_am_tools, 
+				TRACE_FLAGS_TLS_PEAP_ERROR, 
+				(EAPL("ERROR: tls_application_eap_core_c::parse_generic_payload(0x%08x): ")
+				 EAPL("current payload 0x%04x=%s, length 0x%04x, data buffer incorrect.\n"),
+				 payload,
+				 current_payload,
+				 payload->get_tlv_type_string(),
+				 payload->get_data_length()));
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_header_corrupted);
+		}
+
+		// Note here we get a reference to the data bytes of the received packet.
+		eap_status_e status = p_peap_tlv_payloads->get_result_tlv()->set_copy_of_buffer(
+			payload);
+
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+	else if (current_payload == tls_peap_tlv_type_nak)
+	{
+		/*  NAK TLV:
+		 * 0                   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  
+		 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+		 * |M|R|         TLV Type          |            Length             |
+		 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+		 * |                          Vendor-Id                            |
+		 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+		 * |      TLV Type number      | TLVs                              |
+		 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+		 */
+		const u32_t PEAP_TLV_NAK_MINIMUM_LENGTH = 6ul*sizeof(u8_t);
+
+		if (payload->get_data_length() < PEAP_TLV_NAK_MINIMUM_LENGTH)
+		{
+			EAP_TRACE_ERROR(
+				m_am_tools,
+				TRACE_FLAGS_TLS_PEAP_ERROR,
+				(EAPL("ERROR: tls_application_eap_core_c::parse_generic_payload(0x%08x): ")
+				 EAPL("current payload 0x%04x=%s, data length incorrect 0x%04x.\n"),
+				 payload, current_payload, payload->get_tlv_type_string(),
+				 payload->get_data_length()));
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_header_corrupted);
+		}
+
+		u8_t * buffer
+			= static_cast<u8_t *>(payload->get_data(payload->get_data_length()));
+
+		if (buffer == 0)
+		{
+			EAP_TRACE_ERROR(
+				m_am_tools, 
+				TRACE_FLAGS_TLS_PEAP_ERROR, 
+				(EAPL("ERROR: tls_application_eap_core_c::parse_generic_payload(0x%08x): ")
+				 EAPL("current payload 0x%04x=%s, length 0x%04x, data buffer incorrect.\n"),
+				 payload,
+				 current_payload,
+				 payload->get_tlv_type_string(),
+				 payload->get_data_length()));
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_header_corrupted);
+		}
+
+		// Note here we get a reference to the data bytes of the received packet.
+		eap_status_e status = p_peap_tlv_payloads->get_nak_tlv()->set_copy_of_buffer(
+			payload);
+
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+	else if (current_payload == tls_peap_tlv_type_crypto_binding)
+	{
+		/*  Crypto binding TLV:
+		 * 0                   1                   2                   3    
+		 * 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7  
+		 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+		 * |M|R|         TLV Type          |            Length             |
+		 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+		 * | Version       |Received Ver.  |           SubType             |
+		 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+		 * |                                                               |
+		 * +                           NONCE                               +
+		 * |                                                               |
+		 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+		 * |                                                               |
+		 * +                          Compound MAC                         +
+		 * |                                                               |
+		 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+		 */
+		const u32_t PEAP_TLV_CRYPTO_BINDING_LENGTH = sizeof(u16_t)+sizeof(u16_t)+32ul+16ul;
+
+		if (payload->get_data_length() != PEAP_TLV_CRYPTO_BINDING_LENGTH)
+		{
+			EAP_TRACE_ERROR(
+				m_am_tools,
+				TRACE_FLAGS_TLS_PEAP_ERROR,
+				(EAPL("ERROR: tls_application_eap_core_c::parse_generic_payload(0x%08x): ")
+				 EAPL("current payload 0x%04x=%s, data length incorrect 0x%04x.\n"),
+				 payload, current_payload, payload->get_tlv_type_string(),
+				 payload->get_data_length()));
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_header_corrupted);
+		}
+
+		u8_t * buffer
+			= static_cast<u8_t *>(payload->get_data(PEAP_TLV_CRYPTO_BINDING_LENGTH));
+
+		if (buffer == 0)
+		{
+			EAP_TRACE_ERROR(
+				m_am_tools, 
+				TRACE_FLAGS_TLS_PEAP_ERROR, 
+				(EAPL("ERROR: tls_application_eap_core_c::parse_generic_payload(0x%08x): ")
+				 EAPL("current payload 0x%04x=%s, length 0x%04x, data buffer incorrect.\n"),
+				 payload,
+				 current_payload,
+				 payload->get_tlv_type_string(),
+				 payload->get_data_length()));
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_header_corrupted);
+		}
+
+		// Note here we get a reference to the data bytes of the received packet.
+		eap_status_e status = p_peap_tlv_payloads->get_crypto_binding_tlv()->set_copy_of_buffer(
+			payload);
+
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+	else if (current_payload == tls_peap_tlv_eap_payload)
+	{
+		/*  EAP-Payload TLV:
+		 *  0                   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
+		 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+		 * |M|R|         TLV Type          |            Length             |
+		 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+		 * |                          EAP packet...
+		 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+		 * |                             TLVs...
+		 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+		 */
+		const u32_t PEAP_TLV_EAP_PAYLOAD_MINIMUM_LENGTH = 4ul*sizeof(u8_t);
+
+		if (payload->get_data_length() < PEAP_TLV_EAP_PAYLOAD_MINIMUM_LENGTH)
+		{
+			EAP_TRACE_ERROR(
+				m_am_tools,
+				TRACE_FLAGS_TLS_PEAP_ERROR,
+				(EAPL("ERROR: tls_application_eap_core_c::parse_generic_payload(0x%08x): ")
+				 EAPL("current payload 0x%04x=%s, data length incorrect 0x%04x.\n"),
+				 payload, current_payload, payload->get_tlv_type_string(),
+				 payload->get_data_length()));
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_header_corrupted);
+		}
+
+		u8_t * buffer
+			= static_cast<u8_t *>(payload->get_data(payload->get_data_length()));
+
+		if (buffer == 0)
+		{
+			EAP_TRACE_ERROR(
+				m_am_tools, 
+				TRACE_FLAGS_TLS_PEAP_ERROR, 
+				(EAPL("ERROR: tls_application_eap_core_c::parse_generic_payload(0x%08x): ")
+				 EAPL("current payload 0x%04x=%s, length 0x%04x, data buffer incorrect.\n"),
+				 payload,
+				 current_payload,
+				 payload->get_tlv_type_string(),
+				 payload->get_data_length()));
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_header_corrupted);
+		}
+
+		// Note here we get a reference to the data bytes of the received packet.
+		eap_status_e status = p_peap_tlv_payloads->get_eap_payload_tlv()->set_copy_of_buffer(
+			payload);
+
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+	else if (current_payload == tls_peap_tlv_type_intermediate_result)
+	{
+		/*  Intermediate Result TLV:
+		 *  0                   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 
+		 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+		 * |M|R|  TLV Type (AVP Type)      |            Length             |
+		 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+		 * |            Status             |        TLVs...
+		 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+		 */
+		const u32_t PEAP_TLV_INTERMEDIATE_RESULTMINIMUM_LENGTH = 2ul*sizeof(u8_t);
+
+		if (payload->get_data_length() < PEAP_TLV_INTERMEDIATE_RESULTMINIMUM_LENGTH)
+		{
+			EAP_TRACE_ERROR(
+				m_am_tools,
+				TRACE_FLAGS_TLS_PEAP_ERROR,
+				(EAPL("ERROR: tls_application_eap_core_c::parse_generic_payload(0x%08x): ")
+				 EAPL("current payload 0x%04x=%s, data length incorrect 0x%04x.\n"),
+				 payload, current_payload, payload->get_tlv_type_string(),
+				 payload->get_data_length()));
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_header_corrupted);
+		}
+
+		u8_t * buffer
+			= static_cast<u8_t *>(payload->get_data(payload->get_data_length()));
+
+		if (buffer == 0)
+		{
+			EAP_TRACE_ERROR(
+				m_am_tools, 
+				TRACE_FLAGS_TLS_PEAP_ERROR, 
+				(EAPL("ERROR: tls_application_eap_core_c::parse_generic_payload(0x%08x): ")
+				 EAPL("current payload 0x%04x=%s, length 0x%04x, data buffer incorrect.\n"),
+				 payload,
+				 current_payload,
+				 payload->get_tlv_type_string(),
+				 payload->get_data_length()));
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_header_corrupted);
+		}
+
+		// Note here we get a reference to the data bytes of the received packet.
+		eap_status_e status = p_peap_tlv_payloads->get_intermediate_result_tlv()
+			->set_copy_of_buffer(payload);
+
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+	else if (payload->get_flag_mandatory_tlv() == false)
+	{
+		// Silently ignore this optional payload.
+		EAP_TRACE_DEBUG(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("IGNORED: tls_application_eap_core_c::parse_generic_payload(): ")
+			 EAPL("Ignored PEAP_TLV-payload %d=0x%04x.\n"),
+			current_payload,
+			current_payload));
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_ok);
+	}
+	else
+	{
+		/**
+		 * @{ Add creation and sending of message with NAK TLV. }
+		 */
+		EAP_TRACE_ERROR(
+			m_am_tools, 
+			TRACE_FLAGS_TLS_PEAP_ERROR, 
+			(EAPL("ERROR: tls_application_eap_core_c::parse_generic_payload(): ")
+			 EAPL("Unknown PEAP_TLV-payload %d.\n"),
+			current_payload));
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+	}
+}
+
+//--------------------------------------------------
+
+//
+EAP_FUNC_EXPORT eap_status_e tls_application_eap_core_c::parse_peap_tlv_payload(
+	u8_t * const buffer,
+	u32_t * const buffer_length,
+	peap_tlv_payloads_c * const p_peap_tlv_payloads)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("TLS: %s: message_function: tls_application_eap_core_c::parse_peap_tlv_payload()\n"),
+		(m_is_client == true ? "client": "server")));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: tls_application_eap_core_c::parse_peap_tlv_payload()");
+
+	u32_t buffer_offset = 0ul;
+
+	tls_peap_tlv_header_c payload(
+		m_am_tools,
+		buffer+buffer_offset,
+		*buffer_length); // Const correctness is gone.
+
+	tls_peap_tlv_type_e current_payload = payload.get_flag_tlv_type();
+
+	eap_status_e status = eap_status_header_corrupted;
+
+	if (payload.get_is_valid() == true
+		&& current_payload != tls_peap_tlv_type_none)
+	{
+		status = payload.check_header();
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_ERROR(
+				m_am_tools, 
+				TRACE_FLAGS_TLS_PEAP_ERROR, 
+				(EAPL("ERROR: TLV header corrupted.\n")));
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		if (*buffer_length < payload.get_header_length()+payload.get_data_length())
+		{
+			EAP_TRACE_ERROR(
+				m_am_tools,
+				TRACE_FLAGS_TLS_PEAP_ERROR,
+				(EAPL("ERROR: tls_application_eap_core_c::parse_peap_tlv_payload(0x%08x): ")
+				 EAPL("current payload 0x%04x=%s, data length 0x%04x, buffer length 0x%04x.\n"),
+				 payload.get_header_buffer(0ul),
+				 current_payload,
+				 payload.get_tlv_type_string(),
+				 payload.get_data_length(),
+				 *buffer_length));
+			EAP_TRACE_ERROR(
+				m_am_tools,
+				TRACE_FLAGS_TLS_PEAP_ERROR,
+				(EAPL("ERROR: tls_application_eap_core_c::parse_peap_tlv_payload(): ")
+				 EAPL("PEAP_TLV-payload header is corrupted.\n")));
+			EAP_TRACE_DATA_ERROR(m_am_tools, TRACE_FLAGS_DEFAULT, (EAPL("payload"),
+				payload.get_header_buffer(*buffer_length),
+				*buffer_length));
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_header_corrupted);
+		}
+
+		status = parse_generic_payload(current_payload, &payload, p_peap_tlv_payloads);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		if (*buffer_length < payload.get_header_length()+payload.get_data_length())
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_too_short_message);
+		}
+
+		buffer_offset += payload.get_header_length()+payload.get_data_length();
+		*buffer_length -= payload.get_header_length()+payload.get_data_length();
+
+		while(*buffer_length >= payload.get_header_length()
+			&& payload.get_is_valid() == true)
+		{
+			// Sets payload point to the next TLV header.
+			payload.set_header_buffer(
+					buffer+buffer_offset,
+					*buffer_length);
+			if (payload.get_is_valid() == false)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_header_corrupted);
+			}
+
+			current_payload = payload.get_flag_tlv_type();
+
+			if (*buffer_length < payload.get_header_length()+payload.get_data_length())
+			{
+				EAP_TRACE_ERROR(
+					m_am_tools,
+					TRACE_FLAGS_TLS_PEAP_ERROR,
+					(EAPL("ERROR: tls_application_eap_core_c::parse_peap_tlv_payload(0x%08x): ")
+					 EAPL("current payload 0x%04x=%s, data length 0x%04x, buffer length 0x%04x.\n"),
+					 payload.get_header_buffer(0ul),
+					 current_payload,
+					 payload.get_tlv_type_string(),
+					 payload.get_data_length(),
+					 *buffer_length));
+				EAP_TRACE_ERROR(
+					m_am_tools,
+					TRACE_FLAGS_TLS_PEAP_ERROR,
+					(EAPL("ERROR: tls_application_eap_core_c::parse_peap_tlv_payload(): ")
+					 EAPL("PEAP_TLV-payload header is corrupted.\n")));
+				EAP_TRACE_DATA_ERROR(m_am_tools, TRACE_FLAGS_DEFAULT, (EAPL("payload"),
+					payload.get_header_buffer(*buffer_length),
+					*buffer_length));
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_header_corrupted);
+			}
+
+			status = parse_generic_payload(current_payload, &payload, p_peap_tlv_payloads);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			if (*buffer_length < payload.get_header_length()+payload.get_data_length())
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_too_short_message);
+			}
+
+			buffer_offset += payload.get_header_length()+payload.get_data_length();
+			*buffer_length -= payload.get_header_length()+payload.get_data_length();
+		}
+	}
+
+	if (*buffer_length != 0u)
+	{
+		EAP_TRACE_ERROR(
+			m_am_tools,
+			TRACE_FLAGS_TLS_PEAP_ERROR,
+			(EAPL("ERROR: tls_application_eap_core_c::parse_peap_tlv_payload(): ")
+			 EAPL("PEAP_TLV-header is corrupted. Buffer length and payload ")
+			 EAPL("length does not match. %lu illegal bytes.\n"),
+			 *buffer_length));
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_header_corrupted);
+	}
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, eap_status_ok);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e tls_application_eap_core_c::start_ttls_tunneled_authentication(
+	const eap_am_network_id_c * const /*receive_network_id*/,
+	const u8_t received_eap_identifier)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("TTLS: %s: tls_application_eap_core_c::start_ttls_tunneled_authentication()\n"),
+		(m_is_client == true ? "client": "server")));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: tls_application_eap_core_c::start_ttls_tunneled_authentication()");
+
+	if (m_eap_type != eap_type_ttls)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_eap_type);
+	}
+
+	eap_status_e status(eap_status_process_general_error);
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	if (
+#if defined(USE_EAP_EXPANDED_TYPES)
+		m_peap_tunneled_eap_type == eap_expanded_type_ttls_plain_pap.get_type()
+#else
+		m_peap_tunneled_eap_type == eap_type_ttls_plain_pap
+#endif //#if defined(USE_EAP_EXPANDED_TYPES)
+		)
+	{
+		// Query PAP username and password.
+		status = m_application_partner->query_ttls_pap_username_and_password(0);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+	else
+	{
+		eap_variable_data_c forwarded_packet_buffer(m_am_tools);
+
+		status = forwarded_packet_buffer.set_buffer_length(EAP_CORE_PACKET_BUFFER_LENGTH);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		forwarded_packet_buffer.set_data_length(
+			forwarded_packet_buffer.get_buffer_length());
+
+		eap_header_wr_c forwarded_eap_packet(m_am_tools, 0, 0ul);
+
+		forwarded_eap_packet.set_header_buffer(
+			forwarded_packet_buffer.get_data(),
+			forwarded_packet_buffer.get_buffer_length());
+		if (forwarded_eap_packet.get_is_valid() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+		}
+
+		u32_t packet_length = eap_header_wr_c::get_header_length() + 1ul;
+
+		forwarded_eap_packet.reset_header(
+			static_cast<u16_t>(packet_length),
+			m_use_eap_expanded_type);
+		forwarded_eap_packet.set_identifier(received_eap_identifier);
+		forwarded_eap_packet.set_code(eap_code_request);
+		forwarded_eap_packet.set_length(
+			static_cast<u16_t>(packet_length),
+			m_use_eap_expanded_type);
+		forwarded_eap_packet.set_type(
+			eap_type_identity,
+			m_use_eap_expanded_type);
+
+#if defined(EAP_USE_TTLS_PLAIN_MS_CHAP_V2_HACK)
+		if (m_tunneled_eap_in_ttls == false)
+		{
+			set_ttls_tunneled_message_state(eap_ttls_tunneled_message_state_process_identity_request);
+		}
+#endif //#if defined(EAP_USE_TTLS_PLAIN_MS_CHAP_V2_HACK)
+
+		status = packet_forward_to_tunnel(
+			&m_receive_network_id,
+			&forwarded_eap_packet,
+			forwarded_eap_packet.get_length());
+
+		{
+			eap_status_string_c status_string;
+			EAP_UNREFERENCED_PARAMETER(status_string);
+
+#if defined(EAP_USE_TTLS_PLAIN_MS_CHAP_V2_HACK)
+				bool tmp_tunneled_eap_in_ttls(m_tunneled_eap_in_ttls);
+#else
+				bool tmp_tunneled_eap_in_ttls(false);
+#endif //#if defined(EAP_USE_TTLS_PLAIN_MS_CHAP_V2_HACK)
+
+			EAP_UNREFERENCED_PARAMETER(tmp_tunneled_eap_in_ttls);
+			EAP_TRACE_DEBUG(
+				m_am_tools,
+				TRACE_FLAGS_DEFAULT,
+				(EAPL("TTLS: %s: tls_application_eap_core_c::start_ttls_tunneled_authentication() returns status=%d=%s, m_tunneled_eap_in_ttls=%d\n"),
+				(m_is_client == true ? "client": "server"),
+				status,
+				status_string.get_status_string(status),
+				tmp_tunneled_eap_in_ttls));
+		}
+
+#if defined(EAP_USE_TTLS_PLAIN_MS_CHAP_V2_HACK)
+		if (m_tunneled_eap_in_ttls == false
+			&& status == eap_status_pending_request)
+		{
+			set_ttls_tunneled_message_state(eap_ttls_tunneled_message_state_process_identity_request_pending);
+		}
+#endif //#if defined(EAP_USE_TTLS_PLAIN_MS_CHAP_V2_HACK)
+		
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+		// NOTE, here we process client send packets separately to 
+		// reduce stack consumption.
+
+#if defined(EAP_USE_TTLS_PLAIN_MS_CHAP_V2_HACK)
+		if (m_eap_type == eap_type_ttls
+#if defined(USE_EAP_EXPANDED_TYPES)
+			&& m_peap_tunneled_eap_type == eap_expanded_type_ttls_plain_mschapv2.get_type()
+#else
+			&& m_peap_tunneled_eap_type == eap_type_plain_mschapv2
+#endif //#if defined(USE_EAP_EXPANDED_TYPES)
+			)
+		{
+			eap_header_wr_c sent_eap_packet(
+				m_am_tools,
+				m_ttls_sent_eap_packet.get_data(),
+				m_ttls_sent_eap_packet.get_data_length());
+
+			if (m_ttls_sent_eap_packet.get_data_length() > 0ul
+				&& sent_eap_packet.get_data_length() > 0ul
+				&& sent_eap_packet.get_type() == eap_type_identity
+				&& get_ttls_tunneled_message_state() == eap_ttls_tunneled_message_state_process_identity_request)
+			{
+				// Client sent EAP-Response/Identity.
+				// This message should include username.
+
+				status = ttls_tunneled_message_state_process_identity_request(&sent_eap_packet);
+				if (status != eap_status_ok)
+				{
+					EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+					return EAP_STATUS_RETURN(m_am_tools, status);
+				}
+			}
+			else
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_wrong_eap_type_state);
+			}
+		}
+#endif //#if defined(EAP_USE_TTLS_PLAIN_MS_CHAP_V2_HACK)
+
+		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+#if defined(EAP_USE_TTLS_PLAIN_MS_CHAP_V2_HACK)
+		if (m_eap_type == eap_type_ttls
+#if defined(USE_EAP_EXPANDED_TYPES)
+			&& m_peap_tunneled_eap_type == eap_expanded_type_ttls_plain_mschapv2.get_type()
+#else
+			&& m_peap_tunneled_eap_type == eap_type_plain_mschapv2
+#endif //#if defined(USE_EAP_EXPANDED_TYPES)
+			)
+		{
+			eap_header_wr_c sent_eap_packet(
+				m_am_tools,
+				m_ttls_sent_eap_packet.get_data(),
+				m_ttls_sent_eap_packet.get_data_length());
+
+			if (m_ttls_sent_eap_packet.get_data_length() > 0ul
+				&& sent_eap_packet.get_data_length() > 0ul
+				&& sent_eap_packet.get_type() == eap_type_mschapv2
+				&& get_ttls_tunneled_message_state() == eap_ttls_tunneled_message_state_process_challenge_request)
+			{
+				// This message shoud include MS-CHAP-V2 Response.
+				status = ttls_tunneled_message_state_process_challenge_request(&sent_eap_packet);
+				if (status != eap_status_ok)
+				{
+					EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+					return EAP_STATUS_RETURN(m_am_tools, status);
+				}
+			}
+			else
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_wrong_eap_type_state);
+			}
+		}
+#endif //#if defined(EAP_USE_TTLS_PLAIN_MS_CHAP_V2_HACK)
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e tls_application_eap_core_c::create_eap_success_failure_in_forward_to_tunnel(
+	const eap_am_network_id_c * const /*receive_network_id*/,
+	const eap_code_value_e forwarded_eap_code,
+	const u8_t received_eap_identifier)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+	eap_variable_data_c forwarded_packet_buffer(m_am_tools);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("TTLS: %s: tls_application_eap_core_c::create_eap_success_failure_in_forward_to_tunnel()\n"),
+		(m_is_client == true ? "client": "server")));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: tls_application_eap_core_c::create_eap_success_failure_in_forward_to_tunnel()");
+
+	eap_status_e status = forwarded_packet_buffer.set_buffer_length(EAP_CORE_PACKET_BUFFER_LENGTH);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	forwarded_packet_buffer.set_data_length(
+		forwarded_packet_buffer.get_buffer_length());
+
+	eap_header_wr_c forwarded_eap_packet(m_am_tools, 0, 0ul);
+
+	forwarded_eap_packet.set_header_buffer(
+		forwarded_packet_buffer.get_data(),
+		forwarded_packet_buffer.get_buffer_length());
+	if (forwarded_eap_packet.get_is_valid() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	forwarded_eap_packet.reset_header(
+		static_cast<u16_t>(eap_header_wr_c::get_header_length()),
+		m_use_eap_expanded_type);
+	forwarded_eap_packet.set_identifier(received_eap_identifier);
+	forwarded_eap_packet.set_code(forwarded_eap_code);
+	forwarded_eap_packet.set_length(
+		static_cast<u16_t>(eap_header_wr_c::get_header_length()),
+		m_use_eap_expanded_type);
+
+	status = packet_forward_to_tunnel(
+		&m_receive_network_id,
+		&forwarded_eap_packet,
+		forwarded_eap_packet.get_length());
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e tls_application_eap_core_c::check_ttls_eap_payloads(
+	eap_diameter_payloads_c * const payloads,
+	eap_ttls_tunneled_message_type_e * const message_type)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("TTLS: %s: tls_application_eap_core_c::check_ttls_eap_payloads()\n"),
+		(m_is_client == true ? "client": "server")));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: tls_application_eap_core_c::check_ttls_eap_payloads()");
+
+	*message_type = eap_ttls_tunneled_message_type_none;
+
+	eap_array_c<eap_diameter_avp_code_c> needed_payloads(m_am_tools);
+
+	eap_diameter_avp_code_c code_eap_message(eap_diameter_avp_code_eap_message);
+
+	eap_status_e status = needed_payloads.add_object(&code_eap_message, false);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+		
+	// Test the required attribute.
+	status = payloads->check_payloads_existense(
+		&needed_payloads);
+	if (status != eap_status_ok)
+	{
+		// No required payloads.
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	// This packet includes EAP-Message AVP.
+
+	status = payloads->check_mandatory_payloads(
+		&needed_payloads);
+	if (status != eap_status_ok)
+	{
+		// Some mandatory payload is not used here.
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	*message_type = eap_ttls_tunneled_message_type_eap;
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e tls_application_eap_core_c::packet_process_ttls(
+	eap_variable_data_c * const received_eap_message,
+	const u8_t received_eap_identifier,
+	u32_t * const eap_packet_length)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("TLS: %s: message_function: tls_application_eap_core_c::packet_process_ttls()\n"),
+		(m_is_client == true ? "client": "server")));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: tls_application_eap_core_c::packet_process_ttls()");
+
+	eap_status_e status = eap_status_process_general_error;
+
+
+	u32_t payload_length = received_eap_message->get_data_length();
+
+	if (payload_length == 0u)
+	{
+		// No payload in this packet.
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_ok);
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	m_ttls_received_payloads.reset();
+
+	{
+		eap_diameter_avp_header_c received_avp(
+			m_am_tools,
+			received_eap_message->get_data(received_eap_message->get_data_length()),
+			received_eap_message->get_data_length());
+		if (received_avp.get_is_valid() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+		}
+		
+		status = m_ttls_received_payloads.parse_diameter_payloads(
+			&received_avp,
+			&payload_length);
+	}
+
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+	else if (payload_length != 0u)
+	{
+		EAP_TRACE_ERROR(
+			m_am_tools,
+			TRACE_FLAGS_DIAMETER_ERROR,
+			(EAPL("ERROR: eap_radius_payloads_c::parse_radius_packet(): ")
+			 EAPL("RADIUS-header is corrupted. Buffer length and payload ")
+			 EAPL("length does not match. Illegal byte count %lu\n"),
+			 payload_length));
+
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_header_corrupted);
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+#if defined(EAP_USE_TTLS_PLAIN_MS_CHAP_V2_HACK)
+	m_ttls_message_type = eap_ttls_tunneled_message_type_none;
+#endif //#if defined(EAP_USE_TTLS_PLAIN_MS_CHAP_V2_HACK)
+
+
+	if (check_ttls_eap_payloads(&m_ttls_received_payloads, &m_ttls_message_type)
+		== eap_status_ok)
+	{
+		// This packet includes EAP-Message AVP.
+
+		eap_diameter_variable_data_c * const eap_message_payload
+			= m_ttls_received_payloads.get_payload(eap_diameter_avp_code_eap_message);
+
+		if (eap_message_payload == 0)
+		{
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_header_corrupted);
+		}
+
+		eap_variable_data_c * const eap_message
+			= eap_message_payload->get_payload_buffer();
+
+		if (eap_message == 0
+			|| eap_message->get_is_valid_data() == false)
+		{
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_header_corrupted);
+		}
+
+		if (eap_message->get_data_length() < eap_header_base_c::get_header_length())
+		{
+			// Not enough payload in this packet.
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_header_corrupted);
+		}
+
+		*eap_packet_length = eap_message->get_data_length();
+		
+		u8_t * const eap_payload
+			= eap_message->get_data(*eap_packet_length);
+		if (eap_payload == 0)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+		}
+
+		eap_header_wr_c forwarded_eap_packet(m_am_tools, 0, 0ul);
+
+		// We must forward this EAP-packet to tunneled EAP-type.
+		forwarded_eap_packet.set_header_buffer(
+			eap_payload,
+			*eap_packet_length);
+		if (forwarded_eap_packet.get_is_valid() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+		}
+
+		status = packet_forward_to_tunnel(
+			&m_receive_network_id,
+			&forwarded_eap_packet,
+			*eap_packet_length);
+
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+#if defined(EAP_USE_TTLS_PLAIN_MS_CHAP_V2_HACK)
+	else if (check_ttls_plain_mschapv2_payloads(&m_ttls_received_payloads, &m_ttls_message_type)
+		== eap_status_ok)
+	{
+		status = handle_ttls_plain_mschapv2_payloads(
+			&m_ttls_received_payloads,
+			m_ttls_message_type,
+			received_eap_identifier);
+
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+#endif //#if defined(EAP_USE_TTLS_PLAIN_MS_CHAP_V2_HACK)
+
+	else if (check_ttls_plain_pap_payloads(&m_ttls_received_payloads, &m_ttls_message_type)
+		== eap_status_ok)
+	{
+		status = handle_ttls_plain_pap_payloads(
+			&m_ttls_received_payloads,
+			m_ttls_message_type,
+			received_eap_identifier);
+
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	else
+	{
+		// Not correct AVP-payloads are included.
+		EAP_TRACE_ERROR(
+			m_am_tools, 
+			TRACE_FLAGS_TLS_PEAP_ERROR, 
+			(EAPL("ERROR: tls_application_eap_core_c::packet_process_ttls(): ")
+			 EAPL("Not correct AVP-payloads are included in eap_state_variable_e %d.\n"),
+			 m_tunneled_eap_type_authentication_state));
+
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_header_corrupted);
+	}
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e tls_application_eap_core_c::packet_process_xp_peap_v0(
+	eap_variable_data_c * const packet,
+	const u8_t received_eap_identifier,
+	u32_t * const eap_packet_length)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("TTLS: %s: tls_application_eap_core_c::packet_process_xp_peap_v0()\n"),
+		(m_is_client == true ? "client": "server")));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: tls_application_eap_core_c::packet_process_xp_peap_v0()");
+
+	// XP PEAPv0 does not include EAP-header (code, identifier and length).
+	// Here we must create those attributes.
+	// Exception to this rule is, EAP-success and EAP-failure are sent in "Extensions Request Packet"
+	// or "Extensions Response Packet".
+	// The whole EAP-header is included
+	// to "Extensions Request Packet" and "Extensions Response Packet".
+	// See draft-kamath-pppext-peapv0-00.txt.
+	// 
+	// EAP-packet without code, identifier and length:
+	// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+	// |  EAP-type     |  EAP-data ...                                                                  
+	// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+	// EAP-packet with "Extensions Request Packet" or "Extensions Response Packet":
+	// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+	// |  EAP-code     |EAP-identifier | EAP-length                    | EAP-type      |  EAP-data ...  
+	// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+	// 
+	// EAP-code of "Extensions Request Packet" is 1 which is same as EAP-type of EAP-identity.
+	// EAP-code of "Extensions Response Packet" is 2 which is same as EAP-type of EAP-notification.
+
+	eap_status_e status = eap_status_process_general_error;
+
+	u32_t received_eap_packet_length = packet->get_data_length();
+	if (received_eap_packet_length == 0ul)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	
+	eap_type_value_e received_eap_type_or_code(eap_type_none);
+
+	const u8_t * const p_received_eap_type_or_code
+		= static_cast<const u8_t *>(packet->get_data(sizeof(u8_t)));
+
+	if (p_received_eap_type_or_code == 0)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+	else if (*p_received_eap_type_or_code == eap_type_expanded_type)
+	{
+		status = eap_expanded_type_c::read_type(
+			m_am_tools,
+			0ul,
+			p_received_eap_type_or_code,
+			received_eap_packet_length,
+			&received_eap_type_or_code);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+	else
+	{
+		received_eap_type_or_code = static_cast<eap_type_ietf_values_e>(*p_received_eap_type_or_code);
+	}
+
+	EAP_TRACE_ALWAYS(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("packet_process_xp_peap_v0(): XP-PEAPv0 %s, tunneled eap_type_or_code %d, ")
+		 EAPL("eap_packet_length %d, m_tunneled_eap_type_authentication_state %d, ")
+		 EAPL("m_tls_session_type %d.\n"),
+		 (m_is_client == true ? "client": "server"),
+		 convert_eap_type_to_u32_t(received_eap_type_or_code),
+		 received_eap_packet_length,
+		 m_tunneled_eap_type_authentication_state,
+		 m_tls_session_type));
+
+	bool probably_is_extensions_packet = false;
+
+	const u32_t EAP_PEAP_V0_RESULT_AVP_STATUS_LENGTH = sizeof(u16_t);
+	const u32_t EAP_PEAP_V0_EXTENSIONS_PACKET_LENGTH
+		= eap_header_wr_c::get_header_length()
+		+ eap_header_wr_c::get_ietf_type_field_length()
+		+ tls_peap_tlv_header_c::get_header_length()
+		+ EAP_PEAP_V0_RESULT_AVP_STATUS_LENGTH;
+
+	if (received_eap_packet_length >= EAP_PEAP_V0_EXTENSIONS_PACKET_LENGTH)
+	{
+		eap_header_wr_c possible_extensions_eap_packet(
+			m_am_tools,
+			packet->get_data(packet->get_data_length()),
+			packet->get_data_length());
+
+		if (possible_extensions_eap_packet.get_is_valid() == true
+			&& possible_extensions_eap_packet.check_header() == eap_status_ok)
+		{
+			if (m_is_client == true
+				&& possible_extensions_eap_packet.get_code() == eap_code_request
+				&& possible_extensions_eap_packet.get_type() == eap_type_tlv_extensions)
+			{
+				// This is "Extensions Request Packet".
+				probably_is_extensions_packet = true;
+			}
+			else if (m_is_client == false
+				&& possible_extensions_eap_packet.get_code() == eap_code_response
+				&& possible_extensions_eap_packet.get_type() == eap_type_tlv_extensions)
+			{
+				// This is "Extensions Response Packet".
+				probably_is_extensions_packet = true;
+			}
+		}
+	}
+
+
+	eap_code_value_e forwarded_eap_code = eap_code_none;
+	eap_variable_data_c forwarded_packet_buffer(m_am_tools);
+
+	// In the first phase we will check first byte as a EAP-type field.
+	if (probably_is_extensions_packet == false
+		&& ((m_tunneled_eap_type_authentication_state == eap_state_none
+			&& received_eap_type_or_code == eap_type_identity)
+		|| (m_tunneled_eap_type_authentication_state == eap_state_identity_request_received
+			&& (received_eap_type_or_code != eap_type_none
+				&& received_eap_type_or_code != eap_type_identity)
+			)))
+	{
+		EAP_TRACE_ALWAYS(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("packet_process_xp_peap_v0(): XP-PEAPv0 tunneled eap_type %d, ")
+			 EAPL("m_tunneled_eap_type_authentication_state %d.\n"),
+			 convert_eap_type_to_u32_t(received_eap_type_or_code),
+			 m_tunneled_eap_type_authentication_state));
+		
+		if (m_tunneled_eap_type_authentication_state == eap_state_none
+			&& received_eap_type_or_code == eap_type_identity)
+		{
+			m_tunneled_eap_type_authentication_state = eap_state_identity_request_received;
+		}
+
+		*eap_packet_length = eap_header_base_c::get_header_length()+packet->get_data_length();
+
+		status = forwarded_packet_buffer.set_buffer_length(*eap_packet_length);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+		forwarded_packet_buffer.set_data_length(*eap_packet_length);
+
+		status = forwarded_packet_buffer.add_data_to_offset(
+			eap_header_base_c::get_header_length(),
+			packet->get_data(packet->get_data_length()),
+			packet->get_data_length());
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		if (m_is_client == true)
+		{
+			forwarded_eap_code = eap_code_request;
+		}
+		else
+		{
+			forwarded_eap_code = eap_code_response;
+		}
+	}
+	else if (probably_is_extensions_packet == true
+			  // The first case will allow sessions resumption without tunneled EAP-authentication
+			 && ((m_tunneled_eap_type_authentication_state
+				  == eap_state_none && m_peap_allow_tunneled_session_resumption == true)
+				 || m_tunneled_eap_type_authentication_state == eap_state_identity_request_received
+				 || m_tunneled_eap_type_authentication_state
+				 == eap_state_authentication_finished_successfully
+				 || m_tunneled_eap_type_authentication_state
+				 == eap_state_authentication_terminated_unsuccessfully))
+	{
+		// In the second phase we will check first byte as a EAP-code field
+
+		EAP_TRACE_ALWAYS(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("packet_process_xp_peap_v0(): XP-PEAPv0 tunneled eap_code %d, ")
+			 EAPL("m_tunneled_eap_type_authentication_state %d.\n"),
+			 convert_eap_type_to_u32_t(received_eap_type_or_code),
+			 m_tunneled_eap_type_authentication_state));
+		
+		eap_header_wr_c final_eap_packet(
+			m_am_tools,
+			packet->get_data(packet->get_data_length()),
+			packet->get_data_length());
+		if (final_eap_packet.get_is_valid() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+		}
+
+		status = final_eap_packet.check_header();
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_ERROR(
+				m_am_tools, 
+				TRACE_FLAGS_TLS_PEAP_ERROR, 
+				(EAPL("ERROR: EAP-header corrupted.\n")));
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		u32_t parsed_eap_data_length = final_eap_packet.get_type_data_length();
+
+		if (parsed_eap_data_length == 0u)
+		{
+			// No payload in this packet.
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_ok);
+		}
+
+		if (final_eap_packet.get_header_length()+parsed_eap_data_length > packet->get_data_length())
+		{
+			// Corrupted length in EAP-header.
+			EAP_TRACE_ERROR(
+				m_am_tools, 
+				TRACE_FLAGS_TLS_PEAP_ERROR, 
+				(EAPL("ERROR: EAP-header corrupted.\n")));
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_header_corrupted);
+		}
+
+
+		if (m_is_client == true)
+		{
+			trace_tunneled_packet(EAPL("-> TUNNELED packet client"), &final_eap_packet);
+		}
+		else
+		{
+			trace_tunneled_packet(EAPL("-> TUNNELED packet server"), &final_eap_packet);
+		}
+
+
+		peap_tlv_payloads_c * peap_tlv_payloads = new peap_tlv_payloads_c(m_am_tools);
+		eap_automatic_variable_c<peap_tlv_payloads_c> automatic_peap_tlv_payloads(m_am_tools, peap_tlv_payloads);
+
+		if (peap_tlv_payloads == 0
+			|| peap_tlv_payloads->get_is_valid() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+		}
+
+		tls_peap_tlv_header_c tlv(
+			m_am_tools,
+			final_eap_packet.get_type_data(final_eap_packet.get_type_data_length()),
+			parsed_eap_data_length);
+		if (tlv.get_is_valid() == false)
+		{
+			EAP_TRACE_ERROR(
+				m_am_tools, 
+				TRACE_FLAGS_TLS_PEAP_ERROR, 
+				(EAPL("ERROR: No peap_tlv_payloads_c.\n")));
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+		}
+
+		status = tlv.check_header();
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_ERROR(
+				m_am_tools, 
+				TRACE_FLAGS_TLS_PEAP_ERROR, 
+				(EAPL("ERROR: TLV header corrupted.\n")));
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = parse_peap_tlv_payload(
+			final_eap_packet.get_type_data(final_eap_packet.get_type_data_length()),
+			&parsed_eap_data_length,
+			peap_tlv_payloads);
+
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+		else if (parsed_eap_data_length != 0u)
+		{
+			EAP_TRACE_ERROR(
+				m_am_tools,
+				TRACE_FLAGS_TLS_PEAP_ERROR,
+				(EAPL("ERROR: tls_application_eap_core_c::packet_process_xp_peap_v0(): ")
+				 EAPL("PEAP-header is corrupted. Buffer length and payload ")
+				 EAPL("length does not match. Illegal byte count %lu\n"),
+				 parsed_eap_data_length));
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_header_corrupted);
+		}
+
+		// Checks the payloads existence.
+		if (peap_tlv_payloads->check_payloads(
+				peap_tlv_payloads_c::peap_tlv_payload_status_must_be, // result_tlv
+				peap_tlv_payloads_c::peap_tlv_payload_status_must_not_be, // nak_tlv
+				peap_tlv_payloads_c::peap_tlv_payload_status_must_not_be, // crypto_binding_tlv
+				peap_tlv_payloads_c::peap_tlv_payload_status_must_not_be, // eap_payload_tlv
+				peap_tlv_payloads_c::peap_tlv_payload_status_must_not_be  // intermediate_result_tlv
+				) == true
+			)
+		{
+			// OK.
+
+			if (peap_tlv_payloads->get_result_tlv()->get_data_length()
+				< EAP_PEAP_V0_RESULT_AVP_STATUS_LENGTH)
+			{
+				EAP_TRACE_ERROR(
+					m_am_tools, 
+					TRACE_FLAGS_TLS_PEAP_ERROR, 
+					(EAPL("ERROR: TLV-result payload too short.\n")));
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_header_corrupted);
+			}
+
+			u8_t * const tlv_status_network_order
+				= reinterpret_cast<u8_t *>(
+					peap_tlv_payloads->get_result_tlv()->get_data(
+						EAP_PEAP_V0_RESULT_AVP_STATUS_LENGTH));
+			if (tlv_status_network_order == 0)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+			}
+
+			EAP_ASSERT(EAP_PEAP_V0_RESULT_AVP_STATUS_LENGTH == sizeof(u16_t));
+
+			u16_t tlv_status_host_order
+				= eap_read_u16_t_network_order(
+					tlv_status_network_order,
+					EAP_PEAP_V0_RESULT_AVP_STATUS_LENGTH);
+
+			if (tlv_status_host_order == tls_peap_tlv_status_success)
+			{
+				forwarded_eap_code = eap_code_success;
+			}
+			else
+			{
+				// All other EAP-codes are assumed EAP-Failure.
+				forwarded_eap_code = eap_code_failure;
+			}
+
+			eap_header_string_c eap_string;
+			EAP_UNREFERENCED_PARAMETER(eap_string);
+
+			EAP_TRACE_ALWAYS(
+				m_am_tools, 
+				TRACE_FLAGS_DEFAULT, 
+				(EAPL("packet_process_xp_peap_v0(): XP-PEAPv0 tunneled %s received.\n"),
+				 eap_string.get_eap_code_string(forwarded_eap_code)));
+
+			// Here we swap the addresses.
+			eap_am_network_id_c send_network_id(
+				m_am_tools,
+				m_receive_network_id.get_destination_id(),
+				m_receive_network_id.get_source_id(),
+				m_receive_network_id.get_type());
+
+
+			if (m_is_client == true)
+			{
+				if ((m_tls_session_type == tls_session_type_original_session_resumption
+						|| m_tls_session_type == tls_session_type_stateless_session_resumption)
+					&& m_tunneled_eap_type_authentication_state == eap_state_none
+					&& forwarded_eap_code == eap_code_success)
+				{
+					// Send tunneled EAP-Success acknowledge.
+					eap_status_e status = send_tunneled_acknowledge_xp_peap_v0(
+						eap_code_success,
+						received_eap_identifier);
+					if (status != eap_status_ok)
+					{
+						m_tunneled_eap_type_authentication_state
+							= eap_state_authentication_terminated_unsuccessfully;
+					}
+					else
+					{
+						// Send state notification to lower layer.
+
+						eap_state_notification_c * const notification = new eap_state_notification_c(
+							m_am_tools,
+							&send_network_id,
+							m_is_client,
+							eap_state_notification_eap,
+							eap_protocol_layer_eap,
+							m_peap_tunneled_eap_type,
+							eap_state_none,
+							eap_state_authentication_finished_successfully,
+							received_eap_identifier,
+							false);
+
+						eap_automatic_variable_c<eap_state_notification_c> automatic_notification(m_am_tools, notification);
+
+						if (notification == 0)
+						{
+							m_tunneled_eap_type_authentication_state
+								= eap_state_authentication_terminated_unsuccessfully;
+							EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+							return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+						}
+
+						get_application_partner()->state_notification(notification);
+
+						status = eap_status_success;
+					}
+
+					EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+					return EAP_STATUS_RETURN(m_am_tools, status);
+				}
+				else if ((m_tls_session_type == tls_session_type_original_session_resumption
+							|| m_tls_session_type == tls_session_type_stateless_session_resumption)
+					&& m_tunneled_eap_type_authentication_state == eap_state_none
+					&& forwarded_eap_code == eap_code_failure)
+				{
+					// Send tunneled EAP-Failure acknowledge.
+					eap_status_e status = send_tunneled_acknowledge_xp_peap_v0(
+						eap_code_failure,
+						received_eap_identifier);
+
+					m_tunneled_eap_type_authentication_state
+						= eap_state_authentication_terminated_unsuccessfully;
+
+						// Send state notification to lower layer.
+					eap_state_notification_c * const notification = new eap_state_notification_c(
+						m_am_tools,
+						&send_network_id,
+						m_is_client,
+						eap_state_notification_eap,
+						eap_protocol_layer_eap,
+						m_peap_tunneled_eap_type,
+						eap_state_none,
+						eap_state_authentication_terminated_unsuccessfully,
+						received_eap_identifier,
+						false);
+
+					eap_automatic_variable_c<eap_state_notification_c> automatic_notification(m_am_tools, notification);
+
+					if (notification == 0)
+					{
+						m_tunneled_eap_type_authentication_state
+							= eap_state_authentication_terminated_unsuccessfully;
+						EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+						return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+					}
+
+					notification->set_authentication_error(eap_status_authentication_failure);
+
+					get_application_partner()->state_notification(notification);
+					
+					EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+					return EAP_STATUS_RETURN(m_am_tools, status);
+				}
+				else
+				{
+					// Client must forward this EAP-packet to tunneled EAP-type.
+					*eap_packet_length = eap_header_base_c::get_header_length();
+
+					status = forwarded_packet_buffer.set_buffer_length(*eap_packet_length);
+					if (status != eap_status_ok)
+					{
+						EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+						return EAP_STATUS_RETURN(m_am_tools, status);
+					}
+					forwarded_packet_buffer.set_data_length(*eap_packet_length);
+				}
+			}
+			else
+			{
+				status = eap_status_authentication_failure;
+
+				// This is server.
+				// Client sent a acknowledge.
+				if (forwarded_eap_code == eap_code_success
+					&& (m_tunneled_eap_type_authentication_state
+						== eap_state_authentication_finished_successfully
+						|| ((m_tls_session_type == tls_session_type_original_session_resumption
+								|| m_tls_session_type == tls_session_type_stateless_session_resumption)
+							&& m_tunneled_eap_type_authentication_state == eap_state_none)))
+				{
+					// XP-PEAP Authentication OK.
+
+					eap_state_notification_c * const notification = new eap_state_notification_c(
+						m_am_tools,
+						&send_network_id,
+						m_is_client,
+						eap_state_notification_eap,
+						eap_protocol_layer_eap,
+						m_peap_tunneled_eap_type,
+						eap_state_none,
+						eap_state_authentication_finished_successfully,
+						received_eap_identifier,
+						false);
+
+					eap_automatic_variable_c<eap_state_notification_c> automatic_notification(m_am_tools, notification);
+
+					if (notification == 0)
+					{
+						m_tunneled_eap_type_authentication_state
+							= eap_state_authentication_terminated_unsuccessfully;
+						EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+						return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+					}
+
+					get_application_partner()->state_notification(notification);
+
+					status = eap_status_success;
+				}
+				else
+				{
+					// Authentication failed.
+					eap_state_notification_c * const notification = new eap_state_notification_c(
+						m_am_tools,
+						&send_network_id,
+						m_is_client,
+						eap_state_notification_eap,
+						eap_protocol_layer_eap,
+						m_peap_tunneled_eap_type,
+						eap_state_none,
+						eap_state_authentication_terminated_unsuccessfully,
+						received_eap_identifier,
+						false);
+
+					eap_automatic_variable_c<eap_state_notification_c> automatic_notification(m_am_tools, notification);
+
+					if (notification == 0)
+					{
+						m_tunneled_eap_type_authentication_state
+							= eap_state_authentication_terminated_unsuccessfully;
+						EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+						return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+					}
+
+					notification->set_authentication_error(eap_status_authentication_failure);
+
+					get_application_partner()->state_notification(notification);
+
+					status = eap_status_authentication_failure;
+				}
+
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+		}
+		else
+		{
+			// Not correct PEAP_TLV-payloads are included.
+			EAP_TRACE_ERROR(
+				m_am_tools, 
+				TRACE_FLAGS_TLS_PEAP_ERROR, 
+				(EAPL("ERROR: tls_application_eap_core_c::packet_process_xp_peap_v0(): ")
+				 EAPL("Not correct PEAP_TLV-payloads are included in eap_state_variable_e %d.\n"),
+				 m_tunneled_eap_type_authentication_state));
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_header_corrupted);
+		}
+
+	}
+	else
+	{
+		status = eap_status_unexpected_message;
+
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	eap_header_wr_c forwarded_eap_packet(m_am_tools, 0, 0ul);
+
+	forwarded_eap_packet.set_header_buffer(
+		forwarded_packet_buffer.get_data(*eap_packet_length),
+		*eap_packet_length);
+	if (forwarded_eap_packet.get_is_valid() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	forwarded_eap_packet.reset_header(
+		static_cast<u16_t>(*eap_packet_length),
+		m_use_eap_expanded_type);
+	forwarded_eap_packet.set_identifier(received_eap_identifier);
+	forwarded_eap_packet.set_code(forwarded_eap_code);
+	forwarded_eap_packet.set_length(
+		static_cast<u16_t>(*eap_packet_length),
+		m_use_eap_expanded_type);
+
+	status = packet_forward_to_tunnel(
+		&m_receive_network_id,
+		&forwarded_eap_packet,
+		*eap_packet_length);
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e tls_application_eap_core_c::packet_process_peap_v1(
+	eap_variable_data_c * const received_eap_message,
+	const u8_t received_eap_identifier,
+	u32_t * const eap_packet_length)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("TLS: %s: message_function: tls_application_eap_core_c::packet_process_peap_v1()\n"),
+		(m_is_client == true ? "client": "server")));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: tls_application_eap_core_c::packet_process_peap_v1()");
+
+	eap_status_e status = eap_status_process_general_error;
+
+
+	eap_header_wr_c received_eap_header(
+		m_am_tools,
+		received_eap_message->get_data(received_eap_message->get_data_length()),
+		received_eap_message->get_data_length());
+	if (received_eap_header.get_is_valid() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	status = received_eap_header.check_header();
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_ERROR(
+			m_am_tools, 
+			TRACE_FLAGS_TLS_PEAP_ERROR, 
+			(EAPL("ERROR: EAP-header corrupted.\n")));
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	u32_t parsed_eap_data_length = received_eap_header.get_type_data_length();
+
+	if (received_eap_header.get_header_length()+parsed_eap_data_length
+		> received_eap_message->get_data_length())
+	{
+		// Corrupted length in EAP-header.
+		EAP_TRACE_ERROR(
+			m_am_tools, 
+			TRACE_FLAGS_TLS_PEAP_ERROR, 
+			(EAPL("ERROR: EAP-header corrupted.\n")));
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_header_corrupted);
+	}
+
+
+	// This is plain EAP-packet.
+	*eap_packet_length = received_eap_message->get_data_length();
+
+	eap_header_wr_c forwarded_eap_packet(m_am_tools, 0, 0ul);
+
+	forwarded_eap_packet.set_header_buffer(
+		received_eap_message->get_data(*eap_packet_length),
+		*eap_packet_length);
+
+	eap_code_value_e received_eap_code = received_eap_header.get_code();
+
+	// Here we swap the addresses.
+	eap_am_network_id_c send_network_id(
+		m_am_tools,
+		m_receive_network_id.get_destination_id(),
+		m_receive_network_id.get_source_id(),
+		m_receive_network_id.get_type());
+
+	if (
+#if defined(USE_EAP_TLS_PEAP_TPPD_PEAP_V1_NEW_FIXES)
+		m_is_client == false
+#if defined(USE_EAP_PEAPV1_EXTENSIONS)
+		&& m_server_use_peapv1_extensions_request == false
+#endif //#if defined(USE_EAP_PEAPV1_EXTENSIONS)
+		&&
+#endif //#if defined(USE_EAP_TLS_PEAP_TPPD_PEAP_V1_NEW_FIXES)
+		m_use_tppd_tls_peap == true
+		&& (m_tunneled_eap_type_authentication_state
+			== eap_state_authentication_finished_successfully
+		|| m_tunneled_eap_type_authentication_state
+			== eap_state_authentication_terminated_unsuccessfully))
+	{
+		status = eap_status_authentication_failure;
+
+		// This is server.
+		// Client sent a acknowledge.
+		if (m_tunneled_eap_type_authentication_state
+			== eap_state_authentication_finished_successfully
+			&& forwarded_eap_packet.get_type() == eap_type_peap
+			&& forwarded_eap_packet.get_type_data_length() == sizeof(u8_t) // There is only flags and version
+			&& forwarded_eap_packet.get_type_data(sizeof(u8_t)) != 0
+			&& *(forwarded_eap_packet.get_type_data(sizeof(u8_t))) == peap_version_1)
+		{
+			// PEAPv1 Authentication OK.
+
+			eap_state_notification_c notification(
+				m_am_tools,
+				&send_network_id,
+				m_is_client,
+				eap_state_notification_eap,
+				eap_protocol_layer_eap,
+				m_peap_tunneled_eap_type,
+				eap_state_none,
+				eap_state_authentication_finished_successfully,
+				received_eap_identifier,
+				false);
+			get_application_partner()->state_notification(&notification);
+
+			status = eap_status_success;
+		}
+		else
+		{
+			// PEAPv1 Authentication failed.
+			eap_state_notification_c notification(
+				m_am_tools,
+				&send_network_id,
+				m_is_client,
+				eap_state_notification_eap,
+				eap_protocol_layer_eap,
+				m_peap_tunneled_eap_type,
+				eap_state_none,
+				eap_state_authentication_terminated_unsuccessfully,
+				received_eap_identifier,
+				false);
+
+			notification.set_authentication_error(eap_status_authentication_failure);
+
+			get_application_partner()->state_notification(&notification);
+
+			status = eap_status_authentication_failure;
+		}
+
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+#if defined(USE_EAP_TLS_PEAP_TPPD_PEAP_V1_NEW_FIXES)
+	else if (m_is_client == true
+		&& m_use_tppd_tls_peap == true
+		&& (m_tls_session_type == tls_session_type_original_session_resumption
+			|| m_tls_session_type == tls_session_type_stateless_session_resumption)
+		&& m_tunneled_eap_type_authentication_state == eap_state_none
+		&& received_eap_code == eap_code_success)
+	{
+		// Received tunneled EAP-Success.
+		// This will tell client the tunneled
+		// EAP-session was also restored.
+
+		if (m_is_client == true)
+		{
+			trace_tunneled_packet(EAPL("-> TUNNELED packet client"), &forwarded_eap_packet);
+		}
+		else
+		{
+			trace_tunneled_packet(EAPL("-> TUNNELED packet server"), &forwarded_eap_packet);
+		}
+
+		m_tunneled_eap_type_authentication_state = eap_state_authentication_finished_successfully;
+
+		eap_state_notification_c notification(
+			m_am_tools,
+			&send_network_id,
+			m_is_client,
+			eap_state_notification_eap,
+			eap_protocol_layer_eap,
+			m_peap_tunneled_eap_type,
+			eap_state_none,
+			eap_state_tppd_peapv1_authentication_finished_successfully_with_tunneled_eap_success,
+			received_eap_identifier,
+			false);
+		get_application_partner()->state_notification(&notification);
+
+		status = eap_status_success;
+	}
+#endif //#if defined(USE_EAP_TLS_PEAP_TPPD_PEAP_V1_NEW_FIXES)
+	else
+	{
+
+#if defined(USE_EAP_PEAPV1_EXTENSIONS)
+		if ((m_is_client == true
+				&& forwarded_eap_packet.get_code() == eap_code_request
+				&& forwarded_eap_packet.get_type() == eap_type_tlv_extensions)
+#if defined(USE_EAP_CORE_SERVER)
+			|| (m_is_client == false
+				&& forwarded_eap_packet.get_code() == eap_code_response
+				&& forwarded_eap_packet.get_type() == eap_type_tlv_extensions)
+#endif //#if defined(USE_EAP_CORE_SERVER)
+			)
+		{
+			if (m_is_client == true)
+			{
+				trace_tunneled_packet(EAPL("-> TUNNELED packet client"), &forwarded_eap_packet);
+			}
+			else
+			{
+				trace_tunneled_packet(EAPL("-> TUNNELED packet server"), &forwarded_eap_packet);
+			}
+
+
+			peap_tlv_payloads_c * peap_tlv_payloads = new peap_tlv_payloads_c(m_am_tools);
+			eap_automatic_variable_c<peap_tlv_payloads_c> automatic_peap_tlv_payloads(m_am_tools, peap_tlv_payloads);
+
+			if (peap_tlv_payloads == 0
+				|| peap_tlv_payloads->get_is_valid() == false)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+			}
+
+			tls_peap_tlv_header_c tlv(
+				m_am_tools,
+				forwarded_eap_packet.get_type_data(forwarded_eap_packet.get_type_data_length()),
+				parsed_eap_data_length);
+			if (tlv.get_is_valid() == false)
+			{
+				EAP_TRACE_ERROR(
+					m_am_tools, 
+					TRACE_FLAGS_TLS_PEAP_ERROR, 
+					(EAPL("ERROR: No peap_tlv_payloads_c.\n")));
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+			}
+
+			status = tlv.check_header();
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_ERROR(
+					m_am_tools, 
+					TRACE_FLAGS_TLS_PEAP_ERROR, 
+					(EAPL("ERROR: TLV header corrupted.\n")));
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			status = parse_peap_tlv_payload(
+				forwarded_eap_packet.get_type_data(forwarded_eap_packet.get_type_data_length()),
+				&parsed_eap_data_length,
+				peap_tlv_payloads);
+
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+			else if (parsed_eap_data_length != 0u)
+			{
+				EAP_TRACE_ERROR(
+					m_am_tools,
+					TRACE_FLAGS_TLS_PEAP_ERROR,
+					(EAPL("ERROR: tls_application_eap_core_c::packet_process_peap_v1(): ")
+					 EAPL("PEAP-header is corrupted. Buffer length and payload ")
+					 EAPL("length does not match. Illegal byte count %lu\n"),
+					 parsed_eap_data_length));
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_header_corrupted);
+			}
+
+			// Checks the payloads existence.
+			if (peap_tlv_payloads->check_payloads(
+					peap_tlv_payloads_c::peap_tlv_payload_status_must_be, // result_tlv
+					peap_tlv_payloads_c::peap_tlv_payload_status_must_not_be, // nak_tlv
+					peap_tlv_payloads_c::peap_tlv_payload_status_must_not_be, // crypto_binding_tlv
+					peap_tlv_payloads_c::peap_tlv_payload_status_must_not_be, // eap_payload_tlv
+					peap_tlv_payloads_c::peap_tlv_payload_status_must_not_be  // intermediate_result_tlv
+					) == true
+				)
+			{
+				// OK.
+
+				const u32_t EAP_PEAP_V1_RESULT_AVP_STATUS_LENGTH = sizeof(u16_t);
+
+				if (peap_tlv_payloads->get_result_tlv()->get_data_length()
+					< EAP_PEAP_V1_RESULT_AVP_STATUS_LENGTH)
+				{
+					EAP_TRACE_ERROR(
+						m_am_tools, 
+						TRACE_FLAGS_TLS_PEAP_ERROR, 
+						(EAPL("ERROR: TLV-result payload too short.\n")));
+					EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+					return EAP_STATUS_RETURN(m_am_tools, eap_status_header_corrupted);
+				}
+
+				u8_t * const tlv_status_network_order
+					= reinterpret_cast<u8_t *>(
+						peap_tlv_payloads->get_result_tlv()->get_data(
+							EAP_PEAP_V1_RESULT_AVP_STATUS_LENGTH));
+				if (tlv_status_network_order == 0)
+				{
+					EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+					return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+				}
+
+				EAP_ASSERT(EAP_PEAP_V1_RESULT_AVP_STATUS_LENGTH == sizeof(u16_t));
+
+				u16_t tlv_status_host_order
+					= eap_read_u16_t_network_order(
+						tlv_status_network_order,
+						EAP_PEAP_V1_RESULT_AVP_STATUS_LENGTH);
+
+				eap_code_value_e forwarded_eap_code(eap_code_none);
+
+				if (tlv_status_host_order == tls_peap_tlv_status_success)
+				{
+					forwarded_eap_code = eap_code_success;
+				}
+				else
+				{
+					// All other EAP-codes are assumed EAP-Failure.
+					forwarded_eap_code = eap_code_failure;
+				}
+
+				eap_header_string_c eap_string;
+				EAP_UNREFERENCED_PARAMETER(eap_string);
+
+				EAP_TRACE_ALWAYS(
+					m_am_tools, 
+					TRACE_FLAGS_DEFAULT, 
+					(EAPL("tls_application_eap_core_c::packet_process_peap_v1(): PEAPv1 tunneled %s received.\n"),
+					 eap_string.get_eap_code_string(forwarded_eap_code)));
+
+				// Here we swap the addresses.
+				eap_am_network_id_c send_network_id(
+					m_am_tools,
+					m_receive_network_id.get_destination_id(),
+					m_receive_network_id.get_source_id(),
+					m_receive_network_id.get_type());
+
+
+				if (m_is_client == true)
+				{
+					if ((m_tls_session_type == tls_session_type_original_session_resumption
+							|| m_tls_session_type == tls_session_type_stateless_session_resumption)
+						&& m_tunneled_eap_type_authentication_state == eap_state_none
+						&& forwarded_eap_code == eap_code_success)
+					{
+						// Send tunneled EAP-Success acknowledge.
+						status = send_tunneled_acknowledge_xp_peap_v0(
+							eap_code_success,
+							received_eap_identifier);
+						if (status != eap_status_ok)
+						{
+							m_tunneled_eap_type_authentication_state
+								= eap_state_authentication_terminated_unsuccessfully;
+						}
+						else
+						{
+							// Send state notification to lower layer.
+
+							eap_state_notification_c * const notification = new eap_state_notification_c(
+								m_am_tools,
+								&send_network_id,
+								m_is_client,
+								eap_state_notification_eap,
+								eap_protocol_layer_eap,
+								m_peap_tunneled_eap_type,
+								eap_state_none,
+								eap_state_authentication_finished_successfully,
+								received_eap_identifier,
+								false);
+
+							eap_automatic_variable_c<eap_state_notification_c> automatic_notification(m_am_tools, notification);
+
+							if (notification == 0)
+							{
+								m_tunneled_eap_type_authentication_state
+									= eap_state_authentication_terminated_unsuccessfully;
+								EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+								return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+							}
+
+							get_application_partner()->state_notification(notification);
+
+							status = eap_status_success;
+						}
+
+						EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+						return EAP_STATUS_RETURN(m_am_tools, status);
+					}
+					else if ((m_tls_session_type == tls_session_type_original_session_resumption
+								|| m_tls_session_type == tls_session_type_stateless_session_resumption)
+						&& m_tunneled_eap_type_authentication_state == eap_state_none
+						&& forwarded_eap_code == eap_code_failure)
+					{
+						// Send tunneled EAP-Failure acknowledge.
+						eap_status_e status = send_tunneled_acknowledge_xp_peap_v0(
+							eap_code_failure,
+							received_eap_identifier);
+
+						m_tunneled_eap_type_authentication_state
+							= eap_state_authentication_terminated_unsuccessfully;
+
+							// Send state notification to lower layer.
+						eap_state_notification_c * const notification = new eap_state_notification_c(
+							m_am_tools,
+							&send_network_id,
+							m_is_client,
+							eap_state_notification_eap,
+							eap_protocol_layer_eap,
+							m_peap_tunneled_eap_type,
+							eap_state_none,
+							eap_state_authentication_terminated_unsuccessfully,
+							received_eap_identifier,
+							false);
+
+						eap_automatic_variable_c<eap_state_notification_c> automatic_notification(m_am_tools, notification);
+
+						if (notification == 0)
+						{
+							m_tunneled_eap_type_authentication_state
+								= eap_state_authentication_terminated_unsuccessfully;
+							EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+							return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+						}
+
+						notification->set_authentication_error(eap_status_authentication_failure);
+
+						get_application_partner()->state_notification(notification);
+						
+						EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+						return EAP_STATUS_RETURN(m_am_tools, status);
+					}
+					else
+					{
+						// Client must forward this EAP-packet to tunneled EAP-type.
+
+						m_client_send_peapv1_extensions_response = true;
+
+						eap_variable_data_c forwarded_packet_buffer(m_am_tools);
+						if (forwarded_packet_buffer.get_is_valid() == false)
+						{
+							EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+							return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+						}
+
+						*eap_packet_length = eap_header_base_c::get_header_length();
+
+						status = forwarded_packet_buffer.set_buffer_length(*eap_packet_length);
+						if (status != eap_status_ok)
+						{
+							EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+							return EAP_STATUS_RETURN(m_am_tools, status);
+						}
+
+						status = forwarded_packet_buffer.set_data_length(*eap_packet_length);
+						if (status != eap_status_ok)
+						{
+							EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+							return EAP_STATUS_RETURN(m_am_tools, status);
+						}
+
+						{
+							eap_header_wr_c forwarded_eap_packet(m_am_tools, 0, 0ul);
+
+							forwarded_eap_packet.set_header_buffer(
+								forwarded_packet_buffer.get_data(*eap_packet_length),
+								*eap_packet_length);
+							if (forwarded_eap_packet.get_is_valid() == false)
+							{
+								EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+								return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+							}
+
+							forwarded_eap_packet.reset_header(
+								static_cast<u16_t>(*eap_packet_length),
+								m_use_eap_expanded_type);
+							forwarded_eap_packet.set_identifier(received_eap_identifier);
+							forwarded_eap_packet.set_code(forwarded_eap_code);
+							forwarded_eap_packet.set_length(
+								static_cast<u16_t>(*eap_packet_length),
+								m_use_eap_expanded_type);
+
+							// Forward packet to upper layer.
+							status = packet_forward_to_tunnel(
+								&m_receive_network_id,
+								&forwarded_eap_packet,
+								*eap_packet_length);
+						}
+					}
+				}
+				else
+				{
+					status = eap_status_authentication_failure;
+
+					// This is server.
+					// Client sent a acknowledge.
+					if (forwarded_eap_code == eap_code_success
+						&& (m_tunneled_eap_type_authentication_state
+							== eap_state_authentication_finished_successfully
+							|| ((m_tls_session_type == tls_session_type_original_session_resumption
+									|| m_tls_session_type == tls_session_type_stateless_session_resumption)
+								&& m_tunneled_eap_type_authentication_state == eap_state_none)))
+					{
+						// PEAPv1 Authentication OK.
+
+						eap_state_notification_c * const notification = new eap_state_notification_c(
+							m_am_tools,
+							&send_network_id,
+							m_is_client,
+							eap_state_notification_eap,
+							eap_protocol_layer_eap,
+							m_peap_tunneled_eap_type,
+							eap_state_none,
+							eap_state_authentication_finished_successfully_peapv1_extension,
+							received_eap_identifier,
+							false);
+
+						eap_automatic_variable_c<eap_state_notification_c> automatic_notification(m_am_tools, notification);
+
+						if (notification == 0)
+						{
+							m_tunneled_eap_type_authentication_state
+								= eap_state_authentication_terminated_unsuccessfully;
+							EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+							return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+						}
+
+						get_application_partner()->state_notification(notification);
+
+						status = eap_status_success;
+					}
+					else
+					{
+						// Authentication failed.
+						eap_state_notification_c * const notification = new eap_state_notification_c(
+							m_am_tools,
+							&send_network_id,
+							m_is_client,
+							eap_state_notification_eap,
+							eap_protocol_layer_eap,
+							m_peap_tunneled_eap_type,
+							eap_state_none,
+							eap_state_authentication_terminated_unsuccessfully_peapv1_extension,
+							received_eap_identifier,
+							false);
+
+						eap_automatic_variable_c<eap_state_notification_c> automatic_notification(m_am_tools, notification);
+
+						if (notification == 0)
+						{
+							m_tunneled_eap_type_authentication_state
+								= eap_state_authentication_terminated_unsuccessfully;
+							EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+							return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+						}
+
+						notification->set_authentication_error(eap_status_authentication_failure);
+
+						get_application_partner()->state_notification(notification);
+
+						status = eap_status_authentication_failure;
+					}
+
+					EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+					return EAP_STATUS_RETURN(m_am_tools, status);
+				}
+			}
+			else
+			{
+				// Not correct PEAP_TLV-payloads are included.
+				EAP_TRACE_ERROR(
+					m_am_tools, 
+					TRACE_FLAGS_TLS_PEAP_ERROR, 
+					(EAPL("ERROR: tls_application_eap_core_c::packet_process_peap_v1(): ")
+					 EAPL("Not correct PEAP_TLV-payloads are included in eap_state_variable_e %d.\n"),
+					 m_tunneled_eap_type_authentication_state));
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_header_corrupted);
+			}
+		}
+		else
+#endif //#if defined(USE_EAP_PEAPV1_EXTENSIONS)
+		{
+			// Forward packet to upper layer.
+			status = packet_forward_to_tunnel(
+				&m_receive_network_id,
+				&forwarded_eap_packet,
+				*eap_packet_length);
+		}
+	}
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e tls_application_eap_core_c::finish_successfull_authentication_peap_v2(
+	const u8_t received_eap_identifier)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("TLS: %s: message_function: tls_application_eap_core_c::finish_successfull_authentication_peap_v2()\n"),
+		(m_is_client == true ? "client": "server")));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: tls_application_eap_core_c::finish_successfull_authentication_peap_v2()");
+
+	eap_status_e status = eap_status_process_general_error;
+
+	if (m_tunneled_eap_type_authentication_state == eap_state_authentication_finished_successfully)
+	{
+		// PEAPv2 Authentication OK.
+
+		// Here we swap the addresses.
+		eap_am_network_id_c send_network_id(m_am_tools,
+			m_receive_network_id.get_destination_id(),
+			m_receive_network_id.get_source_id(),
+			m_receive_network_id.get_type());
+
+		status = create_compound_session_key_peap_v2();
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = get_application_partner()->packet_data_crypto_keys(
+			&send_network_id,
+			&m_peap_v2_CSK
+			);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		eap_state_notification_c notification(
+			m_am_tools,
+			&send_network_id,
+			m_is_client,
+			eap_state_notification_eap,
+			eap_protocol_layer_eap,
+			m_peap_tunneled_eap_type,
+			eap_state_none,
+			eap_state_authentication_finished_successfully,
+			received_eap_identifier,
+			false);
+		get_application_partner()->state_notification(&notification);
+
+		status = eap_status_success;
+	}
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e tls_application_eap_core_c::store_nonce_peap_v2(
+	const bool is_client_when_true,
+	peap_tlv_payloads_c * const peapv2_tlv_payloads)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("TLS: %s: message_function: tls_application_eap_core_c::store_nonce_peap_v2()\n"),
+		(m_is_client == true ? "client": "server")));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: tls_application_eap_core_c::store_nonce_peap_v2()");
+
+	eap_status_e status = eap_status_process_general_error;
+
+	u8_t * const nonce = reinterpret_cast<u8_t *>(
+		peapv2_tlv_payloads->get_crypto_binding_tlv()->get_data_offset(
+			TLV_NONCE_OFFSET,
+			TLV_NONCE_LENGTH));
+	if (nonce == 0)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	if (is_client_when_true == true)
+	{
+		status = m_peap_v2_server_nonce.set_copy_of_buffer(
+			nonce,
+			TLV_NONCE_LENGTH);
+	}
+	else
+	{
+		status = m_peap_v2_client_nonce.set_copy_of_buffer(
+			nonce,
+			TLV_NONCE_LENGTH);
+	}
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e tls_application_eap_core_c::packet_process_peap_v2(
+	eap_variable_data_c * const received_eap_message,
+	const u8_t received_eap_identifier,
+	u32_t * const eap_packet_length)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("TLS: %s: message_function: tls_application_eap_core_c::packet_process_peap_v2()\n"),
+		(m_is_client == true ? "client": "server")));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: tls_application_eap_core_c::packet_process_peap_v2()");
+
+	eap_status_e status = eap_status_process_general_error;
+	eap_code_value_e forwarded_eap_code = eap_code_none;
+
+
+	eap_header_wr_c received_eap_header(
+		m_am_tools,
+		received_eap_message->get_data(received_eap_message->get_data_length()),
+		received_eap_message->get_data_length());
+	if (received_eap_header.get_is_valid() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	status = received_eap_header.check_header();
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_ERROR(
+			m_am_tools, 
+			TRACE_FLAGS_TLS_PEAP_ERROR, 
+			(EAPL("ERROR: EAP-header corrupted.\n")));
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	u32_t parsed_eap_data_length = received_eap_header.get_type_data_length();
+
+	if (parsed_eap_data_length < sizeof(u8_t))
+	{
+		// Not enough payload in this packet.
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_header_corrupted);
+	}
+
+	if (received_eap_header.get_header_length()+parsed_eap_data_length
+		> received_eap_message->get_data_length())
+	{
+		// Corrupted length in EAP-header.
+		EAP_TRACE_ERROR(
+			m_am_tools, 
+			TRACE_FLAGS_TLS_PEAP_ERROR, 
+			(EAPL("ERROR: EAP-header corrupted.\n")));
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_header_corrupted);
+	}
+
+	if (received_eap_header.get_type() != eap_type_tlv_extensions)
+	{
+		// Not enough payload in this packet.
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_header_corrupted);
+	}
+
+
+	tls_peap_tlv_header_c tlv(
+		m_am_tools,
+		received_eap_header.get_type_data(received_eap_header.get_type_data_length()),
+		parsed_eap_data_length);
+	if (tlv.get_is_valid() == false)
+	{
+		EAP_TRACE_ERROR(
+			m_am_tools, 
+			TRACE_FLAGS_TLS_PEAP_ERROR, 
+			(EAPL("ERROR: No peap_tlv_payloads_c.\n")));
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	status = tlv.check_header();
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_ERROR(
+			m_am_tools, 
+			TRACE_FLAGS_TLS_PEAP_ERROR, 
+			(EAPL("ERROR: TLV header corrupted.\n")));
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	m_peapv2_tlv_payloads.reset();
+
+	status = parse_peap_tlv_payload(
+		received_eap_header.get_type_data(received_eap_header.get_type_data_length()),
+		&parsed_eap_data_length,
+		&m_peapv2_tlv_payloads);
+
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+	else if (parsed_eap_data_length != 0u)
+	{
+		EAP_TRACE_ERROR(
+			m_am_tools,
+			TRACE_FLAGS_TLS_PEAP_ERROR,
+			(EAPL("ERROR: tls_application_eap_core_c::packet_process_peap_v2(): ")
+			 EAPL("PEAP-header is corrupted. Buffer length and payload ")
+			 EAPL("length does not match. Illegal byte count %lu\n"),
+			 parsed_eap_data_length));
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_header_corrupted);
+	}
+
+
+	// EAP-Identifier is used later in verify_tunneled_acknowledge_peap_v2().
+	m_received_eap_identifier = received_eap_identifier;
+
+	// Checks the payloads existence.
+	if (m_peapv2_tlv_payloads.check_payloads(
+			peap_tlv_payloads_c::peap_tlv_payload_status_must_not_be, // result_tlv
+			peap_tlv_payloads_c::peap_tlv_payload_status_must_not_be, // nak_tlv
+			peap_tlv_payloads_c::peap_tlv_payload_status_must_not_be, // crypto_binding_tlv
+			peap_tlv_payloads_c::peap_tlv_payload_status_must_be, // eap_payload_tlv
+			peap_tlv_payloads_c::peap_tlv_payload_status_must_not_be  // intermediate_result_tlv
+			) == true
+		)
+	{
+		// This packet includes EAP-Payload TLV.
+
+		*eap_packet_length = m_peapv2_tlv_payloads.get_eap_payload_tlv()->get_data_length();
+		
+		u8_t * const eap_payload
+			= reinterpret_cast<u8_t *>(
+				m_peapv2_tlv_payloads.get_eap_payload_tlv()->get_data(
+					*eap_packet_length));
+		if (eap_payload == 0)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+		}
+
+		eap_header_wr_c forwarded_eap_packet(m_am_tools, 0, 0ul);
+
+		// We must forward this EAP-packet to tunneled EAP-type.
+		forwarded_eap_packet.set_header_buffer(
+			eap_payload,
+			*eap_packet_length);
+		if (forwarded_eap_packet.get_is_valid() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+		}
+
+		status = packet_forward_to_tunnel(
+			&m_receive_network_id,
+			&forwarded_eap_packet,
+			*eap_packet_length);
+
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+	else if (m_peapv2_tlv_payloads.check_payloads(
+			peap_tlv_payloads_c::peap_tlv_payload_status_must_be, // result_tlv
+			peap_tlv_payloads_c::peap_tlv_payload_status_must_not_be, // nak_tlv
+			peap_tlv_payloads_c::peap_tlv_payload_status_must_be, // crypto_binding_tlv
+			peap_tlv_payloads_c::peap_tlv_payload_status_must_not_be, // eap_payload_tlv
+			peap_tlv_payloads_c::peap_tlv_payload_status_must_be  // intermediate_result_tlv
+			) == true
+		)
+	{
+		// This is Protected termination.
+
+		{
+			u8_t * const tlv_status_network_order = reinterpret_cast<u8_t *>(
+				m_peapv2_tlv_payloads.get_result_tlv()->get_data(sizeof(u16_t)));
+			if (tlv_status_network_order == 0)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+			}
+
+			u16_t tlv_status_host_order
+				= eap_read_u16_t_network_order(tlv_status_network_order, sizeof(u16_t));
+
+			if (tlv_status_host_order == tls_peap_tlv_status_success)
+			{
+				forwarded_eap_code = eap_code_success;
+			}
+			else
+			{
+				// All other EAP-codes are assumed EAP-Failure.
+				forwarded_eap_code = eap_code_failure;
+			}
+
+			eap_header_string_c eap_string;
+			EAP_UNREFERENCED_PARAMETER(eap_string);
+
+			EAP_TRACE_DEBUG(
+				m_am_tools, 
+				TRACE_FLAGS_DEFAULT, 
+				(EAPL("packet_process_peap_v2(): PEAPv2 tunneled %s received.\n"),
+				 eap_string.get_eap_code_string(forwarded_eap_code)));
+		}
+
+
+		if (m_is_client == true)
+		{
+			// Client must forward created EAP-Success or EAP-Failure to tunneled EAP-type.
+			// Result TLV, Crypto Binding TLV and Intermediate Result TLV
+			// are checked within the state_notification() function
+			// by the verify_tunneled_acknowledge_peap_v2() function
+			// when eap_state_authentication_finished_successfully or
+			// eap_state_authentication_terminated_unsuccessfully indication
+			// is received.
+
+			// Client stores the received server nonce. This is used later in key generation.
+			status = store_nonce_peap_v2(m_is_client, &m_peapv2_tlv_payloads);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			status = create_eap_success_failure_in_forward_to_tunnel(
+				&m_receive_network_id,
+				forwarded_eap_code,
+				received_eap_identifier);
+
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+		else
+		{
+			// Server checks the received TLVs immediately.
+
+			status = store_nonce_peap_v2(m_is_client, &m_peapv2_tlv_payloads);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			eap_variable_data_c saved_mac(m_am_tools);
+
+			{
+				u8_t * const mac = reinterpret_cast<u8_t *>(
+					m_peapv2_tlv_payloads.get_crypto_binding_tlv()->get_data_offset(
+						TLV_MAC_OFFSET,
+						TLV_MAC_LENGTH));
+				if (mac == 0)
+				{
+					EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+					return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+				}
+
+				status = saved_mac.set_copy_of_buffer(
+					mac,
+					TLV_MAC_LENGTH);
+				if (status != eap_status_ok)
+				{
+					EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+					return EAP_STATUS_RETURN(m_am_tools, status);
+				}
+
+				m_am_tools->memset(mac, 0, TLV_MAC_LENGTH);
+			}
+
+			// Intermediate Combined Key is stored to m_peap_v2_IPMKn.
+			// Compound MAC Key is stored to m_peap_v2_CMK_Bn.
+			status = create_compound_mac_key_peap_v2(true);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			// Client uses server CMK B1 key
+			// to check the message server sent.
+			eap_variable_data_c * mac_key = &m_peap_v2_CMK_B1_server;
+			if (m_is_client == false)
+			{
+				// Server uses Client CMK B2 key
+				// to check the message client sent.
+				mac_key = &m_peap_v2_CMK_B2_client;
+			}
+
+			eap_variable_data_c mac_data(m_am_tools);
+
+			status = create_crypto_binding_compound_mac(
+				mac_key,
+				m_peapv2_tlv_payloads.get_crypto_binding_tlv()->get_original_header(),
+				&mac_data);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			EAP_TRACE_DATA_DEBUG(
+				m_am_tools,
+				TRACE_FLAGS_DEFAULT,
+				(EAPL("verify TLV MAC"),
+				 saved_mac.get_data(saved_mac.get_data_length()),
+				 saved_mac.get_data_length()));
+
+			// Here we check only saved_mac.get_data_length() bytes.
+			// HMAC-SHA1 generates more than 128 bits used MAC.
+			if (saved_mac.get_data_length() > mac_data.get_data_length()
+ 				|| m_am_tools->memcmp(
+					saved_mac.get_data(saved_mac.get_data_length()),
+					mac_data.get_data(mac_data.get_data_length()),
+					saved_mac.get_data_length()) != 0)
+			{
+				EAP_TRACE_ERROR(
+					m_am_tools,
+					TRACE_FLAGS_DEFAULT,
+					(EAPL("PEAPv2: ERROR: %s: receive_function: ")
+					 EAPL("packet_process_peap_v2(): MAC failed\n"),
+					 (m_is_client == true ? "client": "server")));
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_authentication_failure);
+			}
+			else
+			{
+				EAP_TRACE_DEBUG(
+					m_am_tools,
+					TRACE_FLAGS_DEFAULT,
+					(EAPL("PEAPv2: %s: MAC OK\n"),
+					 (m_is_client == true ? "client": "server")));
+			}
+
+
+			/**
+			 * @{ Check Intermediate Result TLV. }
+			 */
+
+			status = eap_status_authentication_failure;
+
+			// This is server.
+			// Client sent an acknowledge.
+			if (forwarded_eap_code == eap_code_success
+				&& m_tunneled_eap_type_authentication_state
+				== eap_state_authentication_finished_successfully)
+			{
+				// PEAPv2 Authentication OK.
+
+				status = finish_successfull_authentication_peap_v2(
+					received_eap_identifier);
+				if (status != eap_status_ok)
+				{
+					EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+					return EAP_STATUS_RETURN(m_am_tools, status);
+				}
+			}
+			else
+			{
+				// Authentication failed.
+				status = eap_status_authentication_failure;
+			}
+
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+	else
+	{
+		// Not correct PEAP_TLV-payloads are included.
+		EAP_TRACE_ERROR(
+			m_am_tools, 
+			TRACE_FLAGS_TLS_PEAP_ERROR, 
+			(EAPL("ERROR: tls_application_eap_core_c::packet_process_peap_v2(): ")
+			 EAPL("Not correct PEAP_TLV-payloads are included in eap_state_variable_e %d.\n"),
+			 m_tunneled_eap_type_authentication_state));
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_header_corrupted);
+	}
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e tls_application_eap_core_c::verify_tunneled_acknowledge_peap_v2()
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("PEAP: %s: crypto_function: tls_application_eap_core_c::verify_tunneled_acknowledge_peap_v2(): ")
+		 EAPL("this = 0x%08x\n"),
+		 (m_is_client == true ? "client": "server"),
+		 this));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: tls_application_eap_core_c::verify_tunneled_acknowledge_peap_v2()");
+
+	eap_status_e status = eap_status_process_general_error;
+	eap_code_value_e forwarded_eap_code = eap_code_none;
+
+
+	// Checks the payloads existence.
+	if (m_peapv2_tlv_payloads.check_payloads(
+			peap_tlv_payloads_c::peap_tlv_payload_status_must_be, // result_tlv
+			peap_tlv_payloads_c::peap_tlv_payload_status_must_not_be, // nak_tlv
+			peap_tlv_payloads_c::peap_tlv_payload_status_must_be, // crypto_binding_tlv
+			peap_tlv_payloads_c::peap_tlv_payload_status_must_not_be, // eap_payload_tlv
+			peap_tlv_payloads_c::peap_tlv_payload_status_must_be  // intermediate_result_tlv
+			) == true
+		)
+	{
+		// This is Protected termination.
+
+		{
+			u8_t * const tlv_status_network_order = reinterpret_cast<u8_t *>(
+				m_peapv2_tlv_payloads.get_result_tlv()->get_data(sizeof(u16_t)));
+			if (tlv_status_network_order == 0)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+			}
+
+			u16_t tlv_status_host_order
+				= eap_read_u16_t_network_order(tlv_status_network_order, sizeof(u16_t));
+
+			if (tlv_status_host_order == tls_peap_tlv_status_success)
+			{
+				forwarded_eap_code = eap_code_success;
+			}
+			else
+			{
+				// All other EAP-codes are assumed EAP-Failure.
+				forwarded_eap_code = eap_code_failure;
+			}
+		}
+
+
+		// Client and server checks the received TLVs.
+
+		status = store_nonce_peap_v2(m_is_client, &m_peapv2_tlv_payloads);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		eap_variable_data_c saved_mac(m_am_tools);
+
+		{
+			u8_t * const mac = reinterpret_cast<u8_t *>(
+				m_peapv2_tlv_payloads.get_crypto_binding_tlv()->get_data_offset(
+					TLV_MAC_OFFSET,
+					TLV_MAC_LENGTH));
+			if (mac == 0)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+			}
+
+			status = saved_mac.set_copy_of_buffer(
+				mac,
+				TLV_MAC_LENGTH);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			m_am_tools->memset(mac, 0, TLV_MAC_LENGTH);
+		}
+
+		// Client uses server CMK B1 key
+		// to check the message server sent.
+		eap_variable_data_c * mac_key = &m_peap_v2_CMK_B1_server;
+		if (m_is_client == false)
+		{
+			// Server uses Client CMK B2 key
+			// to check the message client sent.
+			mac_key = &m_peap_v2_CMK_B2_client;
+		}
+
+		eap_variable_data_c mac_data(m_am_tools);
+
+		status = create_crypto_binding_compound_mac(
+			mac_key,
+			m_peapv2_tlv_payloads.get_crypto_binding_tlv()->get_original_header(),
+			&mac_data);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		EAP_TRACE_DATA_DEBUG(
+			m_am_tools,
+			TRACE_FLAGS_DEFAULT,
+			(EAPL("verify TLV MAC"),
+			 saved_mac.get_data(saved_mac.get_data_length()),
+			 saved_mac.get_data_length()));
+
+		// Here we check only saved_mac.get_data_length() bytes.
+		// HMAC-SHA1 generates more than 128 bits used MAC.
+		if (saved_mac.get_data_length() > mac_data.get_data_length()
+			|| m_am_tools->memcmp(
+				saved_mac.get_data(saved_mac.get_data_length()),
+				mac_data.get_data(mac_data.get_data_length()),
+				saved_mac.get_data_length()) != 0)
+		{
+			EAP_TRACE_ERROR(
+				m_am_tools,
+				TRACE_FLAGS_DEFAULT,
+				(EAPL("PEAPv2: ERROR: %s: receive_function: ")
+				 EAPL("packet_process_peap_v2(): MAC failed\n"),
+				 (m_is_client == true ? "client": "server")));
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_authentication_failure);
+		}
+		else
+		{
+			EAP_TRACE_DEBUG(
+				m_am_tools,
+				TRACE_FLAGS_DEFAULT,
+				(EAPL("PEAPv2: %s: MAC OK\n"),
+				 (m_is_client == true ? "client": "server")));
+		}
+
+
+		/**
+		 * @{ Check Intermediate Result TLV. }
+		 */
+
+		status = eap_status_authentication_failure;
+
+		// This is server.
+		// Client sent a acknowledge.
+		if (forwarded_eap_code == eap_code_success
+			&& m_tunneled_eap_type_authentication_state
+			== eap_state_authentication_finished_successfully)
+		{
+			// PEAPv2 Authentication OK.
+
+			if (m_is_client == false)
+			{
+				status = finish_successfull_authentication_peap_v2(
+					m_received_eap_identifier);
+				if (status != eap_status_ok)
+				{
+					EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+					return EAP_STATUS_RETURN(m_am_tools, status);
+				}
+			}
+			else
+			{
+				// Server does nothing here.
+				status = eap_status_success;
+			}
+
+		}
+		else
+		{
+			// Authentication failed.
+			status = eap_status_authentication_failure;
+		}
+
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+	else
+	{
+		// Not correct PEAP_TLV-payloads are included.
+		EAP_TRACE_ERROR(
+			m_am_tools, 
+			TRACE_FLAGS_TLS_PEAP_ERROR, 
+			(EAPL("ERROR: tls_application_eap_core_c::verify_tunneled_acknowledge_peap_v2(): ")
+			 EAPL("Not correct PEAP_TLV-payloads are included in eap_state_variable_e %d.\n"),
+			 m_tunneled_eap_type_authentication_state));
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_header_corrupted);
+	}
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e tls_application_eap_core_c::packet_forward_to_tunnel(
+	const eap_am_network_id_c * const /*receive_network_id*/,
+	eap_header_wr_c * const forwarded_eap_packet,
+	const u32_t eap_packet_length)
+{
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("PEAP: %s: crypto_function: tls_application_eap_core_c::packet_forward_to_tunnel(): ")
+		 EAPL("this = 0x%08x\n"),
+		 (m_is_client == true ? "client": "server"),
+		 this));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: tls_application_eap_core_c::packet_forward_to_tunnel()");
+
+	if (m_is_client == true)
+	{
+		trace_tunneled_packet(EAPL("-> TUNNELED packet client"), forwarded_eap_packet);
+	}
+	else
+	{
+		trace_tunneled_packet(EAPL("-> TUNNELED packet server"), forwarded_eap_packet);
+	}
+
+	eap_status_e status(eap_status_drop_packet_quietly);
+
+	if (m_eap_core != 0)
+	{
+		status = m_eap_core->packet_process(
+			&m_receive_network_id,
+			forwarded_eap_packet,
+			eap_packet_length);
+	}
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e tls_application_eap_core_c::packet_process(
+	eap_variable_data_c * const received_eap_message,
+	const u8_t received_eap_identifier)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	u32_t eap_packet_length = 0ul;
+
+	eap_status_e status = eap_status_process_general_error;
+
+	EAP_TRACE_DATA_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("tls_application_eap_core_c::packet_process(): received tunneled data:"),
+		 received_eap_message->get_data(received_eap_message->get_data_length()),
+		 received_eap_message->get_data_length()));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: tls_application_eap_core_c::packet_process()");
+
+	// At least EAP-type or EAP-code field is needed.
+	if (received_eap_message->get_data_length() < sizeof(u8_t))
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_too_short_message);
+	}
+
+	if (received_eap_message->get_data_length() > received_eap_message->get_buffer_length())
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_buffer_too_short);
+	}
+
+	if (m_eap_type == eap_type_peap)
+	{
+		if (m_peap_version == peap_version_0_xp)
+		{
+			status = packet_process_xp_peap_v0(
+				received_eap_message,
+				received_eap_identifier,
+				&eap_packet_length);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			if ((m_tls_session_type == tls_session_type_original_session_resumption
+					|| m_tls_session_type == tls_session_type_stateless_session_resumption)
+				&& m_tunneled_eap_type_authentication_state
+				== eap_state_authentication_finished_successfully)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_ok);
+			}
+		}
+		else if (m_peap_version == peap_version_1)
+		{
+			status = packet_process_peap_v1(
+				received_eap_message,
+				received_eap_identifier,
+				&eap_packet_length);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+		}
+		else if (m_peap_version == peap_version_2)
+		{
+			status = packet_process_peap_v2(
+				received_eap_message,
+				received_eap_identifier,
+				&eap_packet_length);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+		}
+		else
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_no_matching_protocol_version);
+		}
+	}
+	else if (m_eap_type == eap_type_ttls)
+	{
+		status = packet_process_ttls(
+			received_eap_message,
+			received_eap_identifier,
+			&eap_packet_length);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+	else
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_no_matching_protocol_version);
+	}
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e tls_application_eap_core_c::plain_eap_success_failure_packet_received(
+	const eap_am_network_id_c * const receive_network_id,
+	const eap_code_value_e received_eap_code,
+	const u8_t received_eap_identifier)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	eap_tls_trace_string_c state_trace;
+	EAP_TRACE_DEBUG(m_am_tools, TRACE_FLAGS_DEFAULT, (EAPL("\n")));
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("TLS: %s: message_function: plain_eap_success_failure_packet_received(): m_wait_plain_eap_success=%d, EAP-Code=%d, m_tls_session_type=%s, m_eap_core=0x%08x\n"),
+		(m_is_client == true ? "client": "server"),
+		m_wait_plain_eap_success,
+		received_eap_code,
+		eap_tls_trace_string_c::get_tls_session_type_string(m_tls_session_type),
+		m_eap_core));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: tls_application_eap_core_c::plain_eap_success_failure_packet_received()");
+
+	eap_status_e status(eap_status_ok);
+
+	if (m_eap_core != 0
+		&& m_tls_session_type != tls_session_type_original_session_resumption
+		&& m_tls_session_type != tls_session_type_stateless_session_resumption)
+	{
+		status = create_eap_success_failure_in_forward_to_tunnel(
+			receive_network_id,
+			received_eap_code,
+			received_eap_identifier);
+		if (status != eap_status_ok
+			&& status != eap_status_drop_packet_quietly)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+
+	// This object might wait EAP-Success, too.
+	if (m_wait_plain_eap_success == true
+		&& received_eap_code == eap_code_success)
+	{
+		// Here we swap the addresses.
+		eap_am_network_id_c send_network_id(
+			m_am_tools,
+			m_receive_network_id.get_destination_id(),
+			m_receive_network_id.get_source_id(),
+			m_receive_network_id.get_type());
+
+		eap_state_notification_c notification(
+			m_am_tools,
+			&send_network_id,
+			m_is_client,
+			eap_state_notification_eap,
+			eap_protocol_layer_eap,
+			m_eap_type,
+			eap_state_none,
+			eap_state_authentication_finished_successfully,
+			m_received_eap_identifier,
+			true);
+		m_application_partner->state_notification(&notification);
+
+		status = eap_status_ok;
+	}
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e tls_application_eap_core_c::empty_ack_packet_received(
+	const eap_am_network_id_c * const /* receive_network_id */,
+	const u8_t /* received_eap_identifier */)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("TLS: %s: message_function: empty_ack_packet_received()\n"),
+		(m_is_client == true ? "client": "server")));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: tls_application_eap_core_c::empty_ack_packet_received()");
+
+	eap_status_e status(eap_status_not_found);
+
+#if defined(EAP_USE_TTLS_PLAIN_MS_CHAP_V2_HACK)
+
+	if (m_is_client == false
+		&& m_tunneled_eap_in_ttls == false
+		&& m_eap_type == eap_type_ttls)
+	{
+		u32_t eap_length
+			= EAP_MSCHAPV2_OPCODE_SIZE // OpCode is the only payload
+			+ eap_header_base_c::get_type_data_start_offset(m_use_eap_expanded_type);
+
+		eap_buf_chain_wr_c eap_packet_buffer(
+			eap_write_buffer,
+			m_am_tools,
+			eap_length);
+		if (eap_packet_buffer.get_is_valid() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+		}
+
+		eap_header_wr_c forwarded_eap_packet(
+			m_am_tools,
+			eap_packet_buffer.get_data(eap_length),
+			eap_length);
+
+		if (forwarded_eap_packet.get_is_valid() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+		}
+
+		forwarded_eap_packet.reset_header(
+			static_cast<u16_t>(eap_length),
+			m_use_eap_expanded_type);
+		forwarded_eap_packet.set_identifier(m_ttls_plain_ms_chap_v2_eap_identifier);
+		forwarded_eap_packet.set_code(eap_code_response);
+		forwarded_eap_packet.set_length(
+			static_cast<u16_t>(eap_length),
+			m_use_eap_expanded_type);
+		forwarded_eap_packet.set_type(
+			eap_type_mschapv2,
+			m_use_eap_expanded_type);
+
+		mschapv2_header_c mschapv2_header(
+			m_am_tools,
+			forwarded_eap_packet.get_type_data_offset(
+				0,
+				forwarded_eap_packet.get_type_data_length()),
+			forwarded_eap_packet.get_type_data_length());
+
+		mschapv2_header.set_opcode(mschapv2_opcode_success);
+
+		status = packet_forward_to_tunnel(
+			&m_receive_network_id,
+			&forwarded_eap_packet,
+			eap_length);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+
+#endif //#if defined(EAP_USE_TTLS_PLAIN_MS_CHAP_V2_HACK)
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT bool tls_application_eap_core_c::get_is_valid()
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return m_is_valid;
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e tls_application_eap_core_c::reset()
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(m_am_tools, TRACE_FLAGS_DEFAULT, (EAPL("\n")));
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("TLS: %s: function: tls_application_eap_core_c::reset(): this = 0x%08x\n"),
+		 (m_is_client == true ? "client": "server"),
+		 this));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: tls_application_eap_core_c::reset()");
+
+	m_tunneled_eap_type_authentication_state = eap_state_none;
+	m_tls_session_type = tls_session_type_none;
+	m_use_tppd_tls_peap = false;
+	m_use_tppd_peapv1_acknowledge_hack = false;
+
+	eap_status_e status(eap_status_ok);
+
+	if (m_eap_core != 0)
+	{
+		status = m_eap_core->reset();
+	}
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e tls_application_eap_core_c::create_result_tlv_message(
+	eap_buf_chain_wr_c * const packet,
+	const eap_code_value_e result_eap_code,
+	const u8_t eap_identifier,
+	const tls_peap_tlv_type_e tlv_type)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("TLS: %s: message_function: create_result_tlv_message()\n"),
+		(m_is_client == true ? "client": "server")));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: tls_application_eap_core_c::create_result_tlv_message()");
+
+	if (packet->get_is_valid() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	u32_t final_packet_length
+		= eap_header_wr_c::get_header_length()
+		+ sizeof(u8_t) // EAP-type field
+		+tls_peap_tlv_header_c::get_header_length()
+		+sizeof(u16_t);
+
+	eap_status_e status = packet->set_buffer_length(final_packet_length);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	if (packet->get_is_valid_data() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	status = packet->set_data_length(final_packet_length);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	eap_header_wr_c final_eap_packet(
+		m_am_tools,
+		packet->get_data(packet->get_data_length()),
+		packet->get_data_length());
+	if (final_eap_packet.get_is_valid() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	final_eap_packet.reset_header(
+		static_cast<u16_t>(final_packet_length),
+		m_use_eap_expanded_type);
+	final_eap_packet.set_identifier(eap_identifier);
+	if (m_is_client == true)
+	{
+		final_eap_packet.set_code(eap_code_response);
+	}
+	else
+	{
+		final_eap_packet.set_code(eap_code_request);
+	}
+	final_eap_packet.set_length(
+		static_cast<u16_t>(final_packet_length),
+		m_use_eap_expanded_type);
+	final_eap_packet.set_type(
+		eap_type_tlv_extensions,
+		m_use_eap_expanded_type);
+
+	tls_peap_tlv_header_c tlv(
+		m_am_tools,
+		final_eap_packet.get_type_data(final_eap_packet.get_type_data_length()),
+		final_eap_packet.get_type_data_length());
+	if (tlv.get_is_valid() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	/*  Result TLV:
+	 *  0                   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 
+	 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+	 * |M|R|  TLV Type (AVP Type)      |            Length             |
+	 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+	 * |            Status             |
+	 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+	 */
+	tlv.reset_header(static_cast<u16_t>(sizeof(u16_t)));
+	tlv.set_flag_tlv_type(tlv_type);
+	tlv.set_flag_mandatory_tlv(true);
+
+	u16_t * const tlv_status = reinterpret_cast<u16_t *>(tlv.get_data(sizeof(u16_t)));
+	if (tlv_status == 0)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	u16_t tlv_status_network_order = 0ul;
+
+	if (result_eap_code == eap_code_success)
+	{
+		tlv_status_network_order = eap_htons(static_cast<u16_t>(tls_peap_tlv_status_success));
+	}
+	else
+	{
+		tlv_status_network_order = eap_htons(static_cast<u16_t>(tls_peap_tlv_status_failure));
+	}
+
+	m_am_tools->memmove(tlv_status, &tlv_status_network_order, sizeof(tlv_status_network_order));
+
+	EAP_TLS_PEAP_TRACE_PAYLOAD("Add TLV payload", &tlv, m_is_client);
+
+
+	if (m_is_client == true)
+	{
+		trace_tunneled_packet(EAPL("<- TUNNELED packet client"), &final_eap_packet);
+	}
+	else
+	{
+		trace_tunneled_packet(EAPL("<- TUNNELED packet client"), &final_eap_packet);
+	}
+
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, eap_status_ok);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e tls_application_eap_core_c::create_intermediate_result_tlv_message(
+	eap_buf_chain_wr_c * const packet,
+	const eap_code_value_e result_eap_code,
+	const u8_t eap_identifier)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("TLS: %s: message_function: create_intermediate_result_tlv_message()\n"),
+		(m_is_client == true ? "client": "server")));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: tls_application_eap_core_c::create_intermediate_result_tlv_message()");
+
+	/*  Intermediate Result TLV:
+	 *  0                   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 
+	 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+	 * |M|R|  TLV Type (AVP Type)      |            Length             |
+	 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+	 * |            Status             |        TLVs...
+	 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+	 *
+	 * Now we do not support any included TLVs.
+	 * This means Intermediate Result TLV is the same as Result TLV.
+	 */
+
+	eap_status_e status = create_result_tlv_message(
+		packet,
+		result_eap_code,
+		eap_identifier,
+		tls_peap_tlv_type_intermediate_result);
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e tls_application_eap_core_c::create_eap_payload_tlv_message(
+	eap_buf_chain_wr_c * const packet,
+	const eap_header_wr_c * const sent_eap_packet,
+	const u8_t eap_identifier)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("TLS: %s: message_function: tls_application_eap_core_c::create_eap_payload_tlv_message()\n"),
+		(m_is_client == true ? "client": "server")));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: tls_application_eap_core_c::create_eap_payload_tlv_message()");
+
+	if (packet->get_is_valid() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	const u32_t tlv_payload_length
+		= sent_eap_packet->get_length();
+
+	const u32_t final_packet_length
+		= eap_header_wr_c::get_header_length()
+		+ sizeof(u8_t) // EAP-type field
+		+ tls_peap_tlv_header_c::get_header_length()
+		+ tlv_payload_length;
+
+	eap_status_e status = packet->set_buffer_length(final_packet_length);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	if (packet->get_is_valid_data() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	status = packet->set_data_length(final_packet_length);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	eap_header_wr_c final_eap_packet(
+		m_am_tools,
+		packet->get_data(packet->get_data_length()),
+		packet->get_data_length());
+	if (final_eap_packet.get_is_valid() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	final_eap_packet.reset_header(
+		static_cast<u16_t>(final_packet_length),
+		m_use_eap_expanded_type);
+	final_eap_packet.set_identifier(eap_identifier);
+	if (m_is_client == true)
+	{
+		final_eap_packet.set_code(eap_code_response);
+	}
+	else
+	{
+		final_eap_packet.set_code(eap_code_request);
+	}
+	final_eap_packet.set_length(
+		static_cast<u16_t>(final_packet_length),
+		m_use_eap_expanded_type);
+	final_eap_packet.set_type(
+		eap_type_tlv_extensions,
+		m_use_eap_expanded_type);
+
+	tls_peap_tlv_header_c tlv(
+		m_am_tools,
+		final_eap_packet.get_type_data(final_eap_packet.get_type_data_length()),
+		final_eap_packet.get_type_data_length());
+	if (tlv.get_is_valid() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	// EAP-Payload TLV:
+	//  0                   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
+	// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+	// |M|R|         TLV Type          |            Length             |
+	// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+	// |                          EAP packet...
+	// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+	// |                             TLVs...
+	// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+	//
+	// We do not support any included TLVs.
+	//
+	tlv.reset_header(static_cast<u16_t>(tlv_payload_length));
+	tlv.set_flag_tlv_type(tls_peap_tlv_eap_payload);
+	tlv.set_flag_mandatory_tlv(true);
+
+
+	{
+		// Adds EAP-Packet as a payload to EAP-Payload TLV.
+		u16_t * const eap_packet_payload = reinterpret_cast<u16_t *>(
+			tlv.get_data_offset(0ul, sent_eap_packet->get_length()));
+		if (eap_packet_payload == 0)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+		}
+
+		m_am_tools->memmove(
+			eap_packet_payload,
+			sent_eap_packet->get_header_buffer(sent_eap_packet->get_length()),
+			sent_eap_packet->get_length());
+	}
+
+	EAP_TLS_PEAP_TRACE_PAYLOAD("Add TLV payload", &tlv, m_is_client);
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, eap_status_ok);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e tls_application_eap_core_c::create_crypto_binding_tlv_message(
+	eap_buf_chain_wr_c * const packet,
+	const eap_code_value_e result_eap_code,
+	const u8_t eap_identifier,
+	const eap_variable_data_c * const nonce,
+	const u8_t received_version)
+{
+	EAP_UNREFERENCED_PARAMETER(result_eap_code);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("TLS: %s: message_function: tls_application_eap_core_c::create_crypto_binding_tlv_message()\n"),
+		(m_is_client == true ? "client": "server")));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: tls_application_eap_core_c::create_crypto_binding_tlv_message()");
+
+	if (packet->get_is_valid() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	const u32_t result_tlv_payload_length
+		= sizeof(u16_t);
+
+	const u32_t result_tlv_length
+		= tls_peap_tlv_header_c::get_header_length()
+		+ result_tlv_payload_length;
+
+	const u32_t crypto_binding_tlv_payload_length
+		= sizeof(u8_t)
+		+ sizeof(u8_t)
+		+ sizeof(u16_t)
+		+ TLV_NONCE_LENGTH
+		+ TLV_MAC_LENGTH;
+
+	const u32_t crypto_binding_tlv_length
+		= tls_peap_tlv_header_c::get_header_length()
+		+ crypto_binding_tlv_payload_length;
+
+	const u32_t intermediate_result_tlv_payload_length
+		= sizeof(u16_t);
+
+	const u32_t intermediate_result_tlv_length
+		= tls_peap_tlv_header_c::get_header_length()
+		+ intermediate_result_tlv_payload_length;
+
+	const u32_t final_packet_length
+		= eap_header_wr_c::get_header_length()
+		+ sizeof(u8_t) // EAP-type field
+		+ result_tlv_length
+		+ crypto_binding_tlv_length
+		+ intermediate_result_tlv_length;
+
+	eap_status_e status = packet->set_buffer_length(final_packet_length);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	if (packet->get_is_valid_data() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	status = packet->set_data_length(final_packet_length);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	eap_header_wr_c final_eap_packet(
+		m_am_tools,
+		packet->get_data(packet->get_data_length()),
+		packet->get_data_length());
+	if (final_eap_packet.get_is_valid() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	final_eap_packet.reset_header(
+		static_cast<u16_t>(final_packet_length),
+		m_use_eap_expanded_type);
+	final_eap_packet.set_identifier(eap_identifier);
+	if (m_is_client == true)
+	{
+		final_eap_packet.set_code(eap_code_response);
+	}
+	else
+	{
+		final_eap_packet.set_code(eap_code_request);
+	}
+	final_eap_packet.set_length(
+		static_cast<u16_t>(final_packet_length),
+		m_use_eap_expanded_type);
+	final_eap_packet.set_type(
+		eap_type_tlv_extensions,
+		m_use_eap_expanded_type);
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	u32_t tlv_offset = 0ul;
+
+	{
+		// Data includes Result TLV:
+		//  0                   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
+		// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+		// |M|R|         TLV Type          |            Length             |
+		// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+		// |             Status            |        TLVs...
+		// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+		//
+		// We do not support included TLVs.
+		// 
+
+		tls_peap_tlv_header_c result_tlv(
+			m_am_tools,
+			final_eap_packet.get_type_data_offset(tlv_offset, result_tlv_length),
+			final_eap_packet.get_type_data_length());
+		if (result_tlv.get_is_valid() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+		}
+
+		tlv_offset += result_tlv_length;
+
+		result_tlv.reset_header(static_cast<u16_t>(result_tlv_payload_length));
+		result_tlv.set_flag_tlv_type(tls_peap_tlv_type_result);
+		result_tlv.set_flag_mandatory_tlv(true);
+		
+
+		{
+			u16_t * const tlv_status = reinterpret_cast<u16_t *>(
+				result_tlv.get_data_offset(0ul, sizeof(u16_t)));
+			if (tlv_status == 0)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+			}
+
+			u16_t tlv_status_value = 0ul;
+			if (result_eap_code == eap_code_success)
+			{
+				tlv_status_value = static_cast<u16_t>(tls_peap_tlv_status_success);
+			}
+			else
+			{
+				tlv_status_value = static_cast<u16_t>(tls_peap_tlv_status_failure);
+			}
+
+			status = eap_write_u16_t_network_order(
+				tlv_status,
+				sizeof(*tlv_status),
+				tlv_status_value);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+		}
+
+		EAP_TLS_PEAP_TRACE_PAYLOAD("Add TLV payload", &result_tlv, m_is_client);
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	{
+		// Data includes Crypto Binding TLV:
+		//  0                   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
+		// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+		// |M|R|         TLV Type          |            Length             |
+		// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+		// |    Version    |Received Ver.  |         Sub-Type              |
+		// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+		// |                                                               |
+		// ~                             Nonce                             ~
+		// |                                                               |
+		// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+		// |                                                               |
+		// ~                          Compound MAC                         ~
+		// |                                                               |
+		// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+		// 
+
+		tls_peap_tlv_header_c crypto_binding_tlv(
+			m_am_tools,
+			final_eap_packet.get_type_data_offset(tlv_offset, crypto_binding_tlv_length),
+			final_eap_packet.get_type_data_length());
+		if (crypto_binding_tlv.get_is_valid() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+		}
+
+		tlv_offset += crypto_binding_tlv_length;
+
+		crypto_binding_tlv.reset_header(static_cast<u16_t>(crypto_binding_tlv_payload_length));
+		crypto_binding_tlv.set_flag_tlv_type(tls_peap_tlv_type_crypto_binding);
+		crypto_binding_tlv.set_flag_mandatory_tlv(true);
+
+		u32_t tlv_data_offset = 0ul;
+
+		{
+			u8_t * const tlv_version = reinterpret_cast<u8_t *>(
+				crypto_binding_tlv.get_data_offset(tlv_data_offset, sizeof(u8_t)));
+			if (tlv_version == 0)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+			}
+			
+			*tlv_version = static_cast<u8_t>(0ul);
+
+			tlv_data_offset += sizeof(u8_t);
+		}
+
+
+		{
+			u8_t * const tlv_received_version = reinterpret_cast<u8_t *>(
+				crypto_binding_tlv.get_data_offset(tlv_data_offset, sizeof(u8_t)));
+			if (tlv_received_version == 0)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+			}
+			
+			*tlv_received_version = static_cast<u8_t>(received_version);
+
+			tlv_data_offset += sizeof(u8_t);
+		}
+
+
+		{
+			u16_t * const tlv_sub_type = reinterpret_cast<u16_t *>(
+				crypto_binding_tlv.get_data_offset(tlv_data_offset, sizeof(u16_t)));
+			if (tlv_sub_type == 0)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+			}
+
+			u16_t tlv_sub_type_network_order = 0ul;
+			if (m_is_client == true)
+			{
+				tlv_sub_type_network_order = eap_htons(
+					static_cast<u16_t>(tls_peap_tlv_type_crypto_binding_request));
+			}
+			else
+			{
+				tlv_sub_type_network_order = eap_htons(
+					static_cast<u16_t>(tls_peap_tlv_type_crypto_binding_response));
+			}
+			
+			m_am_tools->memmove(
+				tlv_sub_type,
+				&tlv_sub_type_network_order,
+				sizeof(tlv_sub_type_network_order));
+
+			tlv_data_offset += sizeof(u16_t);
+		}
+
+
+		if (nonce->get_data_length() == TLV_NONCE_LENGTH)
+		{
+			u16_t * const tlv_nonce = reinterpret_cast<u16_t *>(
+				crypto_binding_tlv.get_data_offset(tlv_data_offset, TLV_NONCE_LENGTH));
+			if (tlv_nonce == 0)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+			}
+			
+			m_am_tools->memmove(tlv_nonce, nonce->get_data(TLV_NONCE_LENGTH), TLV_NONCE_LENGTH);
+
+			tlv_data_offset += TLV_NONCE_LENGTH;
+		}
+		else
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+		}
+
+
+		{
+			u8_t * const tlv_mac = reinterpret_cast<u8_t *>(
+				crypto_binding_tlv.get_data_offset(tlv_data_offset, TLV_MAC_LENGTH));
+			if (tlv_mac == 0)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+			}
+			
+			m_am_tools->memset(tlv_mac, 0, TLV_MAC_LENGTH);
+
+			eap_variable_data_c * mac_key = &m_peap_v2_CMK_B1_server;
+			if (m_is_client == true)
+			{
+				mac_key = &m_peap_v2_CMK_B2_client;
+			}
+
+			eap_variable_data_c mac_data(m_am_tools);
+
+			status = create_crypto_binding_compound_mac(
+				mac_key,
+				&crypto_binding_tlv,
+				&mac_data);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+			
+			m_am_tools->memmove(
+				tlv_mac,
+				mac_data.get_data(mac_data.get_data_length()),
+				TLV_MAC_LENGTH);
+
+			tlv_data_offset += TLV_MAC_LENGTH;
+		}
+
+		EAP_TLS_PEAP_TRACE_PAYLOAD("Add TLV payload", &crypto_binding_tlv, m_is_client);
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	{
+		// Data includes Intermediate Result TLV:
+		//  0                   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
+		// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+		// |M|R|         TLV Type          |            Length             |
+		// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+		// |             Status            |        TLVs...
+		// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+		// 
+		// We do not support included TLVs.
+		// 
+
+		tls_peap_tlv_header_c intermediate_result_tlv(
+			m_am_tools,
+			final_eap_packet.get_type_data_offset(tlv_offset, intermediate_result_tlv_length),
+			final_eap_packet.get_type_data_length());
+		if (intermediate_result_tlv.get_is_valid() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+		}
+
+		tlv_offset += intermediate_result_tlv_length;
+
+		intermediate_result_tlv.reset_header(
+			static_cast<u16_t>(intermediate_result_tlv_payload_length));
+		intermediate_result_tlv.set_flag_tlv_type(tls_peap_tlv_type_intermediate_result);
+		intermediate_result_tlv.set_flag_mandatory_tlv(true);
+		
+
+		{
+			u16_t * const tlv_intermediate_status
+				= reinterpret_cast<u16_t *>(
+					intermediate_result_tlv.get_data_offset(0ul, sizeof(u16_t)));
+			if (tlv_intermediate_status == 0)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+			}
+
+			u16_t tlv_intermediate_status_value = 0ul;
+			if (result_eap_code == eap_code_success)
+			{
+				tlv_intermediate_status_value = static_cast<u16_t>(tls_peap_tlv_status_success);
+			}
+			else
+			{
+				tlv_intermediate_status_value = static_cast<u16_t>(tls_peap_tlv_status_failure);
+			}
+
+			status = eap_write_u16_t_network_order(
+				tlv_intermediate_status,
+				sizeof(*tlv_intermediate_status),
+				tlv_intermediate_status_value);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+		}
+
+		EAP_TLS_PEAP_TRACE_PAYLOAD("Add TLV payload", &intermediate_result_tlv, m_is_client);
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, eap_status_ok);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e tls_application_eap_core_c::create_eap_diameter_avp_message(
+	eap_buf_chain_wr_c * const packet,
+	const eap_header_wr_c * const sent_eap_packet,
+	const u8_t /* eap_identifier */)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("TLS: %s: message_function: tls_application_eap_core_c::create_eap_diameter_avp_message()\n"),
+		(m_is_client == true ? "client": "server")));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: tls_application_eap_core_c::create_eap_diameter_avp_message()");
+
+	if (packet->get_is_valid() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	const u32_t tlv_payload_length
+		= sent_eap_packet->get_length();
+
+	const u32_t final_packet_length
+		= eap_diameter_avp_header_c::get_header_length(false)
+		+ tlv_payload_length;
+
+	eap_status_e status = packet->set_buffer_length(final_packet_length);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	if (packet->get_is_valid_data() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	status = packet->set_data_length(final_packet_length);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	eap_diameter_avp_header_c avp(
+		m_am_tools,
+		packet->get_data(packet->get_data_length()),
+		packet->get_data_length());
+	if (avp.get_is_valid() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	// These packets are encapsulated to AVP.
+	//  0                   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 
+	// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+	// |                           AVP Code                            |
+	// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+	// |V|M|r r r r r r|                  AVP Length                   |
+	// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+	// |                      Vendor-ID (optional)                     |
+	// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+	// |                   Data includes EAP-packet ...
+	// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+	status = avp.reset_header(static_cast<u16_t>(final_packet_length));
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = avp.set_avp_code(eap_diameter_avp_code_eap_message);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = avp.set_avp_flag_mandatory_avp(false);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+
+	{
+		// Adds EAP-Packet as a payload to AVP.
+		u16_t * const eap_packet_payload = reinterpret_cast<u16_t *>(
+			avp.get_data_offset(0ul, sent_eap_packet->get_length()));
+		if (eap_packet_payload == 0)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+		}
+
+		m_am_tools->memmove(
+			eap_packet_payload,
+			sent_eap_packet->get_header_buffer(sent_eap_packet->get_length()),
+			sent_eap_packet->get_length());
+	}
+
+	EAP_TLS_PEAP_TRACE_TTLS_PAYLOAD("Send TTLS AVP payload", &avp, m_is_client);
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, eap_status_ok);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e tls_application_eap_core_c::create_nonce(
+	eap_variable_data_c * const nonce)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("PEAP: %s: crypto_function: tls_application_eap_core_c::create_nonce(): ")
+		 EAPL("this = 0x%08x\n"),
+		 (m_is_client == true ? "client": "server"),
+		 this));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: tls_application_eap_core_c::create_nonce()");
+
+	eap_status_e status = nonce->set_buffer_length(TLV_NONCE_LENGTH);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+	nonce->set_data_length(nonce->get_buffer_length());
+
+	crypto_random_c rand(m_am_tools);
+	if (rand.get_is_valid() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	status = rand.get_rand_bytes(
+		nonce->get_data(nonce->get_data_length()),
+		nonce->get_data_length());
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e tls_application_eap_core_c::create_nonce_peap_v2(
+	const bool create_client_nonce_when_true)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("PEAP: %s: crypto_function: tls_application_eap_core_c::create_nonce_peap_v2(%s): ")
+		 EAPL("this = 0x%08x\n"),
+		 (m_is_client == true ? "client": "server"),
+		 (create_client_nonce_when_true == true ? "client": "server"),
+		 this));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: tls_application_eap_core_c::create_nonce_peap_v2()");
+
+	eap_status_e status = eap_status_process_general_error;
+
+	eap_variable_data_c * nonce = 0;
+
+	if (create_client_nonce_when_true == true)
+	{
+		nonce = &m_peap_v2_client_nonce;
+	}
+	else
+	{
+		nonce = &m_peap_v2_server_nonce;
+	}
+
+	status = create_nonce(nonce);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e tls_application_eap_core_c::create_compound_mac_key_peap_v2(
+	const bool create_client_CMK_when_true)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("PEAP: %s: crypto_function: tls_application_eap_core_c::create_compound_mac_key_peap_v2(%s): ")
+		 EAPL("this = 0x%08x\n"),
+		 (m_is_client == true ? "client": "server"),
+		 (create_client_CMK_when_true == true ? "client": "server"),
+		 this));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: tls_application_eap_core_c::create_compound_mac_key_peap_v2()");
+
+	eap_status_e status = eap_status_process_general_error;
+
+	if (m_peap_v2_IPMKn.get_is_valid_data() == false
+		|| m_peap_v2_IPMKn.get_data_length() != TLS_PEAP_V2_TK_LENGTH)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+	}
+
+	if (m_peap_v2_ISKn.get_is_valid_data() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+	}
+
+	EAP_TRACE_DATA_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("m_peap_v2_IPMKn"),
+		 m_peap_v2_IPMKn.get_data(m_peap_v2_IPMKn.get_data_length()),
+		 m_peap_v2_IPMKn.get_data_length()));
+
+	EAP_TRACE_DATA_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("m_peap_v2_ISKn"),
+		 m_peap_v2_ISKn.get_data(m_peap_v2_ISKn.get_data_length()),
+		 m_peap_v2_ISKn.get_data_length()));
+
+	{
+		eap_variable_data_c label(m_am_tools);
+
+		status = label.set_copy_of_buffer(
+			TLS_INTERMEDIATE_COMBINED_KEY_LABEL,
+			TLS_INTERMEDIATE_COMBINED_KEY_LABEL_LENGTH);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		crypto_tls_sha1_prf_c tls_sha1_prf(m_am_tools);
+
+		status = tls_sha1_prf.tls_prf_init(
+			&m_peap_v2_IPMKn,
+			&label,
+			&m_peap_v2_ISKn);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		if (tls_sha1_prf.get_is_valid() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+		}
+
+		status = tls_sha1_prf.tls_prf_output(
+			m_peap_v2_IPMKn.get_data(m_peap_v2_IPMKn.get_data_length()),
+			m_peap_v2_IPMKn.get_data_length());
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		EAP_TRACE_DATA_DEBUG(
+			m_am_tools,
+			TRACE_FLAGS_DEFAULT,
+			(EAPL("m_peap_v2_IPMKn"),
+			 m_peap_v2_IPMKn.get_data(m_peap_v2_IPMKn.get_data_length()),
+			 m_peap_v2_IPMKn.get_data_length()));
+	}
+
+	{
+		eap_variable_data_c label(m_am_tools);
+
+		if (create_client_CMK_when_true == true)
+		{
+			status = label.set_copy_of_buffer(
+				TLS_INTERMEDIATE_COMPOUND_CLIENT_MAC_KEY_LABEL,
+				TLS_INTERMEDIATE_COMPOUND_CLIENT_MAC_KEY_LABEL_LENGTH);
+		}
+		else
+		{
+			status = label.set_copy_of_buffer(
+				TLS_INTERMEDIATE_COMPOUND_SERVER_MAC_KEY_LABEL,
+				TLS_INTERMEDIATE_COMPOUND_SERVER_MAC_KEY_LABEL_LENGTH);
+		}
+
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		crypto_tls_sha1_prf_c tls_sha1_prf(m_am_tools);
+
+		EAP_TRACE_DATA_DEBUG(
+			m_am_tools,
+			TRACE_FLAGS_DEFAULT,
+			(EAPL("m_peap_v2_server_nonce"),
+			 m_peap_v2_server_nonce.get_data(m_peap_v2_server_nonce.get_data_length()),
+			 m_peap_v2_server_nonce.get_data_length()));
+
+		if (create_client_CMK_when_true == true)
+		{
+			eap_variable_data_c client_server_nonce(m_am_tools);
+
+			EAP_TRACE_DATA_DEBUG(
+				m_am_tools,
+				TRACE_FLAGS_DEFAULT,
+				(EAPL("m_peap_v2_client_nonce"),
+				 m_peap_v2_client_nonce.get_data(m_peap_v2_client_nonce.get_data_length()),
+				 m_peap_v2_client_nonce.get_data_length()));
+
+			status = client_server_nonce.set_copy_of_buffer(
+				&m_peap_v2_client_nonce);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			status = client_server_nonce.add_data(
+				&m_peap_v2_server_nonce);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			status = tls_sha1_prf.tls_prf_init(
+				&m_peap_v2_IPMKn,
+				&label,
+				&client_server_nonce);
+		}
+		else
+		{
+			status = tls_sha1_prf.tls_prf_init(
+				&m_peap_v2_IPMKn,
+				&label,
+				&m_peap_v2_server_nonce);
+		}
+
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		if (tls_sha1_prf.get_is_valid() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+		}
+
+		eap_variable_data_c * cmk = &m_peap_v2_CMK_B1_server;
+
+		if (create_client_CMK_when_true == true)
+		{
+			cmk = &m_peap_v2_CMK_B2_client;
+		}
+
+		status = cmk->set_buffer_length(TLS_PEAP_V2_COMPOUND_MAC_KEY_LENGTH);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+		cmk->set_data_length(TLS_PEAP_V2_COMPOUND_MAC_KEY_LENGTH);
+
+		status = tls_sha1_prf.tls_prf_output(
+			cmk->get_data(cmk->get_data_length()),
+			cmk->get_data_length());
+
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		EAP_TRACE_DATA_DEBUG(
+			m_am_tools,
+			TRACE_FLAGS_DEFAULT,
+			(EAPL("TLV MAC key CMK"),
+			 cmk->get_data(cmk->get_data_length()),
+			 cmk->get_data_length()));
+	}
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e tls_application_eap_core_c::create_compound_session_key_peap_v2()
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("PEAP: %s: crypto_function: tls_application_eap_core_c::create_compound_session_key_peap_v2(): ")
+		 EAPL("this = 0x%08x\n"),
+		 (m_is_client == true ? "client": "server"),
+		 this));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: tls_application_eap_core_c::create_compound_session_key_peap_v2()");
+
+	eap_status_e status = eap_status_process_general_error;
+
+	if (m_peap_v2_IPMKn.get_is_valid_data() == false
+		|| m_peap_v2_IPMKn.get_data_length() != TLS_PEAP_V2_TK_LENGTH)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+	}
+
+	{
+		eap_variable_data_c label(m_am_tools);
+
+		status = label.set_copy_of_buffer(
+			TLS_INTERMEDIATE_COMPOUND_SESSION_KEY_LABEL,
+			TLS_INTERMEDIATE_COMPOUND_SESSION_KEY_LABEL_LENGTH);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		crypto_tls_sha1_prf_c tls_sha1_prf(m_am_tools);
+
+		eap_variable_data_c client_server_nonce_outputlength(m_am_tools);
+
+		status = client_server_nonce_outputlength.set_copy_of_buffer(
+			&m_peap_v2_client_nonce);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = client_server_nonce_outputlength.add_data(
+			&m_peap_v2_server_nonce);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		/* @{ What is the sizeof the OutputLength? } */
+		
+		u8_t OutputLength = TLS_PEAP_V2_COMPOUND_SESSION_KEY_LENGTH;
+
+		status = client_server_nonce_outputlength.add_data(
+			&OutputLength,
+			sizeof(OutputLength));
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = tls_sha1_prf.tls_prf_init(
+			&m_peap_v2_IPMKn,
+			&label,
+			&client_server_nonce_outputlength);
+
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		if (tls_sha1_prf.get_is_valid() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+		}
+
+		status = m_peap_v2_CSK.set_buffer_length(TLS_PEAP_V2_COMPOUND_SESSION_KEY_LENGTH);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+		m_peap_v2_CSK.set_data_length(TLS_PEAP_V2_COMPOUND_SESSION_KEY_LENGTH);
+
+		status = tls_sha1_prf.tls_prf_output(
+			m_peap_v2_CSK.get_data(m_peap_v2_CSK.get_data_length()),
+			m_peap_v2_CSK.get_data_length());
+
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e tls_application_eap_core_c::create_crypto_binding_compound_mac(
+	const eap_variable_data_c * const peap_v2_CMK,
+	const tls_peap_tlv_header_c * const crypto_binding_tlv,
+	eap_variable_data_c * const mac_data)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	eap_status_e status = eap_status_process_general_error;
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("PEAP: %s: crypto_function: tls_application_eap_core_c::create_crypto_binding_compound_mac(): ")
+		 EAPL("this = 0x%08x\n"),
+		 (m_is_client == true ? "client": "server"),
+		 this));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: tls_application_eap_core_c::create_crypto_binding_compound_mac()");
+
+	EAP_TLS_PEAP_TRACE_PAYLOAD("Create TLV MAC", crypto_binding_tlv, m_is_client);
+
+	crypto_sha1_c sha1(m_am_tools);
+	crypto_hmac_c hmac(m_am_tools, &sha1, false);
+
+	EAP_TRACE_DATA_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("TLV MAC key CMK"),
+		 peap_v2_CMK->get_data(peap_v2_CMK->get_data_length()),
+		 peap_v2_CMK->get_data_length()));
+
+	status = hmac.hmac_set_key(peap_v2_CMK);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	if (hmac.get_is_valid() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_key_error);
+	}
+
+	EAP_TRACE_DATA_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("TLV MAC data"),
+		crypto_binding_tlv->get_header_buffer(
+			crypto_binding_tlv->get_header_length()
+			+ crypto_binding_tlv->get_data_length()),
+		crypto_binding_tlv->get_header_length()
+		+ crypto_binding_tlv->get_data_length()));
+
+	status = hmac.hmac_update(
+		crypto_binding_tlv->get_header_buffer(
+			crypto_binding_tlv->get_header_length()
+			+ crypto_binding_tlv->get_data_length()),
+		crypto_binding_tlv->get_header_length()
+		+ crypto_binding_tlv->get_data_length());
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = mac_data->set_buffer_length(hmac.get_digest_length());
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+	mac_data->set_data_length(hmac.get_digest_length());
+		
+	u32_t mac_length = hmac.get_digest_length();
+
+	status = hmac.hmac_final(
+		mac_data->get_data(mac_data->get_data_length()),
+		&mac_length);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+	else if (mac_length != hmac.get_digest_length())
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+	}
+
+	EAP_TRACE_DATA_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("PEAPv2 Compound MAC"),
+		 mac_data->get_data(mac_data->get_data_length()),
+		 mac_data->get_data_length()));
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e tls_application_eap_core_c::packet_send(
+	const eap_am_network_id_c * const /*network_id*/,
+	eap_buf_chain_wr_c * const sent_packet,
+	const u32_t header_offset,
+	const u32_t data_length,
+	const u32_t buffer_length)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("PEAP: %s: crypto_function: tls_application_eap_core_c::packet_send(): ")
+		 EAPL("this = 0x%08x\n"),
+		 (m_is_client == true ? "client": "server"),
+		 this));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: tls_application_eap_core_c::packet_send()");
+
+	eap_buf_chain_wr_c * forwarded_packet = sent_packet;
+	u32_t forwarded_data_length = data_length;
+	u32_t forwarded_buffer_length = buffer_length;
+	u32_t forwaded_header_offset = header_offset;
+
+	eap_status_e status = eap_status_process_general_error;
+
+	eap_buf_chain_wr_c tmp_packet(
+		eap_write_buffer,
+		m_am_tools);
+	if (tmp_packet.get_is_valid() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	const u32_t eap_packet_length = sent_packet->get_data_length();
+
+	eap_header_wr_c sent_eap_packet(
+		m_am_tools,
+		sent_packet->get_data(eap_packet_length),
+		eap_packet_length);
+	if (sent_eap_packet.get_is_valid() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+
+	if (m_is_client == true)
+	{
+		trace_tunneled_packet(EAPL("<- TUNNELED packet client"), &sent_eap_packet);
+	}
+	else
+	{
+		trace_tunneled_packet(EAPL("<- TUNNELED packet server"), &sent_eap_packet);
+	}
+
+
+	if (m_eap_type == eap_type_peap)
+	{
+		if (m_peap_version == peap_version_0_xp)
+		{
+			// XP PEAP does not include EAP-header (code, identifier and length).
+			// Here we must remove those attributes.
+			// See draft-kamath-pppext-peapv0-00.txt.
+
+			if (sent_eap_packet.get_code() == eap_code_request
+				|| sent_eap_packet.get_code() == eap_code_response)
+			{
+				forwaded_header_offset += eap_header_base_c::get_header_length();
+			}
+			else if (sent_eap_packet.get_code() == eap_code_success
+				|| sent_eap_packet.get_code() == eap_code_failure)
+			{
+				if (m_is_client == true)
+				{
+					// Client does not send these packets.
+					EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+					return EAP_STATUS_RETURN(m_am_tools, eap_status_process_general_error);
+				}
+
+				// XP PEAPv0 does not include EAP-header (code, identifier and length).
+				// Here we must create those attributes.
+				// Exception to this rule is, EAP-success and EAP-failure are sent in "Extensions Request Packet"
+				// or "Extensions Response Packet".
+				// The whole EAP-header is included
+				// to "Extensions Request Packet" and "Extensions Response Packet".
+				// See draft-kamath-pppext-peapv0-00.txt.
+				// 
+				// EAP-packet without code, identifier and length:
+				// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+				// |  EAP-type     |  EAP-data ...                                                                  
+				// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+				// EAP-packet with "Extensions Request Packet" or "Extensions Response Packet":
+				// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+				// |  EAP-code     |EAP-identifier | EAP-length                    | EAP-type      |  EAP-data ...  
+				// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+				// 
+				// EAP-code of "Extensions Request Packet" is 1 which is same as EAP-type of EAP-identity.
+				// EAP-code of "Extensions Response Packet" is 2 which is same as EAP-type of EAP-notification.
+
+				u8_t send_eap_identifier = sent_eap_packet.get_identifier();
+				if (m_is_client == false)
+				{
+					++send_eap_identifier;
+				}
+
+				status = create_result_tlv_message(
+					&tmp_packet,
+					sent_eap_packet.get_code(),
+					send_eap_identifier,
+					tls_peap_tlv_type_result);
+				if (status != eap_status_ok)
+				{
+					EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+					return EAP_STATUS_RETURN(m_am_tools, status);
+				}
+
+				forwarded_packet = &tmp_packet;
+
+				forwarded_data_length = tmp_packet.get_data_length();
+				forwarded_buffer_length = tmp_packet.get_buffer_length();
+				forwaded_header_offset = 0;
+
+				eap_header_string_c eap_string;
+				EAP_UNREFERENCED_PARAMETER(eap_string);
+
+				EAP_TRACE_ALWAYS(
+					m_am_tools, 
+					TRACE_FLAGS_DEFAULT, 
+					(EAPL("packet_send(): XP-PEAPv0 tunneled %s send.\n"),
+					 eap_string.get_eap_code_string(sent_eap_packet.get_code())));
+			}
+		}
+		else if (m_peap_version == peap_version_1)
+		{
+			// This version does not change packet.
+
+#if defined(USE_EAP_PEAPV1_EXTENSIONS)
+			if (m_is_client == false
+				&& m_server_use_peapv1_extensions_request == true
+				&& (sent_eap_packet.get_code() == eap_code_success
+					|| sent_eap_packet.get_code() == eap_code_failure))
+			{
+				u8_t send_eap_identifier = sent_eap_packet.get_identifier();
+				if (m_is_client == false)
+				{
+					++send_eap_identifier;
+				}
+
+				status = create_result_tlv_message(
+					&tmp_packet,
+					sent_eap_packet.get_code(),
+					send_eap_identifier,
+					tls_peap_tlv_type_result);
+				if (status != eap_status_ok)
+				{
+					EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+					return EAP_STATUS_RETURN(m_am_tools, status);
+				}
+
+				forwarded_packet = &tmp_packet;
+
+				forwarded_data_length = tmp_packet.get_data_length();
+				forwarded_buffer_length = tmp_packet.get_buffer_length();
+				forwaded_header_offset = 0;
+
+				eap_header_string_c eap_string;
+				EAP_UNREFERENCED_PARAMETER(eap_string);
+
+				EAP_TRACE_ALWAYS(
+					m_am_tools, 
+					TRACE_FLAGS_DEFAULT, 
+					(EAPL("packet_send(): PEAPv1 tunneled %s send.\n"),
+					 eap_string.get_eap_code_string(sent_eap_packet.get_code())));
+			}
+#endif //#if defined(USE_EAP_PEAPV1_EXTENSIONS)
+
+		}
+		else if (m_peap_version == peap_version_2)
+		{
+			if (sent_eap_packet.get_code() == eap_code_request
+				|| sent_eap_packet.get_code() == eap_code_response)
+			{
+				// These packets are encapsulated to EAP-TLV packet.
+				//  0                   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
+				// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+				// |     Code      |   Identifier  |            Length             |
+				// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+				// |     Type      |                  Data....
+				// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+				// 
+				// Data includes EAP-Payload TLV:
+				//  0                   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
+				// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+				// |M|R|         TLV Type          |            Length             |
+				// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+				// |                          EAP packet...
+				// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+				// |                             TLVs...
+				// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+				status = create_eap_payload_tlv_message(
+					&tmp_packet,
+					&sent_eap_packet,
+					sent_eap_packet.get_identifier());
+				if (status != eap_status_ok)
+				{
+					EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+					return EAP_STATUS_RETURN(m_am_tools, status);
+				}
+
+				forwarded_packet = &tmp_packet;
+
+				forwarded_data_length = tmp_packet.get_data_length();
+				forwarded_buffer_length = tmp_packet.get_buffer_length();
+				forwaded_header_offset = 0;
+			}
+			else if (sent_eap_packet.get_code() == eap_code_success
+				|| sent_eap_packet.get_code() == eap_code_failure)
+			{
+				if (m_is_client == true)
+				{
+					// Client does not send these packets.
+					EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+					return EAP_STATUS_RETURN(m_am_tools, eap_status_process_general_error);
+				}
+
+				// Server blocks the EAP-Success packet.
+				// Result TLV, Crypto Binding TLV and Intermediate Result TLV
+				// are created and sent within the state_notification() function
+				// by the send_tunneled_acknowledge_peap_v2() function
+				// when eap_state_authentication_finished_successfully or
+				// eap_state_authentication_terminated_unsuccessfully indication
+				// is received.
+				EAP_TRACE_DEBUG(
+					m_am_tools,
+					TRACE_FLAGS_DEFAULT,
+					(EAPL("TLS: %s: message_function: tls_application_eap_core_c::")
+					 EAPL("packet_send(), server blocks tunneled EAP-Success and EAP-Failure\n"),
+					(m_is_client == true ? "client": "server")));
+
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_ok);
+
+			}
+		}
+		else
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_no_matching_protocol_version);
+		}
+	}
+	else if (m_eap_type == eap_type_ttls)
+	{
+
+#if defined(EAP_USE_TTLS_PLAIN_MS_CHAP_V2_HACK)
+		if (m_tunneled_eap_in_ttls == true)
+#endif //#if defined(EAP_USE_TTLS_PLAIN_MS_CHAP_V2_HACK)
+		{
+			// These packets are encapsulated to AVP.
+			//  0                   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 
+			// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+			// |                           AVP Code                            |
+			// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+			// |V|M|r r r r r r|                  AVP Length                   |
+			// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+			// |                      Vendor-ID (optional)                     |
+			// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+			// |                   Data includes EAP-packet ...
+			// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+			if (sent_eap_packet.get_code() == eap_code_success
+				|| sent_eap_packet.get_code() == eap_code_failure)
+			{
+				if (m_is_client == true)
+				{
+					// Client does not send these packets.
+					EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+					return EAP_STATUS_RETURN(m_am_tools, eap_status_process_general_error);
+				}
+
+				// Server blocks the tunneled EAP-Success packet.
+				EAP_TRACE_DEBUG(
+					m_am_tools,
+					TRACE_FLAGS_DEFAULT,
+					(EAPL("TLS: %s: message_function: tls_application_eap_core_c::")
+					 EAPL("packet_send(), server blocks tunneled EAP-Success and EAP-Failure\n"),
+					(m_is_client == true ? "client": "server")));
+
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_ok);
+
+			}
+			else
+			{
+				status = create_eap_diameter_avp_message(
+					&tmp_packet,
+					&sent_eap_packet,
+					sent_eap_packet.get_identifier());
+				if (status != eap_status_ok)
+				{
+					EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+					return EAP_STATUS_RETURN(m_am_tools, status);
+				}
+				
+				forwarded_packet = &tmp_packet;
+				
+				forwarded_data_length = tmp_packet.get_data_length();
+				forwarded_buffer_length = tmp_packet.get_buffer_length();
+				forwaded_header_offset = 0;
+			}
+		}
+#if defined(EAP_USE_TTLS_PLAIN_MS_CHAP_V2_HACK)
+		else
+		{
+			// This is plain MsChapv2 tunneled in EAP-TTLS.
+			status = send_ttls_ms_chapv2_packet(&sent_eap_packet);
+
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+#endif //#if defined(EAP_USE_TTLS_PLAIN_MS_CHAP_V2_HACK)
+
+	}
+	else
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_no_matching_protocol_version);
+	}
+
+
+	EAP_TRACE_DATA_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("sent tunneled data:"),
+		 forwarded_packet->get_data_offset(
+			 forwaded_header_offset,
+			 forwarded_packet->get_data_length()-forwaded_header_offset),
+		 forwarded_packet->get_data_length()-forwaded_header_offset));
+
+
+#if defined(USE_EAP_ERROR_TESTS)
+	if (m_enable_random_errors == true
+		&& m_manipulate_only_tunneled_messages == true)
+	{
+		status = m_am_tools->generate_random_error(
+			forwarded_packet,
+			false,
+			m_am_tools->get_packet_index(),
+			0UL,
+			m_error_probability,
+			0UL);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+#endif //#if defined(USE_EAP_ERROR_TESTS)
+
+
+	status = get_application_partner()->packet_send(
+		forwarded_packet,
+		forwaded_header_offset,
+		forwarded_data_length,
+		forwarded_buffer_length);
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e tls_application_eap_core_c::send_tunneled_acknowledge_xp_peap_v0(
+	const eap_code_value_e result_eap_code,
+	const u8_t eap_identifier)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("PEAP: %s: crypto_function: tls_application_eap_core_c::send_tunneled_acknowledge_xp_peap_v0(): ")
+		 EAPL("this = 0x%08x\n"),
+		 (m_is_client == true ? "client": "server"),
+		 this));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: tls_application_eap_core_c::send_tunneled_acknowledge_xp_peap_v0()");
+
+	eap_buf_chain_wr_c eap_ack_packet(
+		eap_write_buffer, 
+		m_am_tools);
+
+	if (eap_ack_packet.get_is_valid() == false)
+	{
+		EAP_TRACE_ERROR(
+			m_am_tools,
+			TRACE_FLAGS_DEFAULT,
+			(EAPL("tls_application_eap_core_c::send_tunneled_acknowledge_xp_peap_v0(): ")
+			 EAPL("packet buffer corrupted.\n")));
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	eap_status_e status = create_result_tlv_message(
+		&eap_ack_packet,
+		result_eap_code,
+		eap_identifier,
+		tls_peap_tlv_type_result);
+
+	// - - - - - - - - - - - - - - - - - - - - - - - -
+
+	eap_header_string_c eap_string;
+	EAP_UNREFERENCED_PARAMETER(eap_string);
+
+	EAP_TRACE_ALWAYS(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("send_tunneled_acknowledge_xp_peap_v0(): XP-PEAPv0 tunneled %s acknowledge send.\n"),
+		 eap_string.get_eap_code_string(result_eap_code)));
+
+	EAP_TRACE_DATA_ALWAYS(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("    send: XP-PEAPv0 EAP acknowledge packet"),
+		eap_ack_packet.get_data(eap_ack_packet.get_data_length()),
+		eap_ack_packet.get_data_length()));
+
+
+#if defined(USE_EAP_ERROR_TESTS)
+	if (m_enable_random_errors == true
+		&& m_manipulate_only_tunneled_messages == true)
+	{
+		status = m_am_tools->generate_random_error(
+			&eap_ack_packet,
+			false,
+			m_am_tools->get_packet_index(),
+			0UL,
+			m_error_probability,
+			0UL);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+#endif //#if defined(USE_EAP_ERROR_TESTS)
+
+	status = get_application_partner()->packet_send(
+		&eap_ack_packet,
+		0ul,
+		eap_ack_packet.get_data_length(),
+		EAP_CORE_PACKET_BUFFER_LENGTH
+		);
+
+	// - - - - - - - - - - - - - - - - - - - - - - - -
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e tls_application_eap_core_c::send_tunneled_acknowledge_peap_v2(
+	const eap_code_value_e result_eap_code,
+	const u8_t eap_identifier)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("TLS: %s: message_function: tls_application_eap_core_c::send_tunneled_acknowledge_peap_v2()\n"),
+		(m_is_client == true ? "client": "server")));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: tls_application_eap_core_c::send_tunneled_acknowledge_peap_v2()");
+
+	eap_variable_data_c *peap_v2_nonce = &m_peap_v2_client_nonce;
+	if (m_is_client == false)
+	{
+		peap_v2_nonce = &m_peap_v2_server_nonce;
+	}
+
+	if (peap_v2_nonce->get_is_valid_data() == false)
+	{
+		EAP_TRACE_ERROR(
+			m_am_tools,
+			TRACE_FLAGS_DEFAULT,
+			(EAPL("tls_application_eap_core_c::send_tunneled_acknowledge_peap_v2(): ")
+			 EAPL("peap_v2_nonce invalid.\n")));
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	eap_buf_chain_wr_c eap_ack_packet(
+		eap_write_buffer, 
+		m_am_tools);
+
+	if (eap_ack_packet.get_is_valid() == false)
+	{
+		EAP_TRACE_ERROR(
+			m_am_tools,
+			TRACE_FLAGS_DEFAULT,
+			(EAPL("tls_application_eap_core_c::send_tunneled_acknowledge_peap_v2(): ")
+			 EAPL("packet buffer corrupted.\n")));
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	eap_status_e status = create_crypto_binding_tlv_message(
+		&eap_ack_packet,
+		result_eap_code,
+		eap_identifier,
+		peap_v2_nonce,
+		static_cast<u8_t>(m_peap_version));
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - -
+
+	eap_header_string_c eap_string;
+	EAP_UNREFERENCED_PARAMETER(eap_string);
+
+	EAP_TRACE_ALWAYS(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("send_tunneled_acknowledge_peap_v2(): PEAPv2 tunneled %s acknowledge send.\n"),
+		 eap_string.get_eap_code_string(result_eap_code)));
+
+	EAP_TRACE_DATA_ALWAYS(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("    send: PEAPv2 EAP acknowledge packet"),
+		eap_ack_packet.get_data(eap_ack_packet.get_data_length()),
+		eap_ack_packet.get_data_length()));
+
+#if defined(USE_EAP_ERROR_TESTS)
+	if (m_enable_random_errors == true
+		&& m_manipulate_only_tunneled_messages == true)
+	{
+		status = m_am_tools->generate_random_error(
+			&eap_ack_packet,
+			false,
+			m_am_tools->get_packet_index(),
+			0UL,
+			m_error_probability,
+			0UL);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+#endif //#if defined(USE_EAP_ERROR_TESTS)
+
+	status = get_application_partner()->packet_send(
+		&eap_ack_packet,
+		0ul,
+		eap_ack_packet.get_data_length(),
+		EAP_CORE_PACKET_BUFFER_LENGTH
+		);
+
+	// - - - - - - - - - - - - - - - - - - - - - - - -
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT u32_t tls_application_eap_core_c::get_header_offset(
+	u32_t * const MTU,
+	u32_t * const trailer_length)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	u32_t offset = get_application_partner()->get_header_offset(
+		MTU,
+		trailer_length);
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return offset;
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e tls_application_eap_core_c::load_module(
+	const eap_type_value_e type,
+	const eap_type_value_e tunneling_type,
+	abs_eap_base_type_c * const partner,
+	eap_base_type_c ** const eap_type,
+	const bool is_client_when_true,
+	const eap_am_network_id_c * const receive_network_id)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	if (tunneling_type != eap_type_none)
+	{
+		EAP_TRACE_ERROR(
+			m_am_tools,
+			TRACE_FLAGS_DEFAULT,
+			(EAPL("TLS: %s: message_function: tls_application_eap_core_c::load_module(), ")
+			 EAPL("tunneling_type is not eap_type_none, it is 0x%08x=%s\n"),
+			(m_is_client == true ? "client": "server"),
+			convert_eap_type_to_u32_t(tunneling_type),
+			eap_header_string_c::get_eap_type_string(tunneling_type)));
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_process_general_error);
+	}
+
+	eap_status_e status = get_application_partner()->load_module(
+		type,
+		m_eap_type, // This is the tunneling EAP-type.
+		partner,
+		eap_type,
+		is_client_when_true,
+		receive_network_id);
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e tls_application_eap_core_c::unload_module(
+	const eap_type_value_e type)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	eap_status_e status = get_application_partner()->unload_module(
+		type);
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e tls_application_eap_core_c::peap_tunnel_ready()
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("TLS: %s: message_function: tls_application_eap_core_c::peap_tunnel_ready()\n"),
+		(m_is_client == true ? "client": "server")));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: tls_application_eap_core_c::peap_tunnel_ready()");
+
+	eap_status_e status = eap_status_ok;
+
+	if (m_peap_version == peap_version_2)
+	{
+		eap_variable_data_c eap_tls_master_session_key(m_am_tools);
+
+		status = get_application_partner()->get_eap_tls_master_session_key(
+			&eap_tls_master_session_key,
+			0);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		if (eap_tls_master_session_key.get_data_length()
+			< TLS_PEAP_V2_TK_OFFSET + TLS_PEAP_V2_TK_LENGTH)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_encryption_parameter_size);
+		}
+
+		status = m_peap_v2_IPMKn.set_copy_of_buffer(
+			eap_tls_master_session_key.get_data_offset(
+				TLS_PEAP_V2_TK_OFFSET, TLS_PEAP_V2_TK_LENGTH),
+			TLS_PEAP_V2_TK_LENGTH);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e tls_application_eap_core_c::start_peap_tunneled_authentication(
+	const eap_am_network_id_c * const receive_network_id,
+	const bool is_client_when_true,
+	const u8_t received_eap_identifier,
+	const tls_session_type_e tls_session_type,
+	const bool /* tls_peap_server_authenticates_client_action */)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("PEAP: %s: crypto_function: tls_application_eap_core_c::start_peap_tunneled_authentication(): ")
+		 EAPL("this = 0x%08x\n"),
+		 (m_is_client == true ? "client": "server"),
+		 this));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: tls_application_eap_core_c::start_peap_tunneled_authentication()");
+
+	eap_status_e status = eap_status_process_general_error;
+
+
+#if defined(USE_EAP_CORE_SERVER)
+
+	if (m_is_client == false)
+	{
+		// Server
+
+		if (m_eap_type == eap_type_peap
+			&& (tls_session_type == tls_session_type_original_session_resumption
+				|| tls_session_type == tls_session_type_stateless_session_resumption)
+			&& m_peap_allow_tunneled_session_resumption == true
+#if !defined(USE_EAP_TLS_PEAP_TPPD_PEAP_V1_NEW_FIXES)
+			&& m_peap_version == peap_version_0_xp
+#endif //#if !defined(USE_EAP_TLS_PEAP_TPPD_PEAP_V1_NEW_FIXES)
+			)
+		{
+			if (m_peap_version == peap_version_0_xp)
+			{
+				if (is_client_when_true == false)
+				{
+					// Server sends tunneled EAP-Success.
+
+					eap_buf_chain_wr_c forwarded_packet(
+						eap_write_buffer,
+						m_am_tools);
+					if (forwarded_packet.get_is_valid() == false)
+					{
+						EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+						return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+					}
+
+					status = create_result_tlv_message(
+						&forwarded_packet,
+						eap_code_success,
+						static_cast<u8_t>(received_eap_identifier+1ul),
+						tls_peap_tlv_type_result);
+					if (status != eap_status_ok)
+					{
+						EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+						return EAP_STATUS_RETURN(m_am_tools, status);
+					}
+
+					u32_t forwarded_data_length = forwarded_packet.get_data_length();
+					u32_t forwarded_buffer_length = forwarded_packet.get_buffer_length();
+					u32_t forwaded_header_offset = 0;
+
+					eap_header_string_c eap_string;
+					EAP_UNREFERENCED_PARAMETER(eap_string);
+
+					EAP_TRACE_ALWAYS(
+						m_am_tools, 
+						TRACE_FLAGS_DEFAULT, 
+						(EAPL("packet_send(): XP-PEAPv0 tunneled %s send.\n"),
+						 eap_string.get_eap_code_string(eap_code_success)));
+
+#if defined(USE_EAP_ERROR_TESTS)
+					if (m_enable_random_errors == true
+						&& m_manipulate_only_tunneled_messages == true)
+					{
+						status = m_am_tools->generate_random_error(
+							&forwarded_packet,
+							false,
+							m_am_tools->get_packet_index(),
+							0UL,
+							m_error_probability,
+							0UL);
+						if (status != eap_status_ok)
+						{
+							EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+							return EAP_STATUS_RETURN(m_am_tools, status);
+						}
+					}
+#endif //#if defined(USE_EAP_ERROR_TESTS)
+
+
+					status = get_application_partner()->packet_send(
+						&forwarded_packet,
+						forwaded_header_offset,
+						forwarded_data_length,
+						forwarded_buffer_length);
+#if defined(USE_EAP_TLS_PEAP_TPPD_PEAP_V1_NEW_FIXES)
+					if (status != eap_status_ok)
+					{
+						EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+						return EAP_STATUS_RETURN(m_am_tools, status);
+					}
+#endif //#if defined(USE_EAP_TLS_PEAP_TPPD_PEAP_V1_NEW_FIXES)
+				}
+			}
+			else if (m_peap_version == peap_version_1)
+			{
+
+#if defined(USE_EAP_TLS_PEAP_TPPD_PEAP_V1_NEW_FIXES)
+
+				// Send tunneled EAP-Success.
+				// This will tell client the tunneled
+				// EAP-session was also restored.
+
+				// Here we swap the addresses.
+				eap_am_network_id_c send_network_id(
+					m_am_tools,
+					receive_network_id->get_destination_id(),
+					receive_network_id->get_source_id(),
+					receive_network_id->get_type());
+
+				if (m_use_tppd_tls_peap == true
+					&& m_use_tppd_peapv1_acknowledge_hack == true)
+				{
+					// This case does not send tunneled EAP-Success
+					// instead it sends plain EAP-Success.
+
+					m_tunneled_eap_type_authentication_state
+						= eap_state_authentication_wait_tppd_peapv1_empty_acknowledge;
+
+					eap_state_notification_c notification(
+						m_am_tools,
+						&send_network_id,
+						m_is_client,
+						eap_state_notification_eap,
+						eap_protocol_layer_eap,
+						m_eap_type,
+						eap_state_none,
+						m_tunneled_eap_type_authentication_state,
+						received_eap_identifier,
+						true);
+					get_application_partner()->state_notification(&notification);
+
+					status = eap_status_ok;
+				}
+				else
+				{
+					if (m_eap_core == 0)
+					{
+						EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+						return EAP_STATUS_RETURN(m_am_tools, eap_status_process_general_error);
+					}
+
+					status = m_eap_core->send_eap_success(
+						&send_network_id,
+						received_eap_identifier);
+					if (status != eap_status_ok)
+					{
+						EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+						return EAP_STATUS_RETURN(m_am_tools, status);
+					}
+
+					m_tunneled_eap_type_authentication_state
+						= eap_state_authentication_finished_successfully;
+
+					eap_state_notification_c notification(
+						m_am_tools,
+						&send_network_id,
+						m_is_client,
+						eap_state_notification_eap,
+						eap_protocol_layer_eap,
+						m_peap_tunneled_eap_type,
+						eap_state_none,
+						m_tunneled_eap_type_authentication_state,
+						received_eap_identifier,
+						false);
+					get_application_partner()->state_notification(&notification);
+
+					status = eap_status_success;
+				}
+
+#else
+
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_not_supported);
+
+#endif //#if defined(USE_EAP_TLS_PEAP_TPPD_PEAP_V1_NEW_FIXES)
+
+			}
+			else if (m_peap_version == peap_version_2)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_not_supported);
+			}
+		}
+		else
+		{
+			if (m_eap_core == 0)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_process_general_error);
+			}
+
+			status = m_eap_core->send_eap_identity_request(
+				receive_network_id);
+		}
+	}
+	else
+#endif //#if defined(USE_EAP_CORE_SERVER)
+	{
+		// Client
+
+		status = eap_status_ok;
+	}
+
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e tls_application_eap_core_c::restart_authentication(
+	const eap_am_network_id_c * const /*receive_network_id*/,
+	const bool /*is_client_when_true*/,
+	const bool /*force_clean_restart*/,
+	const bool /*from_timer*/)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	// NOTE, we do not allow tunneled EAP-type restart authentication.
+	eap_status_e status = eap_status_ok;
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e tls_application_eap_core_c::packet_data_crypto_keys(
+	const eap_am_network_id_c * const /*send_network_id*/,
+	const eap_master_session_key_c * const master_session_key
+	)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("TLS: %s: message_function: tls_application_eap_core_c::packet_data_crypto_keys()\n"),
+		(m_is_client == true ? "client": "server")));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: tls_application_eap_core_c::packet_data_crypto_keys()");
+
+	eap_status_e status = eap_status_process_general_error;
+
+	if (m_peap_version == peap_version_2)
+	{
+		status = m_peap_v2_ISKn.set_copy_of_buffer(
+			master_session_key);
+	}
+	else
+	{
+		// Note older PEAP version does not use keys generated by tunneled EAP-type.
+		// This is sad but true.
+		status = eap_status_ok;
+	}
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e tls_application_eap_core_c::read_configure(
+	const eap_configuration_field_c * const field,
+	eap_variable_data_c * const data)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	eap_status_e status = eap_status_process_general_error;
+
+	if (field == 0
+		|| data == 0)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+	}
+
+	if (cf_str_EAP_default_type_hex_data.get_field()->compare(
+			m_am_tools,
+			field) == true
+		|| cf_str_EAP_server_default_type_hex_data.get_field()->compare(
+			m_am_tools,
+			field) == true)
+	{
+		eap_type_value_e tunneled_type = m_peap_tunneled_eap_type;
+
+#if defined(EAP_USE_TTLS_PLAIN_MS_CHAP_V2_HACK)
+		if (m_tunneled_eap_in_ttls == false
+			&& (
+#if defined(USE_EAP_EXPANDED_TYPES)
+				m_peap_tunneled_eap_type == eap_expanded_type_ttls_plain_mschapv2.get_type()
+#else
+				m_peap_tunneled_eap_type == eap_type_plain_mschapv2
+#endif //#if defined(USE_EAP_EXPANDED_TYPES)
+			))
+		{
+			tunneled_type = eap_type_mschapv2;
+		}
+#endif //#if defined(EAP_USE_TTLS_PLAIN_MS_CHAP_V2_HACK)
+
+		if (field->get_type() == eap_configure_type_u32_t)
+		{
+			u32_t tunneled_eap_type = convert_eap_type_to_u32_t(tunneled_type);
+			status = data->set_copy_of_buffer(&tunneled_eap_type, sizeof(tunneled_eap_type));
+		}
+		else if (field->get_type() == eap_configure_type_hex_data)
+		{
+			eap_expanded_type_c default_type(tunneled_type);
+			status = default_type.get_expanded_type_data(m_am_tools, data);
+		}
+		else
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_configure_type);
+		}
+
+	}
+	else if (cf_str_EAP_CORE_retransmission_counter.get_field()->compare(
+				 m_am_tools,
+				 field) == true)
+	{
+		// We do not allow inner EAP Core to re-transmit packets.
+		u32_t retransmission_counter = 0ul;
+		status = data->set_copy_of_buffer(&retransmission_counter, sizeof(retransmission_counter));
+	}
+	else if (cf_str_EAP_CORE_process_EAP_Nak_immediately.get_field()->compare(
+				 m_am_tools,
+				 field) == true)
+	{
+		// We do not allow inner EAP Core to delay EAP-Nak processing.
+		u32_t EAP_CORE_process_EAP_Nak_immediately = 1ul; // This is true value.
+		status = data->set_copy_of_buffer(
+			&EAP_CORE_process_EAP_Nak_immediately,
+			sizeof(EAP_CORE_process_EAP_Nak_immediately));
+	}
+#if defined(EAP_USE_TTLS_PLAIN_MS_CHAP_V2_HACK)
+	else if (m_tunneled_eap_in_ttls == false
+		&& cf_str_EAP_MSCHAPV2_use_implicit_challenge.get_field()->compare(
+				 m_am_tools,
+				 field) == true)
+	{
+		u32_t use_implicit_challenge = 1ul;
+		status = data->set_copy_of_buffer(&use_implicit_challenge, sizeof(use_implicit_challenge));
+	}
+#endif //#if defined(EAP_USE_TTLS_PLAIN_MS_CHAP_V2_HACK)
+	else if (m_is_client == false
+		&& m_eap_type == eap_type_peap
+		&& m_peap_version == peap_version_0_xp
+		&& cf_str_EAP_TLS_PEAP_check_identifier_of_eap_identity_response.get_field()->compare(
+				 m_am_tools,
+				 field) == true)
+	{
+		// Tunneled type on PEAPv0 server cannot check EAP-Identity exactly.
+		u32_t check_identifier_of_eap_identity_response = 0ul;
+		status = data->set_copy_of_buffer(&check_identifier_of_eap_identity_response, sizeof(check_identifier_of_eap_identity_response));
+	}
+	else
+	{
+		status = get_application_partner()->read_configure(
+			field,
+			data);
+	}
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e tls_application_eap_core_c::write_configure(
+	const eap_configuration_field_c * const field,
+	eap_variable_data_c * const data)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	eap_status_e status = get_application_partner()->write_configure(
+		field,
+		data);
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT void tls_application_eap_core_c::set_tunneled_state(
+	const tls_session_type_e tls_session_type)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("PEAP: %s: crypto_function: tls_application_eap_core_c::set_tunneled_state(): ")
+		 EAPL("this = 0x%08x\n"),
+		 (m_is_client == true ? "client": "server"),
+		 this));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: tls_application_eap_core_c::set_tunneled_state()");
+
+	m_tls_session_type = tls_session_type;
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT void tls_application_eap_core_c::state_notification(
+	const abs_eap_state_notification_c * const state)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("PEAP: %s: crypto_function: tls_application_eap_core_c::state_notification(): notification state=%s")
+		 EAPL("this = 0x%08x\n"),
+		 (m_is_client == true ? "client": "server"),
+		 eap_state_notification_c::get_state_string(state->get_protocol_layer(), state->get_current_state()),
+		 this));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: tls_application_eap_core_c::state_notification()");
+
+
+	const abs_eap_state_notification_c * notification = state;
+	eap_automatic_variable_c<const abs_eap_state_notification_c> automatic_notification(m_am_tools, 0);
+
+	if (state->get_protocol_layer() == eap_protocol_layer_eap)
+	{
+		EAP_TRACE_DEBUG(
+			m_am_tools,
+			TRACE_FLAGS_DEFAULT,
+			(EAPL("PEAP: %s: crypto_function: tls_application_eap_core_c::state_notification(): eap_protocol_layer_eap: state = %s")
+			 EAPL("this = 0x%08x\n"),
+			 (m_is_client == true ? "client": "server"),
+			 eap_state_notification_c::get_state_string(state->get_protocol_layer(), state->get_current_state()),
+			 this));
+
+		if (state->get_current_state() == eap_state_authentication_terminated_unsuccessfully)
+		{
+			// Tunneled EAP-type terminated unsuccessfully.
+			m_tunneled_eap_type_authentication_state = static_cast<eap_state_variable_e>(
+				state->get_current_state());
+			if (m_is_client == true
+				&& m_eap_type == eap_type_peap)
+			{
+				if (m_peap_version == peap_version_0_xp)
+				{
+					// Send tunneled EAP-Success acknowledge.
+					eap_status_e status = send_tunneled_acknowledge_xp_peap_v0(
+						eap_code_failure,
+						state->get_eap_identifier());
+					if (status != eap_status_ok)
+					{
+						m_tunneled_eap_type_authentication_state
+							= eap_state_authentication_terminated_unsuccessfully;
+					}
+					// XP-PEAPv0 Authentication FAILED.
+				}
+#if defined(USE_EAP_PEAPV1_EXTENSIONS)
+				else if (m_peap_version == peap_version_1
+					&& m_client_send_peapv1_extensions_response == true)
+				{
+					// Send tunneled EAP-Success acknowledge.
+					eap_status_e status = send_tunneled_acknowledge_xp_peap_v0(
+						eap_code_failure,
+						state->get_eap_identifier());
+					if (status != eap_status_ok)
+					{
+						m_tunneled_eap_type_authentication_state
+							= eap_state_authentication_terminated_unsuccessfully;
+					}
+					// PEAPv1 Authentication FAILED.
+
+					notification = new eap_state_notification_c(
+						m_am_tools,
+						state->get_send_network_id(),
+						state->get_is_client(),
+						eap_state_notification_eap,
+						state->get_protocol_layer(),
+						state->get_eap_type(),
+						state->get_previous_state(),
+						eap_state_authentication_terminated_unsuccessfully_peapv1_extension,
+						state->get_eap_identifier(),
+						state->get_allow_send_eap_success());
+
+					automatic_notification.set_variable(notification);
+
+					if (notification == 0)
+					{
+						m_tunneled_eap_type_authentication_state
+							= eap_state_authentication_terminated_unsuccessfully;
+						EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+						(void) EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+						return;
+					}
+				}
+#endif //#if defined(USE_EAP_PEAPV1_EXTENSIONS)
+				else if (m_peap_version == peap_version_2)
+				{
+					// Send tunneled EAP-Success acknowledge.
+					eap_status_e status = send_tunneled_acknowledge_peap_v2(
+						eap_code_failure,
+						state->get_eap_identifier());
+					if (status != eap_status_ok)
+					{
+						m_tunneled_eap_type_authentication_state
+							= eap_state_authentication_terminated_unsuccessfully;
+					}
+					// PEAPv2 Authentication FAILED.
+				}
+			}
+			else if (m_eap_type == eap_type_peap)
+			{
+				// Server
+				if (m_peap_version == peap_version_0_xp)
+				{
+					// Does nothing special.
+					// Server waits the EAP Response of Type=Extensions
+					// acknowled packet from client.
+					return;
+				}
+				else if (m_peap_version == peap_version_2)
+				{
+					// Does nothing special.
+					// Server waits the EAP Response of Type=Extensions
+					// acknowled packet from client.
+					return;
+				}
+			}
+		}
+		else if (state->get_current_state() == eap_state_authentication_finished_successfully)
+		{
+			// Tunneled EAP-type finished successfully.
+			m_tunneled_eap_type_authentication_state = static_cast<eap_state_variable_e>(
+				state->get_current_state());
+
+			if (m_is_client == true
+				&& m_eap_type == eap_type_peap)
+			{
+				if (m_peap_version == peap_version_0_xp)
+				{
+					// Send tunneled EAP-Success acknowledge.
+					eap_status_e status = send_tunneled_acknowledge_xp_peap_v0(
+						eap_code_success,
+						state->get_eap_identifier());
+					if (status != eap_status_ok)
+					{
+						m_tunneled_eap_type_authentication_state
+							= eap_state_authentication_terminated_unsuccessfully;
+					}
+
+					// XP-PEAPv0 Authentication OK.
+				}
+#if defined(USE_EAP_PEAPV1_EXTENSIONS)
+				else if (m_peap_version == peap_version_1
+					&& m_client_send_peapv1_extensions_response == true)
+				{
+					// Send tunneled EAP-Success acknowledge.
+					eap_status_e status = send_tunneled_acknowledge_xp_peap_v0(
+						eap_code_success,
+						state->get_eap_identifier());
+					if (status != eap_status_ok)
+					{
+						m_tunneled_eap_type_authentication_state
+							= eap_state_authentication_terminated_unsuccessfully;
+					}
+					// PEAPv1 Authentication OK.
+
+					notification = new eap_state_notification_c(
+						m_am_tools,
+						state->get_send_network_id(),
+						state->get_is_client(),
+						eap_state_notification_eap,
+						state->get_protocol_layer(),
+						state->get_eap_type(),
+						state->get_previous_state(),
+						eap_state_authentication_finished_successfully_peapv1_extension,
+						state->get_eap_identifier(),
+						state->get_allow_send_eap_success());
+
+					automatic_notification.set_variable(notification);
+
+					if (notification == 0)
+					{
+						m_tunneled_eap_type_authentication_state
+							= eap_state_authentication_terminated_unsuccessfully;
+						EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+						(void) EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+						return;
+					}
+				}
+#endif //#if defined(USE_EAP_PEAPV1_EXTENSIONS)
+				else if (m_peap_version == peap_version_2)
+				{
+					// Send tunneled EAP-Success acknowledge.
+
+					eap_status_e status = create_nonce_peap_v2(m_is_client);
+					if (status != eap_status_ok)
+					{
+						m_tunneled_eap_type_authentication_state
+							= eap_state_authentication_terminated_unsuccessfully;
+						EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+						return;
+					}
+
+					// Intermediate Combined Key is stored to m_peap_v2_IPMKn.
+					// Compound MAC Key is stored to m_peap_v2_CMK_Bn.
+					status = create_compound_mac_key_peap_v2(false);
+					if (status != eap_status_ok)
+					{
+						m_tunneled_eap_type_authentication_state
+							= eap_state_authentication_terminated_unsuccessfully;
+						EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+						return;
+					}
+
+					status = create_compound_mac_key_peap_v2(true);
+					if (status != eap_status_ok)
+					{
+						m_tunneled_eap_type_authentication_state
+							= eap_state_authentication_terminated_unsuccessfully;
+						EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+						return;
+					}
+
+					// First we must verify the received TLVs and B1 MAC.
+					status = verify_tunneled_acknowledge_peap_v2();
+					if (status != eap_status_success)
+					{
+						m_tunneled_eap_type_authentication_state
+							= eap_state_authentication_terminated_unsuccessfully;
+						EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+						return;
+					}
+
+					status = send_tunneled_acknowledge_peap_v2(
+						eap_code_success,
+						state->get_eap_identifier());
+					if (status != eap_status_ok)
+					{
+						m_tunneled_eap_type_authentication_state
+							= eap_state_authentication_terminated_unsuccessfully;
+						EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+						return;
+					}
+
+					status = finish_successfull_authentication_peap_v2(
+						m_received_eap_identifier);
+					if (status != eap_status_ok)
+					{
+						m_tunneled_eap_type_authentication_state
+							= eap_state_authentication_terminated_unsuccessfully;
+						EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+						return;
+					}
+
+					EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+					return;
+
+					// PEAPv2 Authentication OK.
+				}
+			}
+			else if (m_eap_type == eap_type_peap)
+			{
+				// Server
+				if (m_peap_version == peap_version_0_xp)
+				{
+					// Does nothing special.
+					// Server waits the EAP Response of Type=Extensions acknowled packet from client.
+					return;
+				}
+				else if (m_peap_version == peap_version_2)
+				{
+					eap_status_e status = create_nonce_peap_v2(m_is_client);
+					if (status != eap_status_ok)
+					{
+						m_tunneled_eap_type_authentication_state
+							= eap_state_authentication_finished_successfully;
+						EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+						return;
+					}
+
+					// Intermediate Combined Key is stored to m_peap_v2_IPMKn.
+					// Compound MAC Key is stored to m_peap_v2_CMK_Bn.
+					status = create_compound_mac_key_peap_v2(m_is_client);
+					if (status != eap_status_ok)
+					{
+						m_tunneled_eap_type_authentication_state
+							= eap_state_authentication_finished_successfully;
+						EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+						return;
+					}
+
+					status = send_tunneled_acknowledge_peap_v2(
+						eap_code_success,
+						state->get_eap_identifier());
+					if (status != eap_status_ok)
+					{
+						m_tunneled_eap_type_authentication_state
+							= eap_state_authentication_finished_successfully;
+						EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+						return;
+					}
+
+					EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+					return;
+				}
+			}
+			else if (m_eap_type == eap_type_ttls)
+			{
+#if defined(EAP_USE_TTLS_PLAIN_MS_CHAP_V2_HACK)
+				if (m_tunneled_eap_in_ttls == true)
+				{
+					// TTLS with tunneled EAP.
+					EAP_TRACE_DEBUG(
+						m_am_tools,
+						TRACE_FLAGS_DEFAULT,
+						(EAPL("PEAP: %s: crypto_function: tls_application_eap_core_c::state_notification(): notification state=%s")
+						 EAPL("this = 0x%08x, TTLS with tunneled EAP.\n"),
+						 (m_is_client == true ? "client": "server"),
+						 eap_state_notification_c::get_state_string(state->get_protocol_layer(), state->get_current_state()),
+						 this));
+				}
+				else if (m_tunneled_eap_in_ttls == false)
+				{
+					// TTLS with plain MsChapv2.
+					EAP_TRACE_DEBUG(
+						m_am_tools,
+						TRACE_FLAGS_DEFAULT,
+						(EAPL("PEAP: %s: crypto_function: tls_application_eap_core_c::state_notification(): notification state=%s")
+						 EAPL("this = 0x%08x, TTLS with plain MsChapv2.\n"),
+						 (m_is_client == true ? "client": "server"),
+						 eap_state_notification_c::get_state_string(state->get_protocol_layer(), state->get_current_state()),
+						 this));
+				}
+#endif //#if defined(EAP_USE_TTLS_PLAIN_MS_CHAP_V2_HACK)
+			}
+		}
+	}
+
+	get_application_partner()->state_notification(notification);
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e tls_application_eap_core_c::asynchronous_init_remove_eap_session(
+	const eap_am_network_id_c * const /* send_network_id */)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	// Here we do not want to remove the session yet. It is removed after PEAP session finishes.
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, eap_status_ok);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e tls_application_eap_core_c::set_timer(
+	abs_eap_base_timer_c * const initializer, 
+	const u32_t id, 
+	void * const data,
+	const u32_t p_time_ms)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	eap_status_e status = get_application_partner()->set_timer(
+		initializer, 
+		id, 
+		data,
+		p_time_ms);
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e tls_application_eap_core_c::cancel_timer(
+	abs_eap_base_timer_c * const initializer, 
+	const u32_t id)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+	eap_status_e status = eap_status_process_general_error;
+	abs_tls_base_application_c * partner = get_application_partner();
+	if (partner != 0)
+	{
+		status = partner->cancel_timer(
+			initializer, 
+			id);
+	}
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e tls_application_eap_core_c::cancel_all_timers()
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+	eap_status_e status = eap_status_process_general_error;
+	abs_tls_base_application_c * partner = get_application_partner();
+	if (partner != 0)
+	{
+		status = partner->cancel_all_timers();
+	}
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e tls_application_eap_core_c::check_is_valid_eap_type(
+	const eap_type_value_e eap_type)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	eap_header_string_c eap_string;
+	EAP_UNREFERENCED_PARAMETER(eap_string);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("tls_application_eap_core_c::check_is_valid_eap_type(): EAP-type=0x%08x=%s\n"),
+		convert_eap_type_to_u32_t(eap_type),
+		eap_string.get_eap_type_string(eap_type)));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: tls_application_eap_core_c::check_is_valid_eap_type()");
+
+	if (m_accepted_tunneled_eap_types.get_object_count() > 0ul)
+	{
+		bool allow_this_eap_type = false;
+
+		eap_type_value_e * type = 0;
+
+		for (u32_t ind = 0ul; ind < m_accepted_tunneled_eap_types.get_object_count(); ind++)
+		{
+			type = m_accepted_tunneled_eap_types.get_object(ind);
+
+			if (type != 0)
+			{
+				EAP_TRACE_DEBUG(
+					m_am_tools, 
+					TRACE_FLAGS_DEFAULT, 
+					(EAPL("%s: tls_application_eap_core_c::check_is_valid_eap_type(): allowed EAP-type %d.\n"),
+					 (m_is_client == true ? "client": "server"),
+					 convert_eap_type_to_u32_t(*type)));
+			}
+
+			if (type != 0
+				&& eap_type == *type)
+			{
+				allow_this_eap_type = true;
+				break;
+			}
+		} // for()
+
+		if (allow_this_eap_type == false)
+		{
+			// Not allowed EAP-type inside PEAP.
+			EAP_TRACE_DEBUG(
+				m_am_tools,
+				TRACE_FLAGS_DEFAULT,
+				(EAPL("ERROR: tls_application_eap_core_c::check_is_valid_eap_type(): No allowed EAP-type=0x%08x=%s\n"),
+				convert_eap_type_to_u32_t(eap_type),
+				eap_string.get_eap_type_string(eap_type)));
+
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_eap_type);
+		}
+		else
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_ok);
+		}
+	}
+	else
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_eap_type);
+	}
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e tls_application_eap_core_c::get_eap_type_list(
+	eap_array_c<eap_type_value_e> * const eap_type_list)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("PEAP: %s: crypto_function: tls_application_eap_core_c::get_eap_type_list(): ")
+		 EAPL("this = 0x%08x\n"),
+		 (m_is_client == true ? "client": "server"),
+		 this));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: tls_application_eap_core_c::get_eap_type_list()");
+
+	for (u32_t ind = 0ul; ind < m_accepted_tunneled_eap_types.get_object_count(); ind++)
+	{
+		const eap_type_value_e * const type = m_accepted_tunneled_eap_types.get_object(ind);
+
+		if (type != 0)
+		{
+			EAP_TRACE_DEBUG(
+				m_am_tools, 
+				TRACE_FLAGS_DEFAULT, 
+				(EAPL("%s: tls_application_eap_core_c::get_eap_type_list(): allowed EAP-type %d.\n"),
+				 (m_is_client == true ? "client": "server"),
+				 convert_eap_type_to_u32_t(*type)));
+		}
+	}
+
+	eap_status_e status = copy_simple<eap_type_value_e>(
+		&m_accepted_tunneled_eap_types,
+		eap_type_list,
+		m_am_tools,
+		false);
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT abs_tls_base_application_c * tls_application_eap_core_c::get_application_partner()
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return m_application_partner;
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e tls_application_eap_core_c::set_application_partner(
+	abs_tls_base_application_c * const partner)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("PEAP: %s: crypto_function: tls_application_eap_core_c::set_application_partner(): ")
+		 EAPL("this = 0x%08x\n"),
+		 (m_is_client == true ? "client": "server"),
+		 this));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: tls_application_eap_core_c::set_application_partner()");
+
+	m_application_partner = partner;
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, eap_status_ok);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e tls_application_eap_core_c::add_rogue_ap(
+	eap_array_c<eap_rogue_ap_entry_c> & rogue_ap_list)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	eap_status_e status = get_application_partner()->add_rogue_ap(rogue_ap_list);
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+//
+EAP_FUNC_EXPORT eap_status_e tls_application_eap_core_c::set_session_timeout(
+	const u32_t session_timeout_ms)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("PEAP: %s: crypto_function: tls_application_eap_core_c::set_session_timeout(): ")
+		 EAPL("this = 0x%08x\n"),
+		 (m_is_client == true ? "client": "server"),
+		 this));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: tls_application_eap_core_c::set_session_timeout()");
+
+	EAP_ASSERT(m_am_tools->get_global_mutex()->get_is_reserved() == true);
+
+	eap_status_e status = get_application_partner()->set_session_timeout(session_timeout_ms);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e tls_application_eap_core_c::read_authority_identity(eap_variable_data_c * const /* authority_identity_payload*/)
+{
+	return EAP_STATUS_RETURN(m_am_tools, eap_status_not_supported);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e tls_application_eap_core_c::save_user_authorization_pac_opaque(const tls_extension_c * const /* extension */)
+{
+	return EAP_STATUS_RETURN(m_am_tools, eap_status_not_supported);
+}
+
+//--------------------------------------------------
+
+// This is commented in tls_base_record_c::query_tunnel_PAC().
+EAP_FUNC_EXPORT eap_status_e tls_application_eap_core_c::query_tunnel_PAC(
+	const eap_fast_variable_data_c * const /* in_A_ID_TLV */)
+{
+	return EAP_STATUS_RETURN(m_am_tools, eap_status_not_supported);
+}
+
+//--------------------------------------------------
+
+// This is commented in tls_base_record_c::cancel_query_tunnel_PAC().
+EAP_FUNC_EXPORT eap_status_e tls_application_eap_core_c::cancel_query_tunnel_PAC()
+{
+	return EAP_STATUS_RETURN(m_am_tools, eap_status_not_supported);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e tls_application_eap_core_c::complete_query_ttls_pap_username_and_password(
+	const eap_variable_data_c * const ttls_pap_username,
+	const eap_variable_data_c * const ttls_pap_password,
+	const eap_status_e query_result)
+{
+	eap_status_e status(eap_status_process_general_error);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("TTLS: %s: tls_application_eap_core_c::complete_query_ttls_pap_username_and_password()\n"),
+		(m_is_client == true ? "client": "server")));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: tls_application_eap_core_c::complete_query_ttls_pap_username_and_password()");
+
+	// Here we swap the addresses.
+	eap_am_network_id_c send_network_id(
+		m_am_tools,
+		m_receive_network_id.get_destination_id(),
+		m_receive_network_id.get_source_id(),
+		m_receive_network_id.get_type());
+
+	if (query_result != eap_status_ok)
+	{
+		{
+			eap_general_state_variable_e general_state_variable(eap_general_state_authentication_error);
+
+			if (query_result == eap_status_user_cancel_authentication)
+			{
+				general_state_variable = eap_general_state_authentication_cancelled;
+			}
+
+			eap_state_notification_c notification(
+				m_am_tools,
+				&send_network_id,
+				true,
+				eap_state_notification_eap,
+				eap_protocol_layer_general,
+				m_peap_tunneled_eap_type,
+				eap_state_none,
+				general_state_variable,
+				m_received_eap_identifier,
+				false);
+
+			notification.set_authentication_error(query_result);
+
+			m_application_partner->state_notification(&notification);
+		}
+
+		{
+			eap_state_notification_c notification(
+				m_am_tools,
+				&send_network_id,
+				m_is_client,
+				eap_state_notification_eap,
+				eap_protocol_layer_eap,
+				m_peap_tunneled_eap_type,
+				eap_state_none,
+				eap_state_authentication_terminated_unsuccessfully,
+				m_received_eap_identifier,
+				true);
+			m_application_partner->state_notification(&notification);
+		}
+	}
+	else
+	{
+		if (ttls_pap_username == 0
+			|| ttls_pap_username->get_is_valid_data() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+		}
+
+		if (ttls_pap_password == 0
+			|| ttls_pap_password->get_is_valid_data() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+		}
+
+		eap_variable_data_c tunneled_data(m_am_tools);
+		eap_variable_data_c avp(m_am_tools);
+
+		{
+			status = create_ttls_diameter_avp(
+				&avp,
+				ttls_pap_username,
+				eap_diameter_avp_code_user_name,
+				false);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			status = tunneled_data.add_data(&avp);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+		}
+
+		{
+			eap_variable_data_c padded_password(m_am_tools);
+
+			status = padded_password.set_copy_of_buffer(ttls_pap_password);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			const u32_t PADDING_LENGTH_MULTIPLIER(16ul);
+
+			const u32_t padding_length((PADDING_LENGTH_MULTIPLIER - (padded_password.get_data_length() % PADDING_LENGTH_MULTIPLIER)) % PADDING_LENGTH_MULTIPLIER);
+
+			if (padding_length > 0ul)
+			{
+				const u8_t PADDING[] = {
+					0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+					0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, };
+
+				status = padded_password.add_data_to_offset(padded_password.get_data_length(), PADDING, padding_length);
+				if (status != eap_status_ok)
+				{
+					EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+					return EAP_STATUS_RETURN(m_am_tools, status);
+				}
+			}
+
+			status = create_ttls_diameter_avp(
+				&avp,
+				&padded_password,
+				eap_diameter_avp_code_user_password,
+				false);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			status = tunneled_data.add_data(&avp);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+		}
+
+		eap_buf_chain_wr_c forwarded_packet(
+			eap_write_buffer,
+			m_am_tools,
+			tunneled_data.get_data(),
+			tunneled_data.get_data_length(),
+			false,
+			false,
+			0ul);
+		if (forwarded_packet.get_is_valid() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+		}
+
+		status = get_application_partner()->packet_send(
+			&forwarded_packet,
+			0ul,
+			forwarded_packet.get_data_length(),
+			forwarded_packet.get_buffer_length());
+	}
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+
+//--------------------------------------------------
+
+
+EAP_FUNC_EXPORT eap_status_e tls_application_eap_core_c::check_ttls_plain_pap_payloads(
+	eap_diameter_payloads_c * const payloads,
+	eap_ttls_tunneled_message_type_e * const message_type)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("TTLS: %s: tls_application_eap_core_c::check_ttls_plain_pap_payloads()\n"),
+		(m_is_client == true ? "client": "server")));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: tls_application_eap_core_c::check_ttls_plain_pap_payloads()");
+
+	*message_type = eap_ttls_tunneled_message_type_none;
+
+	eap_status_e status(eap_status_not_found);
+
+	eap_array_c<eap_diameter_avp_code_c> needed_payloads(m_am_tools);
+
+	needed_payloads.reset();
+
+	if (m_is_client == false)
+	{
+		// First check are there User-Name and User-Password AVPs.
+
+		// - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+		eap_diameter_avp_code_c code_user_name(
+			eap_diameter_avp_code_user_name);
+
+		status = needed_payloads.add_object(&code_user_name, false);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		// - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+		eap_diameter_avp_code_c code_user_password(
+			eap_diameter_avp_code_user_password);
+
+		status = needed_payloads.add_object(&code_user_password, false);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+		
+		// - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+		// Test the required attribute.
+		status = payloads->check_payloads_existense(
+			&needed_payloads);
+		if (status == eap_status_ok)
+		{
+			// This packet includes required AVPs.
+
+			status = payloads->check_mandatory_payloads(
+				&needed_payloads);
+			if (status == eap_status_ok)
+			{
+				// All mandatory AVPs are included.
+
+				*message_type = eap_ttls_tunneled_message_type_pap_response;
+
+				EAP_TRACE_DEBUG(
+					m_am_tools, 
+					TRACE_FLAGS_DEFAULT, 
+					(EAPL("Match User-Name and User-Password AVPs.\n")));
+
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+		}
+	}
+	else //if (m_is_client == true)
+	{
+		// - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+		// First check is there Reply-Message AVP.
+		
+		eap_diameter_avp_code_c code_reply_message(
+			eap_diameter_avp_code_reply_message);
+
+		status = needed_payloads.add_object(&code_reply_message, false);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		// - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+		// Test the required attribute.
+		status = payloads->check_payloads_existense(
+			&needed_payloads);
+		if (status == eap_status_ok)
+		{
+			// This packet includes required AVPs.
+
+			status = payloads->check_mandatory_payloads(
+				&needed_payloads);
+			if (status == eap_status_ok)
+			{
+				// All mandatory AVPs are included.
+
+				*message_type = eap_ttls_tunneled_message_type_pap_reply_message;
+
+				EAP_TRACE_DEBUG(
+					m_am_tools, 
+					TRACE_FLAGS_DEFAULT, 
+					(EAPL("Match Reply-Message AVP.\n")));
+
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+		}
+	}
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+
+//--------------------------------------------------
+
+
+EAP_FUNC_EXPORT eap_status_e tls_application_eap_core_c::ttls_server_handles_pap_response(
+	eap_diameter_payloads_c * const /* payloads */,
+	const u8_t /* received_eap_identifier */)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("TTLS: %s: tls_application_eap_core_c::ttls_server_handles_pap_response()\n"),
+		(m_is_client == true ? "client": "server")));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: tls_application_eap_core_c::ttls_server_handles_pap_response()");
+
+	eap_status_e status(eap_status_not_found);
+
+	eap_diameter_variable_data_c * const user_name_payload
+		= m_ttls_received_payloads.get_payload(eap_diameter_avp_code_user_name);
+
+	if (user_name_payload == 0)
+	{
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_header_corrupted);
+	}
+
+	eap_diameter_variable_data_c * const user_password_payload
+		= m_ttls_received_payloads.get_payload(eap_diameter_avp_code_user_password);
+
+	if (user_password_payload == 0)
+	{
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_header_corrupted);
+	}
+
+	eap_variable_data_c * const password = user_password_payload->get_payload_buffer();
+
+	// Remove possible padding of user_password_payload.
+	while(true)
+	{
+		if (password->get_data_length() < 1ul)
+		{
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_header_corrupted);
+		}
+
+		u8_t * last_char = password->get_data_offset(password->get_data_length()-1ul, 1ul) ;
+
+		if (last_char != 0
+			&& *last_char == 0x00)
+		{
+			status = password->set_data_length(password->get_data_length()-1ul);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+		}
+		else
+		{
+			break;
+		}
+	}
+
+	status = m_application_partner->verify_ttls_pap_username_and_password(
+		user_name_payload->get_payload_buffer(),
+		password);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+
+//--------------------------------------------------
+
+
+EAP_FUNC_EXPORT eap_status_e tls_application_eap_core_c::ttls_server_handles_pap_reply_message(
+	eap_diameter_payloads_c * const /* payloads */,
+	const u8_t /* received_eap_identifier */)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("TTLS: %s: tls_application_eap_core_c::ttls_server_handles_pap_reply_message()\n"),
+		(m_is_client == true ? "client": "server")));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: tls_application_eap_core_c::ttls_server_handles_pap_reply_message()");
+
+	eap_status_e status(eap_status_not_found);
+
+	eap_diameter_variable_data_c * const reply_message_payload
+		= m_ttls_received_payloads.get_payload(eap_diameter_avp_code_reply_message);
+
+	if (reply_message_payload == 0)
+	{
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_header_corrupted);
+	}
+
+	status = m_application_partner->query_ttls_pap_username_and_password(
+		reply_message_payload->get_payload_buffer());
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+
+//--------------------------------------------------
+
+
+EAP_FUNC_EXPORT eap_status_e tls_application_eap_core_c::handle_ttls_plain_pap_payloads(
+	eap_diameter_payloads_c * const payloads,
+	const eap_ttls_tunneled_message_type_e message_type,
+	const u8_t received_eap_identifier)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("TTLS: %s: tls_application_eap_core_c::handle_ttls_plain_pap_payloads()\n"),
+		(m_is_client == true ? "client": "server")));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: tls_application_eap_core_c::handle_ttls_plain_pap_payloads()");
+
+	eap_status_e status(eap_status_not_found);
+
+	switch(message_type)
+	{
+	case eap_ttls_tunneled_message_type_pap_response:
+		// Here are included User-Name, User-Password AVPs.
+		status = ttls_server_handles_pap_response(payloads, received_eap_identifier);
+		break;
+	case eap_ttls_tunneled_message_type_pap_reply_message:
+		// Here are included Reply-Message AVP.
+		status = ttls_server_handles_pap_reply_message(payloads, received_eap_identifier);
+		break;
+	default:
+		status = eap_status_unexpected_message;
+		break;
+	}
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+
+//--------------------------------------------------
+
+
+EAP_FUNC_EXPORT eap_status_e tls_application_eap_core_c::complete_verify_ttls_pap_username_and_password(
+	const eap_status_e authentication_result,
+	const eap_variable_data_c * const ttls_pap_reply_message)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("TTLS: %s: tls_application_eap_core_c::complete_verify_ttls_pap_username_and_password()\n"),
+		(m_is_client == true ? "client": "server")));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: tls_application_eap_core_c::complete_verify_ttls_pap_username_and_password()");
+
+	eap_status_e status(eap_status_not_found);
+
+	// Here we swap the addresses.
+	eap_am_network_id_c send_network_id(
+		m_am_tools,
+		m_receive_network_id.get_destination_id(),
+		m_receive_network_id.get_source_id(),
+		m_receive_network_id.get_type());
+
+	status = authentication_result;
+
+	if (authentication_result == eap_status_ok)
+	{
+		eap_state_notification_c notification(
+			m_am_tools,
+			&send_network_id,
+			m_is_client,
+			eap_state_notification_eap,
+			eap_protocol_layer_eap,
+			m_peap_tunneled_eap_type,
+			eap_state_none,
+			eap_state_authentication_finished_successfully,
+			m_received_eap_identifier,
+			true);
+		m_application_partner->state_notification(&notification);
+	}
+	else
+	{
+		if (ttls_pap_reply_message != 0
+			&& ttls_pap_reply_message->get_is_valid_data() == true)
+		{
+			eap_variable_data_c tunneled_data(m_am_tools);
+			eap_variable_data_c avp(m_am_tools);
+
+			{
+				status = create_ttls_diameter_avp(
+					&avp,
+					ttls_pap_reply_message,
+					eap_diameter_avp_code_reply_message,
+					false);
+				if (status != eap_status_ok)
+				{
+					EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+					return EAP_STATUS_RETURN(m_am_tools, status);
+				}
+
+				status = tunneled_data.add_data(&avp);
+				if (status != eap_status_ok)
+				{
+					EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+					return EAP_STATUS_RETURN(m_am_tools, status);
+				}
+			}
+
+			eap_buf_chain_wr_c forwarded_packet(
+				eap_write_buffer,
+				m_am_tools,
+				tunneled_data.get_data(),
+				tunneled_data.get_data_length(),
+				false,
+				false,
+				0ul);
+			if (forwarded_packet.get_is_valid() == false)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+			}
+
+			status = get_application_partner()->packet_send(
+				&forwarded_packet,
+				0ul,
+				forwarded_packet.get_data_length(),
+				forwarded_packet.get_buffer_length());
+		}
+		else
+		{
+			eap_state_notification_c notification(
+				m_am_tools,
+				&send_network_id,
+				m_is_client,
+				eap_state_notification_eap,
+				eap_protocol_layer_eap,
+				m_peap_tunneled_eap_type,
+				eap_state_none,
+				eap_state_authentication_terminated_unsuccessfully,
+				m_received_eap_identifier,
+				true);
+			m_application_partner->state_notification(&notification);
+		}
+	}
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e tls_application_eap_core_c::alert_received(
+	const tls_alert_level_e alert_level,
+	const tls_alert_description_e alert_description)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_UNREFERENCED_PARAMETER(alert_level);
+	EAP_UNREFERENCED_PARAMETER(alert_description);
+
+	eap_tls_trace_string_c tls_trace;
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("ERROR: %s: tls_application_eap_core_c::alert_received(), level %d=%s, description %d=%s\n"),
+		 (m_is_client == true ? "client": "server"),
+		 alert_level,
+		 tls_trace.get_alert_level_string(alert_level),
+		 alert_description,
+		 tls_trace.get_alert_description_string(alert_description)));
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, eap_status_ok);
+}
+
+//--------------------------------------------------
+
+// End.