eapol/eapol_framework/wapi_common/src/wapi_core.cpp
changeset 17 8840d3e38314
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/eapol/eapol_framework/wapi_common/src/wapi_core.cpp	Fri Mar 19 09:29:58 2010 +0200
@@ -0,0 +1,12821 @@
+/*
+* ============================================================================
+*  Name        : ./accesssec/eapol/eapol_framework/wapi_common/src/wapi_core.cpp
+*  Part of     : WAPI / WAPI       *** Info from the SWAD
+*  Description : WAPI authentication
+*  Version     : %version: 131.1.4 % << Don't touch! Updated by Synergy at check-out.
+*
+*  Copyright © 2001-2009 Nokia.  All rights reserved.
+*  This material, including documentation and any related computer
+*  programs, is protected by copyright controlled by Nokia.  All
+*  rights are reserved.  Copying, including reproducing, storing,
+*  adapting or translating, any or all of this material requires the
+*  prior written consent of Nokia.  This material also contains
+*  confidential information which may not be disclosed to others
+*  without the prior written consent of Nokia.
+* ============================================================================
+* Template version: 4.1.1
+*/
+
+// This is enumeration of WAPI source code.
+#if defined(USE_EAP_MINIMUM_RELEASE_TRACES)
+	#undef EAP_FILE_NUMBER_ENUM
+	#define EAP_FILE_NUMBER_ENUM 712 
+	#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 "abs_wapi_core.h"
+#include "abs_eap_am_mutex.h"
+#include "wapi_core.h"
+#include "eap_state_notification.h"
+#include "eap_network_id_selector.h"
+#include "eap_buffer.h"
+#include "eap_automatic_variable.h"
+#include "wapi_core_retransmission.h"
+#include "wai_protocol_packet_header.h"
+#include "wapi_strings.h"
+#include "eap_crypto_api.h"
+#include "eap_automatic_variable.h"
+#include "eapol_session_key.h"
+#include "wapi_am_crypto_sms4.h"
+#include "asn1_der_type.h"
+#include "wapi_asn1_der_parser.h"
+#include "wapi_am_base_core.h"
+
+//#define WAPI_SKIP_BKID_TEST // This is for testing.
+
+//--------------------------------------------------
+
+// 
+EAP_FUNC_EXPORT wapi_core_c::~wapi_core_c()
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("wapi_core_c::~wapi_core_c(): %s, this = 0x%08x => 0x%08x.\n"),
+		(m_is_client == true) ? "client": "server",
+		this,
+		dynamic_cast<abs_eap_base_timer_c *>(this)));
+
+	EAP_ASSERT(m_shutdown_was_called == true);
+
+	{
+		for (u32_t ind = 0ul; ind < WAPI_USKSA_COUNT; ++ind)
+		{
+			delete m_USKSA[ind];
+			m_USKSA[ind] = 0;
+		} // for()
+	}
+
+	{
+		for (u32_t ind = 0ul; ind < WAPI_MSKSA_COUNT; ++ind)
+		{
+			delete m_MSKSA[ind];
+			m_MSKSA[ind] = 0;
+		} // for()
+	}
+
+	delete m_ec_certificate_store;
+	m_ec_certificate_store = 0;
+
+	delete m_am_wapi_core;
+	m_am_wapi_core = 0;
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+}
+
+//--------------------------------------------------
+
+#if defined(_WIN32) && !defined(__GNUC__)
+	#pragma warning( disable : 4355 ) // 'this' : used in base member initializer list
+#endif
+
+// 
+EAP_FUNC_EXPORT wapi_core_c::wapi_core_c(
+	abs_eap_am_tools_c * const tools,
+	abs_wapi_core_c * const partner,
+	const bool is_client_when_true,
+	const eap_am_network_id_c * const receive_network_id)
+	: m_partner(partner)
+	, m_ec_certificate_store(0)
+	, m_am_wapi_core(0)
+	, m_am_tools(tools)
+	, m_wapi_header_offset(0u)
+	, m_MTU(0u)
+	, m_trailer_length(0u)
+	, m_receive_network_id(tools)
+	, m_retransmission(0)
+	, m_retransmission_time(WAPI_CORE_RETRANSMISSION_TIME)
+	, m_retransmission_counter(WAPI_CORE_RETRANSMISSION_COUNTER)
+	, m_session_timeout(WAPI_CORE_SESSION_TIMEOUT)
+	, m_wapi_core_failure_received_timeout(WAPI_CORE_FAILURE_RECEIVED_TIMEOUT)
+	, m_remove_session_timeout(WAPI_CORE_REMOVE_SESSION_TIMEOUT)
+	, m_wapi_state(wapi_core_state_none)
+	, m_received_wai_message_data(tools, is_client_when_true)
+	, m_new_payloads(tools, is_client_when_true)
+	, m_preshared_key_PSK(tools)
+	, m_BK(tools)
+	, m_BKID(tools)
+	, m_USKID(0u)
+	, m_MSKID(0u)
+	, m_ae_certificate_challenge(tools)
+	, m_asue_certificate_challenge(tools)
+	, m_ae_unicast_challenge(tools)
+	, m_asue_unicast_challenge(tools)
+	, m_authentication_identifier(tools)
+	, m_asue_id(tools)
+	, m_asu_id(tools)
+	, m_ae_id(tools)
+	, m_test_other_asu_id(tools)
+	, m_own_certificate(tools)
+	, m_peer_certificate(tools)
+	, m_ae_certificate(tools)
+	, m_wapi_ie_asue(tools)
+	, m_wapi_ie_ae(tools)
+	, m_unicast_encryption_key_UEK(tools)
+	, m_unicast_integrity_check_key_UCK(tools)
+	, m_message_authentication_key_MAK(tools)
+	, m_key_encryption_key_KEK(tools)
+	, m_next_unicast_challenge(tools)
+	, m_multicast_key(tools)
+	, m_packet_data_number(tools)
+	, m_key_announcement(tools)
+	, m_own_private_key_d(tools)
+	, m_own_public_key_x(tools)
+	, m_own_public_key_y(tools)
+	, m_peer_public_key_x(tools)
+	, m_peer_public_key_y(tools)
+	, m_result_of_certificate_verification(tools)
+	, m_server_signature_trusted_by_asue(tools)
+	, m_server_signature_trusted_by_ae(tools)
+	, m_reassemble_packet(tools)
+	, m_authentication_type(eapol_key_authentication_type_none)
+	, m_wapi_negotiation_state(wapi_negotiation_state_none)
+	, m_wapi_pairwise_cipher(eapol_RSNA_key_header_c::eapol_RSNA_cipher_none)
+	, m_wapi_group_cipher(eapol_RSNA_key_header_c::eapol_RSNA_cipher_none)
+	, m_packet_sequence_number(0u)
+	, m_fragment_sequence_number(0u)
+	, m_is_client(is_client_when_true)
+	, m_is_client_role(is_client_when_true)
+	, m_is_valid(false)
+	, m_client_restart_authentication_initiated(false)
+	, m_marked_removed(false)
+	, m_shutdown_was_called(false)
+	, m_do_certificate_validation(false)
+#if defined(USE_WAPI_CORE_SERVER)
+	, m_only_initial_authentication(false)
+#endif //#if defined(USE_WAPI_CORE_SERVER)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("wapi_core_c::wapi_core_c(): %s, this = 0x%08x => 0x%08x, compiled %s %s.\n"),
+		(m_is_client == true) ? "client": "server",
+		this,
+		dynamic_cast<abs_eap_base_timer_c *>(this),
+		__DATE__,
+		__TIME__));
+
+	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;
+	}
+
+	{
+		for (u32_t ind = 0ul; ind < WAPI_USKSA_COUNT; ++ind)
+		{
+			m_USKSA[ind] = 0;
+		} // for()
+	}
+
+	{
+		for (u32_t ind = 0ul; ind < WAPI_MSKSA_COUNT; ++ind)
+		{
+			m_MSKSA[ind] = 0;
+		}
+	}
+
+	{
+		for (u32_t ind = 0ul; ind < WAPI_USKSA_COUNT; ++ind)
+		{
+			m_USKSA[ind] = 0;
+
+			wai_usksa_c * const usksa = new wai_usksa_c(m_am_tools);
+			if (usksa == 0
+				|| usksa->get_is_valid() == false)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				(void) EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+				return;
+			}
+
+			m_USKSA[ind] = usksa;
+
+		} // for()
+	}
+
+	{
+		for (u32_t ind = 0ul; ind < WAPI_MSKSA_COUNT; ++ind)
+		{
+			m_MSKSA[ind] = 0;
+
+			wai_usksa_c * const msksa = new wai_usksa_c(m_am_tools);
+			if (msksa == 0
+				|| msksa->get_is_valid() == false)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				(void) EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+				return;
+			}
+
+			m_MSKSA[ind] = msksa;
+
+		} // for()
+	}
+
+	m_am_wapi_core = wapi_am_base_core_c::new_wapi_am_core(
+		tools,
+		this,
+		is_client_when_true,
+		&m_receive_network_id);
+	if (m_am_wapi_core == 0
+		|| m_am_wapi_core->get_is_valid() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		(void) EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+		return;
+	}
+
+	m_ec_certificate_store = ec_base_certificate_store_c::new_ec_base_certificate_store_c(
+		tools,
+		this,
+		m_am_wapi_core,
+		is_client_when_true);
+	if (m_ec_certificate_store == 0
+		|| m_ec_certificate_store->get_is_valid() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		(void) EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+		return;
+	}
+
+	status = m_ec_certificate_store->set_receive_network_id(&m_receive_network_id);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		(void) EAP_STATUS_RETURN(m_am_tools, status);
+		return;
+	}
+
+	set_is_valid();
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e wapi_core_c::initialize(
+	const eap_am_network_id_c * const receive_network_id,
+	const eapol_key_authentication_type_e authentication_type)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	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 EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	m_authentication_type = authentication_type;
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e wapi_core_c::initialize(
+	const eap_am_network_id_c * const receive_network_id,
+	const eapol_key_authentication_type_e authentication_type,
+	const eap_variable_data_c * const wapi_ie_ae,
+	const eap_variable_data_c * const wapi_ie_asue,
+	const eapol_RSNA_key_header_c::eapol_RSNA_cipher_e wapi_pairwise_cipher,
+	const eapol_RSNA_key_header_c::eapol_RSNA_cipher_e wapi_group_cipher)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	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 EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	m_authentication_type = authentication_type;
+
+	status = m_wapi_ie_ae.set_copy_of_buffer(wapi_ie_ae);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = m_wapi_ie_asue.set_copy_of_buffer(wapi_ie_asue);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	m_wapi_pairwise_cipher = wapi_pairwise_cipher;
+	m_wapi_group_cipher = wapi_group_cipher;
+	
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+//
+void wapi_core_c::set_wapi_state(wapi_core_state_e wapi_state)
+{
+	EAP_TRACE_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("WAI: %s: wapi_core_c::set_wapi_state(): State from %s to %s, %s.\n"),
+		 (m_is_client == true) ? "client": "server",
+		 wapi_strings_c::get_wapi_core_state_string(m_wapi_state),
+		 wapi_strings_c::get_wapi_core_state_string(wapi_state),
+		 wapi_strings_c::get_wapi_negotiation_state_string(m_wapi_negotiation_state)));
+
+	m_wapi_state = wapi_state;
+}
+
+//--------------------------------------------------
+
+//
+EAP_FUNC_EXPORT abs_wapi_core_c * wapi_core_c::get_partner()
+{
+	EAP_ASSERT(m_am_tools->get_global_mutex()->get_is_reserved() == true);
+
+	return m_partner;
+}
+
+//--------------------------------------------------
+
+//
+EAP_FUNC_EXPORT void wapi_core_c::set_partner(abs_wapi_core_c * const partner)
+{
+	EAP_ASSERT(m_am_tools->get_global_mutex()->get_is_reserved() == true);
+
+	m_partner = partner;
+}
+
+//--------------------------------------------------
+
+//
+EAP_FUNC_EXPORT void wapi_core_c::set_is_valid()
+{
+	m_is_valid = true;
+}
+
+//--------------------------------------------------
+
+//
+EAP_FUNC_EXPORT bool wapi_core_c::get_is_valid()
+{
+	return m_is_valid;
+}
+
+//--------------------------------------------------
+
+//
+EAP_FUNC_EXPORT void wapi_core_c::object_increase_reference_count()
+{
+	// This is an empty function to implement here unused interface function.
+}
+
+//--------------------------------------------------
+
+//
+EAP_FUNC_EXPORT u32_t wapi_core_c::object_decrease_reference_count()
+{
+	return 0u;
+}
+
+//--------------------------------------------------
+
+//
+EAP_FUNC_EXPORT bool wapi_core_c::get_marked_removed()
+{
+	return m_marked_removed;
+}
+
+//--------------------------------------------------
+
+//
+EAP_FUNC_EXPORT void wapi_core_c::set_marked_removed()
+{
+	m_marked_removed = true;
+}
+
+//--------------------------------------------------
+
+//
+EAP_FUNC_EXPORT void wapi_core_c::unset_marked_removed()
+{
+	m_marked_removed = false;
+}
+
+//--------------------------------------------------
+
+//
+eap_status_e wapi_core_c::initialize_asynchronous_init_remove_wapi_session(
+	const u32_t remove_session_timeout)
+{
+	EAP_TRACE_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("wapi_core_c::initialize_asynchronous_init_remove_wapi_session(): %s.\n"),
+		 (m_is_client == true) ? "client": "server"));
+
+	eap_status_e status = eap_status_process_general_error;
+
+
+	if (m_is_client_role == false)
+	{
+		// Server stops re-transmissions.
+		// Client can re-transmit until session is removed.
+		cancel_retransmission();
+	}
+
+	cancel_wapi_failure_timeout();
+
+	cancel_session_timeout();
+
+	set_marked_removed();
+
+
+	if (remove_session_timeout == 0ul)
+	{
+		status = asynchronous_init_remove_wapi_session();
+	}
+	else
+	{
+		EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+		cancel_asynchronous_init_remove_wapi_session();
+
+		status = m_partner->set_timer(
+			this,
+			WAPI_CORE_REMOVE_SESSION_TIMEOUT_ID,
+			0,
+			remove_session_timeout);
+
+		if (status != eap_status_ok)
+		{
+			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("TIMER: %s: WAPI_CORE_REMOVE_SESSION_TIMEOUT_ID set %d ms, this = 0x%08x.\n"),
+			 (m_is_client == true) ? "client": "server",
+			 remove_session_timeout,
+			 this));
+	}
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+eap_status_e wapi_core_c::cancel_asynchronous_init_remove_wapi_session()
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	eap_status_e status = m_partner->cancel_timer(
+		this,
+		WAPI_CORE_REMOVE_SESSION_TIMEOUT_ID);
+
+	EAP_UNREFERENCED_PARAMETER(status); // in release
+	
+	EAP_TRACE_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("TIMER: %s: WAPI_CORE_REMOVE_SESSION_TIMEOUT_ID cancelled status %d, this = 0x%08x.\n"),
+		 (m_is_client == true ? "client": "server"),
+		 status,
+		 this));
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, eap_status_ok);
+}
+
+//--------------------------------------------------
+
+//
+eap_status_e wapi_core_c::asynchronous_init_remove_wapi_session()
+{
+	EAP_TRACE_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("wapi_core_c::asynchronous_init_remove_wapi_session(): %s.\n"),
+		 (m_is_client == true) ? "client": "server"));
+
+		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_status_e status = m_partner->asynchronous_init_remove_wapi_session(
+			&send_network_id);
+
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+//
+eap_status_e wapi_core_c::init_end_of_session(
+	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("wapi_core_c::init_end_of_session(): %s.\n"),
+		 (m_is_client == true) ? "client": "server"));
+
+	eap_status_e status(eap_status_process_general_error);
+
+	// Normally we will remove session after authentication ends.
+	// Remove session only if the stack is not already being deleted
+	if (m_shutdown_was_called == false)
+	{
+
+		#if defined(USE_WAPI_CORE_SIMULATOR_VERSION) && defined(USE_WAPI_CORE_RESTART_AUTHENTICATION)
+
+			// Simulator reuses current session.
+			status = restart_authentication(
+				state->get_send_network_id(),
+				m_is_client);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+		#elif defined(USE_WAPI_CORE_SIMULATOR_VERSION)
+
+			EAP_TRACE_DEBUG(
+				m_am_tools,
+				TRACE_FLAGS_DEFAULT,
+				(EAPL("wapi_core_c::state_notification(): %s, %s, Ignored notification: ")
+				 EAPL("Protocol layer %d, State transition from ")
+				 EAPL("%d=%s to %d=%s, client %d.\n"),
+				 (m_is_client == true) ? "client": "server",
+				 (m_is_tunneled_eap == true) ? "tunneled": "outer most",
+				 state->get_protocol_layer(), 
+				 state->get_previous_state(), 
+				 state->get_previous_state_string(), 
+				 state->get_current_state(), 
+				 state->get_current_state_string(),
+				 state->get_is_client()));
+
+		#endif //#if defined(USE_WAPI_CORE_SIMULATOR_VERSION)
+
+		status = initialize_asynchronous_init_remove_wapi_session(m_remove_session_timeout);
+		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_DEBUG(
+			m_am_tools,
+			TRACE_FLAGS_DEFAULT,
+			(EAPL("wapi_core_c::state_notification(): %s, Ignored notification: ")
+			 EAPL("Protocol layer %d, State transition from ")
+			 EAPL("%d=%s to %d=%s, client %d when shutdown was called.\n"),
+			 (m_is_client == true) ? "client": "server",
+			 state->get_protocol_layer(), 
+			 state->get_previous_state(), 
+			 state->get_previous_state_string(), 
+			 state->get_current_state(), 
+			 state->get_current_state_string(),
+			 state->get_is_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 void wapi_core_c::state_notification(
+	const abs_eap_state_notification_c * const state)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	eap_status_string_c status_string;
+	EAP_UNREFERENCED_PARAMETER(status_string); // in release
+
+	EAP_TRACE_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("wapi_core_c::state_notification(), %s, protocol_layer %d=%s, protocol %d=%s.\n"),
+		(m_is_client == true) ? "client": "server",
+		state->get_protocol_layer(),
+		state->get_protocol_layer_string(),
+		state->get_protocol(),
+		state->get_protocol_string()));
+
+	EAP_TRACE_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("wapi_core_c::state_notification(), %s, current_state %d=%s, error %d=%s.\n"),
+		(m_is_client == true) ? "client": "server",
+		state->get_current_state(),
+		state->get_current_state_string(),
+		state->get_authentication_error(),
+		status_string.get_status_string(state->get_authentication_error())));
+
+	m_partner->state_notification(state);
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+}
+
+//--------------------------------------------------
+
+//
+EAP_FUNC_EXPORT eap_status_e wapi_core_c::initialize_session_timeout(const u32_t session_timeout_ms)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	cancel_session_timeout();
+
+	eap_status_e status = m_partner->set_timer(
+		this,
+		WAPI_CORE_SESSION_TIMEOUT_ID,
+		0,
+		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_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("TIMER: %s: WAPI_CORE_SESSION_TIMEOUT_ID set %d ms, this = 0x%08x.\n"),
+		 (m_is_client == true) ? "client": "server",
+		 session_timeout_ms,
+		 this));
+
+	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 wapi_core_c::cancel_session_timeout()
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	eap_status_e status = m_partner->cancel_timer(
+		this,
+		WAPI_CORE_SESSION_TIMEOUT_ID);
+	
+	EAP_UNREFERENCED_PARAMETER(status); // in release
+	
+	EAP_TRACE_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("TIMER: %s: WAPI_CORE_SESSION_TIMEOUT_ID cancelled status %d, this = 0x%08x.\n"),
+		 (m_is_client == true ? "client": "server"),
+		 status,
+		 this));
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, eap_status_ok);
+}
+
+//--------------------------------------------------
+
+eap_status_e wapi_core_c::create_BKID(
+	eap_variable_data_c * const BKID,
+	const eap_am_network_id_c * const receive_network_id)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("WAPI_Core: this = 0x%08x, %s: wapi_core_c::create_BKID(): state=%s\n"),
+		 this,
+		 (m_is_client == true ? "client": "server"),
+		 wapi_strings_c::get_wapi_core_state_string(m_wapi_state)));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: wapi_core_c::create_BKID()");
+
+	if (BKID == 0
+		|| BKID->get_is_valid() == false
+		|| receive_network_id == 0
+		|| receive_network_id->get_is_valid() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+	}
+
+	eap_status_e status(eap_status_process_general_error);
+
+	// BKID = KD_HMAC_SHA256(BK, MACAE || MACASUE)
+
+	crypto_kd_hmac_sha256_c kd_hmac(m_am_tools);
+	if (kd_hmac.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 label(m_am_tools);
+	if (label.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 eap_variable_data_c * MAC_1 = receive_network_id->get_destination_id();
+	const eap_variable_data_c * MAC_2 = receive_network_id->get_source_id();
+
+	if (m_is_client == true)
+	{
+		MAC_1 = receive_network_id->get_source_id();
+		MAC_2 = receive_network_id->get_destination_id();
+	}
+
+	status = label.set_copy_of_buffer(MAC_1);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = label.add_data(MAC_2);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = kd_hmac.expand_key(
+		BKID,
+		WAPI_BKID_LENGTH,
+		&m_BK,
+		&label);
+	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_status_e wapi_core_c::packet_data_session_key(
+	eap_variable_data_c * const key, ///< Here is the key.
+	const eapol_key_type_e key_type, ///< This the type of the key.
+	const u32_t key_index, ///< This is the index of the key.
+	const bool key_tx_bit, ///< This is the TX bit of the key.
+	const u8_t * const key_RSC, ///< This is the RSC counter
+	const u32_t key_RSC_size ///< This is the size of RSC counter
+	)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	eapol_session_key_c * eapol_session_key = new eapol_session_key_c(
+		m_am_tools,
+		key,
+		key_type,
+		key_index,
+		key_tx_bit,
+		key_RSC,
+		key_RSC_size
+		);
+	if (eapol_session_key == 0)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	if (eapol_session_key->get_is_valid() == false)
+	{
+		delete eapol_session_key;
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	// 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 (send_network_id.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);
+	}
+
+	eap_status_e status = m_partner->packet_data_session_key(
+		&send_network_id,
+		eapol_session_key);
+
+	delete eapol_session_key;
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+eap_status_e wapi_core_c::create_unicast_key(
+	const eap_variable_data_c * const BK,
+	const eap_am_network_id_c * const receive_network_id,
+	const eap_variable_data_c * const ae_challenge,
+	const eap_variable_data_c * const asue_challenge,
+	eap_variable_data_c * const unicast_encryption_key_UEK,
+	eap_variable_data_c * const unicast_integrity_check_key_UCK,
+	eap_variable_data_c * const message_authentication_key_MAK,
+	eap_variable_data_c * const key_encryption_key_KEK,
+	eap_variable_data_c * const challenge_seed)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("WAPI_Core: this = 0x%08x, %s: wapi_core_c::create_unicast_key(): state=%s\n"),
+		 this,
+		 (m_is_client == true ? "client": "server"),
+		 wapi_strings_c::get_wapi_core_state_string(m_wapi_state)));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: wapi_core_c::create_unicast_key()");
+
+	if (BK == 0
+		|| BK->get_is_valid_data() == false
+		|| receive_network_id == 0
+		|| receive_network_id->get_is_valid() == false
+		|| ae_challenge == 0
+		|| ae_challenge->get_is_valid_data() == false
+		|| asue_challenge == 0
+		|| asue_challenge->get_is_valid_data() == false
+		|| unicast_encryption_key_UEK == 0
+		|| unicast_encryption_key_UEK->get_is_valid() == false
+		|| unicast_integrity_check_key_UCK == 0
+		|| unicast_integrity_check_key_UCK->get_is_valid() == false
+		|| message_authentication_key_MAK == 0
+		|| message_authentication_key_MAK->get_is_valid() == false
+		|| key_encryption_key_KEK == 0
+		|| key_encryption_key_KEK->get_is_valid() == false
+		|| challenge_seed == 0
+		|| challenge_seed->get_is_valid() == false
+		)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+	}
+
+	eap_status_e status(eap_status_process_general_error);
+
+	// Output (96) = KD-HMAC-SHA256(BK, ADDID||N_AE||N_ASUE||Label, Length);
+
+	crypto_kd_hmac_sha256_c kd_hmac(m_am_tools);
+	if (kd_hmac.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 label(m_am_tools);
+	if (label.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 eap_variable_data_c * MAC_1 = receive_network_id->get_destination_id();
+	const eap_variable_data_c * MAC_2 = receive_network_id->get_source_id();
+
+	if (m_is_client == true)
+	{
+		MAC_1 = receive_network_id->get_source_id();
+		MAC_2 = receive_network_id->get_destination_id();
+	}
+
+	status = label.set_copy_of_buffer(MAC_1);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = label.add_data(MAC_2);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = label.add_data(ae_challenge);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = label.add_data(asue_challenge);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = label.add_data(WAPI_UNICAST_KEY_LABEL, WAPI_UNICAST_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);
+	}
+
+	eap_variable_data_c unicast_key(m_am_tools);
+	if (unicast_key.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 = kd_hmac.expand_key(
+		&unicast_key,
+		WAPI_UNICAST_KEY_LENGTH,
+		BK,
+		&label);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	// {unicast_encryption_key_UEK (16)
+	//  || unicast_integrity_check_key_UCK (16)
+	//  || message_authentication_key_MAK (16)
+	//  || key_encryption_key_KEK (16)
+	//  || Challenge seed (32)}
+	// = Output (96)
+
+	u32_t offset(0ul);
+	u32_t required_data_length(WAPI_UNICAST_ENCRYPTION_KEY_UEK_LENGTH);
+
+	status = unicast_encryption_key_UEK->set_copy_of_buffer(
+		unicast_key.get_data_offset(offset, required_data_length),
+		required_data_length);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	offset += required_data_length;
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	required_data_length = WAPI_UNICAST_INTEGRITY_CHECK_KEY_UCK_LENGTH;
+
+	status = unicast_integrity_check_key_UCK->set_copy_of_buffer(
+		unicast_key.get_data_offset(offset, required_data_length),
+		required_data_length);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	offset += required_data_length;
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	required_data_length = WAPI_MESSAGE_AUTHENTICATION_KEY_MAK_LENGTH;
+
+	status = message_authentication_key_MAK->set_copy_of_buffer(
+		unicast_key.get_data_offset(offset, required_data_length),
+		required_data_length);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	offset += required_data_length;
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	required_data_length = WAPI_KEY_ENCRYPTION_KEY_KEK_LENGTH;
+
+	status = key_encryption_key_KEK->set_copy_of_buffer(
+		unicast_key.get_data_offset(offset, required_data_length),
+		required_data_length);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	offset += required_data_length;
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	required_data_length = WAPI_CHALLENGE_SEED_LENGTH;
+
+	{
+		eap_variable_data_c next_challenge(m_am_tools);
+		if (next_challenge.get_is_valid() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+		}
+
+		crypto_sha_256_c sha_256(m_am_tools);
+		if (sha_256.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 = sha_256.hash_init();
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = sha_256.hash_update(
+			unicast_key.get_data_offset(offset, required_data_length),
+			required_data_length);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		u32_t md_length(sha_256.get_digest_length());
+
+		status = challenge_seed->set_buffer_length(md_length);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = challenge_seed->set_data_length(md_length);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = sha_256.hash_final(
+			challenge_seed->get_data(),
+			&md_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("next challenge_seed"),
+			 challenge_seed->get_data(),
+			 challenge_seed->get_data_length()));
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+eap_status_e wapi_core_c::create_MAC(
+	const wai_message_payloads_c * const payloads,
+	eap_variable_data_c * const MAC)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("WAPI_Core: this = 0x%08x, %s: wapi_core_c::create_MAC(): state=%s\n"),
+		 this,
+		 (m_is_client == true ? "client": "server"),
+		 wapi_strings_c::get_wapi_core_state_string(m_wapi_state)));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: wapi_core_c::create_MAC()");
+
+	if (payloads == 0
+		|| payloads->get_is_valid() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+	}
+
+	if (MAC == 0
+		|| MAC->get_is_valid() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+	}
+
+	eap_status_e status(eap_status_process_general_error);
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	crypto_sha_256_c sha_256(m_am_tools);
+	if (sha_256.get_is_valid() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	crypto_hmac_c hmac_sha_256(
+		m_am_tools,
+		&sha_256,
+		false);
+	if (hmac_sha_256.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_TRACE_DATA_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("WAPI_Core: m_message_authentication_key_MAK"),
+		 m_message_authentication_key_MAK.get_data(),
+		 m_message_authentication_key_MAK.get_data_length()));
+
+	status = hmac_sha_256.hmac_set_key(&m_message_authentication_key_MAK);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	for (u32_t tlv_index = 0ul; tlv_index < payloads->get_tlv_count(); ++tlv_index)
+	{
+		const wai_variable_data_c * tlv = payloads->get_tlv(tlv_index);
+		if (tlv == 0)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+		}
+
+		if (tlv->get_payload_type() != wai_payload_type_message_authentication_code)
+		{
+			EAP_TRACE_DATA_DEBUG(
+				m_am_tools, 
+				TRACE_FLAGS_DEFAULT, 
+				(EAPL("WAPI_Core: MAC: input data"),
+				tlv->get_data(tlv->get_data_length()),
+				tlv->get_data_length()));
+
+			status = hmac_sha_256.hmac_update(
+				tlv->get_data(tlv->get_data_length()),
+				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);
+			}
+		}
+	} // for()
+
+	status = MAC->set_buffer_length(hmac_sha_256.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);
+	}
+
+	status = MAC->set_data_length(hmac_sha_256.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);
+	}
+
+	u32_t md_length(hmac_sha_256.get_digest_length());
+	
+	status = hmac_sha_256.hmac_final(
+		MAC->get_data(hmac_sha_256.get_digest_length()),
+		&md_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->set_data_length(WAPI_MESSAGE_AUTHENTICATION_CODE_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("WAPI_Core: MAC"),
+		 MAC->get_data(),
+		 MAC->get_data_length()));
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+eap_status_e wapi_core_c::create_HASH(
+	const wai_message_payloads_c * const payloads,
+	const bool hash_all_payloads,
+	eap_variable_data_c * const HASH)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("WAPI_Core: this = 0x%08x, %s: wapi_core_c::create_HASH(): state=%s\n"),
+		 this,
+		 (m_is_client == true ? "client": "server"),
+		 wapi_strings_c::get_wapi_core_state_string(m_wapi_state)));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: wapi_core_c::create_HASH()");
+
+	if (payloads == 0
+		|| payloads->get_is_valid() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+	}
+
+	if (HASH == 0
+		|| HASH->get_is_valid() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+	}
+
+	eap_status_e status(eap_status_process_general_error);
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	crypto_sha_256_c sha_256(m_am_tools);
+	if (sha_256.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 = sha_256.hash_init();
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	for (u32_t tlv_index = 0ul; tlv_index < payloads->get_tlv_count(); ++tlv_index)
+	{
+		const wai_variable_data_c * tlv = payloads->get_tlv(tlv_index);
+		if (tlv == 0)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+		}
+
+		if (hash_all_payloads == true
+			|| (tlv->get_payload_type() != wai_payload_type_message_authentication_code
+				&& tlv->get_payload_type() != wai_payload_type_signature_attributes))
+		{
+			EAP_TRACE_DATA_DEBUG(
+				m_am_tools, 
+				TRACE_FLAGS_DEFAULT, 
+				(EAPL("WAPI_Core: HASH: input data"),
+				tlv->get_data(tlv->get_data_length()),
+				tlv->get_data_length()));
+
+			status = sha_256.hash_update(
+				tlv->get_data(tlv->get_data_length()),
+				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);
+			}
+		}
+	} // for()
+
+	status = HASH->set_buffer_length(sha_256.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);
+	}
+
+	status = HASH->set_data_length(sha_256.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);
+	}
+
+	u32_t md_length(sha_256.get_digest_length());
+	
+	status = sha_256.hash_final(
+		HASH->get_data(sha_256.get_digest_length()),
+		&md_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("WAPI_Core: HASH"),
+		 HASH->get_data(),
+		 HASH->get_data_length()));
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+#if defined(USE_WAPI_CORE_SERVER)
+
+eap_status_e wapi_core_c::encrypt_multicast_key_data(
+	const eap_variable_data_c * const multicast_key,
+	const eap_variable_data_c * const key_announcement,
+	wai_variable_data_c * const key_data)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("TODO: WAPI_Core: this = 0x%08x, %s: wapi_core_c::encrypt_multicast_key_data(): state=%s\n"),
+		 this,
+		 (m_is_client == true ? "client": "server"),
+		 wapi_strings_c::get_wapi_core_state_string(m_wapi_state)));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: wapi_core_c::encrypt_multicast_key_data()");
+
+	eap_status_e status(eap_status_process_general_error);
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	EAP_TRACE_DATA_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("WAPI_Core: multicast_key"),
+		 multicast_key->get_data(),
+		 multicast_key->get_data_length()));
+
+	EAP_TRACE_DATA_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("WAPI_Core: m_key_encryption_key_KEK"),
+		 m_key_encryption_key_KEK.get_data(),
+		 m_key_encryption_key_KEK.get_data_length()));
+
+	wapi_am_crypto_sms4_c sms4(m_am_tools);
+
+	status = sms4.set_key(&m_key_encryption_key_KEK);
+	if (status != eap_status_ok)
+	{
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	eap_variable_data_c encrypted_multicast_key(m_am_tools);
+
+	status = encrypted_multicast_key.set_buffer_length(multicast_key->get_data_length());
+	if (status != eap_status_ok)
+	{
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = encrypted_multicast_key.set_data_length(encrypted_multicast_key.get_buffer_length());
+	if (status != eap_status_ok)
+	{
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	{
+		eap_variable_data_c iv_block(m_am_tools);
+
+		status = iv_block.set_buffer_length(multicast_key->get_data_length());
+		if (status != eap_status_ok)
+		{
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = iv_block.set_data_length(iv_block.get_buffer_length());
+		if (status != eap_status_ok)
+		{
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = sms4.ecb_encrypt(
+			key_announcement->get_data(multicast_key->get_data_length()),
+			iv_block.get_data(multicast_key->get_data_length()),
+			multicast_key->get_data_length()/16);
+		if (status != eap_status_ok)
+		{
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		EAP_TRACE_DATA_DEBUG(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("WAPI_Core: iv_block"),
+			 iv_block.get_data(),
+			 iv_block.get_data_length()));
+
+		// encrypted_multicast_key = multicast_key XOR iv_block.
+		const u8_t * const pIV = iv_block.get_data(multicast_key->get_data_length());
+		const u8_t * const pdata = multicast_key->get_data(multicast_key->get_data_length());
+		u8_t * const output = encrypted_multicast_key.get_data(multicast_key->get_data_length());
+
+		if (pIV == 0
+			|| pdata == 0
+			|| output == 0)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+		}
+
+		for (u32_t ind = 0u; ind < multicast_key->get_data_length(); ind++)
+		{
+			output[ind] = pdata[ind] ^ pIV[ind];
+		}
+	}
+
+	EAP_TRACE_DATA_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("WAPI_Core: encrypted_multicast_key"),
+		 encrypted_multicast_key.get_data(),
+		 encrypted_multicast_key.get_data_length()));
+
+	status = key_data->create(
+		wai_payload_type_key_data,
+		encrypted_multicast_key.get_data(),
+		encrypted_multicast_key.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);
+}
+
+#endif //#if defined(USE_WAPI_CORE_SERVER)
+
+//--------------------------------------------------
+
+eap_status_e wapi_core_c::decrypt_multicast_key_data(
+	const wai_variable_data_c * const key_data,
+	const eap_variable_data_c * const key_announcement,
+	eap_variable_data_c * const multicast_key)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("TODO: WAPI_Core: this = 0x%08x, %s: wapi_core_c::decrypt_multicast_key_data(): state=%s\n"),
+		 this,
+		 (m_is_client == true ? "client": "server"),
+		 wapi_strings_c::get_wapi_core_state_string(m_wapi_state)));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: wapi_core_c::decrypt_multicast_key_data()");
+
+	eap_status_e status(eap_status_process_general_error);
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	EAP_TRACE_DATA_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("WAPI_Core: key_data"),
+		 key_data->get_type_data(key_data->get_type_data_length()),
+		 key_data->get_type_data_length()));
+
+	EAP_TRACE_DATA_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("WAPI_Core: m_key_encryption_key_KEK"),
+		 m_key_encryption_key_KEK.get_data(),
+		 m_key_encryption_key_KEK.get_data_length()));
+
+	wapi_am_crypto_sms4_c sms4(m_am_tools);
+
+	status = sms4.set_key(&m_key_encryption_key_KEK);
+	if (status != eap_status_ok)
+	{
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = multicast_key->set_buffer_length(key_data->get_type_data_length());
+	if (status != eap_status_ok)
+	{
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = multicast_key->set_data_length(multicast_key->get_buffer_length());
+	if (status != eap_status_ok)
+	{
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+
+	{
+		eap_variable_data_c iv_block(m_am_tools);
+		if (iv_block.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 = iv_block.set_buffer_length(key_data->get_type_data_length());
+		if (status != eap_status_ok)
+		{
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = iv_block.set_data_length(iv_block.get_buffer_length());
+		if (status != eap_status_ok)
+		{
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = sms4.ecb_encrypt(
+			key_announcement->get_data(key_data->get_type_data_length()),
+			iv_block.get_data(key_data->get_type_data_length()),
+			key_data->get_type_data_length()/16);
+		if (status != eap_status_ok)
+		{
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		EAP_TRACE_DATA_DEBUG(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("WAPI_Core: iv_block"),
+			 iv_block.get_data(),
+			 iv_block.get_data_length()));
+
+		// multicast_key = encrypted_multicast_key XOR iv_block.
+		const u8_t * const pIV = iv_block.get_data(key_data->get_type_data_length());
+		const u8_t * const pdata = key_data->get_type_data(key_data->get_type_data_length());
+		u8_t * const output = multicast_key->get_data(key_data->get_type_data_length());
+
+		if (pIV == 0
+			|| pdata == 0
+			|| output == 0)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+		}
+
+		for (u32_t ind = 0u; ind < key_data->get_type_data_length(); ind++)
+		{
+			output[ind] = pdata[ind] ^ pIV[ind];
+		}
+	}
+
+	EAP_TRACE_DATA_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("WAPI_Core: multicast_key"),
+		 multicast_key->get_data(),
+		 multicast_key->get_data_length()));
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+eap_status_e wapi_core_c::create_multicast_key(
+	const eap_variable_data_c * const notification_master_key,
+	eap_variable_data_c * const multicast_key)
+{
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("TODO: WAPI_Core: this = 0x%08x, %s: wapi_core_c::create_multicast_key(): state=%s\n"),
+		 this,
+		 (m_is_client == true ? "client": "server"),
+		 wapi_strings_c::get_wapi_core_state_string(m_wapi_state)));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: wapi_core_c::create_multicast_key()");
+
+	EAP_TRACE_DATA_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("WAPI_Core: notification_master_key"),
+		 notification_master_key->get_data(),
+		 notification_master_key->get_data_length()));
+
+	EAP_TRACE_DATA_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("WAPI_Core: m_key_encryption_key_KEK"),
+		 m_key_encryption_key_KEK.get_data(),
+		 m_key_encryption_key_KEK.get_data_length()));
+
+	// multicas_key = KD_HMAC_SHA256(notification_master_key, string label)
+
+	crypto_kd_hmac_sha256_c kd_hmac(m_am_tools);
+	if (kd_hmac.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 label(m_am_tools);
+	if (label.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_status_e status = label.add_data(WAPI_MULTICAST_KEY_EXPANSION_LABEL, WAPI_MULTICAST_KEY_EXPANSION_LABEL_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("WAPI_Core: label"),
+		 label.get_data(),
+		 label.get_data_length()));
+
+	status = kd_hmac.expand_key(
+		multicast_key,
+		WAPI_MULTICAST_KEY_LENGTH,
+		notification_master_key,
+		&label);
+	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("WAPI_Core: multicast_key"),
+		 multicast_key->get_data(),
+		 multicast_key->get_data_length()));
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+eap_status_e wapi_core_c::create_signature_attributes(
+	wai_variable_data_c * const data_signature,
+	const eap_variable_data_c * const signer_id,
+	const eap_variable_data_c * const signature)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("WAPI_Core: this = 0x%08x, %s: wapi_core_c::create_signature_attributes(): state=%s\n"),
+		 this,
+		 (m_is_client == true ? "client": "server"),
+		 wapi_strings_c::get_wapi_core_state_string(m_wapi_state)));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: wapi_core_c::create_signature_attributes()");
+
+	eap_status_e status(eap_status_process_general_error);
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	wai_variable_data_c data_identity(m_am_tools);
+	if (data_identity.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 = data_identity.create(
+		wai_payload_type_identity,
+		signer_id);
+	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("data_identity"),
+		 data_identity.get_full_tlv_buffer()->get_data(),
+		 data_identity.get_full_tlv_buffer()->get_data_length()));
+
+	status = data_signature->create(
+		wai_payload_type_signature_attributes,
+		data_identity.get_full_tlv_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_DATA_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("data_signature"),
+		 data_signature->get_full_tlv_buffer()->get_data(),
+		 data_signature->get_full_tlv_buffer()->get_data_length()));
+
+	u8_t hash_algorithm_id(WAI_HASH_ALGORITHM_ID);
+	u8_t signature_algorithm_id(WAI_SIGNATURE_ALGORITHM_ID);
+	u8_t signature_parameter_id(WAI_SIGNATURE_PARAMETER_ID);
+	u16_t signature_parameter_content_length(sizeof(WAPI_ECDH_OID_PARAMETER));
+	u16_t signature_length(static_cast<u16_t>(signature->get_data_length()));
+
+	u16_t signature_algorithm_length(
+		sizeof(hash_algorithm_id)
+		+ sizeof(signature_algorithm_id)
+		+ sizeof(signature_parameter_id)
+		+ sizeof(signature_parameter_content_length)
+		+ signature_parameter_content_length);
+
+
+	{
+		u16_t network_order_signature_algorithm_length(eap_htons(signature_algorithm_length));
+
+		status = data_signature->add_data(
+			wai_payload_type_signature_attributes,
+			&network_order_signature_algorithm_length,
+			sizeof(network_order_signature_algorithm_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("data_signature"),
+			 data_signature->get_full_tlv_buffer()->get_data(),
+			 data_signature->get_full_tlv_buffer()->get_data_length()));
+	}
+
+	status = data_signature->add_data(
+		wai_payload_type_signature_attributes,
+		&hash_algorithm_id,
+		sizeof(hash_algorithm_id));
+	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("data_signature"),
+		 data_signature->get_full_tlv_buffer()->get_data(),
+		 data_signature->get_full_tlv_buffer()->get_data_length()));
+
+	status = data_signature->add_data(
+		wai_payload_type_signature_attributes,
+		&signature_algorithm_id,
+		sizeof(signature_algorithm_id));
+	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("data_signature"),
+		 data_signature->get_full_tlv_buffer()->get_data(),
+		 data_signature->get_full_tlv_buffer()->get_data_length()));
+
+	status = data_signature->add_data(
+		wai_payload_type_signature_attributes,
+		&signature_parameter_id,
+		sizeof(signature_parameter_id));
+	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("data_signature"),
+		 data_signature->get_full_tlv_buffer()->get_data(),
+		 data_signature->get_full_tlv_buffer()->get_data_length()));
+
+	{
+		u16_t network_order_signature_parameter_content_length(eap_htons(signature_parameter_content_length));
+
+		status = data_signature->add_data(
+			wai_payload_type_signature_attributes,
+			&network_order_signature_parameter_content_length,
+			sizeof(network_order_signature_parameter_content_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("data_signature"),
+			 data_signature->get_full_tlv_buffer()->get_data(),
+			 data_signature->get_full_tlv_buffer()->get_data_length()));
+	}
+
+	status = data_signature->add_data(
+		wai_payload_type_signature_attributes,
+		WAPI_ECDH_OID_PARAMETER,
+		sizeof(WAPI_ECDH_OID_PARAMETER));
+	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("data_signature"),
+		 data_signature->get_full_tlv_buffer()->get_data(),
+		 data_signature->get_full_tlv_buffer()->get_data_length()));
+
+	{
+		u16_t network_order_signature_length(eap_htons(signature_length));
+
+		status = data_signature->add_data(
+			wai_payload_type_signature_attributes,
+			&network_order_signature_length,
+			sizeof(network_order_signature_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("data_signature"),
+			 data_signature->get_full_tlv_buffer()->get_data(),
+			 data_signature->get_full_tlv_buffer()->get_data_length()));
+	}
+
+	status = data_signature->add_data(
+		wai_payload_type_signature_attributes,
+		signature);
+	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("data_signature"),
+		 data_signature->get_full_tlv_buffer()->get_data(),
+		 data_signature->get_full_tlv_buffer()->get_data_length()));
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+eap_status_e wapi_core_c::parse_signature_attributes(
+	const wai_variable_data_c * const data_signature,
+	eap_variable_data_c * const signer_id,
+	eap_variable_data_c * const signature)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("WAPI_Core: this = 0x%08x, %s: wapi_core_c::parse_signature_attributes(): state=%s\n"),
+		 this,
+		 (m_is_client == true ? "client": "server"),
+		 wapi_strings_c::get_wapi_core_state_string(m_wapi_state)));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: wapi_core_c::parse_signature_attributes()");
+
+	eap_status_e status(eap_status_process_general_error);
+
+	if (signer_id == 0
+		|| signer_id->get_is_valid() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	u16_t signature_length(0ul);
+
+	u16_t signature_algorithm_length(0ul);
+
+	u32_t offset(0ul);
+	u32_t remaining_data(data_signature->get_type_data_length());
+
+	if (remaining_data > data_signature->get_full_tlv_buffer()->get_data_length())
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+	}
+
+	status = signer_id->reset_start_offset_and_data_length();
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	{
+		// Read the ASN1/DER encoded Identity (Subject name, Issuer name, and Sequence number).
+
+		if (data_signature->get_data_length() < (offset+remaining_data))
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+
+		void * const identity_header_begins = data_signature->get_type_data_offset(
+			offset,
+			remaining_data);
+		if (identity_header_begins == 0)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+		}
+
+		ec_cs_tlv_header_c identity_header(
+			m_am_tools,
+			identity_header_begins,
+			remaining_data);
+		if (identity_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);
+		}
+
+		eap_variable_data_c input_data(
+			m_am_tools,
+			identity_header.get_data(identity_header.get_data_length()),
+			identity_header.get_data_length(),
+			false,
+			false);
+		if (input_data.get_is_valid() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+		}
+
+		wapi_asn1_der_parser_c asn1_der_parser(m_am_tools);
+		if (asn1_der_parser.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 = asn1_der_parser.decode(&input_data);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = asn1_der_parser.get_wapi_identity(
+			signer_id);
+		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 used_data_length(identity_header.get_header_length() + identity_header.get_data_length());
+
+		offset += used_data_length;
+		remaining_data -= used_data_length;
+
+	}
+
+	{
+		if (data_signature->get_data_length() < (offset+sizeof(u16_t)))
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+
+		const u16_t * const network_order_signature_algorithm_length =
+			reinterpret_cast<u16_t *>(data_signature->get_type_data_offset(
+										  offset,
+										  sizeof(*network_order_signature_algorithm_length)));
+		if (network_order_signature_algorithm_length == 0)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+		}
+
+		signature_algorithm_length = eap_read_u16_t_network_order(
+			network_order_signature_algorithm_length,
+			sizeof(*network_order_signature_algorithm_length));
+
+		offset += sizeof(*network_order_signature_algorithm_length) + signature_algorithm_length;
+	}
+
+	// NOTE, we skip all the Signature algorithm content.
+
+	{
+		if (data_signature->get_data_length() < (offset+sizeof(u16_t)))
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+
+		const u16_t * const network_order_signature_length = reinterpret_cast<u16_t *>(data_signature->get_type_data_offset(
+			offset,
+			sizeof(*network_order_signature_length)));
+		if (network_order_signature_length == 0)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+		}
+
+		signature_length = eap_read_u16_t_network_order(
+			network_order_signature_length,
+			sizeof(*network_order_signature_length));
+
+		offset += sizeof(*network_order_signature_length);
+	}
+
+
+	{
+		if (data_signature->get_data_length() < (offset+signature_length))
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+
+		const u8_t * const pointer_to_signature = data_signature->get_type_data_offset(
+			offset,
+			signature_length);
+		if (pointer_to_signature == 0)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+		}
+
+		status = signature->set_copy_of_buffer(
+			pointer_to_signature,
+			signature_length);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		offset += signature_length;
+	}
+
+	if (offset != data_signature->get_type_data_length())
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+eap_status_e wapi_core_c::create_result_of_certificate_verification(
+	wai_variable_data_c * const result_of_certificate_verification,
+	const eap_variable_data_c * const ae_challenge,
+	const eap_variable_data_c * const asue_challenge,
+	const wapi_certificate_result_e asue_certificate_result,
+	const eap_variable_data_c * const asue_certificate,
+	const wapi_certificate_result_e ae_certificate_result,
+	const eap_variable_data_c * const ae_certificate)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("WAPI_Core: this = 0x%08x, %s: wapi_core_c::create_result_of_certificate_verification(): state=%s\n"),
+		 this,
+		 (m_is_client == true ? "client": "server"),
+		 wapi_strings_c::get_wapi_core_state_string(m_wapi_state)));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: wapi_core_c::create_result_of_certificate_verification()");
+
+	eap_status_e status(eap_status_process_general_error);
+
+	if (result_of_certificate_verification == 0
+		|| result_of_certificate_verification->get_is_valid() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+	}
+
+	if (ae_challenge == 0
+		|| ae_challenge->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 (asue_challenge == 0
+		|| asue_challenge->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 (asue_certificate == 0
+		|| asue_certificate->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 (ae_certificate == 0
+		|| ae_certificate->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("wapi_core_c::create_result_of_certificate_verification(): ae_challenge"),
+		 ae_challenge->get_data(),
+		 ae_challenge->get_data_length()));
+
+	status = result_of_certificate_verification->create(
+		wai_payload_type_result_of_certificate_verification,
+		ae_challenge);
+	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("wapi_core_c::create_result_of_certificate_verification(): asue_challenge"),
+		 asue_challenge->get_data(),
+		 asue_challenge->get_data_length()));
+
+	status = result_of_certificate_verification->add_data(
+		wai_payload_type_result_of_certificate_verification,
+		asue_challenge);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	{
+		u8_t verification_result_1(asue_certificate_result);
+
+		EAP_TRACE_DATA_DEBUG(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("wapi_core_c::create_result_of_certificate_verification(): verification_result_1"),
+			 &verification_result_1,
+			 sizeof(verification_result_1)));
+
+		status = result_of_certificate_verification->add_data(
+			wai_payload_type_result_of_certificate_verification,
+			&verification_result_1,
+			sizeof(verification_result_1));
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+
+	{
+		wai_variable_data_c data_asue_certificate(m_am_tools);
+		if (data_asue_certificate.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_TRACE_DATA_DEBUG(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("wapi_core_c::create_result_of_certificate_verification(): asue_certificate"),
+			 asue_certificate->get_data(),
+			 asue_certificate->get_data_length()));
+
+		status = data_asue_certificate.create(
+			wai_payload_type_certificate,
+			asue_certificate);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = result_of_certificate_verification->add_data(
+			wai_payload_type_result_of_certificate_verification,
+			data_asue_certificate.get_full_tlv_buffer());
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+
+	{
+		u8_t verification_result_2(ae_certificate_result);
+
+		EAP_TRACE_DATA_DEBUG(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("wapi_core_c::create_result_of_certificate_verification(): verification_result_2"),
+			 &verification_result_2,
+			 sizeof(verification_result_2)));
+
+		status = result_of_certificate_verification->add_data(
+			wai_payload_type_result_of_certificate_verification,
+			&verification_result_2,
+			sizeof(verification_result_2));
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+
+	{
+		wai_variable_data_c data_ae_certificate(m_am_tools);
+		if (data_ae_certificate.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_TRACE_DATA_DEBUG(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("wapi_core_c::create_result_of_certificate_verification(): ae_certificate"),
+			 ae_certificate->get_data(),
+			 ae_certificate->get_data_length()));
+
+		status = data_ae_certificate.create(
+			wai_payload_type_certificate,
+			ae_certificate);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = result_of_certificate_verification->add_data(
+			wai_payload_type_result_of_certificate_verification,
+			data_ae_certificate.get_full_tlv_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_status_e wapi_core_c::parse_result_of_certificate_verification(
+	const wai_variable_data_c * const result_of_certificate_verification,
+	eap_variable_data_c * const ae_challenge,
+	eap_variable_data_c * const asue_challenge,
+	wapi_certificate_result_e * const asue_certificate_result,
+	eap_variable_data_c * const asue_certificate,
+	wapi_certificate_result_e * const ae_certificate_result,
+	eap_variable_data_c * const ae_certificate)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("WAPI_Core: this = 0x%08x, %s: wapi_core_c::parse_result_of_certificate_verification(): state=%s\n"),
+		 this,
+		 (m_is_client == true ? "client": "server"),
+		 wapi_strings_c::get_wapi_core_state_string(m_wapi_state)));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: wapi_core_c::parse_result_of_certificate_verification()");
+
+	eap_status_e status(eap_status_process_general_error);
+
+	if (result_of_certificate_verification == 0
+		|| result_of_certificate_verification->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 (ae_challenge == 0
+		|| ae_challenge->get_is_valid() == false
+		|| asue_challenge == 0
+		|| asue_challenge->get_is_valid() == false
+		|| asue_certificate == 0
+		|| asue_certificate->get_is_valid() == false
+		|| ae_certificate == 0
+		|| ae_certificate->get_is_valid() == false
+		|| asue_certificate_result == 0
+		|| ae_certificate_result == 0)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	u32_t offset(0ul);
+
+	if (result_of_certificate_verification->get_type_data_length() < (offset+WAPI_CHALLENGE_LENGTH))
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+	}
+
+	status = ae_challenge->set_copy_of_buffer(
+		result_of_certificate_verification->get_type_data_offset(offset, WAPI_CHALLENGE_LENGTH),
+		WAPI_CHALLENGE_LENGTH);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	offset += WAPI_CHALLENGE_LENGTH;
+
+	EAP_TRACE_DATA_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("wapi_core_c::parse_result_of_certificate_verification(): ae_challenge"),
+		 ae_challenge->get_data(),
+		 ae_challenge->get_data_length()));
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	if (result_of_certificate_verification->get_type_data_length() < (offset+WAPI_CHALLENGE_LENGTH))
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+	}
+
+	status = asue_challenge->set_copy_of_buffer(
+		result_of_certificate_verification->get_type_data_offset(offset, WAPI_CHALLENGE_LENGTH),
+		WAPI_CHALLENGE_LENGTH);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	offset += WAPI_CHALLENGE_LENGTH;
+
+	EAP_TRACE_DATA_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("wapi_core_c::parse_result_of_certificate_verification(): asue_challenge"),
+		 asue_challenge->get_data(),
+		 asue_challenge->get_data_length()));
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	
+	{
+		if (result_of_certificate_verification->get_type_data_length() < (offset+sizeof(u8_t)))
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+
+		const u8_t * const verification_result_1 = result_of_certificate_verification->get_type_data_offset(offset, sizeof(*verification_result_1));
+
+		if (verification_result_1 == 0)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+		}
+
+		*asue_certificate_result = static_cast<wapi_certificate_result_e>(*verification_result_1);
+
+		offset += sizeof(*verification_result_1);
+
+		EAP_TRACE_DATA_DEBUG(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("wapi_core_c::parse_result_of_certificate_verification(): verification_result_1"),
+			 verification_result_1,
+			 sizeof(*verification_result_1)));
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	{
+		if (result_of_certificate_verification->get_type_data_length() < offset)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+
+		u32_t data_length(result_of_certificate_verification->get_type_data_length() - offset);
+
+		if (result_of_certificate_verification->get_type_data_length() < (offset+data_length))
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+
+		ec_cs_tlv_header_c certificate_1(
+			m_am_tools,
+			result_of_certificate_verification->get_type_data_offset(offset, data_length),
+			data_length);
+		if (certificate_1.get_is_valid() == false
+			|| certificate_1.check_header() != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+		}
+
+		status = asue_certificate->set_copy_of_buffer(
+			certificate_1.get_data(certificate_1.get_data_length()),
+			certificate_1.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);
+		}
+
+		offset += (certificate_1.get_header_length() + certificate_1.get_data_length());
+
+		EAP_TRACE_DATA_DEBUG(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("wapi_core_c::parse_result_of_certificate_verification(): asue_certificate"),
+			 asue_certificate->get_data(),
+			 asue_certificate->get_data_length()));
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	{
+		if (result_of_certificate_verification->get_type_data_length() < (offset+sizeof(u8_t)))
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+
+		const u8_t * const verification_result_2 = result_of_certificate_verification->get_type_data_offset(offset, sizeof(*verification_result_2));
+
+		if (verification_result_2 == 0)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+		}
+
+		*ae_certificate_result = static_cast<wapi_certificate_result_e>(*verification_result_2);
+
+		offset += sizeof(*verification_result_2);
+
+		EAP_TRACE_DATA_DEBUG(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("wapi_core_c::parse_result_of_certificate_verification(): verification_result_2"),
+			 verification_result_2,
+			 sizeof(*verification_result_2)));
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	{
+		if (result_of_certificate_verification->get_type_data_length() < offset)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+
+		u32_t data_length(result_of_certificate_verification->get_type_data_length() - offset);
+
+		if (result_of_certificate_verification->get_type_data_length() < (offset+data_length))
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+
+		ec_cs_tlv_header_c certificate_2(
+			m_am_tools,
+			result_of_certificate_verification->get_type_data_offset(offset, data_length),
+			data_length);
+		if (certificate_2.get_is_valid() == false
+			|| certificate_2.check_header() != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+		}
+
+		status = ae_certificate->set_copy_of_buffer(
+			certificate_2.get_data(certificate_2.get_data_length()),
+			certificate_2.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);
+		}
+
+		offset += (certificate_2.get_header_length() + certificate_2.get_data_length());
+
+		EAP_TRACE_DATA_DEBUG(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("wapi_core_c::parse_result_of_certificate_verification(): ae_certificate"),
+			 ae_certificate->get_data(),
+			 ae_certificate->get_data_length()));
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+eap_status_e wapi_core_c::packet_send(
+	wai_message_c * const new_wai_message_data,
+	const wai_protocol_subtype_e wapi_subtype)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("WAPI_Core: this = 0x%08x, %s: wapi_core_c::packet_send(): state=%s\n"),
+		 this,
+		 (m_is_client == true ? "client": "server"),
+		 wapi_strings_c::get_wapi_core_state_string(m_wapi_state)));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: wapi_core_c::packet_send()");
+
+	eap_status_e status(eap_status_process_general_error);
+
+	if (new_wai_message_data == 0
+		|| new_wai_message_data->get_is_valid() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	// 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 (send_network_id.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);
+	}
+
+	EAP_ASSERT_ALWAYS(m_MTU > m_trailer_length);
+
+#if defined(USE_WAPI_CORE_SERVER)
+	if (m_is_client == false)
+	{
+		++m_packet_sequence_number;
+	}
+#endif //#if defined(USE_WAPI_CORE_SERVER)
+
+	// Both client and server initializes re-transmission.
+	// Client will process re-transmitted request again.
+	// Server will re-transmit the packet when timer elapses and no response is received.
+	init_retransmission(
+		&send_network_id,
+		&m_received_wai_message_data,
+		new_wai_message_data,
+		m_packet_sequence_number,
+		wapi_subtype);
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	status = packet_fragment(new_wai_message_data, m_packet_sequence_number);
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+eap_status_e wapi_core_c::packet_fragment(
+	wai_message_c * const new_wai_message_data,
+	const u16_t packet_sequence_number)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("WAPI_Core: this = 0x%08x, %s: wapi_core_c::packet_fragment(): state=%s\n"),
+		 this,
+		 (m_is_client == true ? "client": "server"),
+		 wapi_strings_c::get_wapi_core_state_string(m_wapi_state)));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: wapi_core_c::packet_fragment()");
+
+	eap_status_e status(eap_status_process_general_error);
+
+	if (new_wai_message_data == 0
+		|| new_wai_message_data->get_is_valid() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	// 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 (send_network_id.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);
+	}
+
+	EAP_ASSERT_ALWAYS(m_MTU > m_trailer_length);
+
+	wai_protocol_packet_header_c wai(
+		m_am_tools,
+		new_wai_message_data->get_wai_message_data()->get_data(),
+		new_wai_message_data->get_wai_message_data()->get_data_length());
+
+	if (wai.get_is_valid() == false)
+	{
+		EAP_TRACE_ERROR(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("wapi_core_c::packet_fragment(): %s, packet buffer corrupted.\n"),
+			 (m_is_client_role == true) ? "client": "server"
+			 ));
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_header_corrupted);
+	}
+
+	WAI_PROTOCOL_PACKET_TRACE_HEADER("full packet", &wai, m_is_client);
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	const u32_t data_length(wai.get_data_length());
+	const u32_t header_length(wai.get_header_length());
+	const u32_t FRAGMENT_MULTIPLIER = 8ul;
+	const u32_t header_remainder_length(header_length % FRAGMENT_MULTIPLIER);
+	const u32_t data_mtu(m_MTU - header_length - header_remainder_length);
+	const u32_t data_mtu_8(data_mtu - (data_mtu % FRAGMENT_MULTIPLIER));
+
+	u32_t data_fragment_length = (header_remainder_length + data_mtu_8);
+	const u32_t fragment_count = ((data_length + data_fragment_length - 1) / data_fragment_length);
+
+	if (fragment_count == 1ul
+		&& data_length < data_fragment_length)
+	{
+		data_fragment_length = data_length;
+	}
+
+	const u32_t last_data_fragment_length = (data_length - ((fragment_count-1) * data_fragment_length));
+	const u32_t one_packet_length = (header_length + data_fragment_length);
+	EAP_UNREFERENCED_PARAMETER(one_packet_length);
+
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("wapi_core_c::packet_fragment(): packet_sequence_number=%d, m_MTU=%d, packet_length=%d, data_length=%d, fragment_count=%d, data_fragment_length=%d, last_data_fragment_length=%d, one_packet_length=%d\n"),
+		packet_sequence_number,
+		m_MTU,
+		(header_length+data_length),
+		data_length,
+		fragment_count,
+		data_fragment_length,
+		last_data_fragment_length,
+		one_packet_length));
+
+	EAP_ASSERT(last_data_fragment_length <= data_fragment_length);
+
+	u32_t current_fragment_length(data_fragment_length);
+
+	for (u32_t frag_ind = 0ul; frag_ind < fragment_count; ++frag_ind)
+	{
+		u32_t buffer_size = m_wapi_header_offset + wai.get_header_length() + current_fragment_length + m_trailer_length;
+
+		// Creates a fragment.
+		eap_buf_chain_wr_c wai_packet(
+			eap_write_buffer, 
+			m_am_tools, 
+			buffer_size);
+
+		if (wai_packet.get_is_valid() == false)
+		{
+			EAP_TRACE_ERROR(
+				m_am_tools, 
+				TRACE_FLAGS_DEFAULT, 
+				(EAPL("wapi_core_c::packet_fragment(): %s, packet buffer corrupted.\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_allocation_error);
+		}
+
+		status = wai_packet.add_data_to_offset(
+			m_wapi_header_offset,
+			wai.get_header_buffer(wai.get_header_length()),
+			wai.get_header_length());
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = wai_packet.add_data_to_offset(
+			m_wapi_header_offset+wai.get_header_length(),
+			wai.get_data_offset(frag_ind * data_fragment_length, current_fragment_length),
+			current_fragment_length);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		wai_protocol_packet_header_c wai_fragment_header(
+			m_am_tools,
+			wai_packet.get_data_offset(m_wapi_header_offset, wai.get_header_length() + current_fragment_length),
+			wai.get_header_length() + current_fragment_length);
+
+		if (wai_fragment_header.get_is_valid() == false)
+		{
+			EAP_TRACE_ERROR(
+				m_am_tools, 
+				TRACE_FLAGS_DEFAULT, 
+				(EAPL("wapi_core_c::packet_fragment(): %s, packet buffer corrupted.\n"),
+				 (m_is_client_role == true) ? "client": "server"
+				 ));
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_header_corrupted);
+		}
+
+		status = wai_fragment_header.set_packet_sequence_number(packet_sequence_number);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = wai_fragment_header.set_fragment_sequence_number(static_cast<u8_t>(frag_ind));
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = wai_fragment_header.set_length(wai.get_header_length() + current_fragment_length);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		if ((frag_ind+1ul) < fragment_count)
+		{
+			// Not last fragment.
+			wai_fragment_header.set_flag(wai_fragment_header.get_flag() | wai_protocol_packet_header_c::m_flag_mask_fragment_exists);
+		}
+		else
+		{
+			// Last fragment.
+			wai_fragment_header.set_flag(wai_fragment_header.get_flag() & ~wai_protocol_packet_header_c::m_flag_mask_fragment_exists);
+		}
+
+		if ((frag_ind+2ul) == fragment_count)
+		{
+			current_fragment_length = last_data_fragment_length;
+		}
+
+		WAI_PROTOCOL_PACKET_TRACE_HEADER("fragment", &wai_fragment_header, m_is_client);
+
+		status = packet_send(
+			&send_network_id,
+			&wai_packet,
+			m_wapi_header_offset,
+			wai_fragment_header.get_length(),
+			buffer_size);
+
+		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_status_e wapi_core_c::packet_reassemble(const wai_protocol_packet_header_c * const wai)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("WAPI_Core: this = 0x%08x, %s: wapi_core_c::packet_reassemble(): wait fragment number %d, packet fragment number %d, state=%s\n"),
+		 this,
+		 (m_is_client == true ? "client": "server"),
+		 m_fragment_sequence_number,
+		 wai->get_fragment_sequence_number(),
+		 wapi_strings_c::get_wapi_core_state_string(m_wapi_state)));
+
+	EAP_TRACE_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("           %s: wapi_core_c::packet_reassemble(): sequence number %d, required sequence number %d.\n"),
+		(m_is_client == true) ? "client": "server",
+		wai->get_packet_sequence_number(),
+		m_packet_sequence_number));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: wapi_core_c::packet_reassemble()");
+
+	eap_status_e status(eap_status_process_general_error);
+
+	if (wai == 0
+		|| wai->get_is_valid() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+	}
+
+	status = wai->check_header();
+
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	if (wai->get_fragment_sequence_number() != m_fragment_sequence_number)
+	{
+		EAP_TRACE_DEBUG(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("ERROR: WAI: %s: wapi_core_c::packet_reassemble(): fragment sequence number %d != required fragment sequence number %d.\n"),
+			(m_is_client == true) ? "client": "server",
+			wai->get_fragment_sequence_number(),
+			m_fragment_sequence_number));
+
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_unexpected_message);
+	}
+
+
+	wai_protocol_packet_header_c reass_wai(
+		m_am_tools);
+	
+	if (wai->get_fragment_sequence_number() == 0u)
+	{
+		m_reassemble_packet.reset();
+
+		// Add header and data.
+		status = m_reassemble_packet.set_copy_of_buffer(
+			wai->get_header_buffer(wai->get_length()),
+			wai->get_length());
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = reass_wai.set_header_buffer(
+			m_reassemble_packet.get_data(),
+			m_reassemble_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);
+		}
+	}
+	else
+	{
+		status = reass_wai.set_header_buffer(
+			m_reassemble_packet.get_data(),
+			m_reassemble_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 (wai->get_packet_sequence_number() != reass_wai.get_packet_sequence_number())
+		{
+			EAP_TRACE_DEBUG(
+				m_am_tools, 
+				TRACE_FLAGS_DEFAULT, 
+				(EAPL("ERROR: WAI: %s: wapi_core_c::packet_reassemble(): sequence number %d != required sequence number %d.\n"),
+				(m_is_client == true) ? "client": "server",
+				wai->get_packet_sequence_number(),
+				reass_wai.get_packet_sequence_number()));
+
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_unexpected_message);
+		}
+
+		if (wai->get_subtype() != reass_wai.get_subtype())
+		{
+			EAP_TRACE_DEBUG(
+				m_am_tools, 
+				TRACE_FLAGS_DEFAULT, 
+				(EAPL("ERROR: WAI: %s: wapi_core_c::packet_reassemble(): sub-type %d != required sub-type %d.\n"),
+				(m_is_client == true) ? "client": "server",
+				wai->get_subtype(),
+				reass_wai.get_subtype()));
+
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_unexpected_message);
+		}
+
+		// Add data.
+		status = m_reassemble_packet.add_data(
+			wai->get_data(wai->get_data_length()),
+			wai->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 = reass_wai.set_header_buffer(
+			m_reassemble_packet.get_data(),
+			m_reassemble_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);
+		}
+
+		status = reass_wai.set_length(reass_wai.get_length() + wai->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 = reass_wai.set_fragment_sequence_number(wai->get_fragment_sequence_number());
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+
+	status = reass_wai.set_flag(wai->get_flag());
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	++m_fragment_sequence_number;
+
+	WAI_PROTOCOL_PACKET_TRACE_HEADER("reassembled packet", &reass_wai, m_is_client_role);
+
+	if ((reass_wai.get_flag() & wai_protocol_packet_header_c::m_flag_mask_fragment_exists) != 0)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_pending_request);
+	}
+
+	status = m_received_wai_message_data.set_wai_message_data(&m_reassemble_packet);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+
+	// This is the last fragment.
+
+	m_fragment_sequence_number = 0ul;
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	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 wapi_core_c::start_authentication()
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("WAPI_Core: this = 0x%08x, %s: wapi_core_c::start_authentication(): state=%s\n"),
+		 this,
+		 (m_is_client == true ? "client": "server"),
+		 wapi_strings_c::get_wapi_core_state_string(m_wapi_state)));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: wapi_core_c::start_authentication()");
+
+	eap_status_e status(eap_status_process_general_error);
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	m_fragment_sequence_number = 0ul;
+	m_packet_sequence_number = 0ul;
+
+#if defined(USE_WAPI_CORE_SERVER)
+
+	if (m_wapi_negotiation_state == wapi_negotiation_state_none
+		|| m_only_initial_authentication == true)
+	{
+		m_wapi_negotiation_state = wapi_negotiation_state_initial_negotiation;
+	}
+	else if (m_wapi_negotiation_state == wapi_negotiation_state_initial_negotiation)
+	{
+		m_wapi_negotiation_state = wapi_negotiation_state_rekeying;
+	}
+	else if (m_wapi_negotiation_state == wapi_negotiation_state_rekeying)
+	{
+		// Randomly change to initial negotiation.
+		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);
+		}
+
+		const u32_t MIN_LIMIT = 0ul;
+		const u32_t MAX_LIMIT = 100ul;
+		const u32_t SELECTION_LIMIT = MAX_LIMIT/2ul;
+
+		if (rand.get_rand_integer(MIN_LIMIT, MAX_LIMIT) <= SELECTION_LIMIT)
+		{
+			m_wapi_negotiation_state = wapi_negotiation_state_initial_negotiation;
+		}
+	}
+
+	if (m_is_client == false
+		&& (m_wapi_state == wapi_core_state_none
+			|| m_wapi_state == wapi_core_state_authentication_ok
+			|| m_wapi_state == wapi_core_state_authentication_failed))
+	{
+		if (m_authentication_type == eapol_key_authentication_type_WAI_PSK)
+		{
+			set_wapi_state(wapi_core_state_start_unicast_key_negotiation);
+
+			status = start_unicast_key_negotiation();
+		}
+		else if (m_authentication_type == eapol_key_authentication_type_WAI_certificate)
+		{
+			set_wapi_state(wapi_core_state_start_certificate_negotiation);
+
+			status = start_certificate_negotiation();
+		}
+		else
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_wrong_authentication_type);
+		}
+	}
+	else
+#endif //#if defined(USE_WAPI_CORE_SERVER)
+	{
+		EAP_TRACE_DEBUG(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("ERROR: WAI: %s: wapi_core_c::start_authentication(): Verify state %s != %s, negotiation state = %s.\n"),
+			(m_is_client == true) ? "client": "server",
+			wapi_strings_c::get_wapi_core_state_string(m_wapi_state),
+			wapi_strings_c::get_wapi_core_state_string(wapi_core_state_none),
+			wapi_strings_c::get_wapi_negotiation_state_string(m_wapi_negotiation_state)));
+
+		status =  eap_status_unexpected_message;
+		(void) 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 wapi_core_c::allow_authentication()
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("WAPI_Core: this = 0x%08x, %s: wapi_core_c::allow_authentication(): state=%s, negotiation_state=%s\n"),
+		 this,
+		 (m_is_client == true ? "client": "server"),
+		 wapi_strings_c::get_wapi_core_state_string(m_wapi_state),
+		 wapi_strings_c::get_wapi_negotiation_state_string(m_wapi_negotiation_state)));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: wapi_core_c::allow_authentication()");
+
+	eap_status_e status(eap_status_process_general_error);
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	m_fragment_sequence_number = 0ul;
+
+	if (m_wapi_negotiation_state == wapi_negotiation_state_none)
+	{
+		m_wapi_negotiation_state = wapi_negotiation_state_initial_negotiation;
+	}
+	else if (m_wapi_negotiation_state == wapi_negotiation_state_initial_negotiation)
+	{
+		m_wapi_negotiation_state = wapi_negotiation_state_rekeying;
+	}
+
+	if (m_wapi_state == wapi_core_state_none)
+	{
+		if (m_authentication_type == eapol_key_authentication_type_WAI_PSK)
+		{
+			set_wapi_state(wapi_core_state_wait_unicast_key_negotiation_request_message);
+			status = eap_status_ok;
+		}
+		else if (m_authentication_type == eapol_key_authentication_type_WAI_certificate)
+		{
+			set_wapi_state(wapi_core_state_wait_authentication_activation_message);
+			status = eap_status_ok;
+		}
+		else
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_wrong_authentication_type);
+		}
+	}
+	else
+	{
+		EAP_TRACE_DEBUG(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("ERROR: WAI: %s: wapi_core_c::allow_authentication(): Verify state %s != %s, negotiation state = %s.\n"),
+			(m_is_client == true) ? "client": "server",
+			wapi_strings_c::get_wapi_core_state_string(m_wapi_state),
+			wapi_strings_c::get_wapi_core_state_string(wapi_core_state_none),
+			wapi_strings_c::get_wapi_negotiation_state_string(m_wapi_negotiation_state)));
+
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_unexpected_message);
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e wapi_core_c::init_bksa_caching_timeout()
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("WAPI_Core: this = 0x%08x, %s: wapi_core_c::init_bksa_caching_timeout(): state=%s\n"),
+		 this,
+		 (m_is_client == true ? "client": "server"),
+		 wapi_strings_c::get_wapi_core_state_string(m_wapi_state)));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: wapi_core_c::init_bksa_caching_timeout()");
+
+	eap_status_e status(eap_status_process_general_error);
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("%s: Removes BKSA cache\n"),
+		 (m_is_client == true ? "client": "server")));
+
+	// Now we do not use BKSA cache, clean-up state.
+	(void) reset();
+
+	// Timeout value zero will remove state immediately.
+	status = set_session_timeout(0ul);
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e wapi_core_c::reset_cached_bksa()
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("WAPI_Core: this = 0x%08x, %s: wapi_core_c::reset_cached_bksa(): state=%s\n"),
+		 this,
+		 (m_is_client == true ? "client": "server"),
+		 wapi_strings_c::get_wapi_core_state_string(m_wapi_state)));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: wapi_core_c::reset_cached_bksa()");
+
+	eap_status_e status(eap_status_process_general_error);
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	// Now we do not use BKSA cache, clean-up state.
+	status = reset();
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e wapi_core_c::read_reassociation_parameters(
+	const eap_am_network_id_c * const /* receive_network_id */, ///< source includes remote address, destination includes local address.
+	const eapol_key_authentication_type_e /* required_authentication_type */,
+	eap_variable_data_c * const /* BKSA */,
+	const eap_variable_data_c * const /* received_ie */,
+	const eap_variable_data_c * const /* sent_ie */)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("WAPI_Core: this = 0x%08x, %s: wapi_core_c::read_reassociation_parameters(): state=%s\n"),
+		 this,
+		 (m_is_client == true ? "client": "server"),
+		 wapi_strings_c::get_wapi_core_state_string(m_wapi_state)));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: wapi_core_c::read_reassociation_parameters()");
+
+	eap_status_e status(eap_status_process_general_error);
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	// Now we do not support cached BKSAs.
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+#if defined(USE_WAPI_CORE_SERVER)
+
+eap_status_e wapi_core_c::increase_u128_t_network_order(
+	eap_variable_data_c * const u128_t_integer) const
+{
+	u64_t half_integer[2];
+
+	half_integer[1ul] = eap_read_u64_t_network_order(
+		u128_t_integer->get_data(sizeof(u64_t)),
+		sizeof(u64_t));
+
+	half_integer[0ul] = eap_read_u64_t_network_order(
+		u128_t_integer->get_data_offset(sizeof(u64_t), sizeof(u64_t)),
+		sizeof(u64_t));
+
+	if (half_integer[0ul] == (~0UL))
+	{
+		++half_integer[1ul];
+	}
+	++half_integer[0ul];
+
+	eap_status_e status = eap_write_u64_t_network_order(
+		u128_t_integer->get_data(sizeof(u64_t)),
+		sizeof(u64_t),
+		half_integer[1ul]);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = eap_write_u64_t_network_order(
+		u128_t_integer->get_data_offset(sizeof(u64_t), sizeof(u64_t)),
+		sizeof(u64_t),
+		half_integer[0ul]);
+	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);
+}
+
+#endif //#if defined(USE_WAPI_CORE_SERVER)
+
+//--------------------------------------------------
+
+#if defined(USE_WAPI_CORE_SERVER)
+
+eap_status_e wapi_core_c::start_certificate_negotiation()
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("WAPI_Core: this = 0x%08x, %s: wapi_core_c::start_certificate_negotiation(): state=%s\n"),
+		 this,
+		 (m_is_client == true ? "client": "server"),
+		 wapi_strings_c::get_wapi_core_state_string(m_wapi_state)));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: wapi_core_c::start_certificate_negotiation()");
+
+	eap_status_e status(eap_status_process_general_error);
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	m_packet_sequence_number = 0u;
+
+	status = m_ec_certificate_store->query_asu_id();
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+#endif //#if defined(USE_WAPI_CORE_SERVER)
+
+//--------------------------------------------------
+
+#if defined(USE_WAPI_CORE_SERVER)
+
+eap_status_e wapi_core_c::start_unicast_key_negotiation()
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("WAPI_Core: this = 0x%08x, %s: wapi_core_c::start_unicast_key_negotiation(): state=%s\n"),
+		 this,
+		 (m_is_client == true ? "client": "server"),
+		 wapi_strings_c::get_wapi_core_state_string(m_wapi_state)));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: wapi_core_c::start_unicast_key_negotiation()");
+
+	eap_status_e status(eap_status_process_general_error);
+
+	if (m_authentication_type != eapol_key_authentication_type_WAI_PSK
+		&& m_authentication_type != eapol_key_authentication_type_WAI_certificate)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_wrong_authentication_type);
+	}
+
+	if (m_wapi_state != wapi_core_state_start_unicast_key_negotiation)
+	{
+		EAP_TRACE_DEBUG(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("ERROR: WAI: %s: wapi_core_c::start_unicast_key_negotiation(): Verify state %s != %s, negotiation state = %s.\n"),
+			(m_is_client == true) ? "client": "server",
+			wapi_strings_c::get_wapi_core_state_string(m_wapi_state),
+			wapi_strings_c::get_wapi_core_state_string(wapi_core_state_start_unicast_key_negotiation),
+			wapi_strings_c::get_wapi_negotiation_state_string(m_wapi_negotiation_state)));
+
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_unexpected_message);
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Create BKID.
+
+	status = create_BKID(&m_BKID, &m_receive_network_id);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Create AE challenge.
+
+	if (m_wapi_negotiation_state == wapi_negotiation_state_initial_negotiation)
+	{
+		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(
+			&m_ae_unicast_challenge,
+			WAPI_CHALLENGE_LENGTH);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+	else
+	{
+		status = m_ae_unicast_challenge.set_copy_of_buffer(&m_next_unicast_challenge);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Create the Unicast Key Negotiation Request message.
+
+	wai_message_payloads_c * const payloads = new wai_message_payloads_c(m_am_tools, m_is_client);
+	eap_automatic_variable_c<wai_message_payloads_c> automatic_payloads(m_am_tools, payloads);
+
+	if (payloads == 0
+		|| 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);
+	}
+
+	status = payloads->initialise_header();
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = payloads->get_wai_protocol_packet_header_writable()->set_subtype(wai_protocol_subtype_unicast_key_negotiation_request);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Adds FLAG to data field.
+
+	{
+		wai_variable_data_c data_flag(m_am_tools);
+		if (data_flag.get_is_valid() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+		}
+
+		u8_t flag(wai_data_flag_mask_none);
+
+		if (m_wapi_negotiation_state == wapi_negotiation_state_rekeying)
+		{
+			flag = wai_data_flag_mask_USK_Rekeying;
+		}
+
+		status = data_flag.create(
+			wai_payload_type_flag,
+			&flag,
+			sizeof(flag));
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = payloads->add_tlv(&data_flag);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Adds BKID to data field.
+
+	{
+		wai_variable_data_c data_BKID(m_am_tools);
+		if (data_BKID.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 = data_BKID.create(
+			wai_payload_type_bkid,
+			&m_BKID);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = payloads->add_tlv(&data_BKID);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Adds USKID to data field.
+
+	{
+		wai_variable_data_c data_USKID(m_am_tools);
+		if (data_USKID.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_wapi_negotiation_state == wapi_negotiation_state_rekeying)
+		{
+			m_USKID = (m_USKID + 1u) % 2;
+		}
+
+		status = data_USKID.create(
+			wai_payload_type_uskid,
+			&m_USKID,
+			sizeof(m_USKID));
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = payloads->add_tlv(&data_USKID);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Adds ADDID to data field.
+
+	{
+		wai_variable_data_c data_ADDID(m_am_tools);
+		if (data_ADDID.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 eap_variable_data_c * MAC_1 = m_receive_network_id.get_source_id();
+		const eap_variable_data_c * MAC_2 = m_receive_network_id.get_destination_id();
+
+		if (m_is_client == true)
+		{
+			MAC_1 = m_receive_network_id.get_destination_id();
+			MAC_2 = m_receive_network_id.get_source_id();
+		}
+
+		status = data_ADDID.create(
+			wai_payload_type_addid,
+			MAC_1);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = data_ADDID.add_data(
+			wai_payload_type_addid,
+			MAC_2);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = payloads->add_tlv(&data_ADDID);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Adds AE Challenge to data field.
+
+	{
+		wai_variable_data_c data_AE_challenge(m_am_tools);
+		if (data_AE_challenge.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 = data_AE_challenge.create(
+			wai_payload_type_nonce,
+			&m_ae_unicast_challenge);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = payloads->add_tlv(&data_AE_challenge);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Create and send message.
+
+	wai_message_c new_wai_message_data(m_am_tools, m_is_client);
+	if (new_wai_message_data.get_is_valid() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = payloads->create_wai_tlv_message(&new_wai_message_data, false);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	//m_packet_sequence_number = 0u;
+
+	//cancel_retransmission();
+
+	status = packet_send(
+		&new_wai_message_data,
+		payloads->get_wai_protocol_packet_header_writable()->get_subtype());
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	set_wapi_state(wapi_core_state_wait_unicast_key_negotiation_response_message);
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+#endif //#if defined(USE_WAPI_CORE_SERVER)
+
+//--------------------------------------------------
+
+#if defined(USE_WAPI_CORE_SERVER)
+
+eap_status_e wapi_core_c::start_multicast_key_announcement()
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("WAPI_Core: this = 0x%08x, %s: wapi_core_c::start_multicast_key_announcement(): state=%s\n"),
+		 this,
+		 (m_is_client == true ? "client": "server"),
+		 wapi_strings_c::get_wapi_core_state_string(m_wapi_state)));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: wapi_core_c::start_multicast_key_announcement()");
+
+	eap_status_e status(eap_status_process_general_error);
+
+	if (m_authentication_type != eapol_key_authentication_type_WAI_PSK
+		&& m_authentication_type != eapol_key_authentication_type_WAI_certificate)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_wrong_authentication_type);
+	}
+
+	if (m_wapi_state != wapi_core_state_start_multicast_key_announcement)
+	{
+		EAP_TRACE_DEBUG(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("ERROR: WAI: %s: wapi_core_c::start_multicast_key_announcement(): Verify state %s != %s, negotiation state = %s.\n"),
+			(m_is_client == true) ? "client": "server",
+			wapi_strings_c::get_wapi_core_state_string(m_wapi_state),
+			wapi_strings_c::get_wapi_core_state_string(wapi_core_state_start_multicast_key_announcement),
+			wapi_strings_c::get_wapi_negotiation_state_string(m_wapi_negotiation_state)));
+
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_unexpected_message);
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Create multicast key.
+
+	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);
+	}
+
+	eap_variable_data_c notification_master_key(m_am_tools);
+	if (notification_master_key.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(
+		&notification_master_key,
+		WAPI_NOTIFICATION_MASTER_KEY_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_multicast_key(&notification_master_key, &m_multicast_key);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Create the Multicast Key announcement message.
+
+	wai_message_payloads_c * const payloads = new wai_message_payloads_c(m_am_tools, m_is_client);
+	eap_automatic_variable_c<wai_message_payloads_c> automatic_payloads(m_am_tools, payloads);
+
+	if (payloads == 0
+		|| 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);
+	}
+
+	status = payloads->initialise_header();
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = payloads->get_wai_protocol_packet_header_writable()->set_subtype(wai_protocol_subtype_multicast_key_announcement);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Adds FLAG to data field.
+
+	{
+		wai_variable_data_c data_flag(m_am_tools);
+		if (data_flag.get_is_valid() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+		}
+
+		u8_t flag(wai_data_flag_mask_none);
+
+		status = data_flag.create(
+			wai_payload_type_flag,
+			&flag,
+			sizeof(flag));
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = payloads->add_tlv(&data_flag);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Adds MSKID to data field.
+
+	{
+		wai_variable_data_c data_flag(m_am_tools);
+		if (data_flag.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_wapi_negotiation_state == wapi_negotiation_state_initial_negotiation)
+		{
+			m_MSKID = 0u;
+		}
+		else
+		{
+			m_MSKID = (m_MSKID + 1u) % 2;
+		}
+
+		status = data_flag.create(
+			wai_payload_type_mskid_stakeyid,
+			&m_MSKID,
+			sizeof(m_MSKID));
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = payloads->add_tlv(&data_flag);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Adds USKID to data field.
+
+	{
+		wai_variable_data_c data_USKID(m_am_tools);
+		if (data_USKID.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 = data_USKID.create(
+			wai_payload_type_uskid,
+			&m_USKID,
+			sizeof(m_USKID));
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = payloads->add_tlv(&data_USKID);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Adds ADDID to data field.
+
+	{
+		wai_variable_data_c data_ADDID(m_am_tools);
+		if (data_ADDID.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 eap_variable_data_c * MAC_1 = m_receive_network_id.get_source_id();
+		const eap_variable_data_c * MAC_2 = m_receive_network_id.get_destination_id();
+
+		if (m_is_client == true)
+		{
+			MAC_1 = m_receive_network_id.get_destination_id();
+			MAC_2 = m_receive_network_id.get_source_id();
+		}
+
+		status = data_ADDID.create(
+			wai_payload_type_addid,
+			MAC_1);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = data_ADDID.add_data(
+			wai_payload_type_addid,
+			MAC_2);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = payloads->add_tlv(&data_ADDID);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Adds Data Packet Number to data field.
+
+	{
+		wai_variable_data_c data_packet_number(m_am_tools);
+		if (data_packet_number.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_wapi_negotiation_state == wapi_negotiation_state_initial_negotiation)
+		{
+			const u8_t TEST_DATA_PACKET_NUMBER[] =
+			{
+				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			};
+
+			status = m_packet_data_number.set_copy_of_buffer(
+				TEST_DATA_PACKET_NUMBER,
+				sizeof(TEST_DATA_PACKET_NUMBER));
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+		}
+		else
+		{
+			status = increase_u128_t_network_order(
+				&m_packet_data_number);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+		}
+
+		status = data_packet_number.create(
+			wai_payload_type_data_sequence_number,
+			&m_packet_data_number);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = payloads->add_tlv(&data_packet_number);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Adds Key Announcement to data field.
+
+	{
+		wai_variable_data_c key_announcement(m_am_tools);
+		if (key_announcement.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_wapi_negotiation_state == wapi_negotiation_state_initial_negotiation)
+		{
+			const u8_t TEST_KEY_ANNOUNCEMENT[] =
+			{
+				0x5c, 0x36, 0x5c, 0x36, 0x5c, 0x36, 0x5c, 0x36,
+				0x5c, 0x36, 0x5c, 0x36, 0x5c, 0x36, 0x5c, 0x36,
+			};
+
+			status = m_key_announcement.set_copy_of_buffer(
+				TEST_KEY_ANNOUNCEMENT,
+				sizeof(TEST_KEY_ANNOUNCEMENT));
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+		}
+		else
+		{
+			status = increase_u128_t_network_order(
+				&m_key_announcement);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+		}
+
+		status = key_announcement.create(
+			wai_payload_type_key_announcement_identifier,
+			&m_key_announcement);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = payloads->add_tlv(&key_announcement);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+		// Adds Key Data to data field.
+
+		{
+			wai_variable_data_c key_data(m_am_tools);
+			if (key_data.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 = encrypt_multicast_key_data(&notification_master_key, &m_key_announcement, &key_data);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			status = payloads->add_tlv(&key_data);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+		}
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Adds MAC to data field.
+
+	{
+		wai_variable_data_c data_MAC(m_am_tools);
+		if (data_MAC.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 MAC(m_am_tools);
+		if (MAC.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_MAC(payloads, &MAC);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = MAC.set_data_length(WAPI_MESSAGE_AUTHENTICATION_CODE_LENGTH);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = data_MAC.create(
+			wai_payload_type_message_authentication_code,
+			&MAC);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = payloads->add_tlv(&data_MAC);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Create and send message.
+
+	wai_message_c new_wai_message_data(m_am_tools, m_is_client);
+	if (new_wai_message_data.get_is_valid() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = payloads->create_wai_tlv_message(&new_wai_message_data, false);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	//m_packet_sequence_number = 0u;
+
+	//cancel_retransmission();
+
+	status = packet_send(
+		&new_wai_message_data,
+		payloads->get_wai_protocol_packet_header_writable()->get_subtype());
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	set_wapi_state(wapi_core_state_wait_multicast_announcement_response_message);
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+#endif //#if defined(USE_WAPI_CORE_SERVER)
+
+//--------------------------------------------------
+
+eap_status_e wapi_core_c::handle_authentication_activation(
+	const eap_am_network_id_c * const receive_network_id,
+	const wai_protocol_packet_header_c * const wai)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("WAPI_Core: this = 0x%08x, %s: wapi_core_c::handle_authentication_activation(): state=%s, negotiation state = %s\n"),
+		 this,
+		 (m_is_client == true ? "client": "server"),
+		 wapi_strings_c::get_wapi_core_state_string(m_wapi_state),
+		 wapi_strings_c::get_wapi_negotiation_state_string(m_wapi_negotiation_state)));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: wapi_core_c::handle_authentication_activation()");
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	eap_status_e status(eap_status_process_general_error);
+
+	if (m_authentication_type != eapol_key_authentication_type_WAI_certificate)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_wrong_authentication_type);
+	}
+
+	if (m_wapi_state != wapi_core_state_wait_authentication_activation_message
+		&& m_wapi_state != wapi_core_state_wait_access_authentication_response_message
+		&& m_wapi_state != wapi_core_state_authentication_ok)
+	{
+		EAP_TRACE_DEBUG(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("ERROR: WAI: %s: wapi_core_c::handle_authentication_activation(): Verify state %s != %s, negotiation state = %s.\n"),
+			(m_is_client == true) ? "client": "server",
+			wapi_strings_c::get_wapi_core_state_string(m_wapi_state),
+			wapi_strings_c::get_wapi_core_state_string(wapi_core_state_wait_authentication_activation_message),
+			wapi_strings_c::get_wapi_negotiation_state_string(m_wapi_negotiation_state)));
+
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_unexpected_message);
+	}
+
+	if (receive_network_id == 0
+		|| receive_network_id->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 (wai == 0
+		|| wai->get_is_valid() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_process_illegal_packet_error);
+	}
+
+	status = wai->check_header();
+
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	if (wai->get_packet_sequence_number() != WAI_FIRST_SEQUENCE_NUMBER)
+	{
+		EAP_TRACE_DEBUG(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("ERROR: WAI: %s: wapi_core_c::handle_authentication_activation(): sequence number %d != required sequence number %d.\n"),
+			(m_is_client == true) ? "client": "server",
+			wai->get_packet_sequence_number(),
+			WAI_FIRST_SEQUENCE_NUMBER));
+
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_unexpected_message);
+	}
+
+	m_packet_sequence_number = WAI_FIRST_SEQUENCE_NUMBER;
+
+	set_wapi_state(wapi_core_state_process_authentication_activation_message);
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	if (m_authentication_type == eapol_key_authentication_type_WAI_certificate)
+	{
+		// 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 (send_network_id.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);
+		}
+
+		// This is notification to eapol_core_c object.
+		// WAI unicast negotiation started successfully.
+		eap_state_notification_c * notification = new eap_state_notification_c(
+			m_am_tools,
+			&send_network_id,
+			m_is_client,
+			eap_state_notification_generic,
+			eap_protocol_layer_wai,
+			eapol_key_handshake_type_wai_handshake,
+			eapol_key_state_wapi_authentication_running,
+			eapol_key_state_wapi_authentication_running,
+			0ul,
+			false);
+		if (notification == 0)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+		}
+		m_partner->state_notification(notification);
+
+		delete notification;
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	wai_message_payloads_c parser(
+		m_am_tools,
+		m_is_client);
+	if (parser.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 padding_length(0ul);
+
+	status = parser.parse_wai_payloads(
+		wai->get_header_buffer(wai->get_header_buffer_length()), ///< This is the start of the message buffer.
+		wai->get_header_buffer_length(), ///< This is the length of the buffer. This must match with the length of all payloads.
+		&padding_length ///< Length of possible padding is set to this variable.
+		);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Verify BK rekeying flag.
+
+	{
+		wai_variable_data_c * const flag_payload = parser.get_tlv_pointer(wai_payload_type_flag);
+		if (flag_payload == 0
+			|| flag_payload->get_is_valid_data() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+
+		const u8_t * const flag = flag_payload->get_data(sizeof(*flag));
+		if (flag == 0)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+
+		if (m_wapi_negotiation_state == wapi_negotiation_state_rekeying)
+		{
+			if (((*flag) & wai_data_flag_mask_BK_Rekeying) == 0)
+			{
+				m_wapi_negotiation_state = wapi_negotiation_state_initial_negotiation;
+
+				EAP_TRACE_DEBUG(
+					m_am_tools,
+					TRACE_FLAGS_DEFAULT,
+					(EAPL("WARNING: WAPI_Core: this = 0x%08x, %s: wapi_core_c::handle_authentication_activation(): changed to %s.\n"),
+					 this,
+					 (m_is_client == true ? "client": "server"),
+					 wapi_strings_c::get_wapi_negotiation_state_string(m_wapi_negotiation_state)));
+			}
+		}
+		else
+		{
+			if (((*flag) & wai_data_flag_mask_BK_Rekeying) != 0)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+			}
+		}
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Save Authentication Identifier.
+
+	{
+		wai_variable_data_c * const authentication_identifier = parser.get_tlv_pointer(wai_payload_type_authentication_identifier);
+		if (authentication_identifier == 0
+			|| authentication_identifier->get_is_valid_data() == false
+			|| authentication_identifier->get_data_length() < WAPI_AUTHENTICATION_IDENTIFIER_LENGTH)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+
+		if (m_wapi_negotiation_state == wapi_negotiation_state_rekeying)
+		{
+			// Verify the Authentication Identifier.
+			if (m_am_tools->memcmp(
+				m_authentication_identifier.get_data(WAPI_AUTHENTICATION_IDENTIFIER_LENGTH),
+				authentication_identifier->get_data(WAPI_AUTHENTICATION_IDENTIFIER_LENGTH),
+				WAPI_AUTHENTICATION_IDENTIFIER_LENGTH) != 0)
+			{
+				EAP_TRACE_DATA_DEBUG(
+					m_am_tools, 
+					TRACE_FLAGS_DEFAULT, 
+					(EAPL("ERROR: local m_authentication_identifier"),
+					 m_authentication_identifier.get_data(),
+					 m_authentication_identifier.get_data_length()));
+
+				EAP_TRACE_DATA_DEBUG(
+					m_am_tools, 
+					TRACE_FLAGS_DEFAULT, 
+					(EAPL("ERROR: received authentication_identifier"),
+					authentication_identifier->get_data(WAPI_AUTHENTICATION_IDENTIFIER_LENGTH),
+					WAPI_AUTHENTICATION_IDENTIFIER_LENGTH));
+
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+			}
+		}
+		else
+		{
+			status = m_authentication_identifier.set_copy_of_buffer(
+				authentication_identifier->get_type_data(WAPI_AUTHENTICATION_IDENTIFIER_LENGTH),
+				WAPI_AUTHENTICATION_IDENTIFIER_LENGTH);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+		}
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Save ASU-ID.
+
+	{
+		wai_variable_data_c * const asu_id = parser.get_tlv_pointer(wai_payload_type_identity);
+		if (asu_id == 0
+			|| asu_id->get_is_valid_data() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+
+		status = m_asu_id.set_copy_of_buffer(
+			asu_id->get_type_data(asu_id->get_type_data_length()),
+			asu_id->get_type_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_asu_id"),
+			 m_asu_id.get_data(),
+			 m_asu_id.get_data_length()));
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Save STA_AE certificate.
+
+	{
+		wai_variable_data_c * const sta_ae_certificate = parser.get_tlv_pointer(wai_payload_type_certificate);
+		if (sta_ae_certificate == 0
+			|| sta_ae_certificate->get_is_valid_data() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+
+		status = m_peer_certificate.set_copy_of_buffer(
+			sta_ae_certificate->get_type_data(sta_ae_certificate->get_type_data_length()),
+			sta_ae_certificate->get_type_data_length());
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Verify the ECDH parameter.
+
+	{
+		wai_variable_data_c * const echd_parameter = parser.get_tlv_pointer(wai_payload_type_echd_parameter);
+		if (echd_parameter == 0
+			|| echd_parameter->get_is_valid_data() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+
+		if (sizeof(WAPI_ECDH_OID_PARAMETER) != echd_parameter->get_type_data_length()
+			|| m_am_tools->memcmp(
+				WAPI_ECDH_OID_PARAMETER,
+				echd_parameter->get_type_data(sizeof(WAPI_ECDH_OID_PARAMETER)),
+				sizeof(WAPI_ECDH_OID_PARAMETER)) != 0)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_not_supported);
+		}
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Create ASUE challenge.
+
+	if (m_asue_certificate_challenge.get_is_valid_data() == false)
+	{
+		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(
+			&m_asue_certificate_challenge,
+			WAPI_CHALLENGE_LENGTH);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Selects own certificate issued by ASU-ID.
+
+	EAP_TRACE_DATA_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("m_asu_id"),
+		 m_asu_id.get_data(),
+		 m_asu_id.get_data_length()));
+
+	status = m_ec_certificate_store->select_certificate(&m_asu_id);
+	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_status_e wapi_core_c::handle_access_authentication_response(
+	const eap_am_network_id_c * const receive_network_id,
+	const wai_protocol_packet_header_c * const wai)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("WAPI_Core: this = 0x%08x, %s: wapi_core_c::handle_access_authentication_response(): state=%s, negotiation state = %s\n"),
+		this,
+		(m_is_client == true ? "client": "server"),
+		wapi_strings_c::get_wapi_core_state_string(m_wapi_state),
+		wapi_strings_c::get_wapi_negotiation_state_string(m_wapi_negotiation_state)));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: wapi_core_c::handle_access_authentication_response()");
+
+	if (m_authentication_type != eapol_key_authentication_type_WAI_certificate)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_wrong_authentication_type);
+	}
+
+	if (m_wapi_state != wapi_core_state_wait_access_authentication_response_message
+		&& m_wapi_state != wapi_core_state_wait_unicast_key_negotiation_request_message
+		&& m_wapi_state != wapi_core_state_wait_unicast_key_negotiation_confirmation_message
+		&& m_wapi_state != wapi_core_state_authentication_ok)
+	{
+		EAP_TRACE_DEBUG(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("ERROR: WAI: %s: wapi_core_c::handle_access_authentication_response(): Verify state %s != %s, negotiation state = %s.\n"),
+			(m_is_client == true) ? "client": "server",
+			wapi_strings_c::get_wapi_core_state_string(m_wapi_state),
+			wapi_strings_c::get_wapi_core_state_string(wapi_core_state_wait_access_authentication_response_message),
+			wapi_strings_c::get_wapi_negotiation_state_string(m_wapi_negotiation_state)));
+
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_unexpected_message);
+	}
+
+	eap_status_e status(eap_status_process_general_error);
+
+	if (receive_network_id == 0
+		|| receive_network_id->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 (wai == 0
+		|| wai->get_is_valid() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_process_illegal_packet_error);
+	}
+
+	status = wai->check_header();
+
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	if (wai->get_packet_sequence_number() != (m_packet_sequence_number + 1u))
+	{
+		EAP_TRACE_DEBUG(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("ERROR: WAI: %s: wapi_core_c::handle_access_authentication_response(): sequence number %d != required sequence number %d.\n"),
+			(m_is_client == true) ? "client": "server",
+			wai->get_packet_sequence_number(),
+			(m_packet_sequence_number + 1u)));
+
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_unexpected_message);
+	}
+
+	++m_packet_sequence_number;
+
+	set_wapi_state(wapi_core_state_process_access_authentication_response_message);
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	wai_message_payloads_c parser(
+		m_am_tools,
+		m_is_client);
+	if (parser.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 padding_length(0ul);
+
+	status = parser.parse_wai_payloads(
+		wai->get_header_buffer(wai->get_header_buffer_length()), ///< This is the start of the message buffer.
+		wai->get_header_buffer_length(), ///< This is the length of the buffer. This must match with the length of all payloads.
+		&padding_length ///< Length of possible padding is set to this variable.
+		);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Verify BK rekeying flag.
+
+	{
+		wai_variable_data_c * const flag_payload = parser.get_tlv_pointer(wai_payload_type_flag);
+		if (flag_payload == 0
+			|| flag_payload->get_is_valid_data() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+
+		const u8_t * const pointer_to_flag = flag_payload->get_data(sizeof(*pointer_to_flag));
+		if (pointer_to_flag == 0)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+
+		m_do_certificate_validation = false;
+
+		if (m_wapi_negotiation_state == wapi_negotiation_state_rekeying)
+		{
+			if (((*pointer_to_flag) & wai_data_flag_mask_BK_Rekeying) == 0)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+			}
+
+			if (((*pointer_to_flag) & wai_data_flag_mask_Certificate_Validation_Request) != 0)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+			}
+
+			EAP_TRACE_DEBUG(
+				m_am_tools,
+				TRACE_FLAGS_DEFAULT,
+				(EAPL("WAPI_Core: this = 0x%08x, %s: wapi_core_c::handle_access_authentication_response(): no certificate validation\n"),
+				this,
+				(m_is_client == true ? "client": "server")));
+		}
+		else
+		{
+			if (((*pointer_to_flag) & wai_data_flag_mask_BK_Rekeying) != 0)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+			}
+
+			EAP_TRACE_DEBUG(
+				m_am_tools,
+				TRACE_FLAGS_DEFAULT,
+				(EAPL("WAPI_Core: this = 0x%08x, %s: wapi_core_c::handle_access_authentication_response(): does certificate validation\n"),
+				this,
+				(m_is_client == true ? "client": "server")));
+
+			m_do_certificate_validation = true;
+		}
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Verify AE identity.
+
+	{
+
+		wai_variable_data_c * const ae_identity = parser.get_tlv_pointer(wai_payload_type_identity);
+		if (ae_identity == 0
+			|| ae_identity->get_is_valid_data() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+
+		if (m_ae_id.compare(
+				ae_identity->get_type_data(ae_identity->get_type_data_length()),
+				ae_identity->get_type_data_length()) != 0)
+		{
+			EAP_TRACE_DATA_DEBUG(
+				m_am_tools, 
+				TRACE_FLAGS_DEFAULT, 
+				(EAPL("ERROR: local m_ae_id"),
+				 m_ae_id.get_data(),
+				 m_ae_id.get_data_length()));
+
+			EAP_TRACE_DATA_DEBUG(
+				m_am_tools, 
+				TRACE_FLAGS_DEFAULT, 
+				(EAPL("ERROR: received AE-ID"),
+				 ae_identity->get_type_data(ae_identity->get_type_data_length()),
+				 ae_identity->get_type_data_length()));
+
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+
+		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+		// Verify ASUE identity.
+
+		wai_variable_data_c * const asue_identity = ae_identity->get_next_payload_with_same_tlv_type();
+		if (asue_identity == 0
+			|| asue_identity->get_is_valid_data() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+
+		if (m_asue_id.compare(
+				asue_identity->get_type_data(asue_identity->get_type_data_length()),
+				asue_identity->get_type_data_length()) != 0)
+		{
+			EAP_TRACE_DATA_DEBUG(
+				m_am_tools, 
+				TRACE_FLAGS_DEFAULT, 
+				(EAPL("ERROR: local m_asue_id"),
+				 m_asue_id.get_data(),
+				 m_asue_id.get_data_length()));
+
+			EAP_TRACE_DATA_DEBUG(
+				m_am_tools, 
+				TRACE_FLAGS_DEFAULT, 
+				(EAPL("ERROR: received ASUE-ID"),
+				 asue_identity->get_type_data(asue_identity->get_type_data_length()),
+				 asue_identity->get_type_data_length()));
+
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Verify ASUE challenge.
+
+	{
+
+		wai_variable_data_c * const asue_challenge = parser.get_tlv_pointer(wai_payload_type_nonce);
+		if (asue_challenge == 0
+			|| asue_challenge->get_is_valid_data() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+
+		if (m_asue_certificate_challenge.compare(
+				asue_challenge->get_type_data(asue_challenge->get_type_data_length()),
+				asue_challenge->get_type_data_length()) != 0)
+		{
+			EAP_TRACE_DATA_DEBUG(
+				m_am_tools, 
+				TRACE_FLAGS_DEFAULT, 
+				(EAPL("ERROR: local m_asue_certificate_challenge"),
+				 m_asue_certificate_challenge.get_data(),
+				 m_asue_certificate_challenge.get_data_length()));
+
+			EAP_TRACE_DATA_DEBUG(
+				m_am_tools, 
+				TRACE_FLAGS_DEFAULT, 
+				(EAPL("ERROR: received ASUE-challenge"),
+				 asue_challenge->get_type_data(asue_challenge->get_type_data_length()),
+				 asue_challenge->get_type_data_length()));
+
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+
+		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+		// Save AE challenge.
+
+		wai_variable_data_c * const ae_challenge = asue_challenge->get_next_payload_with_same_tlv_type();
+		if (ae_challenge == 0
+			|| ae_challenge->get_is_valid_data() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+
+		status = m_ae_certificate_challenge.set_copy_of_buffer(ae_challenge->get_data(WAPI_CHALLENGE_LENGTH), WAPI_CHALLENGE_LENGTH);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+
+
+	wai_variable_data_c * ae_signature_trusted_by_asue = 0;
+
+	if (m_do_certificate_validation == true)
+	{
+		EAP_TRACE_DEBUG(
+			m_am_tools,
+			TRACE_FLAGS_DEFAULT,
+			(EAPL("WAPI_Core: this = 0x%08x, %s: wapi_core_c::handle_access_authentication_response(): does certificate validation\n"),
+			this,
+			(m_is_client == true ? "client": "server")));
+
+		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+		// Save result of certificate_verification.
+
+		{
+			wai_variable_data_c * const result_of_certificate_verification = parser.get_tlv_pointer(wai_payload_type_result_of_certificate_verification);
+			if (result_of_certificate_verification == 0
+				|| result_of_certificate_verification->get_is_valid_data() == false)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+			}
+
+			status = m_result_of_certificate_verification.set_copy_of_buffer(result_of_certificate_verification);
+			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 ae_challenge(m_am_tools);
+				eap_variable_data_c asue_challenge(m_am_tools);
+				eap_variable_data_c asue_certificate(m_am_tools);
+
+				wapi_certificate_result_e asue_certificate_result(wapi_certificate_result_none);
+				wapi_certificate_result_e ae_certificate_result(wapi_certificate_result_none);
+
+				status = parse_result_of_certificate_verification(
+					&m_result_of_certificate_verification,
+					&ae_challenge,
+					&asue_challenge,
+					&asue_certificate_result,
+					&asue_certificate,
+					&ae_certificate_result,
+					&m_ae_certificate);
+				if (status != eap_status_ok)
+				{
+					EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+					return EAP_STATUS_RETURN(m_am_tools, status);
+				}
+
+				// Verify AE-Challenge.
+				if (m_ae_certificate_challenge.compare(&ae_challenge) != 0)
+				{
+					EAP_TRACE_DATA_DEBUG(
+						m_am_tools, 
+						TRACE_FLAGS_DEFAULT, 
+						(EAPL("ERROR: local m_ae_certificate_challenge"),
+						 m_ae_certificate_challenge.get_data(),
+						 m_ae_certificate_challenge.get_data_length()));
+
+					EAP_TRACE_DATA_DEBUG(
+						m_am_tools, 
+						TRACE_FLAGS_DEFAULT, 
+						(EAPL("ERROR: received AE-challenge"),
+						 ae_challenge.get_data(),
+						 ae_challenge.get_data_length()));
+
+					EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+					return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+				}
+
+				// Verify ASUE-Challenge.
+				if (m_asue_certificate_challenge.compare(&asue_challenge) != 0)
+				{
+					EAP_TRACE_DATA_DEBUG(
+						m_am_tools, 
+						TRACE_FLAGS_DEFAULT, 
+						(EAPL("ERROR: local m_asue_certificate_challenge"),
+						 m_asue_certificate_challenge.get_data(),
+						 m_asue_certificate_challenge.get_data_length()));
+
+					EAP_TRACE_DATA_DEBUG(
+						m_am_tools, 
+						TRACE_FLAGS_DEFAULT, 
+						(EAPL("ERROR: received ASUE-challenge"),
+						 asue_challenge.get_data(),
+						 asue_challenge.get_data_length()));
+
+					EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+					return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+				}
+
+				// Verify ASUE-Certificate.
+				if (m_own_certificate.compare(&asue_certificate) != 0)
+				{
+					EAP_TRACE_DATA_DEBUG(
+						m_am_tools, 
+						TRACE_FLAGS_DEFAULT, 
+						(EAPL("ERROR: local m_own_certificate"),
+						 m_own_certificate.get_data(),
+						 m_own_certificate.get_data_length()));
+
+					EAP_TRACE_DATA_DEBUG(
+						m_am_tools, 
+						TRACE_FLAGS_DEFAULT, 
+						(EAPL("ERROR: received ASUE-Certificate"),
+						 asue_certificate.get_data(),
+						 asue_certificate.get_data_length()));
+
+					EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+					return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+				}
+
+				if (asue_certificate_result != wapi_certificate_result_valid)
+				{
+					EAP_TRACE_DEBUG(
+						m_am_tools,
+						TRACE_FLAGS_DEFAULT,
+						(EAPL("ERROR: WAPI_Core: this = 0x%08x, %s: wapi_core_c::handle_access_authentication_response(): asue_certificate_result=%d\n"),
+						this,
+						(m_is_client == true ? "client": "server"),
+						asue_certificate_result));
+
+					switch (asue_certificate_result)
+					{
+					case wapi_certificate_result_issuer_is_unknown:
+						EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+						return EAP_STATUS_RETURN(m_am_tools, eap_status_unknown_ca);
+					case wapi_certificate_result_certificate_is_based_on_an_untrusted_root:
+						EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+						return EAP_STATUS_RETURN(m_am_tools, eap_status_unknown_ca);
+					case wapi_certificate_result_certificate_is_not_time_valid:
+						EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+						return EAP_STATUS_RETURN(m_am_tools, eap_status_certificate_expired);
+					case wapi_certificate_result_certificate_have_not_a_valid_signature:
+						EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+						return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_certificate);
+					case wapi_certificate_result_certificate_is_revoked:
+						EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+						return EAP_STATUS_RETURN(m_am_tools, eap_status_certificate_revoked);
+					case wapi_certificate_result_certificate_is_not_valid_for_proposed_usage:
+						EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+						return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_certificate);
+					case wapi_certificate_result_revocation_state_of_the_certificate_is_unknown:
+						EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+						return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_certificate);
+					default:
+						EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+						return EAP_STATUS_RETURN(m_am_tools, eap_status_authentication_failure);
+					}
+				}
+
+				if (ae_certificate_result != wapi_certificate_result_valid)
+				{
+					EAP_TRACE_DEBUG(
+						m_am_tools,
+						TRACE_FLAGS_DEFAULT,
+						(EAPL("ERROR: WAPI_Core: this = 0x%08x, %s: wapi_core_c::handle_access_authentication_response(): ae_certificate_result=%d\n"),
+						this,
+						(m_is_client == true ? "client": "server"),
+						ae_certificate_result));
+
+					switch (ae_certificate_result)
+					{
+					case wapi_certificate_result_issuer_is_unknown:
+						EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+						return EAP_STATUS_RETURN(m_am_tools, eap_status_unknown_ca);
+					case wapi_certificate_result_certificate_is_based_on_an_untrusted_root:
+						EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+						return EAP_STATUS_RETURN(m_am_tools, eap_status_unknown_ca);
+					case wapi_certificate_result_certificate_is_not_time_valid:
+						EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+						return EAP_STATUS_RETURN(m_am_tools, eap_status_certificate_expired);
+					case wapi_certificate_result_certificate_have_not_a_valid_signature:
+						EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+						return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_certificate);
+					case wapi_certificate_result_certificate_is_revoked:
+						EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+						return EAP_STATUS_RETURN(m_am_tools, eap_status_certificate_revoked);
+					case wapi_certificate_result_certificate_is_not_valid_for_proposed_usage:
+						EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+						return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_certificate);
+					case wapi_certificate_result_revocation_state_of_the_certificate_is_unknown:
+						EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+						return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_certificate);
+					default:
+						EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+						return EAP_STATUS_RETURN(m_am_tools, eap_status_authentication_failure);
+					}
+				}
+			}
+		}
+
+		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+		// Save server signature trusted by ASUE.
+
+		{
+			wai_variable_data_c * const server_signature_trusted_by_asue = parser.get_tlv_pointer(wai_payload_type_signature_attributes);
+			if (server_signature_trusted_by_asue == 0
+				|| server_signature_trusted_by_asue->get_is_valid_data() == false)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+			}
+
+			status = m_server_signature_trusted_by_asue.set_copy_of_buffer(server_signature_trusted_by_asue);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+			// Save server signature trusted by AE.
+			// NOTE: This is used only when server trusted by ASUE is different than server trusted by AE.
+
+			wai_variable_data_c * const server_signature_trusted_by_ae = server_signature_trusted_by_asue->get_next_payload_with_same_tlv_type();
+			if (server_signature_trusted_by_ae == 0
+				|| server_signature_trusted_by_ae->get_is_valid_data() == false)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+			}
+
+			// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+			// Save Signature of AE. This is always the last signature.
+
+			ae_signature_trusted_by_asue = server_signature_trusted_by_ae->get_next_payload_with_same_tlv_type();
+
+			if ((ae_signature_trusted_by_asue == 0
+				|| ae_signature_trusted_by_asue->get_is_valid_data() == false))
+			{
+				// Server trusted by AE is the same as server trusted by ASUE.
+				status = m_server_signature_trusted_by_ae.set_copy_of_buffer(server_signature_trusted_by_asue);
+				if (status != eap_status_ok)
+				{
+					EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+					return EAP_STATUS_RETURN(m_am_tools, status);
+				}
+
+				// Signature of AE is always the last signature.
+				ae_signature_trusted_by_asue = server_signature_trusted_by_ae;
+			}
+			else
+			{
+				// Server trusted by AE is different than server trusted by ASUE.
+				status = m_server_signature_trusted_by_ae.set_copy_of_buffer(server_signature_trusted_by_ae);
+				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_DEBUG(
+			m_am_tools,
+			TRACE_FLAGS_DEFAULT,
+			(EAPL("WAPI_Core: this = 0x%08x, %s: wapi_core_c::handle_access_authentication_response(): no certificate validation\n"),
+			this,
+			(m_is_client == true ? "client": "server")));
+
+		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+		// Save Signature of AE.
+
+		ae_signature_trusted_by_asue = parser.get_tlv_pointer(wai_payload_type_signature_attributes);
+		if (ae_signature_trusted_by_asue == 0
+			|| ae_signature_trusted_by_asue->get_is_valid_data() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	{
+		wai_variable_data_c * ae_key_data = 0;
+
+		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+		// Compare ASUE key data.
+		{
+			wai_variable_data_c * const asue_key_data = parser.get_tlv_pointer(wai_payload_type_key_data);
+			if (asue_key_data == 0
+				|| asue_key_data->get_is_valid_data() == false)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+			}
+
+			u32_t offset(0ul);
+
+			const u8_t * const point_type = asue_key_data->get_type_data_offset(offset, sizeof(u8_t));
+			if (point_type == 0)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+			}
+
+			if (*point_type != WAI_EC_POINT_TYPE_NO_COMPRESSION_ID)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+			}
+
+			offset += sizeof(*point_type);
+
+			const u32_t x_key_element_length((1ul + asue_key_data->get_type_data_length() - sizeof(*point_type)) / 2ul);
+			const u32_t y_key_element_length(asue_key_data->get_type_data_length() - sizeof(*point_type) - x_key_element_length);
+
+			EAP_TRACE_DEBUG(
+				m_am_tools,
+				TRACE_FLAGS_DEFAULT,
+				(EAPL("WAPI_Core: this = 0x%08x, %s: sizeof(*point_type)=%d, *point_type=0x%02x, get_type_data_length()=%d, x_key_element_length=%d, y_key_element_length=%d\n"),
+				 this,
+				 (m_is_client == true ? "client": "server"),
+				 sizeof(*point_type),
+				 *point_type,
+				 asue_key_data->get_type_data_length(),
+				 x_key_element_length,
+				 y_key_element_length));
+
+			EAP_TRACE_DATA_DEBUG(
+				m_am_tools, 
+				TRACE_FLAGS_DEFAULT, 
+				(EAPL("m_own_public_key_x"),
+				 m_own_public_key_x.get_data(),
+				 m_own_public_key_x.get_data_length()));
+
+			EAP_TRACE_DATA_DEBUG(
+				m_am_tools, 
+				TRACE_FLAGS_DEFAULT, 
+				(EAPL("received public_key_x"),
+				 asue_key_data->get_type_data_offset(offset, x_key_element_length),
+				 x_key_element_length));
+
+			if (m_own_public_key_x.compare(
+				asue_key_data->get_type_data_offset(offset, x_key_element_length),
+				x_key_element_length) != 0)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+			}
+
+			offset += x_key_element_length;
+
+			EAP_TRACE_DATA_DEBUG(
+				m_am_tools, 
+				TRACE_FLAGS_DEFAULT, 
+				(EAPL("m_own_public_key_y"),
+				 m_own_public_key_y.get_data(),
+				 m_own_public_key_y.get_data_length()));
+
+			EAP_TRACE_DATA_DEBUG(
+				m_am_tools, 
+				TRACE_FLAGS_DEFAULT, 
+				(EAPL("received public_key_y"),
+				 asue_key_data->get_type_data_offset(offset, y_key_element_length),
+				 y_key_element_length));
+
+			if (m_own_public_key_y.compare(
+				asue_key_data->get_type_data_offset(offset, y_key_element_length),
+				y_key_element_length) != 0)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+			}
+
+			ae_key_data = asue_key_data->get_next_payload_with_same_tlv_type();
+			if (asue_key_data == 0
+				|| asue_key_data->get_is_valid_data() == false)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+			}
+
+		}
+
+		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+		// Save AE key data.
+		{
+			u32_t offset(0ul);
+
+			const u8_t * const point_type = ae_key_data->get_type_data_offset(offset, sizeof(u8_t));
+			if (point_type == 0)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+			}
+
+			if (*point_type != WAI_EC_POINT_TYPE_NO_COMPRESSION_ID)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+			}
+
+			offset += sizeof(*point_type);
+
+			const u32_t x_key_element_length((1ul + ae_key_data->get_type_data_length() - sizeof(*point_type)) / 2ul);
+			const u32_t y_key_element_length(ae_key_data->get_type_data_length() - sizeof(*point_type) - x_key_element_length);
+
+			EAP_TRACE_DEBUG(
+				m_am_tools,
+				TRACE_FLAGS_DEFAULT,
+				(EAPL("WAPI_Core: this = 0x%08x, %s: sizeof(*point_type)=%d, *point_type=0x%02x, get_type_data_length()=%d, x_key_element_length=%d, y_key_element_length=%d\n"),
+				 this,
+				 (m_is_client == true ? "client": "server"),
+				 sizeof(*point_type),
+				 *point_type,
+				 ae_key_data->get_type_data_length(),
+				 x_key_element_length,
+				 y_key_element_length));
+
+			status = m_peer_public_key_x.set_copy_of_buffer(
+				ae_key_data->get_type_data_offset(offset, x_key_element_length),
+				x_key_element_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_peer_public_key_x"),
+				 m_peer_public_key_x.get_data(),
+				 m_peer_public_key_x.get_data_length()));
+
+			offset += x_key_element_length;
+
+			status = m_peer_public_key_y.set_copy_of_buffer(
+				ae_key_data->get_type_data_offset(offset, y_key_element_length),
+				y_key_element_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_peer_public_key_y"),
+				 m_peer_public_key_y.get_data(),
+				 m_peer_public_key_y.get_data_length()));
+		}
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Verify Signature of AE.
+
+	{
+		if (ae_signature_trusted_by_asue == 0
+			|| ae_signature_trusted_by_asue->get_is_valid_data() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+
+		eap_variable_data_c signature_data(m_am_tools);
+		if (signature_data.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 received_ae_id(m_am_tools);
+		if (received_ae_id.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 = parse_signature_attributes(
+			ae_signature_trusted_by_asue,
+			&received_ae_id,
+			&signature_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("received_ae_id"),
+			 received_ae_id.get_data(),
+			 received_ae_id.get_data_length()));
+
+		if (received_ae_id.compare(&m_ae_id) != 0)
+		{
+			EAP_TRACE_DATA_DEBUG(
+				m_am_tools, 
+				TRACE_FLAGS_DEFAULT, 
+				(EAPL("ERROR: local m_ae_id"),
+				 m_ae_id.get_data(),
+				 m_ae_id.get_data_length()));
+
+			EAP_TRACE_DATA_DEBUG(
+				m_am_tools, 
+				TRACE_FLAGS_DEFAULT, 
+				(EAPL("ERROR: received AE-ID"),
+				 received_ae_id.get_data(received_ae_id.get_data_length()),
+				 received_ae_id.get_data_length()));
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+
+		eap_variable_data_c HASH(m_am_tools);
+		if (HASH.get_is_valid() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+		}
+
+		{
+			wai_message_payloads_c * const signature_payload = new wai_message_payloads_c(m_am_tools, m_is_client);
+			eap_automatic_variable_c<wai_message_payloads_c> automatic_signature_payload(m_am_tools, signature_payload);
+
+			if (signature_payload == 0
+				|| signature_payload->get_is_valid() == false)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+			}
+
+			// Copy all payloads except the last signature.
+			for (u32_t index = 0ul; (index+1ul) < parser.get_tlv_count(); ++index)
+			{
+				const wai_variable_data_c * const payload = parser.get_tlv(index);
+
+				if (payload != 0)
+				{
+					status = signature_payload->insert_payload(payload);
+					if (status != eap_status_ok)
+					{
+						EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+						return EAP_STATUS_RETURN(m_am_tools, status);
+					}
+				}
+			}
+
+			status = create_HASH(signature_payload, true, &HASH);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+		}
+
+		status = m_ec_certificate_store->set_ae_certificate(
+			&m_ae_certificate);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = m_ec_certificate_store->verify_signature_with_public_key(
+			&m_ae_id,
+			&HASH,
+			&signature_data,
+			true);
+		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);
+}
+
+//--------------------------------------------------
+
+#if defined(USE_WAPI_CORE_SERVER)
+
+eap_status_e wapi_core_c::handle_access_authentication_request(
+	const eap_am_network_id_c * const receive_network_id,
+	const wai_protocol_packet_header_c * const wai)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("WAPI_Core: this = 0x%08x, %s: wapi_core_c::handle_access_authentication_request(): state=%s\n"),
+		 this,
+		 (m_is_client == true ? "client": "server"),
+		 wapi_strings_c::get_wapi_core_state_string(m_wapi_state)));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: wapi_core_c::handle_access_authentication_request()");
+
+	if (m_authentication_type != eapol_key_authentication_type_WAI_certificate)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_wrong_authentication_type);
+	}
+
+	if (m_wapi_state != wapi_core_state_wait_access_authentication_request_message
+		&& m_wapi_state != wapi_core_state_wait_unicast_key_negotiation_response_message
+		&& m_wapi_state != wapi_core_state_authentication_ok)
+	{
+		EAP_TRACE_DEBUG(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("ERROR: WAI: %s: wapi_core_c::handle_access_authentication_request(): Verify state %s != %s, negotiation state = %s.\n"),
+			(m_is_client == true) ? "client": "server",
+			wapi_strings_c::get_wapi_core_state_string(m_wapi_state),
+			wapi_strings_c::get_wapi_core_state_string(wapi_core_state_wait_multicast_announcement_message),
+			wapi_strings_c::get_wapi_negotiation_state_string(m_wapi_negotiation_state)));
+
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_unexpected_message);
+	}
+
+	eap_status_e status(eap_status_process_general_error);
+
+	if (receive_network_id == 0
+		|| receive_network_id->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 (wai == 0
+		|| wai->get_is_valid() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_process_illegal_packet_error);
+	}
+
+	status = wai->check_header();
+
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	if (wai->get_packet_sequence_number() != m_packet_sequence_number)
+	{
+		EAP_TRACE_DEBUG(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("ERROR: WAI: %s: wapi_core_c::handle_access_authentication_request(): sequence number %d != required sequence number %d.\n"),
+			(m_is_client == true) ? "client": "server",
+			wai->get_packet_sequence_number(),
+			m_packet_sequence_number));
+
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_unexpected_message);
+	}
+
+	set_wapi_state(wapi_core_state_process_access_authentication_request_message);
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	wai_message_payloads_c parser(
+		m_am_tools,
+		m_is_client);
+	if (parser.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 padding_length(0ul);
+
+	status = parser.parse_wai_payloads(
+		wai->get_header_buffer(wai->get_header_buffer_length()), ///< This is the start of the message buffer.
+		wai->get_header_buffer_length(), ///< This is the length of the buffer. This must match with the length of all payloads.
+		&padding_length ///< Length of possible padding is set to this variable.
+		);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Verify BK rekeying flag.
+
+	{
+		wai_variable_data_c * const flag_payload = parser.get_tlv_pointer(wai_payload_type_flag);
+		if (flag_payload == 0
+			|| flag_payload->get_is_valid_data() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+
+		const u8_t * const flag = flag_payload->get_data(sizeof(*flag));
+		if (flag == 0)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+
+		m_do_certificate_validation = false;
+
+		if (m_wapi_negotiation_state == wapi_negotiation_state_rekeying)
+		{
+			if (((*flag) & wai_data_flag_mask_BK_Rekeying) == 0)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+			}
+
+			if (((*flag) & wai_data_flag_mask_Certificate_Validation_Request) != 0)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+			}
+
+			EAP_TRACE_DEBUG(
+				m_am_tools,
+				TRACE_FLAGS_DEFAULT,
+				(EAPL("WAPI_Core: this = 0x%08x, %s: wapi_core_c::handle_access_authentication_request(): no certificate validation\n"),
+				this,
+				(m_is_client == true ? "client": "server")));
+		}
+		else
+		{
+			if (((*flag) & wai_data_flag_mask_BK_Rekeying) != 0)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+			}
+
+			if (((*flag) & wai_data_flag_mask_Certificate_Validation_Request) == 0)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+			}
+
+			EAP_TRACE_DEBUG(
+				m_am_tools,
+				TRACE_FLAGS_DEFAULT,
+				(EAPL("WAPI_Core: this = 0x%08x, %s: wapi_core_c::handle_access_authentication_request(): does certificate validation\n"),
+				this,
+				(m_is_client == true ? "client": "server")));
+
+			m_do_certificate_validation = true;
+		}
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Verify the Authentication identifier parameter.
+
+	{
+		wai_variable_data_c * const authentication_identifier = parser.get_tlv_pointer(wai_payload_type_authentication_identifier);
+		if (authentication_identifier == 0
+			|| authentication_identifier->get_is_valid_data() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+
+		if (authentication_identifier->get_type_data_length() != m_authentication_identifier.get_data_length()
+			|| m_am_tools->memcmp(
+				m_authentication_identifier.get_data(),
+				authentication_identifier->get_type_data(authentication_identifier->get_type_data_length()),
+				authentication_identifier->get_type_data_length()) != 0)
+		{
+			EAP_TRACE_DATA_DEBUG(
+				m_am_tools, 
+				TRACE_FLAGS_DEFAULT, 
+				(EAPL("ERROR: local m_authentication_identifier"),
+				 m_authentication_identifier.get_data(),
+				 m_authentication_identifier.get_data_length()));
+
+			EAP_TRACE_DATA_DEBUG(
+				m_am_tools, 
+				TRACE_FLAGS_DEFAULT, 
+				(EAPL("ERROR: received Authentication identifier"),
+				 authentication_identifier->get_type_data(authentication_identifier->get_type_data_length()),
+				 authentication_identifier->get_type_data_length()));
+
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_not_supported);
+		}
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Verify the ECDH parameter.
+
+	{
+		wai_variable_data_c * const echd_parameter = parser.get_tlv_pointer(wai_payload_type_echd_parameter);
+		if (echd_parameter == 0
+			|| echd_parameter->get_is_valid_data() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+
+		if (sizeof(WAPI_ECDH_OID_PARAMETER) != echd_parameter->get_type_data_length()
+			|| m_am_tools->memcmp(
+				WAPI_ECDH_OID_PARAMETER,
+				echd_parameter->get_type_data(sizeof(WAPI_ECDH_OID_PARAMETER)),
+				sizeof(WAPI_ECDH_OID_PARAMETER)) != 0)
+		{
+			EAP_TRACE_DATA_DEBUG(
+				m_am_tools, 
+				TRACE_FLAGS_DEFAULT, 
+				(EAPL("ERROR: local ECDH parameter"),
+				 WAPI_ECDH_OID_PARAMETER,
+				 sizeof(WAPI_ECDH_OID_PARAMETER)));
+
+			EAP_TRACE_DATA_DEBUG(
+				m_am_tools, 
+				TRACE_FLAGS_DEFAULT, 
+				(EAPL("ERROR: received ECDH parameter"),
+				 echd_parameter->get_type_data(echd_parameter->get_type_data_length()),
+				 echd_parameter->get_type_data_length()));
+
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_not_supported);
+		}
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Verify the ID of STA_AE parameter.
+
+	{
+		wai_variable_data_c * const ae_id = parser.get_tlv_pointer(wai_payload_type_identity);
+		if (ae_id == 0
+			|| ae_id->get_is_valid_data() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+
+		if (m_ae_id.get_data_length() != ae_id->get_type_data_length()
+			|| m_am_tools->memcmp(
+				ae_id->get_type_data(m_ae_id.get_data_length()),
+				m_ae_id.get_data(),
+				m_ae_id.get_data_length()) != 0)
+		{
+			EAP_TRACE_DATA_DEBUG(
+				m_am_tools, 
+				TRACE_FLAGS_DEFAULT, 
+				(EAPL("ERROR: local m_ae_id"),
+				 m_ae_id.get_data(),
+				 m_ae_id.get_data_length()));
+
+			EAP_TRACE_DATA_DEBUG(
+				m_am_tools, 
+				TRACE_FLAGS_DEFAULT, 
+				(EAPL("ERROR: received AE-ID"),
+				 ae_id->get_type_data(ae_id->get_type_data_length()),
+				 ae_id->get_type_data_length()));
+
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+		else
+		{
+			EAP_TRACE_DATA_DEBUG(
+				m_am_tools, 
+				TRACE_FLAGS_DEFAULT, 
+				(EAPL("m_ae_id"),
+				 m_ae_id.get_data(),
+				 m_ae_id.get_data_length()));
+		}
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Save ASUE challenge.
+
+	{
+		wai_variable_data_c * const asue_challenge = parser.get_tlv_pointer(wai_payload_type_nonce);
+		if (asue_challenge == 0
+			|| asue_challenge->get_is_valid_data() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+
+		status = m_asue_certificate_challenge.set_copy_of_buffer(
+			asue_challenge->get_type_data(asue_challenge->get_type_data_length()),
+			asue_challenge->get_type_data_length());
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Save ASUE key data.
+
+	{
+		wai_variable_data_c * const asue_key_data = parser.get_tlv_pointer(wai_payload_type_key_data);
+		if (asue_key_data == 0
+			|| asue_key_data->get_is_valid_data() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+
+		u32_t offset(0ul);
+
+		const u8_t * const point_type = asue_key_data->get_type_data_offset(offset, sizeof(u8_t));
+		if (point_type == 0)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+
+		if (*point_type != WAI_EC_POINT_TYPE_NO_COMPRESSION_ID)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+
+		offset += sizeof(*point_type);
+
+		const u32_t x_key_element_length((1ul + asue_key_data->get_type_data_length() - sizeof(*point_type)) / 2ul);
+		const u32_t y_key_element_length(asue_key_data->get_type_data_length() - sizeof(*point_type) - x_key_element_length);
+
+		EAP_TRACE_DEBUG(
+			m_am_tools,
+			TRACE_FLAGS_DEFAULT,
+			(EAPL("WAPI_Core: this = 0x%08x, %s: sizeof(*point_type)=%d, *point_type=0x%02x, get_type_data_length()=%d, x_key_element_length=%d, y_key_element_length=%d\n"),
+			 this,
+			 (m_is_client == true ? "client": "server"),
+			 sizeof(*point_type),
+			 *point_type,
+			 asue_key_data->get_type_data_length(),
+			 x_key_element_length,
+			 y_key_element_length));
+
+		status = m_peer_public_key_x.set_copy_of_buffer(
+			asue_key_data->get_type_data_offset(offset, x_key_element_length),
+			x_key_element_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_peer_public_key_x"),
+			 m_peer_public_key_x.get_data(),
+			 m_peer_public_key_x.get_data_length()));
+
+		offset += x_key_element_length;
+
+		status = m_peer_public_key_y.set_copy_of_buffer(
+			asue_key_data->get_type_data_offset(offset, y_key_element_length),
+			y_key_element_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_peer_public_key_y"),
+			 m_peer_public_key_y.get_data(),
+			 m_peer_public_key_y.get_data_length()));
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Save STA_ASUE certificate.
+
+	{
+		wai_variable_data_c * const sta_asue_certificate = parser.get_tlv_pointer(wai_payload_type_certificate);
+		if (sta_asue_certificate == 0
+			|| sta_asue_certificate->get_is_valid_data() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+
+		status = m_peer_certificate.set_copy_of_buffer(
+			sta_asue_certificate->get_type_data(sta_asue_certificate->get_type_data_length()),
+			sta_asue_certificate->get_type_data_length());
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Verify Signature of ASUE.
+
+	{
+		wai_variable_data_c * const signature_payload = parser.get_tlv_pointer(wai_payload_type_signature_attributes);
+		if (signature_payload == 0
+			|| signature_payload->get_is_valid_data() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+
+		eap_variable_data_c signature_data(m_am_tools);
+		if (signature_data.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 = parse_signature_attributes(
+			signature_payload,
+			&m_asue_id,
+			&signature_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("m_asue_id"),
+			 m_asue_id.get_data(),
+			 m_asue_id.get_data_length()));
+
+		eap_variable_data_c HASH(m_am_tools);
+		if (HASH.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_HASH(&parser, false, &HASH);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = m_ec_certificate_store->verify_signature_with_public_key(
+			&m_asue_id,
+			&HASH,
+			&signature_data,
+			false);
+		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);
+}
+
+#endif //#if defined(USE_WAPI_CORE_SERVER)
+
+//--------------------------------------------------
+
+eap_status_e wapi_core_c::handle_unicast_key_negotiation_request(
+	const eap_am_network_id_c * const receive_network_id,
+	const wai_protocol_packet_header_c * const wai)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("WAPI_Core: this = 0x%08x, %s: wapi_core_c::handle_unicast_key_negotiation_request(): state=%s, negotiation state = %s\n"),
+		 this,
+		 (m_is_client == true ? "client": "server"),
+		 wapi_strings_c::get_wapi_core_state_string(m_wapi_state),
+		 wapi_strings_c::get_wapi_negotiation_state_string(m_wapi_negotiation_state)));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: wapi_core_c::handle_unicast_key_negotiation_request()");
+
+	eap_status_e status(eap_status_process_general_error);
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	if (m_wapi_state != wapi_core_state_wait_unicast_key_negotiation_request_message
+		&& m_wapi_state != wapi_core_state_wait_unicast_key_negotiation_confirmation_message
+		&& m_wapi_state != wapi_core_state_authentication_ok)
+	{
+		EAP_TRACE_DEBUG(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("ERROR: WAI: %s: wapi_core_c::handle_unicast_key_negotiation_request(): Verify state %s != %s, negotiation state = %s.\n"),
+			(m_is_client == true) ? "client": "server",
+			wapi_strings_c::get_wapi_core_state_string(m_wapi_state),
+			wapi_strings_c::get_wapi_core_state_string(wapi_core_state_wait_unicast_key_negotiation_request_message),
+			wapi_strings_c::get_wapi_negotiation_state_string(m_wapi_negotiation_state)));
+
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_unexpected_message);
+	}
+
+	if (m_authentication_type != eapol_key_authentication_type_WAI_PSK
+		&& m_authentication_type != eapol_key_authentication_type_WAI_certificate)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_wrong_authentication_type);
+	}
+
+	if (receive_network_id == 0
+		|| receive_network_id->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 (wai == 0
+		|| wai->get_is_valid() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_process_illegal_packet_error);
+	}
+
+	status = wai->check_header();
+
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	if (wai->get_packet_sequence_number() != (m_packet_sequence_number + 1u))
+	{
+		EAP_TRACE_DEBUG(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("ERROR: WAI: %s: wapi_core_c::handle_unicast_key_negotiation_request(): sequence number %d != required sequence number %d.\n"),
+			(m_is_client == true) ? "client": "server",
+			wai->get_packet_sequence_number(),
+			(m_packet_sequence_number + 1u)));
+
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_unexpected_message);
+	}
+
+	++m_packet_sequence_number;
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	if (m_authentication_type == eapol_key_authentication_type_WAI_PSK)
+	{
+		// 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 (send_network_id.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);
+		}
+
+		// This is notification to eapol_core_c object.
+		// WAI unicast negotiation started successfully.
+		eap_state_notification_c * notification = new eap_state_notification_c(
+			m_am_tools,
+			&send_network_id,
+			m_is_client,
+			eap_state_notification_generic,
+			eap_protocol_layer_wai,
+			eapol_key_handshake_type_wai_handshake,
+			eapol_key_state_wapi_authentication_running,
+			eapol_key_state_wapi_authentication_running,
+			0ul,
+			false);
+		if (notification == 0)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+		}
+		m_partner->state_notification(notification);
+
+		delete notification;
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	status = create_BKID(&m_BKID, receive_network_id);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	wai_message_payloads_c parser(
+		m_am_tools,
+		m_is_client);
+	if (parser.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 padding_length(0ul);
+
+	status = parser.parse_wai_payloads(
+		wai->get_header_buffer(wai->get_header_buffer_length()), ///< This is the start of the message buffer.
+		wai->get_header_buffer_length(), ///< This is the length of the buffer. This must match with the length of all payloads.
+		&padding_length ///< Length of possible padding is set to this variable.
+		);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Verify BKID.
+
+	{
+		wai_variable_data_c * const BKID_payload = parser.get_tlv_pointer(wai_payload_type_bkid);
+		if (BKID_payload == 0
+			|| BKID_payload->get_is_valid_data() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+
+		if (m_BKID.compare(
+			BKID_payload->get_data(BKID_payload->get_data_length()),
+			BKID_payload->get_data_length()) != 0)
+		{
+			EAP_TRACE_DATA_DEBUG(
+				m_am_tools, 
+				TRACE_FLAGS_DEFAULT, 
+				(EAPL("ERROR: local m_BKID"),
+				 m_BKID.get_data(),
+				 m_BKID.get_data_length()));
+
+			EAP_TRACE_DATA_DEBUG(
+				m_am_tools, 
+				TRACE_FLAGS_DEFAULT, 
+				(EAPL("ERROR: received BKID"),
+				 BKID_payload->get_data(BKID_payload->get_data_length()),
+				 BKID_payload->get_data_length()));
+
+#if defined(WAPI_SKIP_BKID_TEST)
+			EAP_TRACE_DEBUG(
+				m_am_tools,
+				TRACE_FLAGS_DEFAULT,
+				(EAPL("WARNING: WAPI_Core: this = 0x%08x, %s: wapi_core_c::handle_unicast_key_negotiation_request(): Skips BKID test.\n"),
+				 this,
+				 (m_is_client == true ? "client": "server")));
+#else
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+#endif //#if !defined(WAPI_SKIP_BKID_TEST)
+
+		}
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Verify USK rekeying flag.
+
+	{
+		wai_variable_data_c * const flag_payload = parser.get_tlv_pointer(wai_payload_type_flag);
+		if (flag_payload == 0
+			|| flag_payload->get_is_valid_data() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+
+		const u8_t * const flag = flag_payload->get_data(sizeof(*flag));
+		if (flag == 0)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+
+		if (m_wapi_negotiation_state == wapi_negotiation_state_rekeying)
+		{
+			if (((*flag) & wai_data_flag_mask_USK_Rekeying) == 0)
+			{
+				m_wapi_negotiation_state = wapi_negotiation_state_initial_negotiation;
+
+				EAP_TRACE_DEBUG(
+					m_am_tools,
+					TRACE_FLAGS_DEFAULT,
+					(EAPL("WARNING: WAPI_Core: this = 0x%08x, %s: wapi_core_c::handle_unicast_key_negotiation_request(): change to %s.\n"),
+					 this,
+					 (m_is_client == true ? "client": "server"),
+					 wapi_strings_c::get_wapi_negotiation_state_string(m_wapi_negotiation_state)));
+			}
+		}
+		else
+		{
+			if (((*flag) & wai_data_flag_mask_USK_Rekeying) != 0)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+			}
+		}
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Verify USKID.
+
+	{
+		wai_variable_data_c * const USKID_payload = parser.get_tlv_pointer(wai_payload_type_uskid);
+		if (USKID_payload == 0
+			|| USKID_payload->get_is_valid_data() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+
+		const u8_t * const USKID_pointer = USKID_payload->get_data(sizeof(*USKID_pointer));
+		if (USKID_pointer == 0)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+
+		u8_t USKID = (*USKID_pointer) & wai_data_uskid_mask_uskid;
+
+		if (USKID >= WAPI_USKSA_COUNT
+			|| m_USKSA[USKID] == 0
+			|| m_USKSA[USKID]->get_is_valid_data() == true)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+
+		m_USKID = USKID;
+
+		m_USKSA[USKID]->set_USKID(m_USKID);
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Save AE challenge.
+
+	{
+		wai_variable_data_c * const ae_challenge = parser.get_tlv_pointer(wai_payload_type_nonce);
+		if (ae_challenge == 0
+			|| ae_challenge->get_is_valid_data() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+
+		if (m_wapi_negotiation_state == wapi_negotiation_state_rekeying)
+		{
+			// Verify the AE challenge.
+			if (m_am_tools->memcmp(m_next_unicast_challenge.get_data(WAPI_CHALLENGE_LENGTH), ae_challenge->get_data(WAPI_CHALLENGE_LENGTH), WAPI_CHALLENGE_LENGTH) != 0)
+			{
+				EAP_TRACE_DATA_DEBUG(
+					m_am_tools, 
+					TRACE_FLAGS_DEFAULT, 
+					(EAPL("ERROR: local m_next_unicast_challenge"),
+					 m_next_unicast_challenge.get_data(),
+					 m_next_unicast_challenge.get_data_length()));
+
+				EAP_TRACE_DATA_DEBUG(
+					m_am_tools, 
+					TRACE_FLAGS_DEFAULT, 
+					(EAPL("ERROR: received AE Challenge"),
+					 ae_challenge->get_data(WAPI_CHALLENGE_LENGTH),
+					 WAPI_CHALLENGE_LENGTH));
+
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+			}
+		}
+
+		status = m_ae_unicast_challenge.set_copy_of_buffer(ae_challenge->get_data(WAPI_CHALLENGE_LENGTH), WAPI_CHALLENGE_LENGTH);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Create ASUE challenge.
+
+	{
+		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(
+			&m_asue_unicast_challenge,
+			WAPI_CHALLENGE_LENGTH);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Create unicast session key.
+
+	status = create_unicast_key(
+		&m_BK,
+		receive_network_id,
+		&m_ae_unicast_challenge,
+		&m_asue_unicast_challenge,
+		&m_unicast_encryption_key_UEK,
+		&m_unicast_integrity_check_key_UCK,
+		&m_message_authentication_key_MAK,
+		&m_key_encryption_key_KEK,
+		&m_next_unicast_challenge);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Create the Unicast Key Negotiation Response message.
+
+	wai_message_payloads_c * const payloads = new wai_message_payloads_c(m_am_tools, m_is_client);
+	// Automatic variable deletes payloads when control returns from this function.
+	eap_automatic_variable_c<wai_message_payloads_c> automatic_payloads(m_am_tools, payloads);
+
+	if (payloads == 0
+		|| 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);
+	}
+
+	status = payloads->initialise_header();
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = payloads->get_wai_protocol_packet_header_writable()->set_subtype(wai_protocol_subtype_unicast_key_negotiation_response);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Adds FLAG to data field.
+
+	{
+		wai_variable_data_c data_flag(m_am_tools);
+		if (data_flag.get_is_valid() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+		}
+
+		u8_t flag(wai_data_flag_mask_none);
+
+		if (m_wapi_negotiation_state == wapi_negotiation_state_rekeying)
+		{
+			flag = wai_data_flag_mask_USK_Rekeying;
+		}
+
+		status = data_flag.create(
+			wai_payload_type_flag,
+			&flag,
+			sizeof(flag));
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = payloads->add_tlv(&data_flag);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Adds BKID to data field.
+
+	{
+		wai_variable_data_c data_BKID(m_am_tools);
+		if (data_BKID.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 = data_BKID.create(
+			wai_payload_type_bkid,
+			&m_BKID);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = payloads->add_tlv(&data_BKID);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Adds USKID to data field.
+
+	{
+		wai_variable_data_c data_USKID(m_am_tools);
+		if (data_USKID.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 = data_USKID.create(
+			wai_payload_type_uskid,
+			&m_USKID,
+			sizeof(m_USKID));
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = payloads->add_tlv(&data_USKID);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Adds ADDID to data field.
+
+	{
+		wai_variable_data_c data_ADDID(m_am_tools);
+		if (data_ADDID.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 eap_variable_data_c * MAC_1 = receive_network_id->get_destination_id();
+		const eap_variable_data_c * MAC_2 = receive_network_id->get_source_id();
+
+		if (m_is_client == true)
+		{
+			MAC_1 = receive_network_id->get_source_id();
+			MAC_2 = receive_network_id->get_destination_id();
+		}
+
+		status = data_ADDID.create(
+			wai_payload_type_addid,
+			MAC_1);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = data_ADDID.add_data(
+			wai_payload_type_addid,
+			MAC_2);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = payloads->add_tlv(&data_ADDID);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Adds ASUE Challenge to data field.
+
+	{
+		wai_variable_data_c data_ASUE_challenge(m_am_tools);
+		if (data_ASUE_challenge.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 = data_ASUE_challenge.create(
+			wai_payload_type_nonce,
+			&m_asue_unicast_challenge);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = payloads->add_tlv(&data_ASUE_challenge);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Adds AE Challenge to data field.
+
+	{
+		wai_variable_data_c data_AE_challenge(m_am_tools);
+		if (data_AE_challenge.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 = data_AE_challenge.create(
+			wai_payload_type_nonce,
+			&m_ae_unicast_challenge);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = payloads->add_tlv(&data_AE_challenge);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Adds WIE ASUE to data field.
+
+	{
+		wai_variable_data_c data_WIE_ASUE(m_am_tools);
+		if (data_WIE_ASUE.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_ASSERT_TOOLS(m_am_tools, m_wapi_ie_asue.get_is_valid() == true && m_wapi_ie_asue.get_data_length() > 0ul);
+
+		status = data_WIE_ASUE.create(
+			wai_payload_type_wie,
+			&m_wapi_ie_asue);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = payloads->add_tlv(&data_WIE_ASUE);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Adds MAC to data field.
+
+	{
+		wai_variable_data_c data_MAC(m_am_tools);
+		if (data_MAC.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 MAC(m_am_tools);
+		if (MAC.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_MAC(payloads, &MAC);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = MAC.set_data_length(WAPI_MESSAGE_AUTHENTICATION_CODE_LENGTH);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = data_MAC.create(
+			wai_payload_type_message_authentication_code,
+			&MAC);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = payloads->add_tlv(&data_MAC);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Create and send message.
+
+	{
+		wai_message_c new_wai_message_data(m_am_tools, m_is_client);
+		if (new_wai_message_data.get_is_valid() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = payloads->create_wai_tlv_message(&new_wai_message_data, false);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		//cancel_retransmission();
+
+		status = packet_send(
+			&new_wai_message_data,
+			payloads->get_wai_protocol_packet_header_writable()->get_subtype());
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+
+	set_wapi_state(wapi_core_state_wait_unicast_key_negotiation_confirmation_message);
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+#if defined(USE_WAPI_CORE_SERVER)
+
+eap_status_e wapi_core_c::handle_unicast_key_negotiation_response(
+	const eap_am_network_id_c * const receive_network_id,
+	const wai_protocol_packet_header_c * const wai)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("WAPI_Core: this = 0x%08x, %s: wapi_core_c::handle_unicast_key_negotiation_response(): state=%s\n"),
+		 this,
+		 (m_is_client == true ? "client": "server"),
+		 wapi_strings_c::get_wapi_core_state_string(m_wapi_state)));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: wapi_core_c::handle_unicast_key_negotiation_response()");
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	if (m_wapi_state != wapi_core_state_wait_unicast_key_negotiation_response_message
+		&& m_wapi_state != wapi_core_state_wait_multicast_announcement_response_message)
+	{
+		EAP_TRACE_DEBUG(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("ERROR: WAI: %s: wapi_core_c::handle_unicast_key_negotiation_response(): Verify state %s != %s, negotiation state = %s.\n"),
+			(m_is_client == true) ? "client": "server",
+			wapi_strings_c::get_wapi_core_state_string(m_wapi_state),
+			wapi_strings_c::get_wapi_core_state_string(wapi_core_state_wait_unicast_key_negotiation_response_message),
+			wapi_strings_c::get_wapi_negotiation_state_string(m_wapi_negotiation_state)));
+
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_unexpected_message);
+	}
+
+	if (m_authentication_type != eapol_key_authentication_type_WAI_PSK
+		&& m_authentication_type != eapol_key_authentication_type_WAI_certificate)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_wrong_authentication_type);
+	}
+
+	eap_status_e status(eap_status_process_general_error);
+
+	if (receive_network_id == 0
+		|| receive_network_id->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 (wai == 0
+		|| wai->get_is_valid() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_process_illegal_packet_error);
+	}
+
+	status = wai->check_header();
+
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	if (wai->get_packet_sequence_number() != m_packet_sequence_number)
+	{
+		EAP_TRACE_DEBUG(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("ERROR: WAI: %s: wapi_core_c::handle_unicast_key_negotiation_response(): sequence number %d != required sequence number %d.\n"),
+			(m_is_client == true) ? "client": "server",
+			wai->get_packet_sequence_number(),
+			m_packet_sequence_number));
+
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_unexpected_message);
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	wai_message_payloads_c parser(
+		m_am_tools,
+		m_is_client);
+	if (parser.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 padding_length(0ul);
+
+	status = parser.parse_wai_payloads(
+		wai->get_header_buffer(wai->get_header_buffer_length()), ///< This is the start of the message buffer.
+		wai->get_header_buffer_length(), ///< This is the length of the buffer. This must match with the length of all payloads.
+		&padding_length ///< Length of possible padding is set to this variable.
+		);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Verify BKID.
+
+	{
+		wai_variable_data_c * const BKID_payload = parser.get_tlv_pointer(wai_payload_type_bkid);
+		if (BKID_payload == 0
+			|| BKID_payload->get_is_valid_data() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+
+		if (m_BKID.compare(
+			BKID_payload->get_data(BKID_payload->get_data_length()),
+			BKID_payload->get_data_length()) != 0)
+		{
+			EAP_TRACE_DATA_DEBUG(
+				m_am_tools, 
+				TRACE_FLAGS_DEFAULT, 
+				(EAPL("ERROR: local m_BKID"),
+				 m_BKID.get_data(),
+				 m_BKID.get_data_length()));
+
+			EAP_TRACE_DATA_DEBUG(
+				m_am_tools, 
+				TRACE_FLAGS_DEFAULT, 
+				(EAPL("ERROR: received BKID"),
+				 BKID_payload->get_data(BKID_payload->get_data_length()),
+				 BKID_payload->get_data_length()));
+
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Verify USK rekeying flag.
+
+	{
+		wai_variable_data_c * const flag_payload = parser.get_tlv_pointer(wai_payload_type_flag);
+		if (flag_payload == 0
+			|| flag_payload->get_is_valid_data() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+
+		const u8_t * const flag = flag_payload->get_data(sizeof(*flag));
+		if (flag == 0)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+
+		if (m_wapi_negotiation_state == wapi_negotiation_state_rekeying)
+		{
+			if (((*flag) & wai_data_flag_mask_USK_Rekeying) == 0)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+			}
+		}
+		else
+		{
+			if (((*flag) & wai_data_flag_mask_USK_Rekeying) != 0)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+			}
+		}
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Verify USKID.
+
+	{
+		wai_variable_data_c * const USKID_payload = parser.get_tlv_pointer(wai_payload_type_uskid);
+		if (USKID_payload == 0
+			|| USKID_payload->get_is_valid_data() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+
+		const u8_t * const USKID_pointer = USKID_payload->get_data(sizeof(*USKID_pointer));
+		if (USKID_pointer == 0)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+
+		u8_t USKID = (*USKID_pointer) & wai_data_uskid_mask_uskid;
+
+		if (USKID >= WAPI_USKSA_COUNT
+			|| m_USKSA[USKID] == 0
+			|| m_USKSA[USKID]->get_is_valid_data() == true)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+
+		m_USKID = USKID;
+
+		m_USKSA[USKID]->set_USKID(m_USKID);
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Save ASUE challenge.
+
+	{
+		wai_variable_data_c * const asue_challenge = parser.get_tlv_pointer(wai_payload_type_nonce);
+		if (asue_challenge == 0
+			|| asue_challenge->get_is_valid_data() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+
+		status = m_asue_unicast_challenge.set_copy_of_buffer(asue_challenge->get_data(WAPI_CHALLENGE_LENGTH), WAPI_CHALLENGE_LENGTH);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+		// Verify AE challenge.
+
+		wai_variable_data_c * const ae_challenge = asue_challenge->get_next_payload_with_same_tlv_type();
+		if (ae_challenge == 0
+			|| ae_challenge->get_is_valid_data() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+
+		if (m_ae_unicast_challenge.compare(
+			ae_challenge->get_data(ae_challenge->get_data_length()),
+			ae_challenge->get_data_length()) != 0)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Verify WIE_ASUE.
+
+	if (m_wapi_negotiation_state == wapi_negotiation_state_initial_negotiation)
+	{
+		wai_variable_data_c * const wie_asue = parser.get_tlv_pointer(wai_payload_type_wie);
+		if (wie_asue == 0
+			|| wie_asue->get_is_valid_data() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+
+		if (m_wapi_ie_asue.compare(
+			wie_asue->get_data(wie_asue->get_data_length()),
+			wie_asue->get_data_length()) != 0)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Create unicast session key.
+
+	status = create_unicast_key(
+		&m_BK,
+		receive_network_id,
+		&m_ae_unicast_challenge,
+		&m_asue_unicast_challenge,
+		&m_unicast_encryption_key_UEK,
+		&m_unicast_integrity_check_key_UCK,
+		&m_message_authentication_key_MAK,
+		&m_key_encryption_key_KEK,
+		&m_next_unicast_challenge);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Verify MAC.
+
+	{
+		wai_variable_data_c * const received_MAC = parser.get_tlv_pointer(wai_payload_type_message_authentication_code);
+		if (received_MAC == 0
+			|| received_MAC->get_is_valid_data() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+
+		eap_variable_data_c local_MAC(m_am_tools);
+		if (local_MAC.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_MAC(&parser, &local_MAC);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = local_MAC.set_data_length(WAPI_MESSAGE_AUTHENTICATION_CODE_LENGTH);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		if (local_MAC.compare(
+			received_MAC->get_data(received_MAC->get_data_length()),
+			received_MAC->get_data_length()) != 0)
+		{
+			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("WAI: %s: wapi_core_c::handle_unicast_key_negotiation_response(): MAC OK.\n"),
+				(m_is_client == true) ? "client": "server"));
+		}
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Create the Unicast Key Negotiation Confirmation message.
+
+	wai_message_payloads_c * const payloads = new wai_message_payloads_c(m_am_tools, m_is_client);
+	eap_automatic_variable_c<wai_message_payloads_c> automatic_payloads(m_am_tools, payloads);
+
+	if (payloads == 0
+		|| 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);
+	}
+
+	status = payloads->initialise_header();
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = payloads->get_wai_protocol_packet_header_writable()->set_subtype(wai_protocol_subtype_unicast_key_negotiation_confirmation);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Adds FLAG to data field.
+
+	{
+		wai_variable_data_c data_flag(m_am_tools);
+		if (data_flag.get_is_valid() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+		}
+
+		u8_t flag(wai_data_flag_mask_none);
+
+		if (m_wapi_negotiation_state == wapi_negotiation_state_rekeying)
+		{
+			flag = wai_data_flag_mask_USK_Rekeying;
+		}
+
+		status = data_flag.create(
+			wai_payload_type_flag,
+			&flag,
+			sizeof(flag));
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = payloads->add_tlv(&data_flag);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Adds BKID to data field.
+
+	{
+		wai_variable_data_c data_BKID(m_am_tools);
+		if (data_BKID.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 = data_BKID.create(
+			wai_payload_type_bkid,
+			&m_BKID);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = payloads->add_tlv(&data_BKID);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Adds USKID to data field.
+
+	{
+		wai_variable_data_c data_USKID(m_am_tools);
+		if (data_USKID.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 = data_USKID.create(
+			wai_payload_type_uskid,
+			&m_USKID,
+			sizeof(m_USKID));
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = payloads->add_tlv(&data_USKID);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Adds ADDID to data field.
+
+	{
+		wai_variable_data_c data_ADDID(m_am_tools);
+		if (data_ADDID.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 eap_variable_data_c * MAC_1 = receive_network_id->get_source_id();
+		const eap_variable_data_c * MAC_2 = receive_network_id->get_destination_id();
+
+		if (m_is_client == true)
+		{
+			MAC_1 = receive_network_id->get_destination_id();
+			MAC_2 = receive_network_id->get_source_id();
+		}
+
+		status = data_ADDID.create(
+			wai_payload_type_addid,
+			MAC_1);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = data_ADDID.add_data(
+			wai_payload_type_addid,
+			MAC_2);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = payloads->add_tlv(&data_ADDID);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Adds ASUE Challenge to data field.
+
+	{
+		wai_variable_data_c data_ASUE_challenge(m_am_tools);
+		if (data_ASUE_challenge.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 = data_ASUE_challenge.create(
+			wai_payload_type_nonce,
+			&m_asue_unicast_challenge);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = payloads->add_tlv(&data_ASUE_challenge);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Adds WIE AE to data field.
+
+	{
+		wai_variable_data_c data_WIE_AE(m_am_tools);
+		if (data_WIE_AE.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 = data_WIE_AE.create(
+			wai_payload_type_wie,
+			&m_wapi_ie_ae);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = payloads->add_tlv(&data_WIE_AE);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Adds MAC to data field.
+
+	{
+		wai_variable_data_c data_MAC(m_am_tools);
+		if (data_MAC.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 MAC(m_am_tools);
+		if (MAC.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_MAC(payloads, &MAC);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = MAC.set_data_length(WAPI_MESSAGE_AUTHENTICATION_CODE_LENGTH);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = data_MAC.create(
+			wai_payload_type_message_authentication_code,
+			&MAC);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = payloads->add_tlv(&data_MAC);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Create and send message.
+
+	wai_message_c new_wai_message_data(m_am_tools, m_is_client);
+	if (new_wai_message_data.get_is_valid() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = payloads->create_wai_tlv_message(&new_wai_message_data, false);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	//cancel_retransmission();
+
+	status = packet_send(
+		&new_wai_message_data,
+		payloads->get_wai_protocol_packet_header_writable()->get_subtype());
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Install unicast session key.
+
+	{
+		eap_variable_data_c unicast_session_key(m_am_tools);
+		if (unicast_session_key.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 = unicast_session_key.set_copy_of_buffer(&m_unicast_encryption_key_UEK);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = unicast_session_key.add_data(&m_unicast_integrity_check_key_UCK);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = packet_data_session_key(
+			&unicast_session_key,
+			eapol_key_type_unicast,
+			m_USKID,
+			false,
+			m_packet_data_number.get_data(),
+			m_packet_data_number.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);
+		}
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	set_wapi_state(wapi_core_state_start_multicast_key_announcement);
+
+	status = start_multicast_key_announcement();
+	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);
+}
+
+#endif //#if defined(USE_WAPI_CORE_SERVER)
+
+//--------------------------------------------------
+
+eap_status_e wapi_core_c::handle_unicast_key_negotiation_confirmation(
+	const eap_am_network_id_c * const receive_network_id,
+	const wai_protocol_packet_header_c * const wai)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("WAPI_Core: this = 0x%08x, %s: wapi_core_c::handle_unicast_key_negotiation_confirmation(): state=%s\n"),
+		 this,
+		 (m_is_client == true ? "client": "server"),
+		 wapi_strings_c::get_wapi_core_state_string(m_wapi_state)));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: wapi_core_c::handle_unicast_key_negotiation_confirmation()");
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	if (m_wapi_state != wapi_core_state_wait_unicast_key_negotiation_confirmation_message
+		&& m_wapi_state != wapi_core_state_wait_multicast_announcement_message)
+	{
+		EAP_TRACE_DEBUG(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("ERROR: WAI: %s: wapi_core_c::handle_unicast_key_negotiation_confirmation(): Verify state %s != %s, negotiation state = %s.\n"),
+			(m_is_client == true) ? "client": "server",
+			wapi_strings_c::get_wapi_core_state_string(m_wapi_state),
+			wapi_strings_c::get_wapi_core_state_string(wapi_core_state_wait_unicast_key_negotiation_confirmation_message),
+			wapi_strings_c::get_wapi_negotiation_state_string(m_wapi_negotiation_state)));
+
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_unexpected_message);
+	}
+
+	if (m_authentication_type != eapol_key_authentication_type_WAI_PSK
+		&& m_authentication_type != eapol_key_authentication_type_WAI_certificate)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_wrong_authentication_type);
+	}
+
+	eap_status_e status(eap_status_process_general_error);
+
+	if (receive_network_id == 0
+		|| receive_network_id->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 (wai == 0
+		|| wai->get_is_valid() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_process_illegal_packet_error);
+	}
+
+	status = wai->check_header();
+
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	if (wai->get_packet_sequence_number() != (m_packet_sequence_number + 1u))
+	{
+		EAP_TRACE_DEBUG(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("ERROR: WAI: %s: wapi_core_c::handle_unicast_key_negotiation_confirmation(): sequence number %d != required sequence number %d.\n"),
+			(m_is_client == true) ? "client": "server",
+			wai->get_packet_sequence_number(),
+			(m_packet_sequence_number + 1u)));
+
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_unexpected_message);
+	}
+
+	++m_packet_sequence_number;
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	wai_message_payloads_c parser(
+		m_am_tools,
+		m_is_client);
+	if (parser.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 padding_length(0ul);
+
+	status = parser.parse_wai_payloads(
+		wai->get_header_buffer(wai->get_header_buffer_length()), ///< This is the start of the message buffer.
+		wai->get_header_buffer_length(), ///< This is the length of the buffer. This must match with the length of all payloads.
+		&padding_length ///< Length of possible padding is set to this variable.
+		);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Verify BKID.
+
+	{
+		wai_variable_data_c * const BKID_payload = parser.get_tlv_pointer(wai_payload_type_bkid);
+		if (BKID_payload == 0
+			|| BKID_payload->get_is_valid_data() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+
+		if (m_BKID.compare(
+			BKID_payload->get_data(BKID_payload->get_data_length()),
+			BKID_payload->get_data_length()) != 0)
+		{
+			EAP_TRACE_DATA_DEBUG(
+				m_am_tools, 
+				TRACE_FLAGS_DEFAULT, 
+				(EAPL("ERROR: local m_BKID"),
+				 m_BKID.get_data(),
+				 m_BKID.get_data_length()));
+
+			EAP_TRACE_DATA_DEBUG(
+				m_am_tools, 
+				TRACE_FLAGS_DEFAULT, 
+				(EAPL("ERROR: received BKID"),
+				 BKID_payload->get_data(BKID_payload->get_data_length()),
+				 BKID_payload->get_data_length()));
+
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Verify MAC.
+
+	{
+		wai_variable_data_c * const received_MAC = parser.get_tlv_pointer(wai_payload_type_message_authentication_code);
+		if (received_MAC == 0
+			|| received_MAC->get_is_valid_data() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+
+		eap_variable_data_c local_MAC(m_am_tools);
+		if (local_MAC.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_MAC(&parser, &local_MAC);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = local_MAC.set_data_length(WAPI_MESSAGE_AUTHENTICATION_CODE_LENGTH);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		if (local_MAC.compare(
+			received_MAC->get_data(received_MAC->get_data_length()),
+			received_MAC->get_data_length()) != 0)
+		{
+			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("WAI: %s: wapi_core_c::handle_unicast_key_negotiation_confirmation(): MAC OK.\n"),
+				(m_is_client == true) ? "client": "server"));
+		}
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Verify USK rekeying flag.
+
+	{
+		wai_variable_data_c * const flag_payload = parser.get_tlv_pointer(wai_payload_type_flag);
+		if (flag_payload == 0
+			|| flag_payload->get_is_valid_data() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+
+		const u8_t * const flag = flag_payload->get_data(sizeof(*flag));
+		if (flag == 0)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+
+		if (m_wapi_negotiation_state == wapi_negotiation_state_rekeying)
+		{
+			if (((*flag) & wai_data_flag_mask_USK_Rekeying) == 0)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+			}
+		}
+		else
+		{
+			if (((*flag) & wai_data_flag_mask_USK_Rekeying) != 0)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+			}
+		}
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Verify USKID.
+
+	{
+		wai_variable_data_c * const USKID_payload = parser.get_tlv_pointer(wai_payload_type_uskid);
+		if (USKID_payload == 0
+			|| USKID_payload->get_is_valid_data() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+
+		const u8_t * const USKID_pointer = USKID_payload->get_data(sizeof(*USKID_pointer));
+		if (USKID_pointer == 0)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+
+		u8_t USKID = (*USKID_pointer) & wai_data_uskid_mask_uskid;
+
+		if (USKID >= WAPI_USKSA_COUNT
+			|| m_USKSA[USKID] == 0
+			|| m_USKSA[USKID]->get_is_valid_data() == true)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+
+		m_USKID = USKID;
+
+		m_USKSA[USKID]->set_USKID(m_USKID);
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Verify ASUE challenge.
+
+	{
+
+		wai_variable_data_c * const asue_challenge = parser.get_tlv_pointer(wai_payload_type_nonce);
+		if (asue_challenge == 0
+			|| asue_challenge->get_is_valid_data() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+
+		if (m_asue_unicast_challenge.compare(
+			asue_challenge->get_data(asue_challenge->get_data_length()),
+			asue_challenge->get_data_length()) != 0)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Verify WIE_AE.
+
+	{
+		wai_variable_data_c * const wie_ae = parser.get_tlv_pointer(wai_payload_type_wie);
+		if (wie_ae == 0
+			|| wie_ae->get_is_valid_data() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+
+		if (m_wapi_ie_ae.compare(
+			wie_ae->get_data(wie_ae->get_data_length()),
+			wie_ae->get_data_length()) != 0)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Install unicast session key.
+
+	{
+		eap_variable_data_c unicast_session_key(m_am_tools);
+		if (unicast_session_key.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 = unicast_session_key.set_copy_of_buffer(&m_unicast_encryption_key_UEK);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = unicast_session_key.add_data(&m_unicast_integrity_check_key_UCK);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = packet_data_session_key(
+			&unicast_session_key,
+			eapol_key_type_unicast,
+			m_USKID,
+			false,
+			m_packet_data_number.get_data(),
+			m_packet_data_number.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_wapi_negotiation_state == wapi_negotiation_state_initial_negotiation)
+	{
+	    set_wapi_state(wapi_core_state_wait_multicast_announcement_message);
+	}
+	else if (m_wapi_negotiation_state == wapi_negotiation_state_rekeying)
+	{
+        // 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 (send_network_id.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);
+        }
+
+        eap_state_notification_c * notification = new eap_state_notification_c(
+            m_am_tools,
+            &send_network_id,
+            m_is_client,
+            eap_state_notification_generic,
+            eap_protocol_layer_wai,
+            eapol_key_handshake_type_wai_handshake,
+            eapol_key_state_wapi_authentication_running,
+            eapol_key_state_wapi_authentication_finished_successfull,
+            0ul,
+            false);
+        if (notification == 0)
+        {
+            EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+            return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+        }
+
+        state_notification(notification);
+
+        delete notification;
+
+		set_wapi_state(wapi_core_state_authentication_ok);
+	}
+	else
+	{
+		EAP_TRACE_DEBUG(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("ERROR: WAI: %s: wapi_core_c::handle_unicast_key_negotiation_confirmation(): negotiation state = %s.\n"),
+			(m_is_client == true) ? "client": "server",
+			wapi_strings_c::get_wapi_negotiation_state_string(m_wapi_negotiation_state)));
+
+        EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+        return EAP_STATUS_RETURN(m_am_tools, eap_status_authentication_failure);
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+eap_status_e wapi_core_c::handle_multicast_key_announcement(
+	const eap_am_network_id_c * const receive_network_id,
+	const wai_protocol_packet_header_c * const wai)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("WAPI_Core: this = 0x%08x, %s: wapi_core_c::handle_multicast_key_announcement(): state=%s\n"),
+		 this,
+		 (m_is_client == true ? "client": "server"),
+		 wapi_strings_c::get_wapi_core_state_string(m_wapi_state)));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: wapi_core_c::handle_multicast_key_announcement()");
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	if (m_wapi_state != wapi_core_state_wait_multicast_announcement_message
+		&& m_wapi_state != wapi_core_state_authentication_ok)
+	{
+		EAP_TRACE_DEBUG(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("ERROR: WAI: %s: wapi_core_c::handle_multicast_key_announcement(): Verify state %s != %s, negotiation state = %s.\n"),
+			(m_is_client == true) ? "client": "server",
+			wapi_strings_c::get_wapi_core_state_string(m_wapi_state),
+			wapi_strings_c::get_wapi_core_state_string(wapi_core_state_wait_multicast_announcement_message),
+			wapi_strings_c::get_wapi_negotiation_state_string(m_wapi_negotiation_state)));
+
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_unexpected_message);
+	}
+
+	if (m_authentication_type != eapol_key_authentication_type_WAI_PSK
+		&& m_authentication_type != eapol_key_authentication_type_WAI_certificate)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_wrong_authentication_type);
+	}
+
+	eap_status_e status(eap_status_process_general_error);
+
+	if (receive_network_id == 0
+		|| receive_network_id->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 (wai == 0
+		|| wai->get_is_valid() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_process_illegal_packet_error);
+	}
+
+	status = wai->check_header();
+
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	if (wai->get_packet_sequence_number() != (m_packet_sequence_number + 1u))
+	{
+		EAP_TRACE_DEBUG(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("ERROR: WAI: %s: wapi_core_c::handle_multicast_key_announcement(): sequence number %d != required sequence number %d.\n"),
+			(m_is_client == true) ? "client": "server",
+			wai->get_packet_sequence_number(),
+			(m_packet_sequence_number + 1u)));
+
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_unexpected_message);
+	}
+
+	++m_packet_sequence_number;
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	wai_message_payloads_c parser(
+		m_am_tools,
+		m_is_client);
+	if (parser.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 padding_length(0ul);
+
+	status = parser.parse_wai_payloads(
+		wai->get_header_buffer(wai->get_header_buffer_length()), ///< This is the start of the message buffer.
+		wai->get_header_buffer_length(), ///< This is the length of the buffer. This must match with the length of all payloads.
+		&padding_length ///< Length of possible padding is set to this variable.
+		);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Verify MAC.
+
+	{
+		wai_variable_data_c * const received_MAC = parser.get_tlv_pointer(wai_payload_type_message_authentication_code);
+		if (received_MAC == 0
+			|| received_MAC->get_is_valid_data() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+
+		eap_variable_data_c local_MAC(m_am_tools);
+		if (local_MAC.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_MAC(&parser, &local_MAC);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = local_MAC.set_data_length(WAPI_MESSAGE_AUTHENTICATION_CODE_LENGTH);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		if (local_MAC.compare(
+			received_MAC->get_data(received_MAC->get_data_length()),
+			received_MAC->get_data_length()) != 0)
+		{
+			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("WAI: %s: wapi_core_c::handle_multicast_key_announcement(): MAC OK.\n"),
+				(m_is_client == true) ? "client": "server"));
+		}
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Verify USK rekeying flag.
+
+	{
+		wai_variable_data_c * const flag_payload = parser.get_tlv_pointer(wai_payload_type_flag);
+		if (flag_payload == 0
+			|| flag_payload->get_is_valid_data() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+
+		const u8_t * const flag = flag_payload->get_data(sizeof(*flag));
+		if (flag == 0)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+
+		if (((*flag) & wai_data_flag_mask_USK_Rekeying) != 0)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Verify MSKID.
+
+	{
+		wai_variable_data_c * const MSKID_payload = parser.get_tlv_pointer(wai_payload_type_mskid_stakeyid);
+		if (MSKID_payload == 0
+			|| MSKID_payload->get_is_valid_data() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+
+		const u8_t * const MSKID_pointer = MSKID_payload->get_data(sizeof(*MSKID_pointer));
+		if (MSKID_pointer == 0)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+
+		u8_t MSKID = (*MSKID_pointer) & wai_data_uskid_mask_mskid;
+
+		if (MSKID >= WAPI_MSKSA_COUNT
+			|| m_MSKSA[MSKID] == 0
+			|| m_MSKSA[MSKID]->get_is_valid_data() == true)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+
+		m_MSKID = MSKID;
+
+		m_MSKSA[MSKID]->set_USKID(m_MSKID);
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Verify USKID.
+
+	{
+		wai_variable_data_c * const USKID_payload = parser.get_tlv_pointer(wai_payload_type_uskid);
+		if (USKID_payload == 0
+			|| USKID_payload->get_is_valid_data() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+
+		const u8_t * const USKID_pointer = USKID_payload->get_data(sizeof(*USKID_pointer));
+		if (USKID_pointer == 0)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+
+		u8_t USKID = (*USKID_pointer) & wai_data_uskid_mask_uskid;
+
+		if (USKID >= WAPI_USKSA_COUNT
+			|| m_USKSA[USKID] == 0
+			|| m_USKSA[USKID]->get_is_valid_data() == true)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+
+		m_USKID = USKID;
+
+		m_USKSA[USKID]->set_USKID(m_USKID);
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Read Data Packet Number.
+
+	{
+
+		wai_variable_data_c * const packet_data_number = parser.get_tlv_pointer(wai_payload_type_data_sequence_number);
+		if (packet_data_number == 0
+			|| packet_data_number->get_is_valid_data() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+
+		status = m_packet_data_number.set_copy_of_buffer(
+			packet_data_number->get_data(packet_data_number->get_data_length()),
+			packet_data_number->get_data_length());
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Read Key Announcement.
+
+	{
+
+		wai_variable_data_c * const key_announcement = parser.get_tlv_pointer(wai_payload_type_key_announcement_identifier);
+		if (key_announcement == 0
+			|| key_announcement->get_is_valid_data() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+
+		status = m_key_announcement.set_copy_of_buffer(
+			key_announcement->get_data(key_announcement->get_data_length()),
+			key_announcement->get_data_length());
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+
+		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+		// Read and decrypt Key Data.
+
+		{
+
+			wai_variable_data_c * const key_data = parser.get_tlv_pointer(wai_payload_type_key_data);
+			if (key_data == 0
+				|| key_data->get_is_valid_data() == false)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+			}
+
+			eap_variable_data_c notification_master_key(m_am_tools);
+			if (notification_master_key.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 = decrypt_multicast_key_data(key_data, &m_key_announcement, &notification_master_key);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			status = create_multicast_key(&notification_master_key, &m_multicast_key);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+		}
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Create the Multicast Key announcement response message.
+
+	wai_message_payloads_c * const payloads = new wai_message_payloads_c(m_am_tools, m_is_client);
+	eap_automatic_variable_c<wai_message_payloads_c> automatic_payloads(m_am_tools, payloads);
+
+	if (payloads == 0
+		|| 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);
+	}
+
+	status = payloads->initialise_header();
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = payloads->get_wai_protocol_packet_header_writable()->set_subtype(wai_protocol_subtype_multicast_key_announcement_response);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Adds FLAG to data field.
+
+	{
+		wai_variable_data_c data_flag(m_am_tools);
+		if (data_flag.get_is_valid() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+		}
+
+		u8_t flag(wai_data_flag_mask_none);
+
+		status = data_flag.create(
+			wai_payload_type_flag,
+			&flag,
+			sizeof(flag));
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = payloads->add_tlv(&data_flag);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Adds MSKID to data field.
+
+	{
+		wai_variable_data_c data_flag(m_am_tools);
+		if (data_flag.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 = data_flag.create(
+			wai_payload_type_mskid_stakeyid,
+			&m_MSKID,
+			sizeof(m_MSKID));
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = payloads->add_tlv(&data_flag);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Adds USKID to data field.
+
+	{
+		wai_variable_data_c data_USKID(m_am_tools);
+		if (data_USKID.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 = data_USKID.create(
+			wai_payload_type_uskid,
+			&m_USKID,
+			sizeof(m_USKID));
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = payloads->add_tlv(&data_USKID);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Adds ADDID to data field.
+
+	{
+		wai_variable_data_c data_ADDID(m_am_tools);
+		if (data_ADDID.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 eap_variable_data_c * MAC_1 = receive_network_id->get_destination_id();
+		const eap_variable_data_c * MAC_2 = receive_network_id->get_source_id();
+
+		if (m_is_client == true)
+		{
+			MAC_1 = receive_network_id->get_source_id();
+			MAC_2 = receive_network_id->get_destination_id();
+		}
+
+		status = data_ADDID.create(
+			wai_payload_type_addid,
+			MAC_1);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = data_ADDID.add_data(
+			wai_payload_type_addid,
+			MAC_2);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = payloads->add_tlv(&data_ADDID);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Adds Key Announcement to data field.
+
+	{
+		wai_variable_data_c key_announcement(m_am_tools);
+		if (key_announcement.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 = key_announcement.create(
+			wai_payload_type_data_sequence_number,
+			&m_key_announcement);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = payloads->add_tlv(&key_announcement);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Adds MAC to data field.
+
+	{
+		wai_variable_data_c data_MAC(m_am_tools);
+		if (data_MAC.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 MAC(m_am_tools);
+		if (MAC.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_MAC(payloads, &MAC);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = MAC.set_data_length(WAPI_MESSAGE_AUTHENTICATION_CODE_LENGTH);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = data_MAC.create(
+			wai_payload_type_message_authentication_code,
+			&MAC);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = payloads->add_tlv(&data_MAC);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Create and send message.
+
+	wai_message_c new_wai_message_data(m_am_tools, m_is_client);
+	if (new_wai_message_data.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 = payloads->create_wai_tlv_message(&new_wai_message_data, false);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	//cancel_retransmission();
+
+	status = packet_send(
+		&new_wai_message_data,
+		payloads->get_wai_protocol_packet_header_writable()->get_subtype());
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Install multicast session key.
+
+	status = packet_data_session_key(
+		&m_multicast_key,
+		eapol_key_type_broadcast,
+		m_MSKID,
+		false,
+		m_packet_data_number.get_data(),
+		m_packet_data_number.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);
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	{
+	    m_wapi_negotiation_state = wapi_negotiation_state_rekeying;
+
+		// 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 (send_network_id.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);
+		}
+
+		// This notification to eapol_core_c object.
+		// WAPI authentication finished successfully.
+		eap_state_notification_c * notification = new eap_state_notification_c(
+			m_am_tools,
+			&send_network_id,
+			m_is_client,
+			eap_state_notification_generic,
+			eap_protocol_layer_wai,
+			eapol_key_handshake_type_wai_handshake,
+			eapol_key_state_wapi_authentication_running,
+			eapol_key_state_wapi_authentication_finished_successfull,
+			0ul,
+			false);
+		if (notification == 0)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+		}
+
+		state_notification(notification);
+
+		delete notification;
+	}
+
+	cancel_session_timeout();
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	set_wapi_state(wapi_core_state_authentication_ok);
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+#if defined(USE_WAPI_CORE_SERVER)
+
+eap_status_e wapi_core_c::handle_multicast_key_announcement_response(
+	const eap_am_network_id_c * const receive_network_id,
+	const wai_protocol_packet_header_c * const wai)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("WAPI_Core: this = 0x%08x, %s: wapi_core_c::handle_multicast_key_announcement_response(): state=%s, negotiation state = %s\n"),
+		 this,
+		 (m_is_client == true ? "client": "server"),
+		 wapi_strings_c::get_wapi_core_state_string(m_wapi_state),
+		 wapi_strings_c::get_wapi_negotiation_state_string(m_wapi_negotiation_state)));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: wapi_core_c::handle_multicast_key_announcement_response()");
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	if (m_wapi_state != wapi_core_state_wait_multicast_announcement_response_message
+		&& m_wapi_state != wapi_core_state_authentication_ok)
+	{
+		EAP_TRACE_DEBUG(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("ERROR: WAI: %s: wapi_core_c::handle_multicast_key_announcement_response(): Verify state %s != %s, negotiation state = %s.\n"),
+			(m_is_client == true) ? "client": "server",
+			wapi_strings_c::get_wapi_core_state_string(m_wapi_state),
+			wapi_strings_c::get_wapi_core_state_string(wapi_core_state_wait_multicast_announcement_response_message),
+			wapi_strings_c::get_wapi_negotiation_state_string(m_wapi_negotiation_state)));
+
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_unexpected_message);
+	}
+
+	if (m_authentication_type != eapol_key_authentication_type_WAI_PSK
+		&& m_authentication_type != eapol_key_authentication_type_WAI_certificate)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_wrong_authentication_type);
+	}
+
+	eap_status_e status(eap_status_process_general_error);
+
+	if (receive_network_id == 0
+		|| receive_network_id->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 (wai == 0
+		|| wai->get_is_valid() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_process_illegal_packet_error);
+	}
+
+	status = wai->check_header();
+
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	wai_message_payloads_c parser(
+		m_am_tools,
+		m_is_client);
+	if (parser.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 padding_length(0ul);
+
+	status = parser.parse_wai_payloads(
+		wai->get_header_buffer(wai->get_header_buffer_length()), ///< This is the start of the message buffer.
+		wai->get_header_buffer_length(), ///< This is the length of the buffer. This must match with the length of all payloads.
+		&padding_length ///< Length of possible padding is set to this variable.
+		);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Verify MAC.
+
+	{
+		wai_variable_data_c * const received_MAC = parser.get_tlv_pointer(wai_payload_type_message_authentication_code);
+		if (received_MAC == 0
+			|| received_MAC->get_is_valid_data() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+
+		eap_variable_data_c local_MAC(m_am_tools);
+		if (local_MAC.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_MAC(&parser, &local_MAC);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = local_MAC.set_data_length(WAPI_MESSAGE_AUTHENTICATION_CODE_LENGTH);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		if (local_MAC.compare(
+			received_MAC->get_data(received_MAC->get_data_length()),
+			received_MAC->get_data_length()) != 0)
+		{
+			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("WAI: %s: wapi_core_c::handle_multicast_key_announcement_response(): MAC OK.\n"),
+				(m_is_client == true) ? "client": "server"));
+		}
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Verify USK rekeying flag.
+
+	{
+		wai_variable_data_c * const flag_payload = parser.get_tlv_pointer(wai_payload_type_flag);
+		if (flag_payload == 0
+			|| flag_payload->get_is_valid_data() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+
+		const u8_t * const flag = flag_payload->get_data(sizeof(*flag));
+		if (flag == 0)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+
+		if (((*flag) & wai_data_flag_mask_USK_Rekeying) != 0)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Verify MSKID.
+
+	{
+		wai_variable_data_c * const MSKID_payload = parser.get_tlv_pointer(wai_payload_type_mskid_stakeyid);
+		if (MSKID_payload == 0
+			|| MSKID_payload->get_is_valid_data() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+
+		const u8_t * const MSKID_pointer = MSKID_payload->get_data(sizeof(*MSKID_pointer));
+		if (MSKID_pointer == 0)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+
+		u8_t MSKID = (*MSKID_pointer) & wai_data_uskid_mask_mskid;
+
+		if (MSKID != m_MSKID
+			|| m_MSKSA[MSKID] == 0
+			|| m_MSKSA[MSKID]->get_is_valid_data() == true)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Verify USKID.
+
+	{
+		wai_variable_data_c * const USKID_payload = parser.get_tlv_pointer(wai_payload_type_uskid);
+		if (USKID_payload == 0
+			|| USKID_payload->get_is_valid_data() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+
+		const u8_t * const USKID_pointer = USKID_payload->get_data(sizeof(*USKID_pointer));
+		if (USKID_pointer == 0)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+
+		u8_t USKID = (*USKID_pointer) & wai_data_uskid_mask_uskid;
+
+		if (USKID >= WAPI_USKSA_COUNT
+			|| m_USKSA[USKID] == 0
+			|| m_USKSA[USKID]->get_is_valid_data() == true)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Verify Key Announcement.
+
+	{
+
+		wai_variable_data_c * const key_announcement = parser.get_tlv_pointer(wai_payload_type_key_announcement_identifier);
+		if (key_announcement == 0
+			|| key_announcement->get_is_valid_data() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+
+		if (m_key_announcement.compare(
+			key_announcement->get_data(key_announcement->get_data_length()),
+			key_announcement->get_data_length()) != 0)
+		{
+			EAP_TRACE_DATA_DEBUG(
+				m_am_tools, 
+				TRACE_FLAGS_DEFAULT, 
+				(EAPL("ERROR: local m_key_announcement"),
+				 m_key_announcement.get_data(),
+				 m_key_announcement.get_data_length()));
+
+			EAP_TRACE_DATA_DEBUG(
+				m_am_tools, 
+				TRACE_FLAGS_DEFAULT, 
+				(EAPL("ERROR: received key_announcement"),
+				key_announcement->get_data(key_announcement->get_data_length()),
+				key_announcement->get_data_length()));
+
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+		}
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Install multicast session key.
+
+	status = packet_data_session_key(
+		&m_multicast_key,
+		eapol_key_type_broadcast,
+		m_MSKID,
+		false,
+		m_packet_data_number.get_data(),
+		m_packet_data_number.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);
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	{
+	    m_wapi_negotiation_state = wapi_negotiation_state_rekeying;
+
+		// 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 (send_network_id.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);
+		}
+
+		// This notification to eapol_core_c object.
+		// WAPI authentication finished successfully.
+		eap_state_notification_c * notification = new eap_state_notification_c(
+			m_am_tools,
+			&send_network_id,
+			m_is_client,
+			eap_state_notification_generic,
+			eap_protocol_layer_wai,
+			eapol_key_handshake_type_wai_handshake,
+			eapol_key_state_wapi_authentication_running,
+			eapol_key_state_wapi_authentication_finished_successfull,
+			0ul,
+			false);
+		if (notification == 0)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+		}
+
+		state_notification(notification);
+
+		delete notification;
+	}
+
+	cancel_session_timeout();
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	set_wapi_state(wapi_core_state_authentication_ok);
+
+	//cancel_retransmission();
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+#endif //#if defined(USE_WAPI_CORE_SERVER)
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e wapi_core_c::packet_process(
+	const eap_am_network_id_c * const receive_network_id,
+	eap_general_header_base_c * const packet_data,
+	const u32_t packet_length)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("WAI: %s: wapi_core_c::packet_process(): state = %s, negotiation state = %s.\n"),
+		(m_is_client == true) ? "client": "server",
+		wapi_strings_c::get_wapi_core_state_string(m_wapi_state),
+		wapi_strings_c::get_wapi_negotiation_state_string(m_wapi_negotiation_state)));
+
+	EAP_ASSERT(m_am_tools->get_global_mutex()->get_is_reserved() == true);
+
+	eap_status_e status(eap_status_process_general_error);
+
+
+	// This automatic variable stores the current packet sequence number.
+	// If the received packet is illegal the current packet sequence number is restored.
+	eap_automatic_simple_value_c<u16_t> automatic_packet_sequence_number(
+		m_am_tools,
+		&m_packet_sequence_number,
+		m_packet_sequence_number);
+
+	// This automatic variable stores the current WAPI-state.
+	// If the received packet is illegal the current WAPI-state is restored.
+	eap_automatic_simple_value_c<wapi_core_state_e> automatic_wapi_state(
+		m_am_tools,
+		&m_wapi_state,
+		m_wapi_state);
+
+
+	if (m_wapi_state == wapi_core_state_none)
+	{
+		EAP_TRACE_DEBUG(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("ERROR: WAI: %s: wapi_core_c::packet_process(): Verify state %s == %s, negotiation state = %s, drop packet.\n"),
+			(m_is_client == true) ? "client": "server",
+			wapi_strings_c::get_wapi_core_state_string(m_wapi_state),
+			wapi_strings_c::get_wapi_core_state_string(wapi_core_state_none),
+			wapi_strings_c::get_wapi_negotiation_state_string(m_wapi_negotiation_state)));
+
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_drop_packet_quietly);
+	}
+
+	if (packet_data == 0
+		|| packet_data->get_is_valid() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_process_illegal_packet_error);
+	}
+
+	if (receive_network_id == 0
+		|| receive_network_id->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 (packet_length < eap_header_base_c::get_header_length())
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_too_short_message);
+	}
+
+	wai_protocol_packet_header_c wai(
+		m_am_tools,
+		packet_data->get_header_buffer(packet_length),
+		packet_length);
+
+	if (wai.get_is_valid() == false)
+	{
+		EAP_TRACE_ERROR(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("packet_process: %s, packet buffer corrupted.\n"),
+			 (m_is_client_role == true) ? "client": "server"
+			 ));
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_header_corrupted);
+	}
+
+	EAP_TRACE_DATA_DEBUG(
+		m_am_tools, 
+		EAP_TRACE_FLAGS_MESSAGE_DATA|TRACE_TEST_VECTORS, 
+		(EAPL("WAI-packet"),
+		 wai.get_header_buffer(packet_length),
+		 packet_length));
+
+	WAI_PROTOCOL_PACKET_TRACE_HEADER("wapi_core_c::packet_process(): ->", &wai, m_is_client_role);
+
+	status = wai.check_header();
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	if (m_shutdown_was_called == true
+		&& m_is_client_role == true)
+	{
+		EAP_TRACE_DEBUG(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("WARNING: WAPI_Core: %s, wapi_core_c::packet_process(): %s packet dropped quietly because shutdown was already called.\n"),
+			 (m_is_client_role == true) ? "client": "server",
+			 wapi_strings_c::get_wai_protocol_subtype_string(wai.get_subtype())));
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_drop_packet_quietly);
+	}
+
+	status = packet_reassemble(&wai);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+
+	wai_protocol_packet_header_c reass_wai(
+		m_am_tools,
+		m_reassemble_packet.get_data(),
+		m_reassemble_packet.get_data_length());
+		
+	if (reass_wai.get_is_valid() == false)
+	{
+		EAP_TRACE_ERROR(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("wapi_core_c::packet_process(): %s, packet buffer corrupted.\n"),
+			 (m_is_client_role == true) ? "client": "server"
+			 ));
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_header_corrupted);
+	}
+
+
+	if (m_is_client_role == true)
+	{
+		status = check_retransmission(&reass_wai);
+
+		if (status == eap_status_ok)
+		{
+			// OK, re-transmitted an old packet.
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+
+		switch(reass_wai.get_subtype())
+		{
+		case wai_protocol_subtype_authentication_activation:
+			status = handle_authentication_activation(receive_network_id, &reass_wai);
+			break;
+		case wai_protocol_subtype_access_authentication_response:
+			status = handle_access_authentication_response(receive_network_id, &reass_wai);
+			break;
+		case wai_protocol_subtype_unicast_key_negotiation_request:
+			status = handle_unicast_key_negotiation_request(receive_network_id, &reass_wai);
+			break;
+		case wai_protocol_subtype_unicast_key_negotiation_confirmation:
+			status = handle_unicast_key_negotiation_confirmation(receive_network_id, &reass_wai);
+			break;
+		case wai_protocol_subtype_multicast_key_announcement:
+			status = handle_multicast_key_announcement(receive_network_id, &reass_wai);
+			break;
+		default:
+			EAP_TRACE_DEBUG(
+				m_am_tools, 
+				TRACE_FLAGS_DEFAULT, 
+				(EAPL("ERROR: WAPI_Core: %s, wapi_core_c::packet_process(): Unknown %d=%s packet dropped quietly.\n"),
+				 (m_is_client_role == true) ? "client": "server",
+				 reass_wai.get_subtype(),
+				 wapi_strings_c::get_wai_protocol_subtype_string(reass_wai.get_subtype())));
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_drop_packet_quietly);
+		};
+	}
+#if defined(USE_WAPI_CORE_SERVER)
+	else
+	{
+		switch(reass_wai.get_subtype())
+		{
+		case wai_protocol_subtype_access_authentication_request:
+			status = handle_access_authentication_request(receive_network_id, &reass_wai);
+			break;
+		case wai_protocol_subtype_unicast_key_negotiation_response:
+			status = handle_unicast_key_negotiation_response(receive_network_id, &reass_wai);
+			break;
+		case wai_protocol_subtype_multicast_key_announcement_response:
+			status = handle_multicast_key_announcement_response(receive_network_id, &reass_wai);
+			break;
+		default:
+			EAP_TRACE_DEBUG(
+				m_am_tools, 
+				TRACE_FLAGS_DEFAULT, 
+				(EAPL("ERROR: WAPI_Core: %s, wapi_core_c::packet_process(): Unknown %d=%s packet dropped quietly.\n"),
+				 (m_is_client_role == true) ? "client": "server",
+				 reass_wai.get_subtype(),
+				 wapi_strings_c::get_wai_protocol_subtype_string(reass_wai.get_subtype())));
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_drop_packet_quietly);
+		};
+	}
+#endif //#if defined(USE_WAPI_CORE_SERVER)
+
+	if (status == eap_status_ok
+		|| status == eap_status_pending_request)
+	{
+		automatic_packet_sequence_number.do_not_restore_variable();
+		automatic_wapi_state.do_not_restore_variable();
+	}
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+//
+EAP_FUNC_EXPORT eap_status_e wapi_core_c::packet_send(
+	const eap_am_network_id_c * const send_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_ASSERT(m_am_tools->get_global_mutex()->get_is_reserved() == true);
+
+	wai_protocol_packet_header_c wai(
+		m_am_tools,
+		sent_packet->get_data_offset(
+			header_offset, data_length),
+		data_length);
+
+	if (wai.get_is_valid() == false)
+	{
+		EAP_TRACE_ERROR(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("packet_send(): %s, packet buffer corrupted.\n"),
+			 (m_is_client_role == true) ? "client": "server"
+			 ));
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_header_corrupted);
+	}
+
+	EAP_ASSERT(header_offset < sent_packet->get_data_length());
+	EAP_ASSERT(data_length <= sent_packet->get_data_length());
+	EAP_ASSERT(sent_packet->get_data_length() <= buffer_length);
+
+	EAP_TRACE_DATA_DEBUG(
+		m_am_tools, 
+		EAP_TRACE_FLAGS_MESSAGE_DATA|TRACE_TEST_VECTORS, 
+		(EAPL("WAI-packet"),
+		 wai.get_header_buffer(data_length),
+		 data_length));
+
+	WAI_PROTOCOL_PACKET_TRACE_HEADER("wapi_core_c::packet_send(): <-", &wai, m_is_client_role);
+
+	if (m_shutdown_was_called == true
+		&& m_is_client_role == true)
+	{
+		EAP_TRACE_DEBUG(
+			m_am_tools,
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("WARNING: WAPI_Core: %s, wapi_core_c::packet_send(): %s packet dropped quietly because shutdown was already called.\n"),
+			 (m_is_client_role == true) ? "client": "server",
+			 wapi_strings_c::get_wai_protocol_subtype_string(wai.get_subtype())));
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_drop_packet_quietly);
+	}
+
+	eap_status_e status = m_partner->packet_send(
+		send_network_id, sent_packet, header_offset, data_length, buffer_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 wapi_core_c::resend_packet(
+	const eap_am_network_id_c * const send_network_id,
+	const wai_message_c * const wai_message_data,
+	const u32_t retransmission_counter,
+	const u16_t packet_sequence_number)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_UNREFERENCED_PARAMETER(retransmission_counter); // Only trace uses this.
+
+	EAP_TRACE_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("<- WAPI_Core: %s: wapi_core_c::resend_packet(), counter %d.\n"),
+		 (m_is_client_role == true) ? "client": "server",
+		 retransmission_counter
+		 ));
+
+	// We make a copy because random error test may corrupt the data.
+	wai_message_c * const copy_packet = wai_message_data->copy();
+
+	if (copy_packet == 0)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	// NOTE: send packet directly to partner object.
+	// This will skip initialization of re-transmission for re-transmitted packet.
+	eap_status_e status = packet_fragment(
+		copy_packet,
+		packet_sequence_number);
+
+	delete copy_packet;
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e wapi_core_c::cancel_retransmission()
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_ASSERT(m_am_tools->get_global_mutex()->get_is_reserved() == true);
+
+	{
+		EAP_TRACE_DEBUG(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("TIMER: %s: WAPI_CORE_TIMER_RETRANSMISSION_ID cancelled.\n"),
+			 (m_is_client_role == true ? "client": "server")
+			 ));
+
+		if (m_is_client_role == false)
+		{
+			// Only WAPI-server uses timer to re-transmits WAI-packets.
+			m_partner->cancel_timer(this, WAPI_CORE_TIMER_RETRANSMISSION_ID);
+		}
+
+		m_retransmission.reset();
+	}
+
+	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 wapi_core_c::init_retransmission(
+	const eap_am_network_id_c * const send_network_id,
+	const wai_message_c * const received_wai_message_data,
+	const wai_message_c * const new_wai_message_data,
+	const u16_t packet_sequence_number,
+	const wai_protocol_subtype_e wapi_subtype
+	)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_ASSERT(m_am_tools->get_global_mutex()->get_is_reserved() == true);
+
+	if (m_is_client_role == false)
+	{
+		if (m_retransmission_time == 0u
+			|| m_retransmission_counter == 0u)
+		{
+			// No retransmission.
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_ok);
+		}
+	}
+
+	EAP_ASSERT(send_network_id->get_source() != 0);
+	EAP_ASSERT(send_network_id->get_destination() != 0);
+
+	wapi_core_retransmission_c * retransmission = new wapi_core_retransmission_c(
+		m_am_tools,
+		send_network_id,
+		received_wai_message_data,
+		new_wai_message_data,
+		m_retransmission_time,
+		m_retransmission_counter,
+		packet_sequence_number,
+		wapi_subtype);
+	if (retransmission == 0
+		|| retransmission->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_status_e status = m_retransmission.add_object_to_begin(
+		retransmission,
+		true);
+	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_role == false)
+	{
+		// Only WAPI-server uses timer to re-transmits WAI-packets.
+		m_partner->cancel_timer(this, WAPI_CORE_TIMER_RETRANSMISSION_ID);
+
+		EAP_TRACE_DEBUG(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("TIMER: %s: WAPI_CORE_TIMER_RETRANSMISSION_ID cancelled.\n"),
+			 (m_is_client_role == true ? "client": "server")
+			 ));
+	}
+
+	retransmission = m_retransmission.get_object(0ul);
+
+	if (retransmission == 0)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	if (retransmission->get_is_valid() == true)
+	{
+		if (m_is_client_role == false)
+		{
+			// Only WAPI-server uses timer to re-transmits WAI-packets.
+			u32_t next_retransmission_time = retransmission->get_next_retransmission_time();
+
+			eap_status_e status = m_partner->set_timer(this, WAPI_CORE_TIMER_RETRANSMISSION_ID, 0,
+				next_retransmission_time);
+
+			EAP_TRACE_DEBUG(
+				m_am_tools, 
+				TRACE_FLAGS_DEFAULT, 
+				(EAPL("TIMER: %s: WAPI_CORE_TIMER_RETRANSMISSION_ID set %d ms.\n"),
+				 (m_is_client_role == true ? "client": "server"),
+				 next_retransmission_time));
+
+			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_ok);
+		}
+	}
+	else
+	{
+		(void) m_retransmission.remove_object(0ul);
+
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+}
+
+//--------------------------------------------------
+
+eap_status_e wapi_core_c::check_retransmission(const wai_protocol_packet_header_c * const received_wai)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("WAPI_Core: this = 0x%08x, %s: wapi_core_c::check_retransmission(): state=%s\n"),
+		 this,
+		 (m_is_client == true ? "client": "server"),
+		 wapi_strings_c::get_wapi_core_state_string(m_wapi_state)));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: wapi_core_c::check_retransmission()");
+
+	EAP_ASSERT(m_is_client == true);
+
+	eap_status_e status(eap_status_process_general_error);
+
+	if (received_wai == 0
+		|| received_wai->get_is_valid() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	for (u32_t index = 0ul; index < m_retransmission.get_object_count(); ++index)
+	{
+		const wapi_core_retransmission_c * const retransmission = m_retransmission.get_object(index);
+		if (retransmission == 0)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_not_found);
+		}
+
+		const wai_message_c * const message = retransmission->get_wai_received_message_data();
+		if (message == 0)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_not_found);
+		}
+		else
+		{
+			wai_protocol_packet_header_c wai(
+				m_am_tools,
+				message->get_wai_message_data()->get_data(),
+				message->get_wai_message_data()->get_data_length());
+
+			if (wai.get_is_valid() == false)
+			{
+				EAP_TRACE_ERROR(
+					m_am_tools, 
+					TRACE_FLAGS_DEFAULT, 
+					(EAPL("wapi_core_c::packet_fragment(): %s, packet buffer corrupted.\n"),
+					 (m_is_client_role == true) ? "client": "server"
+					 ));
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_header_corrupted);
+			}
+
+			EAP_TRACE_DEBUG(
+				m_am_tools,
+				TRACE_FLAGS_DEFAULT,
+				(EAPL("%s: wapi_core_c::check_retransmission(): wai.get_version()=%d, received_wai->get_version()=%d\n"),
+				 (m_is_client == true ? "client": "server"),
+				 wai.get_version(),
+				 received_wai->get_version()));
+
+			EAP_TRACE_DEBUG(
+				m_am_tools,
+				TRACE_FLAGS_DEFAULT,
+				(EAPL("%s: wapi_core_c::check_retransmission(): wai.get_type()=%d, received_wai->get_type()=%d\n"),
+				 (m_is_client == true ? "client": "server"),
+				 wai.get_type(),
+				 received_wai->get_type()));
+
+			EAP_TRACE_DEBUG(
+				m_am_tools,
+				TRACE_FLAGS_DEFAULT,
+				(EAPL("%s: wapi_core_c::check_retransmission(): wai.get_subtype()=%d, received_wai->get_subtype()=%d\n"),
+				 (m_is_client == true ? "client": "server"),
+				 wai.get_subtype(),
+				 received_wai->get_subtype()));
+
+			EAP_TRACE_DEBUG(
+				m_am_tools,
+				TRACE_FLAGS_DEFAULT,
+				(EAPL("%s: wapi_core_c::check_retransmission(): wai.get_packet_sequence_number()=%d, received_wai->get_packet_sequence_number()=%d\n"),
+				 (m_is_client == true ? "client": "server"),
+				 wai.get_packet_sequence_number(),
+				 received_wai->get_packet_sequence_number()));
+
+			if (wai.get_version() == received_wai->get_version()
+				&& wai.get_type() == received_wai->get_type()
+				&& wai.get_subtype() == received_wai->get_subtype()
+				&& wai.get_packet_sequence_number() == received_wai->get_packet_sequence_number())
+			{
+				status = resend_packet(
+					retransmission->get_send_network_id(),
+					retransmission->get_wai_message_data(),
+					retransmission->get_retransmission_counter(),
+					retransmission->get_packet_sequence_number());
+				if (status == eap_status_ok)
+				{
+					EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+					return EAP_STATUS_RETURN(m_am_tools, status);
+				}
+			}
+		} // for()
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, eap_status_not_found);
+}
+
+//--------------------------------------------------
+
+//
+EAP_FUNC_EXPORT eap_status_e wapi_core_c::set_wapi_failure_timeout()
+{
+	eap_status_e status = m_partner->set_timer(
+		this,
+		WAPI_CORE_FAILURE_RECEIVED_ID,
+		0,
+		m_wapi_core_failure_received_timeout);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_ERROR(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("ERROR: TIMER: %s: WAPI_CORE_FAILURE_RECEIVED_ID failed.\n"),
+			 (m_is_client_role == true ? "client": "server")
+			 ));
+	}
+	else
+	{
+		EAP_TRACE_DEBUG(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("TIMER: %s: WAPI_CORE_FAILURE_RECEIVED_ID set %d ms.\n"),
+			 (m_is_client_role == true ? "client": "server"),
+			 m_wapi_core_failure_received_timeout
+			 ));
+	}
+
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+//
+EAP_FUNC_EXPORT eap_status_e wapi_core_c::cancel_wapi_failure_timeout()
+{
+	EAP_TRACE_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("TIMER: %s: WAPI_CORE_FAILURE_RECEIVED_ID cancelled.\n"),
+		 (m_is_client_role == true ? "client": "server")
+		 ));
+
+	return m_partner->cancel_timer(
+		this,
+		WAPI_CORE_FAILURE_RECEIVED_ID);
+}
+
+//--------------------------------------------------
+
+//
+EAP_FUNC_EXPORT u32_t wapi_core_c::get_header_offset(
+	u32_t * const MTU,
+	u32_t * const trailer_length)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_ASSERT(m_am_tools->get_global_mutex()->get_is_reserved() == true);
+
+	const u32_t offset = m_partner->get_header_offset(MTU, trailer_length);
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("wapi_core_c::get_header_offset(): offset=%d, MTU=%d, trailer_length=%d\n"),
+		offset,
+		*MTU,
+		*trailer_length));
+
+	return offset;
+}
+
+//--------------------------------------------------
+
+//
+EAP_FUNC_EXPORT eap_status_e wapi_core_c::configure()
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+#if !defined(USE_EAP_DEBUG_TRACE)
+	EAP_TRACE_ALWAYS(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("wapi_core_c::configure(): %s\n"),
+		 ((m_is_client == true) ? "client": "server")));
+#else
+	EAP_TRACE_ALWAYS(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("wapi_core_c::configure(): %s: this = 0x%08x => 0x%08x.\n"),
+		 ((m_is_client == true) ? "client": "server"),
+		 this,
+		 dynamic_cast<abs_eap_base_timer_c *>(this)));
+#endif
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: wapi_core_c::configure()");
+
+	EAP_ASSERT(m_am_tools->get_global_mutex()->get_is_reserved() == true);
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+#if defined(USE_EAP_TEST_VECTORS)
+
+	{
+		eap_variable_data_c data(m_am_tools);
+
+		eap_status_e status = m_partner->read_configure(
+			cf_str_EAP_TRACE_only_trace_messages.get_field(),
+			&data);
+		if (status == eap_status_ok
+			&& data.get_data_length() == sizeof(u32_t)
+			&& data.get_data(data.get_data_length()) != 0)
+		{
+			if (*(reinterpret_cast<u32_t *>(data.get_data(data.get_data_length()))) != 0u)
+			{
+				// Activate only WAPI message traces.
+				m_am_tools->set_trace_mask(
+					eap_am_tools_c::eap_trace_mask_always
+					| eap_am_tools_c::eap_trace_mask_eap_messages);
+			}
+			else
+			{
+				// Disable only WAPI message traces.
+				m_am_tools->set_trace_mask(
+					m_am_tools->get_trace_mask() & (~eap_am_tools_c::eap_trace_mask_eap_messages));
+			}
+		}
+		else
+		{
+			// Disable only WAPI message traces.
+			m_am_tools->set_trace_mask(
+				m_am_tools->get_trace_mask() & (~eap_am_tools_c::eap_trace_mask_eap_messages));
+		}
+	}
+
+
+	{
+		eap_variable_data_c data(m_am_tools);
+
+		eap_status_e status = m_partner->read_configure(
+			cf_str_EAP_TRACE_only_test_vectors.get_field(),
+			&data);
+		if (status == eap_status_ok
+			&& data.get_data_length() == sizeof(u32_t)
+			&& data.get_data(data.get_data_length()) != 0)
+		{
+			if (*(reinterpret_cast<u32_t *>(data.get_data(data.get_data_length()))) != 0u)
+			{
+				// Activates only WAPI test vector traces.
+				m_am_tools->set_trace_mask(eap_am_tools_c::eap_trace_mask_test_vectors);
+			}
+		}
+	}
+
+
+	{
+		eap_variable_data_c data(m_am_tools);
+
+		eap_status_e status = m_partner->read_configure(
+			cf_str_EAP_TRACE_crypto_test_vectors_sha1.get_field(),
+			&data);
+		if (status == eap_status_ok
+			&& data.get_data_length() == sizeof(u32_t)
+			&& data.get_data(data.get_data_length()) != 0)
+		{
+			if (*(reinterpret_cast<u32_t *>(data.get_data(data.get_data_length()))) != 0u)
+			{
+				// Activates SHA1 WAPI test vector traces.
+				m_am_tools->set_trace_mask(m_am_tools->get_trace_mask()
+					| eap_am_tools_c::eap_trace_mask_crypto_sha1);
+			}
+		}
+	}
+
+
+	{
+		eap_variable_data_c data(m_am_tools);
+
+		eap_status_e status = m_partner->read_configure(
+			cf_str_EAP_TRACE_crypto_test_vectors_rc4.get_field(),
+			&data);
+		if (status == eap_status_ok
+			&& data.get_data_length() == sizeof(u32_t)
+			&& data.get_data(data.get_data_length()) != 0)
+		{
+			if (*(reinterpret_cast<u32_t *>(data.get_data(data.get_data_length()))) != 0u)
+			{
+				// Activates RC4 WAPI test vector traces.
+				m_am_tools->set_trace_mask(m_am_tools->get_trace_mask()
+					| eap_am_tools_c::eap_trace_mask_crypto_rc4);
+			}
+		}
+	}
+
+
+	{
+		eap_variable_data_c data(m_am_tools);
+
+		eap_status_e status = m_partner->read_configure(
+			cf_str_EAP_TRACE_crypto_test_vectors_md4.get_field(),
+			&data);
+		if (status == eap_status_ok
+			&& data.get_data_length() == sizeof(u32_t)
+			&& data.get_data(data.get_data_length()) != 0)
+		{
+			if (*(reinterpret_cast<u32_t *>(data.get_data(data.get_data_length()))) != 0u)
+			{
+				// Activates MD4 WAPI test vector traces.
+				m_am_tools->set_trace_mask(m_am_tools->get_trace_mask()
+					| eap_am_tools_c::eap_trace_mask_crypto_md4);
+			}
+		}
+	}
+
+
+	{
+		eap_variable_data_c data(m_am_tools);
+
+		eap_status_e status = m_partner->read_configure(
+			cf_str_EAP_TRACE_crypto_test_vectors_test_random.get_field(),
+			&data);
+		if (status == eap_status_ok
+			&& data.get_data_length() == sizeof(u32_t)
+			&& data.get_data(data.get_data_length()) != 0)
+		{
+			if (*(reinterpret_cast<u32_t *>(data.get_data(data.get_data_length()))) != 0u)
+			{
+				// Activates test random generator WAPI test vector traces.
+				m_am_tools->set_trace_mask(m_am_tools->get_trace_mask()
+					| eap_am_tools_c::eap_trace_mask_crypto_test_random
+					| eap_am_tools_c::eap_trace_mask_crypto_sha1);
+			}
+		}
+	}
+
+#endif //#if defined(USE_EAP_TEST_VECTORS)
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+#if defined(USE_WAPI_CORE_SERVER)
+	if (m_is_client == false)
+	{
+		eap_variable_data_c retransmission_time(m_am_tools);
+
+		eap_status_e status = read_configure(
+			cf_str_WAPI_CORE_retransmission_time.get_field(),
+			&retransmission_time);
+		if (status == eap_status_ok
+			&& retransmission_time.get_is_valid_data() == true)
+		{
+			u32_t *retransmission_time_value = reinterpret_cast<u32_t *>(
+				retransmission_time.get_data(sizeof(u32_t)));
+			if (retransmission_time_value != 0)
+			{
+				m_retransmission_time = *retransmission_time_value;
+			}
+			else
+			{
+				m_retransmission_time = WAPI_CORE_RETRANSMISSION_TIME;
+			}
+		}
+		else
+		{
+			m_retransmission_time = WAPI_CORE_RETRANSMISSION_TIME;
+		}
+	}
+#endif //#if defined(USE_WAPI_CORE_SERVER)
+
+	{
+		eap_variable_data_c retransmission_counter(m_am_tools);
+
+		eap_status_e status = read_configure(
+			cf_str_WAPI_CORE_retransmission_counter.get_field(),
+			&retransmission_counter);
+		if (status == eap_status_ok
+			&& retransmission_counter.get_is_valid_data() == true)
+		{
+			u32_t *retransmission_counter_value = reinterpret_cast<u32_t *>(
+				retransmission_counter.get_data(sizeof(u32_t)));
+			if (retransmission_counter_value != 0)
+			{
+				m_retransmission_counter = *retransmission_counter_value;
+			}
+			else
+			{
+				m_retransmission_counter = WAPI_CORE_RETRANSMISSION_COUNTER;
+			}
+		}
+		else
+		{
+			m_retransmission_counter = WAPI_CORE_RETRANSMISSION_COUNTER;
+		}
+	}
+
+	//----------------------------------------------------------
+
+	{
+		eap_variable_data_c session_timeout(m_am_tools);
+
+		eap_status_e status = read_configure(
+			cf_str_WAPI_CORE_session_timeout.get_field(),
+			&session_timeout);
+		if (status == eap_status_ok
+			&& session_timeout.get_is_valid_data() == true)
+		{
+			u32_t *handler_timeout = reinterpret_cast<u32_t *>(
+				session_timeout.get_data(sizeof(u32_t)));
+			if (handler_timeout != 0)
+			{
+				m_session_timeout = *handler_timeout;
+			}
+			else
+			{
+				m_session_timeout = WAPI_CORE_SESSION_TIMEOUT;
+			}
+		}
+		else
+		{
+			m_session_timeout = WAPI_CORE_SESSION_TIMEOUT;
+		}
+	}
+
+
+#if defined(USE_WAPI_CORE_SERVER)
+
+	if (m_is_client == false)
+	{
+		eap_variable_data_c session_timeout(m_am_tools);
+
+		eap_status_e status = read_configure(
+			cf_str_WAPI_CORE_server_session_timeout.get_field(),
+			&session_timeout);
+		if (status == eap_status_ok
+			&& session_timeout.get_is_valid_data() == true)
+		{
+			u32_t *handler_timeout = reinterpret_cast<u32_t *>(
+				session_timeout.get_data(sizeof(u32_t)));
+			if (handler_timeout != 0)
+			{
+				// This is optional.
+				m_session_timeout = *handler_timeout;
+			}
+		}
+	}
+
+	//----------------------------------------------------------
+
+	if (m_is_client == false)
+	{
+		eap_variable_data_c only_initial_authentication(m_am_tools);
+
+		eap_status_e status = read_configure(
+			cf_str_WAPI_CORE_server_only_initial_authentication.get_field(),
+			&only_initial_authentication);
+		if (status == eap_status_ok
+			&& only_initial_authentication.get_is_valid_data() == true)
+		{
+			u32_t *flag = reinterpret_cast<u32_t *>(
+				only_initial_authentication.get_data(sizeof(u32_t)));
+			if (flag != 0)
+			{
+				if (*flag == 0)
+				{
+					m_only_initial_authentication = false;
+				}
+				else
+				{
+					m_only_initial_authentication = true;
+				}
+			}
+		}
+	}
+
+	//----------------------------------------------------------
+
+	if (m_is_client == false)
+	{
+		eap_status_e status = m_partner->read_configure(
+			cf_str_WAPI_CORE_server_test_other_asu_id.get_field(),
+			&m_test_other_asu_id);
+		if (status == eap_status_ok
+			&& m_test_other_asu_id.get_is_valid_data() == true)
+		{
+			// This is optional for testing purposes.
+			EAP_TRACE_DEBUG(
+				m_am_tools,
+				TRACE_FLAGS_DEFAULT,
+				(EAPL("WAPI_Core: this = 0x%08x, %s: wapi_core_c::configure(): Other ASU ID\n"),
+				 this,
+				 (m_is_client == true ? "client": "server")));
+		}
+	}
+
+#endif //#if defined(USE_WAPI_CORE_SERVER)
+
+	//----------------------------------------------------------
+
+	{
+		eap_variable_data_c failure_received_timeout(m_am_tools);
+
+		eap_status_e status = read_configure(
+			cf_str_WAPI_CORE_failure_received_timeout.get_field(),
+			&failure_received_timeout);
+		if (status == eap_status_ok
+			&& failure_received_timeout.get_is_valid_data() == true)
+		{
+			u32_t *timeout = reinterpret_cast<u32_t *>(
+				failure_received_timeout.get_data(sizeof(u32_t)));
+			if (timeout != 0)
+			{
+				m_wapi_core_failure_received_timeout = *timeout;
+			}
+		}
+	}
+
+	//----------------------------------------------------------
+
+	{
+		eap_status_e status = read_configure(
+			cf_str_WAPI_CORE_PSK.get_field(),
+			&m_preshared_key_PSK);
+		if (status == eap_status_ok)
+		{
+			if (m_preshared_key_PSK.get_data_length() == WAPI_BK_LENGTH)
+			{
+				status = m_BK.set_copy_of_buffer(&m_preshared_key_PSK);
+				if (status != eap_status_ok)
+				{
+					EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+					return EAP_STATUS_RETURN(m_am_tools, status);
+				}
+			}
+			else
+			{
+				// Create BK from PSK and label.
+				crypto_kd_hmac_sha256_c kd_hmac(m_am_tools);
+				if (kd_hmac.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 eap_variable_data_c label(
+					m_am_tools,
+					WAPI_PRESHARED_KEY_LABEL,
+					WAPI_PRESHARED_KEY_LABEL_LENGTH,
+					false,
+					false);
+				if (label.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 = kd_hmac.expand_key(
+					&m_BK,
+					WAPI_BK_LENGTH,
+					&m_preshared_key_PSK,
+					&label);
+				if (status != eap_status_ok)
+				{
+					EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+					return EAP_STATUS_RETURN(m_am_tools, status);
+				}
+			}
+		}
+	}
+
+	//----------------------------------------------------------
+
+	m_wapi_header_offset = m_partner->get_header_offset(&m_MTU, &m_trailer_length);
+
+
+	// Add session timeout.
+	initialize_session_timeout(m_session_timeout);
+
+	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 wapi_core_c::shutdown()
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+	
+	EAP_ASSERT(m_am_tools->get_global_mutex()->get_is_reserved() == true);
+
+	eap_status_e status(eap_status_ok);
+
+#if !defined(USE_EAP_DEBUG_TRACE)
+	EAP_TRACE_ALWAYS(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("wapi_core_c::shutdown(): %s: m_shutdown_was_called=%d.\n"),
+		 ((m_is_client == true) ? "client": "server"),
+		 m_shutdown_was_called));
+#else
+	EAP_TRACE_ALWAYS(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("wapi_core_c::shutdown(): %s: this = 0x%08x => 0x%08x, ")
+		 EAPL("m_shutdown_was_called=%d.\n"),
+		 ((m_is_client == true) ? "client": "server"),
+		 this,
+		 dynamic_cast<abs_eap_base_timer_c *>(this),
+		 m_shutdown_was_called));
+#endif
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: wapi_core_c::shutdown()");
+
+	if (m_shutdown_was_called == true)
+	{
+		// Shutdown was already called once.
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_ok);
+	}
+	m_shutdown_was_called = true;
+
+	cancel_retransmission();
+	cancel_session_timeout();
+	cancel_wapi_failure_timeout();
+	cancel_asynchronous_init_remove_wapi_session();
+
+	if (m_partner != 0)
+	{
+		cancel_session_timeout();
+	}
+
+	if (m_ec_certificate_store != 0)
+	{
+		m_ec_certificate_store->shutdown();
+	}
+
+	if (m_am_wapi_core != 0)
+	{
+		m_am_wapi_core->shutdown();
+	}
+
+#if !defined(USE_EAP_DEBUG_TRACE)
+	EAP_TRACE_ALWAYS(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("wapi_core_c::shutdown(): %s: m_shutdown_was_called=%d, status=%d returns.\n"),
+		 ((m_is_client == true) ? "client": "server"),
+		 m_shutdown_was_called,
+		 status));
+#else
+	EAP_TRACE_ALWAYS(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("wapi_core_c::shutdown(): %s: this = 0x%08x => 0x%08x, ")
+		 EAPL("m_shutdown_was_called=%d, status=%d returns.\n"),
+		 ((m_is_client == true) ? "client": "server"),
+		 this,
+		 dynamic_cast<abs_eap_base_timer_c *>(this),
+		 m_shutdown_was_called,
+		 status));
+#endif
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+//
+EAP_FUNC_EXPORT eap_status_e wapi_core_c::restart_authentication(
+	const eap_am_network_id_c * const send_network_id,
+	const bool is_client_when_true)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_ASSERT(m_am_tools->get_global_mutex()->get_is_reserved() == true);
+
+	// Here we swap the addresses.
+	eap_am_network_id_c receive_network_id(m_am_tools,
+		send_network_id->get_destination_id(),
+		send_network_id->get_source_id(),
+		send_network_id->get_type());
+
+	eap_status_e status = eap_status_process_general_error;
+
+	initialize_session_timeout(m_session_timeout);
+
+	if (is_client_when_true == false)
+	{
+		status = start_authentication();
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		m_client_restart_authentication_initiated = true;
+	}
+	else
+	{
+		if (m_client_restart_authentication_initiated == true)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_ok);
+		}
+
+		status = allow_authentication();
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		m_client_restart_authentication_initiated = true;
+	}
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e wapi_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_ASSERT(m_am_tools->get_global_mutex()->get_is_reserved() == true);
+
+	const eap_status_e status = m_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 wapi_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_ASSERT(m_am_tools->get_global_mutex()->get_is_reserved() == true);
+
+	const eap_status_e status = m_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 eap_status_e wapi_core_c::timer_expired(
+	const u32_t id, void *data)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_UNREFERENCED_PARAMETER(data); // Only trace uses this.
+	
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("TIMER: [0x%08x]->wapi_core_c::timer_expired(id 0x%02x, data 0x%08x), %s.\n"),
+		 this,
+		 id,
+		 data,
+		 (m_is_client == true) ? "client": "server"));
+
+	EAP_ASSERT(m_am_tools->get_global_mutex()->get_is_reserved() == true);
+
+	eap_status_e status(eap_status_process_general_error);
+
+	if (id == WAPI_CORE_TIMER_RETRANSMISSION_ID)
+	{
+		EAP_TRACE_DEBUG(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("TIMER: %s: WAPI_CORE_TIMER_RETRANSMISSION_ID elapsed.\n"),
+			 (m_is_client == true ? "client": "server")
+			 ));
+
+		if (m_retransmission.get_object_count() > 0ul)
+		{
+			wapi_core_retransmission_c * const retransmission = m_retransmission.get_object(0ul);
+
+			if (retransmission != 0
+				&& retransmission->get_is_valid() == true
+				&& retransmission->get_retransmission_counter() > 0)
+			{
+				EAP_TRACE_DEBUG(
+					m_am_tools,
+					TRACE_FLAGS_DEFAULT,
+					(EAPL("TIMER: %s, new retransmission, retransmission->get_is_valid()=%d, ")
+					 EAPL("retransmission->get_retransmission_counter()=%d.\n"),
+					 (m_is_client == true) ? "client": "server",
+					 retransmission->get_is_valid(),
+					 retransmission->get_retransmission_counter()));
+				
+				status = eap_status_ok;
+
+				if (retransmission->get_wapi_subtype() == wai_protocol_subtype_unicast_key_negotiation_request
+					|| retransmission->get_wapi_subtype() == wai_protocol_subtype_multicast_key_announcement)
+				{
+					// Also the previous message must be re-transmitted.
+					if (m_retransmission.get_object_count() > 1ul)
+					{
+						wapi_core_retransmission_c * const prev_retransmission = m_retransmission.get_object(1ul);
+						
+						if (prev_retransmission != 0
+							&& prev_retransmission->get_is_valid() == true
+							&& prev_retransmission->get_retransmission_counter() > 0)
+						{
+							status = resend_packet(
+								prev_retransmission->get_send_network_id(),
+								prev_retransmission->get_wai_message_data(),
+								prev_retransmission->get_retransmission_counter(),
+								prev_retransmission->get_packet_sequence_number());
+						}
+					}
+				}
+				
+				if (status == eap_status_ok)
+				{
+					status = resend_packet(
+						retransmission->get_send_network_id(),
+						retransmission->get_wai_message_data(),
+						retransmission->get_retransmission_counter(),
+						retransmission->get_packet_sequence_number());
+				}
+				
+				if (status == eap_status_ok)
+				{
+					if (retransmission->get_retransmission_counter() > 0u)
+					{
+						// OK, initialize the next time to retransmit.
+						u32_t next_retransmission_time
+							= retransmission->get_next_retransmission_time();
+
+						status = m_partner->set_timer(
+							this,
+							WAPI_CORE_TIMER_RETRANSMISSION_ID,
+							0,
+							next_retransmission_time);
+						if (status != eap_status_ok)
+						{
+							EAP_TRACE_DEBUG(
+								m_am_tools, 
+								TRACE_FLAGS_DEFAULT, 
+								(EAPL("ERROR: TIMER: %s: WAPI_CORE_TIMER_RETRANSMISSION_ID ")
+								 EAPL("set %d ms, retransmission_counter %d, failed.\n"),
+								 (m_is_client == true ? "client": "server"),
+								 next_retransmission_time,
+								 retransmission->get_retransmission_counter()));
+						}
+						else
+						{
+							retransmission->get_next_retransmission_counter(); // This decrements the counter.
+							
+							EAP_TRACE_DEBUG(
+								m_am_tools, 
+								TRACE_FLAGS_DEFAULT, 
+								(EAPL("TIMER: %s: WAPI_CORE_TIMER_RETRANSMISSION_ID ")
+								 EAPL("set %d ms, retransmission_counter %d.\n"),
+								 (m_is_client == true ? "client": "server"),
+								 next_retransmission_time,
+								 retransmission->get_retransmission_counter()));
+						}
+					}
+
+					EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+					return EAP_STATUS_RETURN(m_am_tools, status);
+				}
+				else
+				{
+					status = eap_status_ok;
+
+					EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+					return EAP_STATUS_RETURN(m_am_tools, status);
+				}
+			}
+			else
+			{
+				EAP_TRACE_DEBUG(
+					m_am_tools, 
+					TRACE_FLAGS_DEFAULT, 
+					(EAPL("TIMER: %s, no retransmission, m_retransmission=0x%08x.\n"),
+					 (m_is_client == true) ? "client": "server",
+					 retransmission));
+				if (retransmission != 0)
+				{
+					EAP_TRACE_DEBUG(
+						m_am_tools, 
+						TRACE_FLAGS_DEFAULT, 
+						(EAPL("TIMER: %s, no retransmission, retransmission->get_is_valid()=%d, ")
+						 EAPL("retransmission->get_retransmission_counter()=%d.\n"),
+						 (m_is_client == true) ? "client": "server",
+						 retransmission->get_is_valid(),
+						 retransmission->get_retransmission_counter()));
+				}
+				
+				// No good WAI-Response received to WAI-Requests.
+				// Terminate the session.
+
+				{
+					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,
+						eap_type_none,
+						eap_state_none,
+						eap_state_authentication_terminated_unsuccessfully,
+						0ul,
+						false);
+
+					notification.set_authentication_error(eap_status_authentication_failure);
+					
+					state_notification(&notification);
+				}
+
+				status = eap_status_ok;
+			}
+		}
+		else
+		{
+			EAP_TRACE_DEBUG(
+				m_am_tools, 
+				TRACE_FLAGS_DEFAULT, 
+				(EAPL("TIMER: %s, no retransmission, count of m_retransmission=%d.\n"),
+				 (m_is_client == true) ? "client": "server",
+				 m_retransmission.get_object_count()));
+		}
+	}
+	else if (id == WAPI_CORE_SESSION_TIMEOUT_ID)
+	{
+		EAP_TRACE_DEBUG(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("TIMER: %s: WAPI_CORE_SESSION_TIMEOUT_ID elapsed.\n"),
+			 (m_is_client == true ? "client": "server")
+			 ));
+
+		// we will remove this session immediately.
+		status = initialize_asynchronous_init_remove_wapi_session(0ul);
+
+		{
+			// 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 (send_network_id.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);
+			}
+
+			// This notification to eapol_core_c object.
+			// WAI authentication terminated unsuccessfully.
+			eap_state_notification_c * notification = new eap_state_notification_c(
+				m_am_tools,
+				&send_network_id,
+				m_is_client,
+				eap_state_notification_generic,
+				eap_protocol_layer_wai,
+				eapol_key_handshake_type_wai_handshake,
+				eapol_key_state_wapi_authentication_running,
+				eapol_key_state_wapi_authentication_terminated_unsuccessfull,
+				0ul,
+				false);
+			if (notification == 0)
+			{
+				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);
+
+			state_notification(notification);
+
+			delete notification;
+
+			set_wapi_state(wapi_core_state_authentication_failed);
+		}
+
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+	else if (id == WAPI_CORE_REMOVE_SESSION_TIMEOUT_ID)
+	{
+		EAP_TRACE_DEBUG(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("TIMER: %s: WAPI_CORE_REMOVE_SESSION_TIMEOUT_ID elapsed.\n"),
+			 (m_is_client == true ? "client": "server")
+			 ));
+
+		status = asynchronous_init_remove_wapi_session();
+	}
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+//
+EAP_FUNC_EXPORT eap_status_e wapi_core_c::timer_delete_data(
+	const u32_t id, void *data)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_UNREFERENCED_PARAMETER(data); // Only trace uses this.
+
+	EAP_TRACE_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("TIMER: [0x%08x]->wapi_core_c::timer_delete_data(id 0x%02x, data 0x%08x): %s.\n"),
+		 this,
+		 id,
+		 data,
+		 (m_is_client == true) ? "client": "server"
+		 ));
+
+	EAP_ASSERT(m_am_tools->get_global_mutex()->get_is_reserved() == true);
+
+	if (id == WAPI_CORE_TIMER_RETRANSMISSION_ID)
+	{
+		EAP_TRACE_DEBUG(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("TIMER: %s: WAPI_CORE_TIMER_RETRANSMISSION_ID delete data.\n"),
+			 (m_is_client == true ? "client": "server")
+			 ));
+
+		if (m_retransmission.get_object_count() > 0ul)
+		{
+			wapi_core_retransmission_c * const retransmission = m_retransmission.get_object(0ul);
+
+			if (retransmission != 0
+				&& retransmission->get_is_valid() == true
+				&& retransmission->get_retransmission_counter() > 0)
+			{
+				// Do not delete yet.
+				// cancel_retransmission() will delete m_retransmission.
+			}
+			else if (retransmission != 0)
+			{
+				(void) m_retransmission.remove_object(0ul);
+			}
+		}
+	}
+	else if (id == WAPI_CORE_REMOVE_SESSION_TIMEOUT_ID)
+	{
+		// Nothing to do.
+	}
+
+	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 wapi_core_c::reset()
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+#if !defined(USE_EAP_DEBUG_TRACE)
+	EAP_TRACE_ALWAYS(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("wapi_core_c::reset(): %s.\n"),
+		 ((m_is_client == true) ? "client": "server")));
+#else
+	EAP_TRACE_ALWAYS(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("wapi_core_c::reset(): %s: this = 0x%08x => 0x%08x.\n"),
+		 ((m_is_client == true) ? "client": "server"),
+		 this,
+		 dynamic_cast<abs_eap_base_timer_c *>(this)));
+#endif
+
+	eap_status_e status = eap_status_ok;
+
+	cancel_retransmission();
+
+	cancel_session_timeout();
+
+	cancel_wapi_failure_timeout();
+
+	cancel_asynchronous_init_remove_wapi_session();
+
+    // restart message sequencing
+    m_packet_sequence_number = 0ul;
+
+	// Add session timeout.
+	initialize_session_timeout(m_session_timeout);
+
+	m_wapi_state = wapi_core_state_none;
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+//
+EAP_FUNC_EXPORT eap_status_e wapi_core_c::set_session_timeout(
+	const u32_t session_timeout_ms)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_ASSERT(m_am_tools->get_global_mutex()->get_is_reserved() == true);
+
+	eap_status_e status = initialize_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 wapi_core_c::set_timer(
+	abs_eap_base_timer_c * const p_initializer, 
+	const u32_t p_id, 
+	void * const p_data,
+	const u32_t p_time_ms)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_ASSERT(m_am_tools->get_global_mutex()->get_is_reserved() == true);
+
+	const eap_status_e status = m_partner->set_timer(
+		p_initializer, 
+		p_id, 
+		p_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 wapi_core_c::cancel_timer(
+	abs_eap_base_timer_c * const p_initializer, 
+	const u32_t p_id)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_ASSERT(m_am_tools->get_global_mutex()->get_is_reserved() == true);
+
+	const eap_status_e status = m_partner->cancel_timer(
+		p_initializer, 
+		p_id);
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+//
+EAP_FUNC_EXPORT eap_status_e wapi_core_c::cancel_all_timers()
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_ASSERT(m_am_tools->get_global_mutex()->get_is_reserved() == true);
+
+	const eap_status_e status = m_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 wapi_core_c::set_authentication_role(const bool when_true_set_client)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	cancel_retransmission();
+
+	cancel_wapi_failure_timeout();
+
+	m_is_client_role = when_true_set_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 wapi_core_c::cancel_authentication_session()
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("WAPI_Core: this = 0x%08x, %s: wapi_core_c::cancel_authentication_session(): state=%s\n"),
+		 this,
+		 (m_is_client == true ? "client": "server"),
+		 wapi_strings_c::get_wapi_core_state_string(m_wapi_state)));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: wapi_core_c::cancel_authentication_session()");
+
+	cancel_retransmission();
+	cancel_wapi_failure_timeout();
+	cancel_session_timeout();
+
+	m_fragment_sequence_number = 0ul;
+	m_packet_sequence_number = 0u;
+
+	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 wapi_core_c::check_bksa_cache(
+	const eapol_key_authentication_type_e selected_eapol_key_authentication_type,
+	const eapol_RSNA_key_header_c::eapol_RSNA_cipher_e pairwise_key_cipher_suite,
+	const eapol_RSNA_key_header_c::eapol_RSNA_cipher_e group_key_cipher_suite)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("WAPI_Core: this = 0x%08x, %s: wapi_core_c::check_bksa_cache(): state=%s\n"),
+		 this,
+		 (m_is_client == true ? "client": "server"),
+		 wapi_strings_c::get_wapi_core_state_string(m_wapi_state)));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: wapi_core_c::check_bksa_cache()");
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, eap_status_not_supported);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e wapi_core_c::complete_query_asu_id(
+	const eap_variable_data_c * const asn1_der_subject_name,
+	const eap_variable_data_c * const asn1_der_issuer_name,
+	const eap_variable_data_c * const asn1_der_sequence_number,
+	const eap_status_e id_status)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("WAPI_Core: this = 0x%08x, %s: wapi_core_c::complete_query_asu_id(): state=%s\n"),
+		 this,
+		 (m_is_client == true ? "client": "server"),
+		 wapi_strings_c::get_wapi_core_state_string(m_wapi_state)));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: wapi_core_c::complete_query_asu_id()");
+
+	if (id_status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, id_status);
+	}
+
+	eap_status_e status = m_asu_id.set_copy_of_buffer(asn1_der_subject_name);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = m_asu_id.add_data(asn1_der_issuer_name);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = m_asu_id.add_data(asn1_der_sequence_number);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+
+	status = m_ec_certificate_store->get_own_certificate();
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e wapi_core_c::complete_get_own_certificate(
+	const eap_variable_data_c * const own_certificate)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("WAPI_Core: this = 0x%08x, %s: wapi_core_c::complete_get_own_certificate(): state=%s\n"),
+		 this,
+		 (m_is_client == true ? "client": "server"),
+		 wapi_strings_c::get_wapi_core_state_string(m_wapi_state)));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: wapi_core_c::complete_get_own_certificate()");
+
+	eap_status_e status(eap_status_not_supported);
+
+#if defined(USE_WAPI_CORE_SERVER)
+	if (m_is_client == false
+		&& m_wapi_state == wapi_core_state_start_certificate_negotiation)
+	{
+		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+		// Saves own certificate.
+
+		status = m_own_certificate.set_copy_of_buffer(own_certificate);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+		// Reads the ID of STA_AE
+
+		status = m_ec_certificate_store->read_id_of_certificate(&m_own_certificate);
+		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_WAPI_CORE_SERVER)
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e wapi_core_c::complete_select_certificate(
+	const eap_variable_data_c * const issuer_ID,
+	const eap_variable_data_c * const certificate_ID,
+	const eap_variable_data_c * const certificate)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("WAPI_Core: this = 0x%08x, %s: wapi_core_c::complete_select_certificate(): state=%s\n"),
+		 this,
+		 (m_is_client == true ? "client": "server"),
+		 wapi_strings_c::get_wapi_core_state_string(m_wapi_state)));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: wapi_core_c::complete_select_certificate()");
+
+	eap_status_e status(eap_status_not_supported);
+
+	if (issuer_ID == 0
+		|| issuer_ID->get_is_valid() == false
+		|| certificate_ID == 0
+		|| certificate_ID->get_is_valid() == false
+		|| certificate == 0
+		|| certificate->get_is_valid() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+	}
+
+	if (m_is_client == true
+		&& m_wapi_state == wapi_core_state_process_authentication_activation_message)
+	{
+		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+		// Saves own ID.
+
+		status = m_asue_id.set_copy_of_buffer(certificate_ID);
+		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_asue_id"),
+			 m_asue_id.get_data(),
+			 m_asue_id.get_data_length()));
+
+		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+		// Saves own certificate.
+
+		status = m_own_certificate.set_copy_of_buffer(certificate);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+		// Reads the ID of STA_AE
+
+		status = m_ec_certificate_store->read_id_of_certificate(&m_peer_certificate);
+		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 wapi_core_c::complete_read_id_of_certificate(
+	const eap_variable_data_c * const ID)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("WAPI_Core: this = 0x%08x, %s: wapi_core_c::complete_read_id_of_certificate(): state=%s\n"),
+		 this,
+		 (m_is_client == true ? "client": "server"),
+		 wapi_strings_c::get_wapi_core_state_string(m_wapi_state)));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: wapi_core_c::complete_read_id_of_certificate()");
+
+	eap_status_e status(eap_status_not_supported);
+
+#if defined(USE_WAPI_CORE_SERVER)
+	if (m_is_client == false
+		&& m_wapi_state == wapi_core_state_start_certificate_negotiation)
+	{
+		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+		// Save AE-ID.
+
+		status = m_ae_id.set_copy_of_buffer(ID);
+		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_ae_id"),
+			 m_ae_id.get_data(),
+			 m_ae_id.get_data_length()));
+
+		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+		// Create Authentication Identifier.
+
+		if (m_wapi_negotiation_state == wapi_negotiation_state_initial_negotiation)
+		{
+			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(
+				&m_authentication_identifier,
+				WAPI_AUTHENTICATION_IDENTIFIER_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("new local m_authentication_identifier"),
+				 m_authentication_identifier.get_data(),
+				 m_authentication_identifier.get_data_length()));
+		}
+
+		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+		// Create the Authentication Activation Packet.
+
+		wai_message_payloads_c * const payloads = new wai_message_payloads_c(m_am_tools, m_is_client);
+		eap_automatic_variable_c<wai_message_payloads_c> automatic_payloads(m_am_tools, payloads);
+
+		if (payloads == 0
+			|| 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);
+		}
+
+		status = payloads->initialise_header();
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = payloads->get_wai_protocol_packet_header_writable()->set_subtype(wai_protocol_subtype_authentication_activation);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+		// Adds FLAG to data field.
+
+		{
+			wai_variable_data_c data_flag(m_am_tools);
+			if (data_flag.get_is_valid() == false)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+			}
+
+			u8_t flag(wai_data_flag_mask_none);
+
+			if (m_wapi_negotiation_state == wapi_negotiation_state_rekeying)
+			{
+				flag = wai_data_flag_mask_BK_Rekeying;
+			}
+
+			status = data_flag.create(
+				wai_payload_type_flag,
+				&flag,
+				sizeof(flag));
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			status = payloads->add_tlv(&data_flag);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+		}
+
+		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+		// Adds Authentication Identifier to data field.
+
+		{
+			wai_variable_data_c data_authentication_identifier(m_am_tools);
+			if (data_authentication_identifier.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 = data_authentication_identifier.create(
+				wai_payload_type_authentication_identifier,
+				&m_authentication_identifier);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			status = payloads->add_tlv(&data_authentication_identifier);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+		}
+
+		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+		// Adds ID of local ASU to data field.
+
+		{
+			wai_variable_data_c data_id_of_local_asu(m_am_tools);
+			if (data_id_of_local_asu.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_test_other_asu_id.get_is_valid_data() == true)
+			{
+				status = data_id_of_local_asu.create(
+					wai_payload_type_identity,
+					&m_test_other_asu_id);
+			}
+			else
+			{
+				status = data_id_of_local_asu.create(
+					wai_payload_type_identity,
+					&m_asu_id);
+			}
+
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			status = payloads->add_tlv(&data_id_of_local_asu);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+		}
+
+		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+		// Adds STA_AE Certificate to data field.
+
+		{
+			wai_variable_data_c data_certificate(m_am_tools);
+			if (data_certificate.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 = data_certificate.create(
+				wai_payload_type_certificate,
+				&m_own_certificate);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			status = payloads->add_tlv(&data_certificate);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+		}
+
+		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+		// Adds ECDH parameter to data field.
+
+		{
+			wai_variable_data_c data_ecdh_parameter(m_am_tools);
+			if (data_ecdh_parameter.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 = data_ecdh_parameter.create(
+				wai_payload_type_echd_parameter,
+				WAPI_ECDH_OID_PARAMETER,
+				sizeof(WAPI_ECDH_OID_PARAMETER));
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			status = payloads->add_tlv(&data_ecdh_parameter);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+		}
+
+		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+		// Create and send message.
+
+		wai_message_c new_wai_message_data(m_am_tools, m_is_client);
+		if (new_wai_message_data.get_is_valid() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = payloads->create_wai_tlv_message(&new_wai_message_data, false);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		//cancel_retransmission();
+
+		status = packet_send(
+			&new_wai_message_data,
+			payloads->get_wai_protocol_packet_header_writable()->get_subtype());
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		set_wapi_state(wapi_core_state_wait_access_authentication_request_message);
+
+		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	}
+	else
+#endif //#if defined(USE_WAPI_CORE_SERVER)
+	if (m_is_client == true
+		&& m_wapi_state == wapi_core_state_process_authentication_activation_message)
+	{
+		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+		// Saves ID of STA_AE.
+
+		status = m_ae_id.set_copy_of_buffer(ID);
+		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_ae_id"),
+			 m_ae_id.get_data(),
+			 m_ae_id.get_data_length()));
+
+		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+		// Creates ECDH temporary keys.
+
+		status = m_ec_certificate_store->create_ecdh_temporary_keys();
+		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 wapi_core_c::complete_create_signature_with_private_key(
+	const eap_variable_data_c * const signature,
+	const eap_status_e signature_status)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("WAPI_Core: this = 0x%08x, %s: wapi_core_c::complete_create_signature_with_private_key(): state=%s\n"),
+		 this,
+		 (m_is_client == true ? "client": "server"),
+		 wapi_strings_c::get_wapi_core_state_string(m_wapi_state)));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: wapi_core_c::complete_create_signature_with_private_key()");
+
+	if (signature_status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, signature_status);
+	}
+
+	eap_status_e status(eap_status_not_supported);
+
+	
+#if defined(USE_WAPI_CORE_SERVER)
+	if (m_is_client == false
+		&& m_wapi_state == wapi_core_state_process_access_authentication_request_message_AE_signature_trusted_by_ASUE)
+	{
+		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+		// Adds signature of AE trusted by ASUE to data field.
+
+		{
+			wai_variable_data_c data_signature(m_am_tools);
+			if (data_signature.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_TRACE_DATA_DEBUG(
+				m_am_tools, 
+				TRACE_FLAGS_DEFAULT, 
+				(EAPL("m_ae_id"),
+				 m_ae_id.get_data(),
+				 m_ae_id.get_data_length()));
+
+			status = create_signature_attributes(
+				&data_signature,
+				&m_ae_id,
+				signature);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			status = m_new_payloads.add_tlv(&data_signature);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+		}
+
+		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+		// Create and send message.
+
+		wai_message_c new_wai_message_data(m_am_tools, m_is_client);
+		if (new_wai_message_data.get_is_valid() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = m_new_payloads.create_wai_tlv_message(&new_wai_message_data, false);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		//cancel_retransmission();
+
+		status = packet_send(
+			&new_wai_message_data,
+			m_new_payloads.get_wai_protocol_packet_header_writable()->get_subtype());
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		m_new_payloads.reset();
+
+		set_wapi_state(wapi_core_state_start_unicast_key_negotiation);
+
+		status = start_unicast_key_negotiation();
+		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_is_client == false
+		&& m_wapi_state == wapi_core_state_process_access_authentication_request_message_ASU_signature_trusted_by_AE)
+	{
+		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+		// Adds signature of server trusted by AE to data field.
+
+		status = create_signature_attributes(
+			&m_server_signature_trusted_by_ae,
+			&m_asu_id,
+			signature);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+		// Creates ECDH temporary keys.
+
+		status = m_ec_certificate_store->create_ecdh_temporary_keys();
+		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_is_client == false
+		&& m_wapi_state == wapi_core_state_process_access_authentication_request_message)
+	{
+		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+		// Adds signature of server trusted by ASUE to data field.
+
+		status = create_signature_attributes(
+			&m_server_signature_trusted_by_asue,
+			&m_asu_id,
+			signature);
+		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 HASH(m_am_tools);
+			if (HASH.get_is_valid() == false)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+			}
+
+			wai_message_payloads_c * const payloads = new wai_message_payloads_c(m_am_tools, m_is_client);
+			eap_automatic_variable_c<wai_message_payloads_c> automatic_payloads(m_am_tools, payloads);
+
+			if (payloads == 0
+				|| 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);
+			}
+
+			status = payloads->insert_payload(&m_result_of_certificate_verification);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			status = payloads->insert_payload(&m_server_signature_trusted_by_asue);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			status = create_HASH(payloads, true, &HASH);
+			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_asu_id"),
+				 m_asu_id.get_data(),
+				 m_asu_id.get_data_length()));
+
+			set_wapi_state(wapi_core_state_process_access_authentication_request_message_ASU_signature_trusted_by_AE);
+
+			status = m_ec_certificate_store->create_signature_with_private_key(
+				&HASH,
+				&m_asu_id);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+		}
+	}
+	else
+#endif //#if defined(USE_WAPI_CORE_SERVER)
+	if (m_is_client == true
+		&& m_wapi_state == wapi_core_state_process_authentication_activation_message)
+	{
+		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+		// Adds Signature of ASUE to data field.
+
+		{
+			wai_variable_data_c data_signature(m_am_tools);
+			if (data_signature.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_TRACE_DATA_DEBUG(
+				m_am_tools, 
+				TRACE_FLAGS_DEFAULT, 
+				(EAPL("m_asue_id"),
+				 m_asue_id.get_data(),
+				 m_asue_id.get_data_length()));
+
+			status = create_signature_attributes(
+				&data_signature,
+				&m_asue_id,
+				signature);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			status = m_new_payloads.add_tlv(&data_signature);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+		}
+
+		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+		// Create and send message.
+
+		wai_message_c new_wai_message_data(m_am_tools, m_is_client);
+		if (new_wai_message_data.get_is_valid() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = m_new_payloads.create_wai_tlv_message(&new_wai_message_data, false);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = packet_send(
+			&new_wai_message_data,
+			m_new_payloads.get_wai_protocol_packet_header_writable()->get_subtype());
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		m_new_payloads.reset();
+
+		set_wapi_state(wapi_core_state_wait_access_authentication_response_message);
+
+		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	}
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e wapi_core_c::complete_verify_signature_with_public_key(
+	const eap_status_e verification_status)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("WAPI_Core: this = 0x%08x, %s: wapi_core_c::complete_verify_signature_with_public_key(): state=%s\n"),
+		 this,
+		 (m_is_client == true ? "client": "server"),
+		 wapi_strings_c::get_wapi_core_state_string(m_wapi_state)));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: wapi_core_c::complete_verify_signature_with_public_key()");
+
+	eap_status_e status(eap_status_not_supported);
+
+#if defined(USE_WAPI_CORE_SERVER)
+	if (m_is_client == false
+		&& m_wapi_state == wapi_core_state_process_access_authentication_request_message)
+	{
+		if (verification_status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, verification_status);
+		}
+
+		// Create the AE challenge.
+		if (m_ae_certificate_challenge.get_is_valid_data() == false)
+		{
+			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(
+				&m_ae_certificate_challenge,
+				WAPI_CHALLENGE_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_do_certificate_validation == true)
+		{
+			EAP_TRACE_DEBUG(
+				m_am_tools,
+				TRACE_FLAGS_DEFAULT,
+				(EAPL("WAPI_Core: this = 0x%08x, %s: wapi_core_c::complete_verify_signature_with_public_key(): does certificate validation\n"),
+				this,
+				(m_is_client == true ? "client": "server")));
+
+			// First we need to create verification results by server (ASU) of both certificates, ASUE certificate and AE certificate.
+			// Second thing to create are server (ASU) signatures trusted by ASUE and AE.
+			//     Signature trusted by ASUE signs field Authentication Result for certificate in the Certificate Authentication Response packet.
+			//     Signature trusted by AE signs both fields Authentication Result for certificate and Signature trusted by ASUE in the Certificate Authentication Response packet.
+			// All operations are simulated here without external server (ASU) and without Certificate Authentication Request and Certificate Authentication Response packets.
+
+			// Create the result of certificate verification.
+			status = create_result_of_certificate_verification(
+				&m_result_of_certificate_verification,
+				&m_ae_certificate_challenge,
+				&m_asue_certificate_challenge,
+				wapi_certificate_result_valid,
+				&m_peer_certificate, // ASUE certificate
+				wapi_certificate_result_valid,
+				&m_own_certificate); // AE certificate
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+			// Create Signature of ASU.
+
+			{
+				eap_variable_data_c HASH(m_am_tools);
+				if (HASH.get_is_valid() == false)
+				{
+					EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+					return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+				}
+
+				wai_message_payloads_c * const payloads = new wai_message_payloads_c(m_am_tools, m_is_client);
+				eap_automatic_variable_c<wai_message_payloads_c> automatic_payloads(m_am_tools, payloads);
+
+				if (payloads == 0
+					|| 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);
+				}
+
+				status = payloads->initialise_header();
+				if (status != eap_status_ok)
+				{
+					EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+					return EAP_STATUS_RETURN(m_am_tools, status);
+				}
+
+				status = payloads->get_wai_protocol_packet_header_writable()->set_subtype(wai_protocol_subtype_certificate_authentication_response);
+				if (status != eap_status_ok)
+				{
+					EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+					return EAP_STATUS_RETURN(m_am_tools, status);
+				}
+
+				status = payloads->add_tlv(&m_result_of_certificate_verification);
+				if (status != eap_status_ok)
+				{
+					EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+					return EAP_STATUS_RETURN(m_am_tools, status);
+				}
+
+				status = create_HASH(payloads, true, &HASH);
+				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_asu_id"),
+					 m_asu_id.get_data(),
+					 m_asu_id.get_data_length()));
+
+				status = m_ec_certificate_store->create_signature_with_private_key(
+					&HASH,
+					&m_asu_id);
+				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_DEBUG(
+				m_am_tools,
+				TRACE_FLAGS_DEFAULT,
+				(EAPL("WAPI_Core: this = 0x%08x, %s: wapi_core_c::complete_verify_signature_with_public_key(): no certificate validation\n"),
+				this,
+				(m_is_client == true ? "client": "server")));
+
+			// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+			// Creates ECDH temporary keys.
+
+			set_wapi_state(wapi_core_state_process_access_authentication_request_message_ASU_signature_trusted_by_AE);
+
+			status = m_ec_certificate_store->create_ecdh_temporary_keys();
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+		}
+	}
+	else
+#endif //#if defined(USE_WAPI_CORE_SERVER)
+	if (m_is_client == true
+		&& m_wapi_state == wapi_core_state_process_access_authentication_response_message)
+	{
+		if (verification_status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, verification_status);
+		}
+
+		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+		// Verify Signature of ASU.
+
+		if (m_do_certificate_validation == true)
+		{
+			EAP_TRACE_DEBUG(
+				m_am_tools,
+				TRACE_FLAGS_DEFAULT,
+				(EAPL("WAPI_Core: this = 0x%08x, %s: wapi_core_c::complete_verify_signature_with_public_key(): does certificate validation\n"),
+				this,
+				(m_is_client == true ? "client": "server")));
+
+			if (m_server_signature_trusted_by_ae.get_is_valid_data() == false)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
+			}
+
+			eap_variable_data_c signature_data(m_am_tools);
+			if (signature_data.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 received_asu_id(m_am_tools);
+			if (received_asu_id.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 = parse_signature_attributes(
+				&m_server_signature_trusted_by_ae,
+				&received_asu_id,
+				&signature_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("received_asu_id"),
+				 received_asu_id.get_data(),
+				 received_asu_id.get_data_length()));
+
+			eap_variable_data_c HASH(m_am_tools);
+			if (HASH.get_is_valid() == false)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+			}
+
+			// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+			// Adds Multiple Certificate Verification Result to data field.
+
+			{
+				status = m_new_payloads.reset();
+				if (status != eap_status_ok)
+				{
+					EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+					return EAP_STATUS_RETURN(m_am_tools, status);
+				}
+
+				status = m_new_payloads.add_tlv(&m_result_of_certificate_verification);
+				if (status != eap_status_ok)
+				{
+					EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+					return EAP_STATUS_RETURN(m_am_tools, status);
+				}
+			}
+
+			status = create_HASH(&m_new_payloads, false, &HASH);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			set_wapi_state(wapi_core_state_process_access_authentication_response_message_ASU_signature);
+
+			status = m_ec_certificate_store->verify_signature_with_public_key(
+				&m_asu_id,
+				&HASH,
+				&signature_data,
+				false);
+			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_DEBUG(
+				m_am_tools,
+				TRACE_FLAGS_DEFAULT,
+				(EAPL("WAPI_Core: this = 0x%08x, %s: wapi_core_c::complete_verify_signature_with_public_key(): no certificate validation\n"),
+				this,
+				(m_is_client == true ? "client": "server")));
+
+			// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+			// Create temporary ECDH keys.
+
+			set_wapi_state(wapi_core_state_process_access_authentication_response_message_ASU_signature);
+
+			status = m_ec_certificate_store->create_ecdh(
+				&m_own_private_key_d,
+				&m_peer_public_key_x,
+				&m_peer_public_key_y);
+			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_is_client == true
+		&& m_wapi_state == wapi_core_state_process_access_authentication_response_message_ASU_signature)
+	{
+		if (verification_status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, verification_status);
+		}
+
+		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+		// Create temporary ECDH keys.
+
+		status = m_ec_certificate_store->create_ecdh(
+			&m_own_private_key_d,
+			&m_peer_public_key_x,
+			&m_peer_public_key_y);
+		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 wapi_core_c::complete_create_ecdh_temporary_keys(
+	const eap_variable_data_c * const private_key_d,
+	const eap_variable_data_c * const public_key_x,
+	const eap_variable_data_c * const public_key_y)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("WAPI_Core: this = 0x%08x, %s: wapi_core_c::complete_create_ecdh_temporary_keys(): state=%s\n"),
+		 this,
+		 (m_is_client == true ? "client": "server"),
+		 wapi_strings_c::get_wapi_core_state_string(m_wapi_state)));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: wapi_core_c::complete_create_ecdh_temporary_keys()");
+
+	eap_status_e status(eap_status_not_supported);
+
+	EAP_TRACE_DATA_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("private_key_d"),
+		 private_key_d->get_data(),
+		 private_key_d->get_data_length()));
+
+	EAP_TRACE_DATA_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("public_key_x"),
+		 public_key_x->get_data(),
+		 public_key_x->get_data_length()));
+
+	EAP_TRACE_DATA_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("public_key_y"),
+		 public_key_y->get_data(),
+		 public_key_y->get_data_length()));
+
+	
+#if defined(USE_WAPI_CORE_SERVER)
+	if (m_is_client == false
+		&& m_wapi_state == wapi_core_state_process_access_authentication_request_message_ASU_signature_trusted_by_AE)
+	{
+		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+		// Save ECDH keys.
+
+		// We do not use the new keys. Only the first generated keys.
+		if (m_own_private_key_d.get_is_valid_data() == false
+			|| m_own_public_key_x.get_is_valid_data() == false
+			|| m_own_public_key_y.get_is_valid_data() == false)
+		{
+			status = m_own_private_key_d.set_copy_of_buffer(private_key_d);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			status = m_own_public_key_x.set_copy_of_buffer(public_key_x);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			status = m_own_public_key_y.set_copy_of_buffer(public_key_y);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+		}
+
+		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+		// Create temporary ECDH keys.
+
+		status = m_ec_certificate_store->create_ecdh(
+			&m_own_private_key_d,
+			&m_peer_public_key_x,
+			&m_peer_public_key_y);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+	}
+	else
+#endif //#if defined(USE_WAPI_CORE_SERVER)
+	if (m_is_client == true
+		&& m_wapi_state == wapi_core_state_process_authentication_activation_message)
+	{
+		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+		// Save ECDH keys.
+
+		// We do not use the new keys. Only the first generated keys.
+		if (m_own_private_key_d.get_is_valid_data() == false
+			|| m_own_public_key_x.get_is_valid_data() == false
+			|| m_own_public_key_y.get_is_valid_data() == false)
+		{
+			status = m_own_private_key_d.set_copy_of_buffer(private_key_d);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			status = m_own_public_key_x.set_copy_of_buffer(public_key_x);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			status = m_own_public_key_y.set_copy_of_buffer(public_key_y);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+		}
+
+		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+		// Create the Access Authentication Request Packet.
+
+		m_new_payloads.reset();
+
+		if (m_new_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);
+		}
+
+		status = m_new_payloads.initialise_header();
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = m_new_payloads.get_wai_protocol_packet_header_writable()->set_subtype(wai_protocol_subtype_access_authentication_request);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+		// Adds FLAG to data field.
+
+		{
+			wai_variable_data_c data_flag(m_am_tools);
+			if (data_flag.get_is_valid() == false)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+			}
+
+			u8_t flag(wai_data_flag_mask_none | wai_data_flag_mask_Certificate_Validation_Request);
+
+			if (m_wapi_negotiation_state == wapi_negotiation_state_rekeying)
+			{
+				flag = wai_data_flag_mask_BK_Rekeying;
+			}
+
+			status = data_flag.create(
+				wai_payload_type_flag,
+				&flag,
+				sizeof(flag));
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			status = m_new_payloads.add_tlv(&data_flag);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+		}
+
+		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+		// Adds Authentication Identifier to data field.
+
+		{
+			wai_variable_data_c data_authentication_identifier(m_am_tools);
+			if (data_authentication_identifier.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 = data_authentication_identifier.create(
+				wai_payload_type_authentication_identifier,
+				&m_authentication_identifier);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			status = m_new_payloads.add_tlv(&data_authentication_identifier);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+		}
+
+		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+		// Adds ASUE Challenge to data field.
+
+		{
+			wai_variable_data_c data_ASUE_challenge(m_am_tools);
+			if (data_ASUE_challenge.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 = data_ASUE_challenge.create(
+				wai_payload_type_nonce,
+				&m_asue_certificate_challenge);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			status = m_new_payloads.add_tlv(&data_ASUE_challenge);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+		}
+
+		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+		// Adds ASUE key data to data field.
+
+		{
+			wai_variable_data_c ASUE_key_data(m_am_tools);
+			if (ASUE_key_data.get_is_valid() == false)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+			}
+
+			u8_t ec_point_type(WAI_EC_POINT_TYPE_NO_COMPRESSION_ID);
+
+			status = ASUE_key_data.create(
+				wai_payload_type_key_data,
+				&ec_point_type,
+				sizeof(ec_point_type));
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			status = ASUE_key_data.add_data(
+				wai_payload_type_key_data,
+				&m_own_public_key_x);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			status = ASUE_key_data.add_data(
+				wai_payload_type_key_data,
+				&m_own_public_key_y);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			status = m_new_payloads.add_tlv(&ASUE_key_data);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+		}
+
+		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+		// Adds ID of STA_AE to data field.
+
+		{
+			wai_variable_data_c data_id_of_ae(m_am_tools);
+			if (data_id_of_ae.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_TRACE_DATA_DEBUG(
+				m_am_tools, 
+				TRACE_FLAGS_DEFAULT, 
+				(EAPL("m_ae_id"),
+				 m_ae_id.get_data(),
+				 m_ae_id.get_data_length()));
+
+			status = data_id_of_ae.create(
+				wai_payload_type_identity,
+				&m_ae_id);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			status = m_new_payloads.add_tlv(&data_id_of_ae);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+		}
+
+		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+		// Adds STA_ASUE Certificate to data field.
+
+		{
+			wai_variable_data_c data_certificate(m_am_tools);
+			if (data_certificate.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 = data_certificate.create(
+				wai_payload_type_certificate,
+				&m_own_certificate);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			status = m_new_payloads.add_tlv(&data_certificate);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+		}
+
+		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+		// Adds ECDH parameter to data field.
+
+		{
+			wai_variable_data_c data_ecdh_parameter(m_am_tools);
+			if (data_ecdh_parameter.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 = data_ecdh_parameter.create(
+				wai_payload_type_echd_parameter,
+				WAPI_ECDH_OID_PARAMETER,
+				sizeof(WAPI_ECDH_OID_PARAMETER));
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			status = m_new_payloads.add_tlv(&data_ecdh_parameter);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+		}
+
+		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+		// Adds optional ASU list trusted by ASUE. We do not add.
+
+
+		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+		// Create Signature of ASUE.
+
+		{
+			eap_variable_data_c HASH(m_am_tools);
+			if (HASH.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_HASH(&m_new_payloads, false, &HASH);
+			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_asue_id"),
+				 m_asue_id.get_data(),
+				 m_asue_id.get_data_length()));
+
+			status = m_ec_certificate_store->create_signature_with_private_key(
+				&HASH,
+				&m_asue_id);
+			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);
+}
+
+//--------------------------------------------------
+
+bool wapi_core_c::compare_issuer_name(const eap_variable_data_c * const asue_id, const eap_variable_data_c * const ae_id)
+{
+	eap_variable_data_c asue_subject_name(m_am_tools);
+	eap_variable_data_c asue_issuer_name(m_am_tools);
+	eap_variable_data_c asue_sequence_number(m_am_tools);
+
+	if (asue_subject_name.get_is_valid() == false
+		|| asue_issuer_name.get_is_valid() == false
+		|| asue_sequence_number.get_is_valid() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		(void) EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+		return false;
+	}
+
+	{
+		wapi_asn1_der_parser_c asue(m_am_tools);
+
+		if (asue.get_is_valid() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			(void) EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+			return false;
+		}
+
+		eap_status_e status = asue.decode(asue_id);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			(void) EAP_STATUS_RETURN(m_am_tools, status);
+			return false;
+		}
+
+		status = asue.get_wapi_identity(
+			&asue_subject_name,
+			&asue_issuer_name,
+			&asue_sequence_number);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			(void) EAP_STATUS_RETURN(m_am_tools, status);
+			return false;
+		}
+	}
+
+
+	eap_variable_data_c ae_subject_name(m_am_tools);
+	eap_variable_data_c ae_issuer_name(m_am_tools);
+	eap_variable_data_c ae_sequence_number(m_am_tools);
+
+	if (ae_subject_name.get_is_valid() == false
+		|| ae_issuer_name.get_is_valid() == false
+		|| ae_sequence_number.get_is_valid() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		(void) EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+		return false;
+	}
+
+	{
+		wapi_asn1_der_parser_c ae(m_am_tools);
+
+		if (ae.get_is_valid() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			(void) EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+			return false;
+		}
+
+		eap_status_e status = ae.decode(ae_id);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			(void) EAP_STATUS_RETURN(m_am_tools, status);
+			return false;
+		}
+
+		status = ae.get_wapi_identity(
+			&ae_subject_name,
+			&ae_issuer_name,
+			&ae_sequence_number);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			(void) EAP_STATUS_RETURN(m_am_tools, status);
+			return false;
+		}
+	}
+
+	return asue_issuer_name.compare(&ae_issuer_name) == 0;
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e wapi_core_c::complete_create_ecdh(
+	const eap_variable_data_c * const K_AB_x4,
+	const eap_variable_data_c * const K_AB_y4)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("WAPI_Core: this = 0x%08x, %s: wapi_core_c::complete_create_ecdh(): state=%s\n"),
+		 this,
+		 (m_is_client == true ? "client": "server"),
+		 wapi_strings_c::get_wapi_core_state_string(m_wapi_state)));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: wapi_core_c::complete_create_ecdh()");
+
+	eap_variable_data_c key(m_am_tools);
+	if (key.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_TRACE_DATA_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("K_AB_x4"),
+		 K_AB_x4->get_data(),
+		 K_AB_x4->get_data_length()));
+
+	EAP_TRACE_DATA_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("K_AB_y4"),
+		 K_AB_y4->get_data(),
+		 K_AB_y4->get_data_length()));
+
+	eap_status_e status(eap_status_process_general_error);
+
+	// Only the x-coordinate is used in key generation.
+	status = key.set_copy_of_buffer(K_AB_x4);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	// BK || Challenge seed = KD_HMAC_SHA256((yxP) abscissa, NONCE_AE || NONCE_ASUE || string label)
+
+	crypto_kd_hmac_sha256_c kd_hmac(m_am_tools);
+	if (kd_hmac.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 label(m_am_tools);
+	if (label.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 = label.set_copy_of_buffer(&m_ae_certificate_challenge);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = label.add_data(&m_asue_certificate_challenge);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = label.add_data(WAPI_CERTIFICATE_KEY_LABEL, WAPI_CERTIFICATE_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);
+	}
+
+	eap_variable_data_c bk_challenge_seed(m_am_tools);
+	if (bk_challenge_seed.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 = kd_hmac.expand_key(
+		&bk_challenge_seed,
+		WAPI_BK_LENGTH + WAPI_CHALLENGE_SEED_LENGTH,
+		&key,
+		&label);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = m_BK.set_copy_of_buffer(
+		bk_challenge_seed.get_data(WAPI_BK_LENGTH),
+		WAPI_BK_LENGTH);
+	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 next_challenge(m_am_tools);
+		if (next_challenge.get_is_valid() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+		}
+
+		crypto_sha_256_c sha_256(m_am_tools);
+		if (sha_256.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 = sha_256.hash_init();
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = sha_256.hash_update(
+			bk_challenge_seed.get_data_offset(WAPI_BK_LENGTH, WAPI_CHALLENGE_SEED_LENGTH),
+			WAPI_CHALLENGE_SEED_LENGTH);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		u32_t md_length(sha_256.get_digest_length());
+
+		status = m_authentication_identifier.set_buffer_length(md_length);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = m_authentication_identifier.set_data_length(md_length);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = sha_256.hash_final(
+			m_authentication_identifier.get_data(),
+			&md_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("next local m_authentication_identifier"),
+			 m_authentication_identifier.get_data(),
+			 m_authentication_identifier.get_data_length()));
+	}
+
+#if defined(USE_WAPI_CORE_SERVER)
+	if (m_is_client == false
+		&& m_wapi_state == wapi_core_state_process_access_authentication_request_message_ASU_signature_trusted_by_AE)
+	{
+		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+		// Create the Access Authentication Request Packet.
+
+		m_new_payloads.reset();
+
+		if (m_new_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);
+		}
+
+		status = m_new_payloads.initialise_header();
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = m_new_payloads.get_wai_protocol_packet_header_writable()->set_subtype(wai_protocol_subtype_access_authentication_response);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+		// Adds FLAG to data field.
+
+		{
+			wai_variable_data_c data_flag(m_am_tools);
+			if (data_flag.get_is_valid() == false)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+			}
+
+			u8_t flag(wai_data_flag_mask_none | wai_data_flag_mask_Optional_Field | wai_data_flag_mask_Certificate_Validation_Request);
+
+			if (m_wapi_negotiation_state == wapi_negotiation_state_rekeying)
+			{
+				flag = wai_data_flag_mask_BK_Rekeying;
+			}
+
+			status = data_flag.create(
+				wai_payload_type_flag,
+				&flag,
+				sizeof(flag));
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			status = m_new_payloads.add_tlv(&data_flag);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+		}
+
+		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+		// Adds ASUE Challenge to data field.
+
+		{
+			wai_variable_data_c data_ASUE_challenge(m_am_tools);
+			if (data_ASUE_challenge.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 = data_ASUE_challenge.create(
+				wai_payload_type_nonce,
+				&m_asue_certificate_challenge);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			status = m_new_payloads.add_tlv(&data_ASUE_challenge);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+		}
+
+		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+		// Adds AE Challenge to data field.
+
+		{
+			wai_variable_data_c data_AE_challenge(m_am_tools);
+			if (data_AE_challenge.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 = data_AE_challenge.create(
+				wai_payload_type_nonce,
+				&m_ae_certificate_challenge);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			status = m_new_payloads.add_tlv(&data_AE_challenge);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+		}
+
+		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+		// Adds Access Result to data field.
+
+		{
+			wai_variable_data_c data_AE_challenge(m_am_tools);
+			if (data_AE_challenge.get_is_valid() == false)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+			}
+
+			wapi_access_result_e result(wapi_access_result_successfull_access);
+
+			status = data_AE_challenge.create(
+				wai_payload_type_access_result,
+				&result,
+				sizeof(result));
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			status = m_new_payloads.add_tlv(&data_AE_challenge);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+		}
+
+		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+		// Adds ASUE key data to data field.
+
+		{
+			wai_variable_data_c ASUE_key_data(m_am_tools);
+			if (ASUE_key_data.get_is_valid() == false)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+			}
+
+			u8_t ec_point_type(WAI_EC_POINT_TYPE_NO_COMPRESSION_ID);
+
+			status = ASUE_key_data.create(
+				wai_payload_type_key_data,
+				&ec_point_type,
+				sizeof(ec_point_type));
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			status = ASUE_key_data.add_data(
+				wai_payload_type_key_data,
+				&m_peer_public_key_x);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			status = ASUE_key_data.add_data(
+				wai_payload_type_key_data,
+				&m_peer_public_key_y);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			status = m_new_payloads.add_tlv(&ASUE_key_data);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+		}
+
+		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+		// Adds AE key data to data field.
+
+		{
+			wai_variable_data_c ASUE_key_data(m_am_tools);
+			if (ASUE_key_data.get_is_valid() == false)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+			}
+
+			u8_t ec_point_type(WAI_EC_POINT_TYPE_NO_COMPRESSION_ID);
+
+			status = ASUE_key_data.create(
+				wai_payload_type_key_data,
+				&ec_point_type,
+				sizeof(ec_point_type));
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			status = ASUE_key_data.add_data(
+				wai_payload_type_key_data,
+				&m_own_public_key_x);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			status = ASUE_key_data.add_data(
+				wai_payload_type_key_data,
+				&m_own_public_key_y);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			status = m_new_payloads.add_tlv(&ASUE_key_data);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+		}
+
+		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+		// Adds ID of STA_AE to data field.
+
+		{
+			wai_variable_data_c data_id_of_ae(m_am_tools);
+			if (data_id_of_ae.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_TRACE_DATA_DEBUG(
+				m_am_tools, 
+				TRACE_FLAGS_DEFAULT, 
+				(EAPL("m_ae_id"),
+				 m_ae_id.get_data(),
+				 m_ae_id.get_data_length()));
+
+			status = data_id_of_ae.create(
+				wai_payload_type_identity,
+				&m_ae_id);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			status = m_new_payloads.add_tlv(&data_id_of_ae);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+		}
+
+		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+		// Adds ID of STA_ASUE to data field.
+
+		{
+			wai_variable_data_c data_id_of_asue(m_am_tools);
+			if (data_id_of_asue.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_TRACE_DATA_DEBUG(
+				m_am_tools, 
+				TRACE_FLAGS_DEFAULT, 
+				(EAPL("m_asue_id"),
+				 m_asue_id.get_data(),
+				 m_asue_id.get_data_length()));
+
+			status = data_id_of_asue.create(
+				wai_payload_type_identity,
+				&m_asue_id);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			status = m_new_payloads.add_tlv(&data_id_of_asue);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+		}
+
+		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+		// Adds Multiple Certificate Verification Result to data field.
+
+		if (m_do_certificate_validation == true)
+		{
+			status = m_new_payloads.add_tlv(&m_result_of_certificate_verification);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			status = m_new_payloads.add_tlv(&m_server_signature_trusted_by_asue);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			if (compare_issuer_name(&m_asu_id, &m_ae_id) == false)
+			{
+				status = m_new_payloads.add_tlv(&m_server_signature_trusted_by_ae);
+				if (status != eap_status_ok)
+				{
+					EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+					return EAP_STATUS_RETURN(m_am_tools, status);
+				}
+			}
+		}
+
+		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+		// Create Signature of AE.
+
+		{
+			eap_variable_data_c HASH(m_am_tools);
+			if (HASH.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_HASH(&m_new_payloads, true, &HASH);
+			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_asue_id"),
+				 m_asue_id.get_data(),
+				 m_asue_id.get_data_length()));
+
+			set_wapi_state(wapi_core_state_process_access_authentication_request_message_AE_signature_trusted_by_ASUE);
+
+			status = m_ec_certificate_store->create_signature_with_private_key(
+				&HASH,
+				&m_ae_id);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+		}
+	}
+	else
+#endif //#if defined(USE_WAPI_CORE_SERVER)
+	if (m_is_client == true
+		&& m_wapi_state == wapi_core_state_process_access_authentication_response_message_ASU_signature)
+	{
+		set_wapi_state(wapi_core_state_wait_unicast_key_negotiation_request_message);
+	}
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+// End.