eapol/eapol_framework/eapol_common/core/eapol_key_state_server.cpp
changeset 0 c8830336c852
child 2 1c7bc153c08e
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/eapol/eapol_framework/eapol_common/core/eapol_key_state_server.cpp	Thu Dec 17 08:47:43 2009 +0200
@@ -0,0 +1,2488 @@
+/*
+* Copyright (c) 2001-2006 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of the License "Eclipse Public License v1.0"
+* which accompanies this distribution, and is available
+* at the URL "http://www.eclipse.org/legal/epl-v10.html".
+*
+* Initial Contributors:
+* Nokia Corporation - initial contribution.
+*
+* Contributors:
+*
+* Description:  EAP and WLAN authentication protocols.
+*
+*/
+
+
+// This is enumeration of EAPOL source code.
+#if defined(USE_EAP_MINIMUM_RELEASE_TRACES)
+	#undef EAP_FILE_NUMBER_ENUM
+	#define EAP_FILE_NUMBER_ENUM 51 
+	#undef EAP_FILE_NUMBER_DATE 
+	#define EAP_FILE_NUMBER_DATE 1127594498 
+#endif //#if defined(USE_EAP_MINIMUM_RELEASE_TRACES)
+
+
+#if !defined(NO_EAPOL_KEY_STATE_SERVER)
+
+#include "eap_am_memory.h"
+#include "eapol_key_state.h"
+#include "eapol_key_header.h"
+#include "eap_crypto_api.h"
+#include "abs_eap_am_mutex.h"
+#include "eap_state_notification.h"
+#include "eap_automatic_variable.h"
+#include "eapol_rsna_key_data_gtk_header.h"
+#include "eap_buffer.h"
+#include "eapol_rsna_key_data_payloads.h"
+#include "abs_eapol_key_state.h"
+#include "eapol_rc4_key_header.h"
+#include "eapol_key_state_string.h"
+
+
+//--------------------------------------------------
+
+//
+eap_status_e eapol_key_state_c::create_4_way_handshake_message_1(
+	eap_buf_chain_wr_c * const sent_packet,
+	const u32_t eapol_header_offset,
+	u32_t * const data_length,
+	u32_t * const buffer_length,
+	const eapol_protocol_version_e used_eapol_version,
+	const eapol_key_descriptor_type_e received_key_descriptor_type)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+	eap_status_e status = eap_status_process_general_error;
+
+	EAP_TRACE_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("EAPOL_KEY: %s: eapol_key_state_c::create_4_way_handshake_message_1()\n"),
+		(m_is_client == true ? "client": "server")));
+
+	// Only server (Authenticator) could create 4-Way Handshake message 1.
+	EAP_ASSERT_ALWAYS(m_is_client == false);
+
+	if (sent_packet == 0
+		|| sent_packet->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_ANonce.get_is_valid_data() == false
+		|| m_ANonce.get_data_length() != eapol_RSNA_key_header_c::EAPOL_RSNA_KEY_NONCE_SIZE)
+	{
+		EAP_TRACE_ERROR(
+			m_am_tools,
+			TRACE_FLAGS_EAPOL_KEY_DATA_ERROR,
+			(EAPL("ERROR: EAPOL_KEY: eapol_key_state_c::create_4_way_handshake_message_1(): ")
+			 EAPL("m_ANonce is missing or corrupted.\n")));
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+	}
+
+	if (m_PMKID.get_is_valid_data() == false
+		|| m_PMKID.get_data_length()
+		!= eapol_RSNA_key_header_c::EAPOL_RSNA_KEY_DATA_PMKID_SIZE)
+	{
+		EAP_TRACE_ERROR(
+			m_am_tools,
+			TRACE_FLAGS_EAPOL_KEY_DATA_ERROR,
+			(EAPL("ERROR: EAPOL_KEY: eapol_key_state_c::create_4_way_handshake_message_1(): ")
+			 EAPL("m_PMKID is missing or corrupted.\n")));
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+	}
+
+
+	u32_t eapol_key_data_length
+		= eapol_RSNA_key_header_c::get_header_length();
+
+	if (get_is_RSNA() == true)
+	{
+		eapol_key_data_length += EAPOL_RSNA_4_WAY_HANDSHAKE_MESSAGE_1_KEY_DATA_LENGTH_BYTES;
+	}
+
+	*buffer_length
+		= eapol_header_offset
+		+ eapol_key_data_length;
+
+	status = sent_packet->set_buffer_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);
+	}
+	sent_packet->set_data_length(
+		sent_packet->get_buffer_length());
+
+	eapol_RSNA_key_header_c eapol_key_message(
+		m_am_tools,
+		get_is_RSNA(),
+		get_is_WPXM(),
+		sent_packet->get_data_offset(eapol_header_offset, eapol_key_data_length),
+		sent_packet->get_data_length());
+
+	if (eapol_key_message.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 = eapol_key_message.reset_header(
+		0ul,
+		m_authentication_type,
+		m_eapol_pairwise_cipher,
+		get_key_reply_counter(),
+		true, // Pairwise key type bit is on.
+		false, // Install bit is NOT set.
+		true, // Key Ack bit is on.
+		false, // Key MIC bit is NOT set.
+		false, // Secure bit is NOT set.
+		false, // Error bit is NOT set.
+		false, // Request bit is NOT set.
+		false, // STAKey bit is NOT set.
+		false, // Encrypted Key Data bit is NOT set.
+		used_eapol_version,
+		received_key_descriptor_type);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+
+	{
+		// Add ANonce.
+		u8_t * const nonce_field = eapol_key_message.get_key_NONCE();
+		if (nonce_field == 0
+			|| m_ANonce.get_data_length() != eapol_RSNA_key_header_c::EAPOL_RSNA_KEY_NONCE_SIZE)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+		}
+		m_am_tools->memmove(
+			nonce_field,
+			m_ANonce.get_data(eapol_RSNA_key_header_c::EAPOL_RSNA_KEY_NONCE_SIZE),
+			eapol_RSNA_key_header_c::EAPOL_RSNA_KEY_NONCE_SIZE);
+	}
+
+
+	if (get_is_RSNA() == true)
+	{
+		if (m_skip_PMKID_key_data_in_message_1 == false)
+		{
+			// Add PMKID to Key Data.
+			eapol_rsna_key_data_header_c key_data_header(
+				m_am_tools,
+				get_is_RSNA(),
+				get_is_WPXM(),
+				eapol_key_message.get_key_data(
+						EAPOL_RSNA_4_WAY_HANDSHAKE_MESSAGE_1_KEY_DATA_LENGTH_BYTES),
+				EAPOL_RSNA_4_WAY_HANDSHAKE_MESSAGE_1_KEY_DATA_LENGTH_BYTES);
+			if (key_data_header.get_is_valid() == false)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+			}
+
+			status = key_data_header.reset_header();
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			// Here Type and Length fields are NOT included.
+			status = key_data_header.set_length(
+				EAPOL_RSNA_4_WAY_HANDSHAKE_MESSAGE_1_KEY_DATA_LENGTH_BYTES - 2ul);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			status = key_data_header.set_payload_type(eapol_RSNA_key_payload_type_pmkid);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			u8_t * const PMKID_field = key_data_header.get_key_data_payload(
+				eapol_RSNA_key_header_c::EAPOL_RSNA_KEY_DATA_PMKID_SIZE);
+			if (PMKID_field == 0)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+			}
+
+			EAP_TRACE_DEBUG(m_am_tools, TRACE_FLAGS_DEFAULT|TRACE_TEST_VECTORS,
+				(EAPL("EAPOL_KEY: PMKID_field = 0x%08x\n"),
+				PMKID_field));
+
+			EAP_TRACE_DATA_DEBUG(m_am_tools, TRACE_FLAGS_DEFAULT|TRACE_TEST_VECTORS,
+				(EAPL("m_PMKID"),
+				m_PMKID.get_data(eapol_RSNA_key_header_c::EAPOL_RSNA_KEY_DATA_PMKID_SIZE),
+				eapol_RSNA_key_header_c::EAPOL_RSNA_KEY_DATA_PMKID_SIZE));
+
+			m_am_tools->memmove(
+				PMKID_field,
+				m_PMKID.get_data(eapol_RSNA_key_header_c::EAPOL_RSNA_KEY_DATA_PMKID_SIZE),
+				eapol_RSNA_key_header_c::EAPOL_RSNA_KEY_DATA_PMKID_SIZE);
+
+			EAPOL_RSNA_KEY_DATA_TRACE_PAYLOAD(
+				get_is_RSNA(),
+				get_is_WPXM(),
+				eapol_key_message.get_key_descriptor_type(),
+				"Added EAPOL Key Data key_data_payload",
+				&key_data_header,
+				0ul);
+
+			status = eapol_key_message.set_key_data_length(
+				EAPOL_RSNA_4_WAY_HANDSHAKE_MESSAGE_1_KEY_DATA_LENGTH_BYTES);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+		}
+
+
+		u16_t key_length(0ul);
+
+		status = get_key_length(
+			m_eapol_pairwise_cipher,
+			&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 = eapol_key_message.set_key_length(key_length);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+
+
+	*data_length
+		= eapol_key_message.get_header_length()
+		+ eapol_key_message.get_key_data_length();
+
+	status = eapol_key_message.set_eapol_packet_body_length(
+		static_cast<u16_t>(*data_length - eapol_header_base_c::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);
+	}
+
+	sent_packet->set_data_length(
+		eapol_header_offset + *data_length);
+
+	TRACE_EAPOL_KEY_MESSAGE(
+		"Send 4-Way Handshake Message 1",
+		&eapol_key_message);
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+//
+eap_status_e eapol_key_state_c::create_4_way_handshake_message_3(
+	eap_buf_chain_wr_c * const sent_packet,
+	const u32_t eapol_header_offset,
+	u32_t * const data_length,
+	u32_t * const buffer_length,
+	const eapol_protocol_version_e used_eapol_version,
+	const eapol_key_descriptor_type_e received_key_descriptor_type)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+	eap_status_e status(eap_status_process_general_error);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("EAPOL_KEY: %s: eapol_key_state_c::create_4_way_handshake_message_3()\n"),
+		(m_is_client == true ? "client": "server")));
+
+	// Only server (Authenticator) could create 4-Way Handshake message 3.
+	EAP_ASSERT_ALWAYS(m_is_client == false);
+
+	if (m_ANonce.get_is_valid_data() == false
+		|| m_ANonce.get_data_length() != eapol_RSNA_key_header_c::EAPOL_RSNA_KEY_NONCE_SIZE)
+	{
+		EAP_TRACE_ERROR(
+			m_am_tools,
+			TRACE_FLAGS_EAPOL_KEY_DATA_ERROR,
+			(EAPL("ERROR: EAPOL_KEY: eapol_key_state_c::create_4_way_handshake_message_3(): ")
+			 EAPL("m_ANonce is missing or corrupted.\n")));
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+	}
+
+	if (m_authenticator_RSNA_IE.get_is_valid_data() == false
+		|| m_authenticator_RSNA_IE.get_data_length() == 0ul
+		|| m_authenticator_RSNA_IE.get_data_length()
+		> eapol_RSNA_key_header_c::EAPOL_RSNA_KEY_DATA_MAXIMUM_RSN_IE_SIZE)
+	{
+		EAP_TRACE_ERROR(
+			m_am_tools,
+			TRACE_FLAGS_EAPOL_KEY_DATA_ERROR,
+			(EAPL("ERROR: EAPOL_KEY: eapol_key_state_c::create_4_way_handshake_message_3(): ")
+			 EAPL("m_authenticator_RSNA_IE is missing or corrupted.\n")));
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+	}
+
+
+	u32_t eapol_key_data_length
+		= eapol_RSNA_key_header_c::get_header_length()
+		+ m_authenticator_RSNA_IE.get_data_length();
+
+	u32_t extra_encryption_padding_and_block(0ul);
+
+	if (m_eapol_pairwise_cipher == eapol_RSNA_key_header_c::eapol_RSNA_cipher_CCMP
+		|| m_eapol_group_cipher == eapol_RSNA_key_header_c::eapol_RSNA_cipher_CCMP)
+	{
+		extra_encryption_padding_and_block = 2ul * EAP_CRYPTO_AES_WRAP_BLOCK_SIZE;
+	}
+
+	if ((get_is_RSNA() == true
+			&& m_eapol_group_cipher != eapol_RSNA_key_header_c::eapol_RSNA_cipher_none)
+		|| (get_is_WPXM() == true
+				&& received_key_descriptor_type == eapol_key_descriptor_type_RSNA))
+	{
+		u16_t GTK_length = 0ul;
+
+		status = get_key_length(m_eapol_group_cipher, &GTK_length);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		// Creates GTK.
+		status = create_nonce(&m_group_GTK, GTK_length);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		eapol_key_data_length +=
+			eapol_rsna_key_data_header_c::EAPOL_RSNA_KEY_HEADER_LENGTH
+			+ eapol_rsna_key_data_header_c::EAPOL_RSNA_KEY_ID_AND_GROUP_KEY_HEADER_SIZE
+			+ m_group_GTK.get_data_length();
+	}
+
+
+	*buffer_length
+		= eapol_header_offset
+		+ eapol_key_data_length
+		+ extra_encryption_padding_and_block;
+
+	status = sent_packet->set_buffer_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);
+	}
+	sent_packet->set_data_length(
+		sent_packet->get_buffer_length());
+
+	eapol_RSNA_key_header_c eapol_key_message(
+		m_am_tools,
+		get_is_RSNA(),
+		get_is_WPXM(),
+		sent_packet->get_data_offset(eapol_header_offset, eapol_key_data_length),
+		sent_packet->get_data_length());
+
+	if (eapol_key_message.get_is_valid() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+
+	bool secure_bit = false; // Secure bit is NOT set in WPA.
+	bool encrypted_key_data = false; // Encrypted Key Data bit is NOT set in WPA.
+
+	if (get_is_RSNA() == true
+		|| (get_is_WPXM() == true
+				&& received_key_descriptor_type == eapol_key_descriptor_type_RSNA))
+	{
+		secure_bit = true; // Secure bit is on in RSNA.
+		encrypted_key_data = true; // Encrypted Key Data bit is on in RSNA.
+	}
+
+
+	status = eapol_key_message.reset_header(
+		0ul,
+		m_authentication_type,
+		m_eapol_pairwise_cipher,
+		get_key_reply_counter(),
+		true, // Pairwise key type bit is on.
+		true, // Install bit is on. /** @{ This could be false when AP does not support key mapping keys. } */
+		true, // Key Ack bit is on.
+		true, // Key MIC bit is on.
+		secure_bit,
+		false, // Error bit is NOT set.
+		false, // Request bit is NOT set.
+		false, // STAKey bit is NOT set.
+		encrypted_key_data,
+		used_eapol_version,
+		received_key_descriptor_type);
+
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+
+	{
+		// Add ANonce.
+		u8_t * const nonce_field = eapol_key_message.get_key_NONCE();
+		if (nonce_field == 0
+			|| m_ANonce.get_data_length() != eapol_RSNA_key_header_c::EAPOL_RSNA_KEY_NONCE_SIZE)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+		}
+
+		m_am_tools->memmove(
+			nonce_field,
+			m_ANonce.get_data(eapol_RSNA_key_header_c::EAPOL_RSNA_KEY_NONCE_SIZE),
+			eapol_RSNA_key_header_c::EAPOL_RSNA_KEY_NONCE_SIZE);
+	}
+
+	if (m_eapol_pairwise_cipher == eapol_RSNA_key_header_c::eapol_RSNA_cipher_TKIP)
+	{
+		// Add Key IV.
+		// Creates EAPOL-Key IV.
+		status = create_nonce(&m_EAPOL_key_IV, eapol_RSNA_key_header_c::EAPOL_RSNA_EAPOL_KEY_IV_SIZE);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		u8_t * const key_iv_field = eapol_key_message.get_EAPOL_key_IV();
+		if (key_iv_field == 0
+			|| m_EAPOL_key_IV.get_data_length() < eapol_RSNA_key_header_c::EAPOL_RSNA_EAPOL_KEY_IV_SIZE)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+		}
+
+		// Note only the lower sixteen octets of the counter are value are used.
+		u32_t offset = m_EAPOL_key_IV.get_data_length() - eapol_RSNA_key_header_c::EAPOL_RSNA_EAPOL_KEY_IV_SIZE;
+		u8_t * const iv_data = m_EAPOL_key_IV.get_data_offset(offset, eapol_RSNA_key_header_c::EAPOL_RSNA_EAPOL_KEY_IV_SIZE);
+
+		if (iv_data == 0)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+		}
+
+		m_am_tools->memmove(
+			key_iv_field,
+			iv_data,
+			eapol_RSNA_key_header_c::EAPOL_RSNA_EAPOL_KEY_IV_SIZE);
+	}
+
+	/**
+	 * @{ Missing payload: starting sequence Authenticator's STA will use in packets protected by GTK. }
+	 */
+
+	u32_t total_key_data_length(0ul);
+
+	status = add_RSN_IE_payload(
+		&eapol_key_message,
+		&m_authenticator_RSNA_IE,
+		&total_key_data_length);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+
+	/**
+	 * @{ Missing payload: a second RSN IE that is Authenticator's unicast cipher suite assigment. }
+	 */
+
+	/**
+	 * @{ Missing payload: GTK when multicast cipher has been negotiated. }
+	 */
+
+	if ((get_is_RSNA() == true
+			&& m_eapol_group_cipher != eapol_RSNA_key_header_c::eapol_RSNA_cipher_none)
+		|| (get_is_WPXM() == true
+				&& received_key_descriptor_type == eapol_key_descriptor_type_RSNA))
+	{
+		status = add_RSN_GTK_payload(
+			&eapol_key_message,
+			&m_group_GTK,
+			&total_key_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 (total_key_data_length > 0xffff)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_too_long_message);
+	}
+
+	status = eapol_key_message.set_key_data_length(
+		static_cast<u16_t>(total_key_data_length));
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	*data_length
+		= eapol_key_message.get_header_length()
+		+ eapol_key_message.get_key_data_length();
+
+	status = eapol_key_message.set_eapol_packet_body_length(
+		static_cast<u16_t>(*data_length - eapol_header_base_c::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);
+	}
+
+
+	if (get_is_RSNA() == true
+		|| (get_is_WPXM() == true
+				&& received_key_descriptor_type == eapol_key_descriptor_type_RSNA))
+	{
+		status = encrypt_key_data(&eapol_key_message);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		// NOTE the encryption may increase length of the key data field.
+		*data_length
+			= eapol_key_message.get_header_length()
+			+ eapol_key_message.get_key_data_length();
+		
+		status = eapol_key_message.set_eapol_packet_body_length(
+			static_cast<u16_t>(*data_length - eapol_header_base_c::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 = create_key_mic(
+		&eapol_key_message,
+		&m_confirmation_KCK);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	// Encryption may change the length of EAPOL-Key data.
+	*data_length
+		= eapol_key_message.get_header_length()
+		+ eapol_key_message.get_key_data_length();
+
+	sent_packet->set_data_length(
+		eapol_header_offset + *data_length);
+
+	TRACE_EAPOL_KEY_MESSAGE(
+		"Send 4-Way Handshake Message 3",
+		&eapol_key_message);
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+//
+eap_status_e eapol_key_state_c::send_RC4_eapol_key_message(
+	const eapol_RC4_key_flags_e flags)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	eap_status_e status(eap_status_process_general_error);
+
+	u8_t unicast_test_key[] = "12345";
+	u32_t unicast_test_key_length = sizeof(unicast_test_key)-1ul;
+
+	const u32_t eapol_key_data_length
+		= eapol_RC4_key_header_c::get_header_length()
+		+ unicast_test_key_length;
+
+	const u32_t buffer_length
+		= m_eapol_header_offset
+		+ eapol_key_data_length;
+
+	eap_buf_chain_wr_c sent_packet(
+		eap_write_buffer,
+		m_am_tools);
+	if (sent_packet.get_is_valid() == false)
+	{
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	status = sent_packet.set_buffer_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);
+	}
+	sent_packet.set_data_length(
+		sent_packet.get_buffer_length());
+
+	eapol_RC4_key_header_c eapol_key_message(
+		m_am_tools,
+		sent_packet.get_data_offset(m_eapol_header_offset, eapol_key_data_length),
+		sent_packet.get_data_length());
+	if (eapol_key_message.get_is_valid() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_header_corrupted);
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Create IV.
+	eap_variable_data_c RC4_IV(m_am_tools);
+
+	status = create_nonce(&RC4_IV, EAPOL_RC4_KEY_IV_LENGTH);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	m_am_tools->memmove(eapol_key_message.get_key_IV(), RC4_IV.get_data(), EAPOL_RC4_KEY_IV_LENGTH);
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	eapol_key_message.set_eapol_protocol_version(eapol_protocol_version_1);
+
+	eapol_key_message.set_eapol_packet_type(eapol_packet_type_key);
+
+	eapol_key_message.set_eapol_packet_body_length(static_cast<u16_t>(eapol_key_data_length - eapol_header_base_c::get_header_length()));
+
+	eapol_key_message.set_key_descriptor_type(eapol_key_descriptor_type_RC4);
+
+	m_am_tools->memset(eapol_key_message.get_replay_counter(), 0, EAPOL_RC4_KEY_REPLAY_COUNTER_LENGTH);
+
+	eapol_key_message.set_key_flag(flags);
+
+	eapol_key_message.set_key_index(0ul);
+
+	eapol_key_message.set_key_length(static_cast<u16_t>(unicast_test_key_length));
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Get MS-MPPE-Recv-Key and MS-MPPE-Send-Key	
+	eap_variable_data_c mppe_recv_key(m_am_tools);
+	eap_variable_data_c mppe_send_key(m_am_tools);
+
+	if (m_pairwise_PMK_WPXK3.get_data_length() == eapol_key_state_mppe_key_length_leap)
+	{
+		// LEAP only generates 16 bytes PMK. Also with LEAP the receive and send keys are the same.
+		status = mppe_recv_key.set_buffer(
+			m_pairwise_PMK_WPXK3.get_data(
+			eapol_key_state_mppe_key_length_leap),
+			eapol_key_state_mppe_key_length_leap,
+			false,
+			false);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = mppe_send_key.set_buffer(
+			m_pairwise_PMK_WPXK3.get_data(
+			eapol_key_state_mppe_key_length_leap),
+			eapol_key_state_mppe_key_length_leap,
+			false,
+			false);
+		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_pairwise_PMK_WPXK3.get_data_length() >= 64ul)
+	{
+		// Usually types generate at least 64 bytes PMK.
+		// Recv-Key is the first 32 bytes of master session key and Send-Key is the next 32 bytes.
+		status = mppe_recv_key.set_buffer(
+			m_pairwise_PMK_WPXK3.get_data(
+			eapol_key_state_mppe_key_length),
+			eapol_key_state_mppe_key_length,
+			false,
+			false);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = mppe_send_key.set_buffer(
+			m_pairwise_PMK_WPXK3.get_data_offset(
+				eapol_key_state_mppe_key_length,
+				eapol_key_state_mppe_key_length),
+			eapol_key_state_mppe_key_length,
+			false,
+			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_ERROR(
+			m_am_tools,
+			TRACE_FLAGS_EAPOL_KEY_DATA_ERROR,
+			(EAPL("ERROR: EAPOL_KEY: Unsupported PMK key length for RC4 EAPOL-key handshake.\n")));
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_not_supported);	
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	// Set-up RC4 key. Key is the IV and the MS-MPPE-Recv-Key truncated together.
+	eap_variable_data_c rc4_key(m_am_tools);
+	status = rc4_key.set_copy_of_buffer(eapol_key_message.get_key_IV(), EAPOL_RC4_KEY_IV_LENGTH);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = rc4_key.add_data(&mppe_recv_key);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	// Set-up RC4 module
+	crypto_rc4_c rc4(m_am_tools);
+	if (rc4.get_is_valid() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	// Set the key for RC4
+	if (rc4.set_key(&rc4_key) != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_process_general_error);
+	}
+
+	// Decrypt the key to key_out
+	status = rc4.encrypt_data(
+		unicast_test_key,
+		eapol_key_message.get_key(),
+		unicast_test_key_length);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}		
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// Create the the MD5 signature in Eapol-Key
+
+	crypto_md5_c md5(m_am_tools);
+	crypto_hmac_c hmac_md5(m_am_tools, &md5, false);
+	if (hmac_md5.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 (hmac_md5.get_is_valid() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	// MPPE-Send-Key is used as the signature key.
+	if (hmac_md5.hmac_set_key(&mppe_send_key) != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	// Replace the signature with zeros.
+	eapol_key_message.zero_key_signature(m_am_tools);
+
+	// Send the data to HMAC-MD5 module
+	if (hmac_md5.hmac_update(
+			eapol_key_message.get_header_buffer(eapol_key_data_length),
+			eapol_key_data_length)
+		!= eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);		
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_process_general_error);
+	}
+
+	// Get the calculated signature
+	u8_t tmp_signature[EAPOL_RC4_KEY_SIGNATURE_LENGTH];
+	u32_t length;
+	if (hmac_md5.hmac_final(tmp_signature, &length) != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_process_general_error);
+	}
+	
+	// Copy the calculated signature to message.
+	m_am_tools->memmove(
+		eapol_key_message.get_key_signature(),
+		tmp_signature,
+		EAPOL_RC4_KEY_SIGNATURE_LENGTH);
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	status = packet_send(
+		&m_send_network_id,
+		&sent_packet,
+		m_eapol_header_offset,
+		eapol_key_data_length,
+		sent_packet.get_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_status_e eapol_key_state_c::send_RC4_eapol_key_messages()
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	eap_status_e status(eap_status_process_general_error);
+
+	status = send_RC4_eapol_key_message(eapol_RC4_key_flag_unicast);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = send_RC4_eapol_key_message(eapol_RC4_key_flag_broadcast);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	{
+		// This is notification to eapol_core_c object.
+		// Dynamic WEP (802.1x) authentication finished successfully.
+		eap_state_notification_c * notification = new eap_state_notification_c(
+			m_am_tools,
+			&m_send_network_id,
+			m_is_client,
+			eap_state_notification_generic,
+			eap_protocol_layer_eapol_key,
+			eapol_key_handshake_type_dynamic_WEP,
+			get_eapol_key_state(),
+			eapol_key_state_802_11i_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);
+		}
+		m_key_state_partner->state_notification(notification);
+
+		delete notification;
+	}
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+//
+eap_status_e eapol_key_state_c::start_4_way_handshake(
+	const eap_am_network_id_c * const receive_network_id)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+	EAP_UNREFERENCED_PARAMETER(receive_network_id);
+
+	eap_status_e status(eap_status_process_general_error);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("EAPOL_KEY: %s: eapol_key_state_c::start_4_way_handshake()\n"),
+		(m_is_client == true ? "client": "server")));
+
+	// Only server (Authenticator) could start 4-Way Handshake.
+	EAP_ASSERT_ALWAYS(m_is_client == false);
+
+	// First check do we need 4-Way Handshake.
+	if (get_is_RSNA() == true
+		|| get_is_WPA() == true
+#if defined(EAP_USE_WPXM)
+		|| get_is_WPXM() == true
+#endif //#if defined(EAP_USE_WPXM)
+		)
+	{
+		// OK, we need 4-Way Handshake.
+		EAP_TRACE_DEBUG(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("EAPOL_KEY: %s: eapol_key_state_c::start_4_way_handshake(): ")
+			 EAPL("Start 4-Way Handshake, m_authentication_type=%d\n"),
+			 (m_is_client == true ? "client": "server"),
+			 m_authentication_type));
+	}
+	else if (m_authentication_type == eapol_key_authentication_type_802_1X)
+	{
+		// No 4-Way Handshake needed.
+		// AP will send unicast and broad cast keys in EAPOL key messages.
+		EAP_TRACE_DEBUG(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("EAPOL_KEY: %s: eapol_key_state_c::start_4_way_handshake(): ")
+			 EAPL("Dynamic WEP, m_authentication_type=%d\n"),
+			(m_is_client == true ? "client": "server"),
+			m_authentication_type));
+
+		m_eapol_key_handshake_type = eapol_key_handshake_type_dynamic_WEP;
+
+		send_RC4_eapol_key_messages();
+
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_ok);
+	}
+	else
+	{
+		// No 4-Way Handshake needed.
+		EAP_TRACE_DEBUG(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("EAPOL_KEY: %s: eapol_key_state_c::start_4_way_handshake(): ")
+			 EAPL("No 4-Way Handshake, m_authentication_type=%d\n"),
+			(m_is_client == true ? "client": "server"),
+			m_authentication_type));
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_ok);
+	}
+
+	if (m_eapol_key_handshake_type != eapol_key_handshake_type_none
+		&& m_eapol_key_handshake_type != eapol_key_handshake_type_4_way_handshake)
+	{
+		eapol_key_state_string_c state_string;
+		EAP_TRACE_ERROR(
+			m_am_tools,
+			TRACE_FLAGS_DEFAULT,
+			(EAPL("WARNING: EAPOL_KEY: %s: start_4_way_handshake(): wrong handshake type %s\n"),
+			(m_is_client == true ? "client": "server"),
+			state_string.get_eapol_key_handshake_type_string(m_eapol_key_handshake_type)));
+
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_wrong_eap_type_state);
+	}
+
+	if (get_eapol_key_state() != eapol_key_state_none
+		&& get_eapol_key_state() != eapol_key_state_preauthenticated
+		&& get_eapol_key_state() != eapol_key_state_group_key_handshake_successfull)
+	{
+		eapol_key_state_string_c state_string;
+		EAP_TRACE_ERROR(
+			m_am_tools,
+			TRACE_FLAGS_DEFAULT,
+			(EAPL("WARNING: EAPOL_KEY: %s: start_4_way_handshake(): wrong state %s\n"),
+			(m_is_client == true ? "client": "server"),
+			state_string.get_eapol_key_state_string(get_eapol_key_state())));
+
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_wrong_eap_type_state);
+	}
+
+	// Creates ANonce.
+	status = create_nonce(&m_ANonce, EAPOL_RSNA_NONCE_LENGTH_BYTES);
+	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("ANonce"),
+		m_ANonce.get_data(
+			m_ANonce.get_data_length()),
+		m_ANonce.get_data_length()));
+
+	// Create PMKID.
+	status = create_PMKID();
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	set_key_reply_counter(0ul);
+	increase_key_reply_counter();
+
+	eap_buf_chain_wr_c sent_packet(
+		eap_write_buffer,
+		m_am_tools);
+
+	if (sent_packet.get_is_valid() == false)
+	{
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	u32_t send_data_length = 0ul;
+	u32_t send_buffer_length = 0ul;
+
+	status = create_4_way_handshake_message_1(
+		&sent_packet,
+		m_eapol_header_offset,
+		&send_data_length,
+		&send_buffer_length,
+		eapol_protocol_version_2,
+#if defined(EAP_USE_WPXM)
+		m_EAPOL_WPXM_key_descriptor_type
+#else
+		eapol_key_descriptor_type_RSNA
+#endif //#if defined(EAP_USE_WPXM)
+		);
+	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(
+		&m_send_network_id,
+		&sent_packet,
+		m_eapol_header_offset,
+		send_data_length,
+		send_buffer_length);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = init_handshake_timeout(m_handshake_timeout);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	set_eapol_key_state(eapol_key_state_wait_4_way_handshake_message_2);
+
+	m_eapol_key_handshake_type = eapol_key_handshake_type_4_way_handshake;
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+//
+eap_status_e eapol_key_state_c::process_4_way_handshake_message_2_payloads(
+	const eap_am_network_id_c * const receive_network_id,
+	eapol_RSNA_key_header_c * const eapol_key_message,
+	const u32_t packet_length)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+	EAP_UNREFERENCED_PARAMETER(receive_network_id);
+
+	eap_status_e status = eap_status_process_general_error;
+
+	u32_t received_buffer_length = eapol_key_message->get_key_data_length();
+
+	if (received_buffer_length == 0u)
+	{
+		// No payload in this packet.
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_process_illegal_packet_error);
+	}
+
+	if (received_buffer_length > eapol_key_message->get_header_buffer_length()
+		|| eapol_key_message->get_key_data(received_buffer_length) == 0)
+	{
+		// Not enough payload in this packet.
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_process_illegal_packet_error);
+	}
+
+	eapol_rsna_key_data_payloads_c key_data_payloads(
+		m_am_tools,
+		get_is_RSNA(),
+		get_is_WPXM());
+
+	eapol_rsna_key_data_header_c key_data(
+		m_am_tools,
+		get_is_RSNA(),
+		get_is_WPXM(),
+		eapol_key_message->get_key_data(received_buffer_length),
+		packet_length);
+	if (key_data.get_is_valid() == false)
+	{
+		EAP_TRACE_ERROR(
+			m_am_tools, 
+			TRACE_FLAGS_EAPOL_KEY_DATA_ERROR, 
+			(EAPL("ERROR: EAPOL_KEY: No EAPOL-Key data payloads.\n")));
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	status = key_data.check_header();
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_ERROR(
+			m_am_tools, 
+			TRACE_FLAGS_EAPOL_KEY_DATA_ERROR, 
+			(EAPL("ERROR: EAPOL_KEY: EAPOL-Key data payload header corrupted.\n")));
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = parse_key_data(
+		eapol_key_message->get_key_descriptor_type(),
+		&key_data,
+		&received_buffer_length,
+		&key_data_payloads,
+		eapol_key_state_wait_4_way_handshake_message_2,
+		eapol_key_message->get_key_information_key_descriptor_version());
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	// Check the valid payload is included.
+	if (false == key_data_payloads.check_payloads(
+		eapol_rsna_key_data_payloads_c::eapol_rsna_key_data_payload_status_must_not_be, // key_id_and_group_key
+		eapol_rsna_key_data_payloads_c::eapol_rsna_key_data_payload_status_must_not_be, // STAKey
+		eapol_rsna_key_data_payloads_c::eapol_rsna_key_data_payload_status_must_not_be, // PMKID
+		eapol_rsna_key_data_payloads_c::eapol_rsna_key_data_payload_status_must_be // One or more RSN IE
+		))
+	{
+		// Not correct EAPOL Key Data payloads are included.
+		EAP_TRACE_ERROR(
+			m_am_tools, 
+			TRACE_FLAGS_EAPOL_KEY_DATA_ERROR, 
+			(EAPL("ERROR: EAPOL_KEY: eapol_key_state_c::process_4_way_handshake_message_2_payloads(): ")
+			 EAPL("Not correct EAPOL Key Data payloads are included.\n")));
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_header_corrupted);
+	}
+
+#if defined(EAP_USE_WPXM)
+	// Derive PTK.
+	if (get_is_WPXM() == true)
+	{
+		EAP_TRACE_DEBUG(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("EAPOL_KEY: eapol_key_state_c::process_4_way_handshake_message_2_payloads(): ")
+			 EAPL("m_WPXM_WPXC=%d.\n"),
+			 m_WPXM_WPXC));
+
+		status = derive_WPXM_WPXK1_WPXK2();
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = derive_WPXM_PTK(eapol_key_constant_wpxm_initial_wpxc_counter_value);
+	}
+	else
+#endif //#if defined(EAP_USE_WPXM)
+	{
+		status = derive_PTK();
+	}
+
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	const eap_variable_data_c * confirmation_key = &m_confirmation_KCK;
+#if defined(EAP_USE_WPXM)
+	if (get_is_WPXM() == true)
+	{
+		confirmation_key = &m_WPXM_WPXK1;
+	}
+#endif //#if defined(EAP_USE_WPXM)
+
+	// Check MIC.
+	status = verify_key_mic(
+		eapol_key_message,
+		confirmation_key);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+
+	// We do have the expected RSN IE, compare it.
+	if (get_supplicant_RSNA_IE()->compare(
+			key_data_payloads.get_RSN_IE()->get_object(0ul)) != 0)
+	{
+		// Illegal RSN IE.
+		EAP_TRACE_ERROR(
+			m_am_tools, 
+			TRACE_FLAGS_EAPOL_KEY_DATA_ERROR, 
+			(EAPL("ERROR: EAPOL_KEY: eapol_key_state_c::process_4_way_handshake_message_2_payloads(): ")
+			 EAPL("Not correct RSN IE received.\n")));
+		EAP_TRACE_DATA_ERROR(
+			m_am_tools,
+			TRACE_FLAGS_EAPOL_KEY_DATA_ERROR,
+			(EAPL("Supplicant RSN IE"),
+			get_supplicant_RSNA_IE()->get_data(
+				get_supplicant_RSNA_IE()->get_data_length()),
+			get_supplicant_RSNA_IE()->get_data_length()));
+		EAP_TRACE_DATA_ERROR(
+			m_am_tools,
+			TRACE_FLAGS_EAPOL_KEY_DATA_ERROR,
+			(EAPL("Received RSN IE"),
+			key_data_payloads.get_RSN_IE()->get_object(0ul)->get_data(
+				key_data_payloads.get_RSN_IE()->get_object(0ul)->get_data_length()),
+			key_data_payloads.get_RSN_IE()->get_object(0ul)->get_data_length()));
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_header_corrupted);
+	}
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+
+//--------------------------------------------------
+
+//
+eap_status_e eapol_key_state_c::process_4_way_handshake_message_0(
+	const eap_am_network_id_c * const receive_network_id,
+	eapol_RSNA_key_header_c * const eapol_key_message,
+	const u32_t /* packet_length */)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+	EAP_UNREFERENCED_PARAMETER(receive_network_id);
+
+	eap_status_e status = eap_status_process_general_error;
+
+	eapol_key_state_string_c state_string;
+
+	EAP_TRACE_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("EAPOL_KEY: %s: eapol_key_state_c::process_4_way_handshake_message_0(): eapol_key_descriptor_type = %s = 0x%02x\n"),
+		(m_is_client == true ? "client": "server"),
+		state_string.get_eapol_key_descriptor_type_string(eapol_key_message->get_key_descriptor_type()),
+		eapol_key_message->get_key_descriptor_type()));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: eapol_key_state_c::process_4_way_handshake_message_0()");
+
+	// Only server (Authenticator) could receive 4-Way Handshake message 0.
+	// Client initializes the 4-Way Handshake with this message.
+	EAP_ASSERT_ALWAYS(m_is_client == false);
+
+	if (m_eapol_key_handshake_type == eapol_key_handshake_type_none)
+	{
+		m_eapol_key_handshake_type = eapol_key_handshake_type_4_way_handshake;
+	}
+	else if (m_eapol_key_handshake_type != eapol_key_handshake_type_4_way_handshake)
+	{
+		eapol_key_state_string_c state_string;
+		EAP_TRACE_ERROR(
+			m_am_tools,
+			TRACE_FLAGS_DEFAULT,
+			(EAPL("WARNING: EAPOL_KEY: %s: process_4_way_handshake_message_0(): wrong handshake type %s\n"),
+			(m_is_client == true ? "client": "server"),
+			state_string.get_eapol_key_handshake_type_string(m_eapol_key_handshake_type)));
+
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_wrong_eap_type_state);
+	}
+
+	if (get_eapol_key_state() != eapol_key_state_none
+		&& get_eapol_key_state() != eapol_key_state_preauthenticated
+		&& get_eapol_key_state() != eapol_key_state_group_key_handshake_successfull
+		)
+	{
+		EAP_TRACE_ERROR(
+			m_am_tools,
+			TRACE_FLAGS_DEFAULT,
+			(EAPL("WARNING: EAPOL_KEY: %s: process_4_way_handshake_message_0(): wrong state %s\n"),
+			(m_is_client == true ? "client": "server"),
+			state_string.get_eapol_key_state_string(get_eapol_key_state())));
+
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_wrong_eap_type_state);
+	}
+
+	if (eapol_key_message->get_key_length() != 0ul)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_padding);
+	}
+
+	status = verify_field_is_zero(
+		eapol_key_message->get_EAPOL_key_IV(),
+		eapol_RSNA_key_header_c::EAPOL_RSNA_EAPOL_KEY_IV_SIZE);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = verify_field_is_zero(
+		eapol_key_message->get_key_RSC(),
+		eapol_RSNA_key_header_c::EAPOL_RSNA_KEY_RSC_SIZE);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = verify_field_is_zero(
+		eapol_key_message->get_key_STA_MAC_address(),
+		eapol_RSNA_key_header_c::EAPOL_RSNA_KEY_STA_MAC_ADDRESS_SIZE);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = verify_field_is_zero(
+		eapol_key_message->get_key_reserved(),
+		eapol_RSNA_key_header_c::EAPOL_RSNA_KEY_RESERVED_SIZE);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = start_4_way_handshake(
+		receive_network_id);
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+//
+eap_status_e eapol_key_state_c::process_4_way_handshake_message_2(
+	const eap_am_network_id_c * const receive_network_id,
+	eapol_RSNA_key_header_c * const eapol_key_message,
+	const u32_t packet_length)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+	EAP_UNREFERENCED_PARAMETER(receive_network_id);
+
+	eap_status_e status = eap_status_process_general_error;
+
+	eapol_key_state_string_c state_string;
+
+	EAP_TRACE_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("EAPOL_KEY: %s: eapol_key_state_c::process_4_way_handshake_message_2(): eapol_key_descriptor_type = %s = 0x%02x\n"),
+		(m_is_client == true ? "client": "server"),
+		state_string.get_eapol_key_descriptor_type_string(eapol_key_message->get_key_descriptor_type()),
+		eapol_key_message->get_key_descriptor_type()));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: eapol_key_state_c::process_4_way_handshake_message_2()");
+
+	// Only server (Authenticator) could receive 4-Way Handshake message 2.
+	EAP_ASSERT_ALWAYS(m_is_client == false);
+
+	if (m_eapol_key_handshake_type != eapol_key_handshake_type_4_way_handshake)
+	{
+		EAP_TRACE_ERROR(
+			m_am_tools,
+			TRACE_FLAGS_DEFAULT,
+			(EAPL("WARNING: EAPOL_KEY: %s: process_4_way_handshake_message_0(): wrong handshake type %s\n"),
+			(m_is_client == true ? "client": "server"),
+			state_string.get_eapol_key_handshake_type_string(m_eapol_key_handshake_type)));
+
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_wrong_eap_type_state);
+	}
+
+	if (get_eapol_key_state() != eapol_key_state_wait_4_way_handshake_message_2)
+	{
+		eapol_key_state_string_c state_string;
+		EAP_TRACE_ERROR(
+			m_am_tools,
+			TRACE_FLAGS_DEFAULT,
+			(EAPL("WARNING: EAPOL_KEY: %s: process_4_way_handshake_message_2(): wrong state %s\n"),
+			(m_is_client == true ? "client": "server"),
+			state_string.get_eapol_key_state_string(get_eapol_key_state())));
+
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_wrong_eap_type_state);
+	}
+
+	if (eapol_key_message->get_key_length() != 0ul)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_padding);
+	}
+
+	status = verify_field_is_zero(
+		eapol_key_message->get_EAPOL_key_IV(),
+		eapol_RSNA_key_header_c::EAPOL_RSNA_EAPOL_KEY_IV_SIZE);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = verify_field_is_zero(
+		eapol_key_message->get_key_RSC(),
+		eapol_RSNA_key_header_c::EAPOL_RSNA_KEY_RSC_SIZE);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = verify_field_is_zero(
+		eapol_key_message->get_key_STA_MAC_address(),
+		eapol_RSNA_key_header_c::EAPOL_RSNA_KEY_STA_MAC_ADDRESS_SIZE);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = verify_field_is_zero(
+		eapol_key_message->get_key_reserved(),
+		eapol_RSNA_key_header_c::EAPOL_RSNA_KEY_RESERVED_SIZE);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+
+	// Save SNonce.
+	const u8_t * const SNonce = eapol_key_message->get_key_NONCE();
+	if (SNonce == 0)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_process_illegal_packet_error);
+	}
+
+	status = m_SNonce.set_copy_of_buffer(
+		SNonce,
+		eapol_RSNA_key_header_c::EAPOL_RSNA_KEY_NONCE_SIZE);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = process_4_way_handshake_message_2_payloads(
+		receive_network_id,
+		eapol_key_message,
+		packet_length);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+
+	eap_buf_chain_wr_c sent_packet(
+		eap_write_buffer,
+		m_am_tools);
+
+	if (sent_packet.get_is_valid() == false)
+	{
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	u32_t send_data_length = 0ul;
+	u32_t send_buffer_length = 0ul;
+
+	increase_key_reply_counter();
+
+	status = create_4_way_handshake_message_3(
+		&sent_packet,
+		m_eapol_header_offset,
+		&send_data_length,
+		&send_buffer_length,
+		eapol_key_message->get_eapol_protocol_version(),
+		eapol_key_message->get_key_descriptor_type());
+	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(
+		&m_send_network_id,
+		&sent_packet,
+		m_eapol_header_offset,
+		send_data_length,
+		send_buffer_length);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	set_eapol_key_state(eapol_key_state_wait_4_way_handshake_message_4);
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+//
+eap_status_e eapol_key_state_c::process_4_way_handshake_message_4(
+	const eap_am_network_id_c * const receive_network_id,
+	eapol_RSNA_key_header_c * const eapol_key_message,
+	const u32_t packet_length)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+	EAP_UNREFERENCED_PARAMETER(packet_length);
+
+	eap_status_e status = eap_status_process_general_error;
+
+	eapol_key_state_string_c state_string;
+
+	EAP_TRACE_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("EAPOL_KEY: %s: eapol_key_state_c::process_4_way_handshake_message_4(): eapol_key_descriptor_type = %s = 0x%02x\n"),
+		(m_is_client == true ? "client": "server"),
+		state_string.get_eapol_key_descriptor_type_string(eapol_key_message->get_key_descriptor_type()),
+		eapol_key_message->get_key_descriptor_type()));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: eapol_key_state_c::process_4_way_handshake_message_4()");
+
+	// Only server (Authenticator) could receive 4-Way Handshake message 4.
+	EAP_ASSERT_ALWAYS(m_is_client == false);
+
+	if (m_eapol_key_handshake_type != eapol_key_handshake_type_4_way_handshake)
+	{
+		eapol_key_state_string_c state_string;
+		EAP_TRACE_ERROR(
+			m_am_tools,
+			TRACE_FLAGS_DEFAULT,
+			(EAPL("WARNING: EAPOL_KEY: %s: start_4_way_handshake(): wrong handshake type %s\n"),
+			(m_is_client == true ? "client": "server"),
+			state_string.get_eapol_key_handshake_type_string(m_eapol_key_handshake_type)));
+
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_wrong_eap_type_state);
+	}
+
+	if (get_eapol_key_state() != eapol_key_state_wait_4_way_handshake_message_4)
+	{
+		EAP_TRACE_ERROR(
+			m_am_tools,
+			TRACE_FLAGS_DEFAULT,
+			(EAPL("WARNING: EAPOL_KEY: %s: process_4_way_handshake_message_4(): wrong state %s\n"),
+			(m_is_client == true ? "client": "server"),
+			state_string.get_eapol_key_state_string(get_eapol_key_state())));
+
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_wrong_eap_type_state);
+	}
+
+	if (eapol_key_message->get_key_length() != 0ul)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_padding);
+	}
+
+	status = verify_field_is_zero(
+		eapol_key_message->get_key_NONCE(),
+		eapol_RSNA_key_header_c::EAPOL_RSNA_KEY_NONCE_SIZE);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = verify_field_is_zero(
+		eapol_key_message->get_EAPOL_key_IV(),
+		eapol_RSNA_key_header_c::EAPOL_RSNA_EAPOL_KEY_IV_SIZE);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = verify_field_is_zero(
+		eapol_key_message->get_key_RSC(),
+		eapol_RSNA_key_header_c::EAPOL_RSNA_KEY_RSC_SIZE);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = verify_field_is_zero(
+		eapol_key_message->get_key_STA_MAC_address(),
+		eapol_RSNA_key_header_c::EAPOL_RSNA_KEY_STA_MAC_ADDRESS_SIZE);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = verify_field_is_zero(
+		eapol_key_message->get_key_reserved(),
+		eapol_RSNA_key_header_c::EAPOL_RSNA_KEY_RESERVED_SIZE);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	if (eapol_key_message->get_key_data_length() != 0ul)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_padding);
+	}
+
+
+	status = verify_key_mic(
+		eapol_key_message,
+		&m_confirmation_KCK);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+
+#if defined(EAP_USE_WPXM)
+	if (get_is_WPXM() == true)
+	{
+		status = packet_data_session_key(
+			&m_WPXM_WPXK1,
+			eapol_key_type_wpxm_wpxk1,
+			0ul,
+			false,
+			0,
+			0ul);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+#endif //#if defined(EAP_USE_WPXM)
+
+
+	status = packet_data_session_key(
+		&m_temporal_TK,
+		eapol_key_type_unicast,
+		0ul,
+		false,
+		0,
+		0ul);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+
+	{
+		// This is notification to eapol_core_c object.
+		// 4-Way Handshake finished successfully.
+		eap_state_notification_c notification(
+			m_am_tools,
+			&m_send_network_id,
+			m_is_client,
+			eap_state_notification_generic,
+			eap_protocol_layer_eapol_key,
+			eapol_key_handshake_type_4_way_handshake,
+			get_eapol_key_state(),
+			eapol_key_state_4_way_handshake_successfull,
+			0ul,
+			false);
+		m_key_state_partner->state_notification(&notification);
+	}
+
+
+	if (get_is_RSNA() == true
+		&& m_eapol_group_cipher != eapol_RSNA_key_header_c::eapol_RSNA_cipher_none
+		&& m_group_GTK.get_is_valid_data() == true)
+	{
+		// We do have the expected GTK, pass it to lower layers.
+		status = packet_data_session_key(
+			&m_group_GTK,
+			eapol_key_type_broadcast,
+			m_group_GTK_ID,
+			m_group_GTK_Tx_bit,
+			eapol_key_message->get_key_RSC(),
+			eapol_RSNA_key_header_c::EAPOL_RSNA_KEY_RSC_SIZE);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		{
+			// This is notification to eapol_core_c object.
+			// Group Key Handshake finished successfully.
+			eap_state_notification_c notification(
+				m_am_tools,
+				&m_send_network_id,
+				m_is_client,
+				eap_state_notification_generic,
+				eap_protocol_layer_eapol_key,
+				eapol_key_handshake_type_group_key_handshake,
+				get_eapol_key_state(),
+				eapol_key_state_group_key_handshake_successfull,
+				0ul,
+				false);
+			m_key_state_partner->state_notification(&notification);
+		}
+	}
+
+	if (get_is_RSNA() == true)
+	{
+		{
+			// This is notification to eapol_core_c object.
+			// 802.11i authentication finished successfully.
+			eap_state_notification_c * notification = new eap_state_notification_c(
+				m_am_tools,
+				&m_send_network_id,
+				m_is_client,
+				eap_state_notification_generic,
+				eap_protocol_layer_eapol_key,
+				eapol_key_handshake_type_802_11i_handshake,
+				get_eapol_key_state(),
+				eapol_key_state_802_11i_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);
+			}
+			m_key_state_partner->state_notification(notification);
+
+			delete notification;
+		}
+
+		cancel_handshake_timeout();
+	}
+	else
+	{
+		// Note the WPA version always does separate Group Key Handshake.
+		// Authentication is successfull after the Group Key Handshake
+		// Finishes successfully.
+	}
+
+	set_eapol_key_state(eapol_key_state_4_way_handshake_successfull);
+
+	m_eapol_key_handshake_type = eapol_key_handshake_type_none;
+
+	cancel_retransmission();
+
+	EAP_TRACE_ALWAYS(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("EAPOL_KEY: %s: 4-Way Handshake SUCCESS\n"),
+		(m_is_client == true ? "client": "server")));
+
+	status = start_group_key_handshake(
+		receive_network_id,
+		eapol_key_message->get_eapol_protocol_version(),
+		eapol_key_message->get_key_descriptor_type());
+	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 eapol_key_state_c::create_group_key_handshake_message_1(
+	eap_buf_chain_wr_c * const sent_packet,
+	const u32_t eapol_header_offset,
+	u32_t * const data_length,
+	u32_t * const buffer_length,
+	const eapol_protocol_version_e used_eapol_version,
+	const eapol_key_descriptor_type_e received_key_descriptor_type)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+	eap_status_e status = eap_status_process_general_error;
+
+	EAP_TRACE_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("EAPOL_KEY: %s: eapol_key_state_c::create_group_key_handshake_message_1()\n"),
+		(m_is_client == true ? "client": "server")));
+
+	// Only server (Authenticator) could create Group Key Handshake message 1.
+	EAP_ASSERT_ALWAYS(m_is_client == false);
+
+	if (m_confirmation_KCK.get_is_valid_data() == false
+		|| m_confirmation_KCK.get_data_length() == 0)
+	{
+		EAP_TRACE_ERROR(
+			m_am_tools,
+			TRACE_FLAGS_EAPOL_KEY_DATA_ERROR,
+			(EAPL("ERROR: EAPOL_KEY: eapol_key_state_c::create_group_key_handshake_message_1(): ")
+			 EAPL("m_confirmation_KCK is missing or corrupted.\n")));
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+	}
+
+	if (m_encryption_KEK.get_is_valid_data() == false
+		|| m_encryption_KEK.get_data_length() == 0)
+	{
+		EAP_TRACE_ERROR(
+			m_am_tools,
+			TRACE_FLAGS_EAPOL_KEY_DATA_ERROR,
+			(EAPL("ERROR: EAPOL_KEY: eapol_key_state_c::create_group_key_handshake_message_1(): ")
+			 EAPL("m_encryption_KEK is missing or corrupted.\n")));
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+	}
+
+	u16_t GTK_length = 0ul;
+
+	status = get_key_length(m_eapol_group_cipher, &GTK_length);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	// Creates GTK.
+	status = create_nonce(&m_group_GTK, GTK_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 eapol_key_data_length
+		= eapol_RSNA_key_header_c::get_header_length()
+		+ eapol_rsna_key_data_header_c::EAPOL_RSNA_KEY_HEADER_LENGTH
+		+eapol_rsna_key_data_header_c::EAPOL_RSNA_KEY_ID_AND_GROUP_KEY_HEADER_SIZE
+		+GTK_length;
+
+
+	u32_t extra_encryption_padding_and_block(0ul);
+
+	if (m_eapol_pairwise_cipher == eapol_RSNA_key_header_c::eapol_RSNA_cipher_CCMP
+		|| m_eapol_group_cipher == eapol_RSNA_key_header_c::eapol_RSNA_cipher_CCMP)
+	{
+		extra_encryption_padding_and_block = 2ul * EAP_CRYPTO_AES_WRAP_BLOCK_SIZE;
+	}
+
+
+	*buffer_length
+		= eapol_header_offset
+		+ eapol_key_data_length
+		+ extra_encryption_padding_and_block;
+
+	status = sent_packet->set_buffer_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);
+	}
+	sent_packet->set_data_length(
+		sent_packet->get_buffer_length());
+
+	eapol_RSNA_key_header_c eapol_key_message(
+		m_am_tools,
+		get_is_RSNA(),
+		get_is_WPXM(),
+		sent_packet->get_data_offset(eapol_header_offset, eapol_key_data_length),
+		sent_packet->get_data_length());
+
+	if (eapol_key_message.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 = eapol_key_message.reset_header(
+		1ul,
+		m_authentication_type,
+		m_eapol_pairwise_cipher,
+		get_key_reply_counter(),
+		false, // Pairwise key type bit is NOT set.
+		true, // Install bit is on. Use group key for send and receive.
+		true, // Key Ack bit is on.
+		true, // Key MIC bit is on.
+		true, // Secure bit is on.
+		false, // Error bit is NOT set.
+		false, // Request bit is NOT set.
+		false, // STAKey bit is NOT set.
+		true, // Encrypted Key Data bit is on.
+		used_eapol_version,
+		received_key_descriptor_type);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+
+	if (m_eapol_pairwise_cipher == eapol_RSNA_key_header_c::eapol_RSNA_cipher_TKIP)
+	{
+		// Add Key IV.
+		// Creates EAPOL-Key IV.
+		status = create_nonce(&m_EAPOL_key_IV, eapol_RSNA_key_header_c::EAPOL_RSNA_EAPOL_KEY_IV_SIZE);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		u8_t * const key_iv_field = eapol_key_message.get_EAPOL_key_IV();
+		if (key_iv_field == 0
+			|| m_EAPOL_key_IV.get_data_length() < eapol_RSNA_key_header_c::EAPOL_RSNA_EAPOL_KEY_IV_SIZE)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+		}
+
+		// Note only the lower sixteen octets of the counter are value are used.
+		u32_t offset = m_EAPOL_key_IV.get_data_length() - eapol_RSNA_key_header_c::EAPOL_RSNA_EAPOL_KEY_IV_SIZE;
+
+		u8_t * const iv_data = m_EAPOL_key_IV.get_data_offset(offset, eapol_RSNA_key_header_c::EAPOL_RSNA_EAPOL_KEY_IV_SIZE);
+		if (iv_data == 0)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+		}
+
+		m_am_tools->memmove(
+			key_iv_field,
+			iv_data,
+			eapol_RSNA_key_header_c::EAPOL_RSNA_EAPOL_KEY_IV_SIZE);
+	}
+
+	/**
+	 * @{ Missing payload: last transmit sequence number for GTK. }
+	 */
+
+
+	u32_t eapol_data_length(0ul);
+	
+	if (get_is_RSNA() == true)
+	{
+		status = add_RSN_GTK_payload(
+			&eapol_key_message,
+			&m_group_GTK,
+			&eapol_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
+	{
+		// Add authenticator GTK to Key Data.
+		u8_t * data_field_of_GTK
+			= eapol_key_message.get_key_data(
+					GTK_length);
+		if (data_field_of_GTK == 0)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+		}
+
+		m_am_tools->memmove(
+			data_field_of_GTK,
+			m_group_GTK.get_data(GTK_length),
+			GTK_length);
+
+		EAP_TRACE_DATA_DEBUG(
+			m_am_tools,
+			TRACE_FLAGS_DEFAULT,
+			(EAPL("send WPA Group Key"),
+			 m_group_GTK.get_data(GTK_length),
+			GTK_length));
+
+		eapol_data_length = GTK_length;
+	}
+
+
+
+	if (eapol_data_length > 0xffff)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_too_long_message);
+	}
+
+	status = eapol_key_message.set_key_data_length(
+		static_cast<u16_t>(eapol_data_length));
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	*data_length
+		= eapol_key_message.get_header_length()
+		+ eapol_key_message.get_key_data_length();
+
+	status = eapol_key_message.set_eapol_packet_body_length(
+		static_cast<u16_t>(*data_length - eapol_header_base_c::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 = encrypt_key_data(&eapol_key_message);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	// NOTE the encryption may increase length of the key data field.
+	*data_length
+		= eapol_key_message.get_header_length()
+		+ eapol_key_message.get_key_data_length();
+
+	status = eapol_key_message.set_eapol_packet_body_length(
+		static_cast<u16_t>(*data_length - eapol_header_base_c::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 = create_key_mic(
+		&eapol_key_message,
+		&m_confirmation_KCK);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	// Encryption may change the length of EAPOL-Key data.
+	*data_length
+		= eapol_key_message.get_header_length()
+		+ eapol_key_message.get_key_data_length();
+
+	sent_packet->set_data_length(
+		eapol_header_offset + *data_length);
+
+	TRACE_EAPOL_KEY_MESSAGE(
+		"Send Group Key Handshake Message 1",
+		&eapol_key_message);
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+//
+eap_status_e eapol_key_state_c::process_group_key_handshake_message_0(
+	const eap_am_network_id_c * const receive_network_id,
+	eapol_RSNA_key_header_c * const eapol_key_message,
+	const u32_t packet_length)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+	EAP_UNREFERENCED_PARAMETER(packet_length);
+	eap_status_e status = eap_status_process_general_error;
+
+	EAP_TRACE_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("EAPOL_KEY: %s: eapol_key_state_c::process_group_key_handshake_message_0()\n"),
+		(m_is_client == true ? "client": "server")));
+
+	// Only server (Authenticator) could receive Group Key Handshake message 0.
+	EAP_ASSERT_ALWAYS(m_is_client == false);
+
+	if (m_eapol_key_handshake_type != eapol_key_handshake_type_none)
+	{
+		eapol_key_state_string_c state_string;
+		EAP_TRACE_ERROR(
+			m_am_tools,
+			TRACE_FLAGS_DEFAULT,
+			(EAPL("WARNING: EAPOL_KEY: %s: process_group_key_handshake_message_0(): wrong handshake type %s\n"),
+			(m_is_client == true ? "client": "server"),
+			state_string.get_eapol_key_handshake_type_string(m_eapol_key_handshake_type)));
+
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_wrong_eap_type_state);
+	}
+
+	if (get_eapol_key_state() != eapol_key_state_4_way_handshake_successfull
+		&& get_eapol_key_state() != eapol_key_state_group_key_handshake_successfull)
+	{
+		eapol_key_state_string_c state_string;
+		EAP_TRACE_ERROR(
+			m_am_tools,
+			TRACE_FLAGS_DEFAULT,
+			(EAPL("WARNING: EAPOL_KEY: %s: process_group_key_handshake_message_0(): wrong state %s\n"),
+			(m_is_client == true ? "client": "server"),
+			state_string.get_eapol_key_state_string(get_eapol_key_state())));
+
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_wrong_eap_type_state);
+	}
+
+
+	if (eapol_key_message->get_key_length() != 0ul)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_padding);
+	}
+
+	status = verify_field_is_zero(
+		eapol_key_message->get_key_NONCE(),
+		eapol_RSNA_key_header_c::EAPOL_RSNA_KEY_NONCE_SIZE);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = verify_field_is_zero(
+		eapol_key_message->get_EAPOL_key_IV(),
+		eapol_RSNA_key_header_c::EAPOL_RSNA_EAPOL_KEY_IV_SIZE);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = verify_field_is_zero(
+		eapol_key_message->get_key_RSC(),
+		eapol_RSNA_key_header_c::EAPOL_RSNA_KEY_RSC_SIZE);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = verify_field_is_zero(
+		eapol_key_message->get_key_STA_MAC_address(),
+		eapol_RSNA_key_header_c::EAPOL_RSNA_KEY_STA_MAC_ADDRESS_SIZE);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = verify_field_is_zero(
+		eapol_key_message->get_key_reserved(),
+		eapol_RSNA_key_header_c::EAPOL_RSNA_KEY_RESERVED_SIZE);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	if (eapol_key_message->get_key_data_length() != 0ul)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_padding);
+	}
+
+
+	status = verify_key_mic(
+		eapol_key_message,
+		&m_confirmation_KCK);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = start_group_key_handshake(
+		receive_network_id,
+		eapol_key_message->get_eapol_protocol_version(),
+		eapol_key_message->get_key_descriptor_type());
+	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 eapol_key_state_c::process_group_key_handshake_message_2(
+	const eap_am_network_id_c * const receive_network_id,
+	eapol_RSNA_key_header_c * const eapol_key_message,
+	const u32_t packet_length)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+	EAP_UNREFERENCED_PARAMETER(receive_network_id);
+	EAP_UNREFERENCED_PARAMETER(packet_length);
+
+	eap_status_e status = eap_status_process_general_error;
+
+	EAP_TRACE_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("EAPOL_KEY: %s: eapol_key_state_c::process_group_key_handshake_message_2()\n"),
+		(m_is_client == true ? "client": "server")));
+
+	// Only server (Authenticator) could receive Group Key Handshake message 2.
+	EAP_ASSERT_ALWAYS(m_is_client == false);
+
+	if (m_eapol_key_handshake_type != eapol_key_handshake_type_group_key_handshake)
+	{
+		eapol_key_state_string_c state_string;
+		EAP_TRACE_ERROR(
+			m_am_tools,
+			TRACE_FLAGS_DEFAULT,
+			(EAPL("WARNING: EAPOL_KEY: %s: start_group_key_handshake(): wrong handshake type %s\n"),
+			(m_is_client == true ? "client": "server"),
+			state_string.get_eapol_key_handshake_type_string(m_eapol_key_handshake_type)));
+
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_wrong_eap_type_state);
+	}
+
+	if (get_eapol_key_state() != eapol_key_state_wait_group_key_handshake_message_2)
+	{
+		eapol_key_state_string_c state_string;
+		EAP_TRACE_ERROR(
+			m_am_tools,
+			TRACE_FLAGS_DEFAULT,
+			(EAPL("WARNING: EAPOL_KEY: %s: process_group_key_handshake_message_2(): wrong state %s\n"),
+			(m_is_client == true ? "client": "server"),
+			state_string.get_eapol_key_state_string(get_eapol_key_state())));
+
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_wrong_eap_type_state);
+	}
+
+
+	if (eapol_key_message->get_key_length() != 0ul)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_padding);
+	}
+
+	status = verify_field_is_zero(
+		eapol_key_message->get_key_NONCE(),
+		eapol_RSNA_key_header_c::EAPOL_RSNA_KEY_NONCE_SIZE);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = verify_field_is_zero(
+		eapol_key_message->get_EAPOL_key_IV(),
+		eapol_RSNA_key_header_c::EAPOL_RSNA_EAPOL_KEY_IV_SIZE);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = verify_field_is_zero(
+		eapol_key_message->get_key_RSC(),
+		eapol_RSNA_key_header_c::EAPOL_RSNA_KEY_RSC_SIZE);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = verify_field_is_zero(
+		eapol_key_message->get_key_STA_MAC_address(),
+		eapol_RSNA_key_header_c::EAPOL_RSNA_KEY_STA_MAC_ADDRESS_SIZE);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = verify_field_is_zero(
+		eapol_key_message->get_key_reserved(),
+		eapol_RSNA_key_header_c::EAPOL_RSNA_KEY_RESERVED_SIZE);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	if (eapol_key_message->get_key_data_length() != 0ul)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_padding);
+	}
+
+	
+	status = verify_key_mic(
+		eapol_key_message,
+		&m_confirmation_KCK);
+	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(
+		&m_group_GTK,
+		eapol_key_type_broadcast,
+		m_group_GTK_ID,
+		m_group_GTK_Tx_bit,
+		eapol_key_message->get_key_RSC(),
+		eapol_RSNA_key_header_c::EAPOL_RSNA_KEY_RSC_SIZE);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	{
+		// This is notification to eapol_core_c object.
+		// Group Key Handshake finished successfully.
+		eap_state_notification_c notification(
+			m_am_tools,
+			&m_send_network_id,
+			m_is_client,
+			eap_state_notification_generic,
+			eap_protocol_layer_eapol_key,
+			eapol_key_handshake_type_group_key_handshake,
+			get_eapol_key_state(),
+			eapol_key_state_group_key_handshake_successfull,
+			0ul,
+			false);
+		m_key_state_partner->state_notification(&notification);
+
+
+		cancel_group_key_update_timeout();
+
+		if (m_server_TEST_group_key_update == true)
+		{
+			init_group_key_update_timeout(EAPOL_KEY_STATE_TIMER_GROUP_KEY_UPDATE_TIMEOUT);
+		}
+	}
+
+	if (get_is_RSNA() == false)
+	{
+		// Note the WPA version always does separate Group Key Handshake.
+		// Authentication is successfull after the Group Key Handshake
+		// Finishes successfully.
+
+		{
+			// This is notification to eapol_core_c object.
+			// WPA authentication finished successfully.
+			eap_state_notification_c * notification = new eap_state_notification_c(
+				m_am_tools,
+				&m_send_network_id,
+				m_is_client,
+				eap_state_notification_generic,
+				eap_protocol_layer_eapol_key,
+				eapol_key_handshake_type_802_11i_handshake,
+				get_eapol_key_state(),
+				eapol_key_state_802_11i_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);
+			}
+			m_key_state_partner->state_notification(notification);
+
+			delete notification;
+		}
+	}
+
+	set_eapol_key_state(eapol_key_state_group_key_handshake_successfull);
+
+	m_eapol_key_handshake_type = eapol_key_handshake_type_none;
+
+	cancel_retransmission();
+	cancel_handshake_timeout();
+
+	EAP_TRACE_ALWAYS(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("EAPOL_KEY: %s: Group Key Handshake SUCCESS\n"),
+		(m_is_client == true ? "client": "server")));
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+#endif //#if !defined(NO_EAPOL_KEY_STATE_SERVER)
+
+//--------------------------------------------------
+
+// End.