/*
* 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.
*
*/

/*
* %version: 32 %
*/

// This is enumeration of EAPOL source code.
#if defined(USE_EAP_MINIMUM_RELEASE_TRACES)
	#undef EAP_FILE_NUMBER_ENUM
	#define EAP_FILE_NUMBER_ENUM 66 
	#undef EAP_FILE_NUMBER_DATE 
	#define EAP_FILE_NUMBER_DATE 1127594498 
#endif //#if defined(USE_EAP_MINIMUM_RELEASE_TRACES)



#include "eap_am_memory.h"
#include "eap_tools.h"
#include "eap_type_aka.h"
#include "eap_type_aka_header.h"
#include "eap_type_aka_payloads.h"
#include "abs_eap_am_type_aka.h"
#include "eap_crypto_api.h"
#include "eap_state_notification.h"
#include "abs_eap_am_mutex.h"
#include "eap_automatic_variable.h"
#include "eap_buffer.h"


//--------------------------------------------------

/** @file */

//--------------------------------------------------

// 
EAP_FUNC_EXPORT eap_status_e eap_type_aka_c::generate_nai(
	eap_variable_data_c * const new_nai,
	const bool use_manual_default_realm,
	const eap_variable_data_c * const nai_realm,
	const eap_variable_data_c * const id_IMSI_or_pseudonym,
	const bool id_is_imsi,
	const eap_variable_data_c * const IMSI,
	const eap_variable_data_c * const automatic_realm,
	const u32_t length_of_mnc)
{
	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);

	if (id_IMSI_or_pseudonym == 0
		|| id_IMSI_or_pseudonym->get_is_valid_data() == false)
	{
		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return EAP_STATUS_RETURN(m_am_tools, eap_status_process_general_error);
	}

	eap_status_e status = eap_status_not_supported;
	
	status = new_nai->init(id_IMSI_or_pseudonym->get_data_length()+1u);
	if (status != eap_status_ok)
	{
		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return EAP_STATUS_RETURN(m_am_tools, status);
	}
	new_nai->set_is_valid();

	eap_variable_data_c local_username(m_am_tools);
	if (local_username.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 = local_username.set_buffer_length(id_IMSI_or_pseudonym->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);
	}
	local_username.set_data_length(id_IMSI_or_pseudonym->get_data_length());

	m_am_tools->memmove(
		local_username.get_data(id_IMSI_or_pseudonym->get_data_length()),
		id_IMSI_or_pseudonym->get_data(id_IMSI_or_pseudonym->get_data_length()),
		id_IMSI_or_pseudonym->get_data_length());

	new_nai->set_data_length(0u);

	if (id_is_imsi == true)
	{
		// Note the first octet is reserved for IMSI prefix.
		status = new_nai->add_data(AKA_IMSI_PREFIX_CHARACTER, 1u);
		if (status != eap_status_ok)
		{
			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
			return EAP_STATUS_RETURN(m_am_tools, status);
		}
	}

	// This could be IMSI or pseudonym.
	status = new_nai->add_data(&local_username);
	if (status != eap_status_ok)
	{
		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return EAP_STATUS_RETURN(m_am_tools, status);
	}

	if (use_manual_default_realm == true
		&& nai_realm != 0
		&& nai_realm->get_is_valid_data() == true)
	{
		if (nai_realm->get_data_length() > 0ul)
		{
			status = new_nai->add_data(AKA_AT_CHARACTER, 1u);
			if (status != eap_status_ok)
			{
				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
				return EAP_STATUS_RETURN(m_am_tools, status);
			}
		}

		status = new_nai->add_data(nai_realm);
		if (status != eap_status_ok)
		{
			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
			return EAP_STATUS_RETURN(m_am_tools, status);
		}
	}
	else if (IMSI != 0
		&& IMSI->get_is_valid_data() == true
		&& IMSI->get_data_length() >= EAP_TYPE_AKA_MINIMUM_IMSI_LENGTH)
	{
		// We must use automatic realm (Example: "wlan.mnc456.mcc123.3gppnetwork.org").

		status = new_nai->add_data(AKA_AT_CHARACTER, 1u);
		if (status != eap_status_ok)
		{
			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
			return EAP_STATUS_RETURN(m_am_tools, status);
		}

		if (automatic_realm != 0
			&& automatic_realm->get_is_valid_data() == true)
		{
			status = new_nai->add_data(automatic_realm);
			if (status != eap_status_ok)
			{
				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
				return EAP_STATUS_RETURN(m_am_tools, status);
			}
		}
		else
		{
			status = new_nai->add_data(AKA_OWLAN_ORG_PREFIX_STRING, AKA_OWLAN_ORG_PREFIX_STRING_LENGTH);
			if (status != eap_status_ok)
			{
				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
				return EAP_STATUS_RETURN(m_am_tools, status);
			}

			status = new_nai->add_data(AKA_OWLAN_DOT_STRING, AKA_OWLAN_DOT_STRING_LENGTH);
			if (status != eap_status_ok)
			{
				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
				return EAP_STATUS_RETURN(m_am_tools, status);
			}

			status = new_nai->add_data(AKA_OWLAN_MNC_STRING, AKA_OWLAN_MNC_STRING_LENGTH);
			if (status != eap_status_ok)
			{
				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
				return EAP_STATUS_RETURN(m_am_tools, status);
			}

			// MNC part.
			if (length_of_mnc == EAP_TYPE_AKA_MNC_LENGTH_2_BYTES)
			{
				// Add zero digit.
				u8_t zero = '0';
				status = new_nai->add_data(&zero, sizeof(zero));
				if (status != eap_status_ok)
				{
					EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
					return EAP_STATUS_RETURN(m_am_tools, status);
				}
			}
			status = new_nai->add_data(IMSI->get_data_offset(EAP_TYPE_AKA_MNC_OFFSET, length_of_mnc), length_of_mnc);
			if (status != eap_status_ok)
			{
				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
				return EAP_STATUS_RETURN(m_am_tools, status);
			}

			status = new_nai->add_data(AKA_OWLAN_DOT_STRING, AKA_OWLAN_DOT_STRING_LENGTH);
			if (status != eap_status_ok)
			{
				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
				return EAP_STATUS_RETURN(m_am_tools, status);
			}

			status = new_nai->add_data(AKA_OWLAN_MCC_STRING, AKA_OWLAN_MCC_STRING_LENGTH);
			if (status != eap_status_ok)
			{
				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
				return EAP_STATUS_RETURN(m_am_tools, status);
			}

			// MCC part is in index 0 and it is 3 bytes long.
			status = new_nai->add_data(IMSI->get_data_offset(EAP_TYPE_AKA_MCC_OFFSET, EAP_TYPE_AKA_MCC_LENGTH), EAP_TYPE_AKA_MCC_LENGTH);
			if (status != eap_status_ok)
			{
				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
				return EAP_STATUS_RETURN(m_am_tools, status);
			}

			status = new_nai->add_data(AKA_OWLAN_DOT_STRING, AKA_OWLAN_DOT_STRING_LENGTH);
			if (status != eap_status_ok)
			{
				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
				return EAP_STATUS_RETURN(m_am_tools, status);
			}

			status = new_nai->add_data(AKA_OWLAN_ORG_STRING, AKA_OWLAN_ORG_STRING_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_nai"),
		new_nai->get_data(new_nai->get_data_length()),
		new_nai->get_data_length()));

	if (new_nai->get_data_length() < sizeof(u8_t))
	{
		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_nai);
	}

	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 eap_type_aka_c::query_AKA_IMSI_or_pseudonym_or_reauthentication_id(
	eap_variable_data_c * const IMSI,
	eap_variable_data_c * const pseudonym_identity,
	eap_variable_data_c * const reauthentication_identity,
	eap_variable_data_c * const automatic_realm, ///< If this is not used, do not add any data to this parameter.
	u32_t * const length_of_mnc,
	const bool /*must_be_synchronous*/,
	const aka_payload_AT_type_e required_identity,
	const eap_type_aka_complete_e required_completion,
	const u8_t received_eap_identifier)
{
	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_process_general_error;

	save_current_state();
	set_state(eap_type_aka_state_pending_identity_query);

	status = m_am_type_aka->query_AKA_IMSI_or_pseudonym_or_reauthentication_id(
		IMSI,
		pseudonym_identity,
		reauthentication_identity,
		automatic_realm,
		length_of_mnc,
		required_identity,
		required_completion,
		received_eap_identifier);

	if (status == eap_status_pending_request)
	{
		// This is pending query, that will be completed by complete_AKA_IMSI_or_pseudonym_or_reauthentication_id_query() call.
		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return EAP_STATUS_RETURN(m_am_tools, status);
	}
	else if (status == eap_status_completed_request)
	{
		// This is already completed by complete_AKA_IMSI_or_pseudonym_or_reauthentication_id_query() call.
		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return EAP_STATUS_RETURN(m_am_tools, status);
	}
	else if (status != eap_status_ok
		&& status != eap_status_success)
	{
		// This is an error case.
		restore_saved_previous_state();
		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return EAP_STATUS_RETURN(m_am_tools, status);
	}
	else if (status == eap_status_ok)
	{
		if (m_use_pseudonym_identity == false)
		{
			// We do not want use pseudonym identity.
			pseudonym_identity->reset();
		}

		if (m_use_reauthentication_identity == false)
		{
			// We do not want use re-authentication identity.
			reauthentication_identity->reset();
		}
	}

	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
	return EAP_STATUS_RETURN(m_am_tools, status);
}

//--------------------------------------------------

//
EAP_FUNC_EXPORT eap_status_e eap_type_aka_c::query_AKA_RES(
	eap_type_aka_authentication_vector_c * const authentication_vector
	)
{
	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_process_general_error;

	status = set_RAND(authentication_vector->get_RAND());
	if (status != eap_status_ok)
	{
		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return EAP_STATUS_RETURN(m_am_tools, status);
	}

	status = set_AUTN(authentication_vector->get_AUTN());
	if (status != eap_status_ok)
	{
		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return EAP_STATUS_RETURN(m_am_tools, status);
	}

	set_state(eap_type_aka_state_pending_kc_sres_query);

	status = m_am_type_aka->query_AKA_RES(authentication_vector);

	if (status == eap_status_pending_request)
	{
		// This is pending query, that will be completed by complete_AKA_RES_query() call.
		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return EAP_STATUS_RETURN(m_am_tools, status);
	}
	else if (status == eap_status_completed_request)
	{
		// This is already completed by complete_AKA_RES_query() call.
		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return EAP_STATUS_RETURN(m_am_tools, status);
	}
	else if (status != eap_status_ok
		&& status != eap_status_success)
	{
		// This is a error case.
		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);
}

//--------------------------------------------------

u32_t eap_type_aka_c::get_mnc_length(const u32_t mcc)
{
	u32_t * const mcc_array = reinterpret_cast<u32_t *>(m_2_digit_mnc_map_of_mcc_of_imsi_array.get_data(
		m_2_digit_mnc_map_of_mcc_of_imsi_array.get_data_length()));

	u32_t count = m_2_digit_mnc_map_of_mcc_of_imsi_array.get_data_length() / sizeof(u32_t);

	for (u32_t ind = 0ul; ind < count; ind++)
	{
		if (mcc == mcc_array[ind])
		{
			return EAP_TYPE_AKA_MNC_LENGTH_2_BYTES;
		}
	}

	return EAP_TYPE_AKA_DEFAULT_MNC_LENGTH_3_BYTES;
}

//--------------------------------------------------

eap_status_e eap_type_aka_c::create_uma_realm(
	eap_variable_data_c * const automatic_realm,
	const eap_variable_data_c * const IMSI,
	const u32_t length_of_mnc)
{
	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);

	eap_status_e status(eap_status_ok);

	// Create special automatic realm for UMA.
	automatic_realm->reset();

	if (m_use_uma_profile == true)
	{
		// Create special automatic realm for UMA.
		automatic_realm->reset();

		if (m_uma_automatic_realm_prefix.get_is_valid_data() == true)
		{
			status = automatic_realm->add_data(&m_uma_automatic_realm_prefix);
			if (status != eap_status_ok)
			{
				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
				return EAP_STATUS_RETURN(m_am_tools, status);
			}
		}

		if (automatic_realm->get_data_length() > 0ul)
		{
			status = automatic_realm->add_data(AKA_OWLAN_DOT_STRING, AKA_OWLAN_DOT_STRING_LENGTH);
			if (status != eap_status_ok)
			{
				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
				return EAP_STATUS_RETURN(m_am_tools, status);
			}
		}

		status = automatic_realm->add_data(AKA_OWLAN_MNC_STRING, AKA_OWLAN_MNC_STRING_LENGTH);
		if (status != eap_status_ok)
		{
			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
			return EAP_STATUS_RETURN(m_am_tools, status);
		}

		// MNC part.
		if (length_of_mnc == EAP_TYPE_AKA_MNC_LENGTH_2_BYTES)
		{
			// Add zero digit.
			u8_t zero = '0';
			status = automatic_realm->add_data(&zero, sizeof(zero));
			if (status != eap_status_ok)
			{
				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
				return EAP_STATUS_RETURN(m_am_tools, status);
			}
		}
		status = automatic_realm->add_data(IMSI->get_data_offset(EAP_TYPE_AKA_MNC_OFFSET, length_of_mnc), length_of_mnc);
		if (status != eap_status_ok)
		{
			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
			return EAP_STATUS_RETURN(m_am_tools, status);
		}

		status = automatic_realm->add_data(AKA_OWLAN_DOT_STRING, AKA_OWLAN_DOT_STRING_LENGTH);
		if (status != eap_status_ok)
		{
			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
			return EAP_STATUS_RETURN(m_am_tools, status);
		}

		status = automatic_realm->add_data(AKA_OWLAN_MCC_STRING, AKA_OWLAN_MCC_STRING_LENGTH);
		if (status != eap_status_ok)
		{
			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
			return EAP_STATUS_RETURN(m_am_tools, status);
		}

		// MCC part is in index 0 and it is 3 bytes long.
		status = automatic_realm->add_data(IMSI->get_data_offset(EAP_TYPE_AKA_MCC_OFFSET, EAP_TYPE_AKA_MCC_LENGTH), EAP_TYPE_AKA_MCC_LENGTH);
		if (status != eap_status_ok)
		{
			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
			return EAP_STATUS_RETURN(m_am_tools, status);
		}
		
		status = automatic_realm->add_data(AKA_OWLAN_DOT_STRING, AKA_OWLAN_DOT_STRING_LENGTH);
		if (status != eap_status_ok)
		{
			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
			return EAP_STATUS_RETURN(m_am_tools, status);
		}
		
		status = automatic_realm->add_data(AKA_OWLAN_ORG_STRING, AKA_OWLAN_ORG_STRING_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 eap_type_aka_c::complete_AKA_IMSI_or_pseudonym_or_reauthentication_id_query(
	const eap_variable_data_c * const IMSI,
	const eap_variable_data_c * const p_pseudonym_identity,
	const eap_variable_data_c * const p_reauthentication_identity,
	const eap_variable_data_c * const p_automatic_realm, ///< If this is not used, do not add any data to this parameter.
	const u32_t length_of_mnc,
	const eap_type_aka_complete_e required_completion,
	const u8_t received_eap_identifier,
	const eap_status_e completion_status
)
{
	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_process_general_error;

	if (completion_status != eap_status_ok
		|| IMSI == 0
		|| p_pseudonym_identity == 0
		|| p_reauthentication_identity == 0)
	{
		set_state(eap_type_aka_state_failure);

		// The eap_status_process_general_error error value is more important
		// than return value of set_session_timeout().
		(void) get_type_partner()->set_session_timeout(0ul);

		return EAP_STATUS_RETURN(m_am_tools, eap_status_process_general_error);
	}

	const eap_variable_data_c * local_pseudonym_identity = p_pseudonym_identity;
	const eap_variable_data_c * local_reauthentication_identity = p_reauthentication_identity;

	if (m_use_pseudonym_identity == false)
	{
		// We do not want use pseudonym identity.
		local_pseudonym_identity = 0;
	}
	
	if (m_use_reauthentication_identity == false)
	{
		// We do not want use re-authentication identity.
		local_reauthentication_identity = 0;
	}

	if (get_state() != eap_type_aka_state_pending_identity_query)
	{
		// Authentication is terminated or state is wrong. Cannot continue.
		set_state(eap_type_aka_state_failure);

		// The eap_status_handler_does_not_exists_error error value is more important
		// than return value of set_session_timeout().
		(void) get_type_partner()->set_session_timeout(0ul);

		return EAP_STATUS_RETURN(m_am_tools, eap_status_handler_does_not_exists_error);
	}

	// - - - - - - - - - - - - - - - - - - - - - - - -

	eap_variable_data_c local_automatic_realm(m_am_tools);

	if (p_automatic_realm == 0
		|| p_automatic_realm->get_is_valid_data() == false)
	{
		{
			// Here we read the length of the MNC.
			const u8_t * const MCC = IMSI->get_data_offset(EAP_TYPE_AKA_MCC_OFFSET, EAP_TYPE_AKA_MCC_LENGTH);
			if (MCC == 0)
			{
				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
				return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
			}

			u32_t MCC_value(0ul);

			status = m_am_tools->number_string_to_u32(
				MCC,
				3ul,
				&MCC_value);
			if (status != eap_status_ok)
			{
				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
				return EAP_STATUS_RETURN(m_am_tools, status);
			}

			m_length_of_mnc = get_mnc_length(MCC_value);
		}

		status = create_uma_realm(&local_automatic_realm, IMSI, m_length_of_mnc);
		if (status != eap_status_ok)
		{
			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
			return EAP_STATUS_RETURN(m_am_tools, status);
		}

		if (local_automatic_realm.get_is_valid_data() == true)
		{
			status = m_automatic_realm.set_copy_of_buffer(&local_automatic_realm);
			if (status != eap_status_ok)
			{
				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
				return EAP_STATUS_RETURN(m_am_tools, status);
			}
		}
	}
	else
	{
		status = local_automatic_realm.set_copy_of_buffer(p_automatic_realm);
		if (status != eap_status_ok)
		{
			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
			return EAP_STATUS_RETURN(m_am_tools, status);
		}

		m_length_of_mnc = length_of_mnc;

		status = m_automatic_realm.set_copy_of_buffer(p_automatic_realm);
		if (status != eap_status_ok)
		{
			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
			return EAP_STATUS_RETURN(m_am_tools, status);
		}
	}

	m_automatic_realm_read = true;

	// - - - - - - - - - - - - - - - - - - - - - - - -

	if (required_completion == eap_type_aka_complete_query_eap_identity)
	{
		status = handle_eap_identity_query(
			get_send_network_id(),
			0,
			get_last_eap_identifier(),
			IMSI,
			local_pseudonym_identity,
			local_reauthentication_identity,
			&local_automatic_realm,
			m_length_of_mnc,
			false);
		if (status != eap_status_ok
			&& status != eap_status_completed_request)
		{
			restore_saved_previous_state();
		}
	}
	else if (required_completion == eap_type_aka_complete_aka_identity_request)
	{
		if (local_reauthentication_identity != 0
			&& local_reauthentication_identity->get_is_valid_data() == true)
		{
			EAP_TRACE_DEBUG(
				m_am_tools, 
				TRACE_FLAGS_DEFAULT, 
				(EAPL("complete_AKA_IMSI_or_pseudonym_or_reauthentication_id_query reauthentication_identity\n")));
			
			status = get_reauthentication_identity()->set_copy_of_buffer(local_reauthentication_identity);
			if (status != eap_status_ok)
			{
				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
				return EAP_STATUS_RETURN(m_am_tools, status);
			}
			
			status = store_identity(get_reauthentication_identity(), 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 (local_pseudonym_identity != 0
			&& local_pseudonym_identity->get_is_valid_data() == true)
		{
			EAP_TRACE_DEBUG(
				m_am_tools, 
				TRACE_FLAGS_DEFAULT, 
				(EAPL("complete_AKA_IMSI_or_pseudonym_or_reauthentication_id_query pseudonym\n")));
			
			get_reauthentication_identity()->reset();
			status = get_pseudonym()->set_copy_of_buffer(local_pseudonym_identity);
			if (status != eap_status_ok)
			{
				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
				return EAP_STATUS_RETURN(m_am_tools, status);
			}
			
			status = store_identity(get_pseudonym(), 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 (IMSI->get_is_valid_data() == true
			&& IMSI->get_data_length() >= EAP_TYPE_AKA_MINIMUM_IMSI_LENGTH)
		{
			EAP_TRACE_DEBUG(
				m_am_tools, 
				TRACE_FLAGS_DEFAULT, 
				(EAPL("complete_AKA_IMSI_or_pseudonym_or_reauthentication_id_query IMSI\n")));
			
			get_reauthentication_identity()->reset();
			get_pseudonym()->reset();
			status = get_IMSI()->set_copy_of_buffer(IMSI);
			if (status != eap_status_ok)
			{
				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
				return EAP_STATUS_RETURN(m_am_tools, status);
			}
			
			status = store_identity(get_IMSI(), true);
			if (status != eap_status_ok)
			{
				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
				return EAP_STATUS_RETURN(m_am_tools, status);
			}
		}

		save_current_state();
		set_state(eap_type_aka_state_analyse_aka_identity_request);
		
		EAP_TRACE_DEBUG(
			m_am_tools, 
			TRACE_FLAGS_DEFAULT, 
			(EAPL("complete_AKA_IMSI_or_pseudonym_or_reauthentication_id_query(0x%08x).\n"),
			 this));
		
		// Here we swap the addresses.
		eap_am_network_id_c receive_network_id(m_am_tools,
			get_send_network_id()->get_destination_id(),
			get_send_network_id()->get_source_id(),
			get_send_network_id()->get_type());

		status = send_aka_identity_response_message(
			&receive_network_id,
			received_eap_identifier,
			get_include_identity_to_aka_identity_response(),
			&local_automatic_realm);
		
		if (status != eap_status_ok)
		{
			restore_saved_previous_state();
			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 eap_type_aka_c::complete_AKA_RES_query(
	const eap_type_aka_authentication_vector_c * const authentication_vector,
	const eap_status_e completion_status)
{
	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_process_general_error;

	if (completion_status != eap_status_ok
		|| authentication_vector == 0
		|| authentication_vector->get_is_valid() == false)
	{
		if (completion_status == eap_status_not_fresh_challenges)
		{
			(void) initialize_error_message(completion_status);

			restore_saved_previous_state();
		}
		else
		{
			set_state(eap_type_aka_state_failure);

			// The eap_status_process_general_error error value is more important
			// than return value of set_session_timeout().
			(void) get_type_partner()->set_session_timeout(0ul);
		}

		return EAP_STATUS_RETURN(m_am_tools, completion_status);
	}

	if (get_state() != eap_type_aka_state_pending_kc_sres_query)
	{
		// Authentication is terminated or state is wrong. Cannot continue.
		set_state(eap_type_aka_state_failure);

		// The eap_status_handler_does_not_exists_error error value is more important
		// than return value of set_session_timeout().
		(void) get_type_partner()->set_session_timeout(0ul);

		return EAP_STATUS_RETURN(m_am_tools, eap_status_handler_does_not_exists_error);
	}

	// - - - - - - - - - - - - - - - - - - - - - - - -

	status = process_AKA_kc_sres(
		authentication_vector);

	if (status != eap_status_ok
		&& status != eap_status_success)
	{
		(void) initialize_error_message(status);

		restore_saved_previous_state();
	}

	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
	return EAP_STATUS_RETURN(m_am_tools, status);
}

//--------------------------------------------------

//
EAP_FUNC_EXPORT eap_status_e eap_type_aka_c::process_AKA_kc_sres(
	const eap_type_aka_authentication_vector_c * const authentication_vector)
{
	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);

	eap_status_e status = eap_status_process_general_error;

	eap_variable_data_c MAC_RAND(m_am_tools);
	if (MAC_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);
	}

	if (get_state() != eap_type_aka_state_pending_kc_sres_query
		|| authentication_vector == 0
		|| authentication_vector->get_is_valid() == false)
	{
		return EAP_STATUS_RETURN(m_am_tools, eap_status_process_general_error);
	}

	if (get_identity() == 0
		|| get_identity()->get_is_valid_data() == false)
	{
		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_eap_identity);
	}

	// - - - - - - - - - - - - - - - - - - - - - - - -

	EAP_TRACE_DATA_DEBUG(m_am_tools, TRACE_FLAGS_DEFAULT|TRACE_TEST_VECTORS, (EAPL("RAND"),
		authentication_vector->get_RAND()->get_data(authentication_vector->get_RAND()->get_data_length()),
		authentication_vector->get_RAND()->get_data_length()));
	EAP_TRACE_DATA_DEBUG(m_am_tools, TRACE_FLAGS_DEFAULT, (EAPL("AUTN"),
		authentication_vector->get_AUTN()->get_data(authentication_vector->get_AUTN()->get_data_length()),
		authentication_vector->get_AUTN()->get_data_length()));

	if (authentication_vector->get_vector_status() == eap_status_ok)
	{
		EAP_TRACE_DATA_DEBUG(m_am_tools, TRACE_FLAGS_DEFAULT, (EAPL("RES"),
			authentication_vector->get_RES()->get_data(authentication_vector->get_RES()->get_data_length()),
			authentication_vector->get_RES()->get_data_length()));
		EAP_TRACE_DATA_DEBUG(m_am_tools, TRACE_FLAGS_DEFAULT, (EAPL("CK"),
			authentication_vector->get_CK()->get_data(authentication_vector->get_CK()->get_data_length()),
			authentication_vector->get_CK()->get_data_length()));
		EAP_TRACE_DATA_DEBUG(m_am_tools, TRACE_FLAGS_DEFAULT, (EAPL("IK"),
			authentication_vector->get_IK()->get_data(authentication_vector->get_IK()->get_data_length()),
			authentication_vector->get_IK()->get_data_length()));
	}
	else if (authentication_vector->get_vector_status() == eap_status_syncronization_failure)
	{
		// ERROR: send EAP-Response/AKA-Synchronization-Failure
		status = send_aka_synchronization_failure_response_message(
			authentication_vector);
		if (status != eap_status_ok)
		{
			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);
	}
	else
	{
		// ERROR: send EAP-Response/AKA-Authentication-Reject
		status = send_aka_authentication_reject_response_message(
			authentication_vector);
		if (status != eap_status_ok)
		{
			return EAP_STATUS_RETURN(m_am_tools, status);
		}

		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return EAP_STATUS_RETURN(m_am_tools, authentication_vector->get_vector_status());
	}

	EAP_TRACE_DATA_DEBUG(m_am_tools, TRACE_FLAGS_DEFAULT, (EAPL("identity"),
		get_identity()->get_data(get_identity()->get_data_length()),
		get_identity()->get_data_length()));

	status = get_RES()->set_copy_of_buffer(authentication_vector->get_RES());
	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 XKEY(m_am_tools);
	eap_variable_data_c K_encr(m_am_tools);
	eap_variable_data_c K_aut(m_am_tools);
	eap_variable_data_c master_session_key(m_am_tools);

	if (XKEY.get_is_valid() == false
		|| K_encr.get_is_valid() == false
		|| K_aut.get_is_valid() == false
		|| master_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 = generate_shared_secred_keys(
		EAP_TYPE_AKA_KEYMAT_SIZE,
		authentication_vector->get_CK(),
		authentication_vector->get_IK(),
		&XKEY,
		&K_encr,
		&K_aut,
		&master_session_key);
	if (status != eap_status_ok)
	{
		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return EAP_STATUS_RETURN(m_am_tools, status);
	}

	if (get_saved_EAP_packet()->get_is_valid_data() == true)
	{
		aka_payloads_c * const l_aka_payloads = new aka_payloads_c(m_am_tools);
		eap_automatic_variable_c<aka_payloads_c> l_aka_payloads_automatic(m_am_tools, l_aka_payloads);

		if (l_aka_payloads == 0
			|| l_aka_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);
		}

		aka_header_c saved_EAP_packet(
			m_am_tools,
			get_saved_EAP_packet()->get_data(get_saved_EAP_packet()->get_data_length()),
			get_saved_EAP_packet()->get_data_length());

		if (saved_EAP_packet.get_is_valid() == false)
		{
			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
			return EAP_STATUS_RETURN(m_am_tools, eap_status_header_corrupted);
		}

		status = parse_aka_packet(
			&saved_EAP_packet,
			get_saved_EAP_packet()->get_data_length(),
			l_aka_payloads);
		if (status != eap_status_ok)
		{
			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
			return EAP_STATUS_RETURN(m_am_tools, status);
		}

		status = check_message_authentication_code(
			&K_aut,
			l_aka_payloads,
			&saved_EAP_packet,
			saved_EAP_packet.get_length());
		if (status != eap_status_ok)
		{
			(void) initialize_error_message(status);

			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
			return EAP_STATUS_RETURN(m_am_tools, status);
		}

		if (m_received_checkcode.get_is_valid_data() == true)
		{
			// Verify AT_CHECKCODE.
			status = checkcode_verify(
				&m_received_checkcode);
			if (status != eap_status_ok)
			{
				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
				return EAP_STATUS_RETURN(m_am_tools, status);
			}
		}

		// Decrypt and parse encrypted payload.
		if (l_aka_payloads->get_IV()->get_payload_included() == true
		|| l_aka_payloads->get_ENCR_DATA()->get_payload_included() == true)
		{
			if (l_aka_payloads->get_IV()->get_payload_included() == true
			&& l_aka_payloads->get_ENCR_DATA()->get_payload_included() == true
			&& l_aka_payloads->get_MAC()->get_payload_included() == true)
			{
				status = decrypt_DATA_payload(
					l_aka_payloads,
					&K_encr);
				if (status != eap_status_ok)
				{
					EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
					return EAP_STATUS_RETURN(m_am_tools, status);
				}

				status = handle_DATA_payload(
					aka_subtype_Challenge,
					l_aka_payloads);
				if (status != eap_status_ok)
				{
					EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
					return EAP_STATUS_RETURN(m_am_tools, status);
				}
			}
			else
			{
				// All of these must be included
				// or none of these must be included.
				return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
			}
		}
		else
		{
			// No encrypted payload.
			// AKA AM should remove re-authentication identity from favourite place.

			EAP_TRACE_DEBUG(
				m_am_tools, 
				TRACE_FLAGS_DEFAULT, 
				(EAPL("eap_type_aka_c::process_AKA_kc_sres(0x%08x): Resets re-authentication identity from database.\n"),
				 this));

			status = m_am_type_aka->store_reauthentication_id(
				&m_send_network_id,
				0);
			if (status != eap_status_ok)
			{
				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
				return EAP_STATUS_RETURN(m_am_tools, status);
			}

			get_reauthentication_identity()->reset();
		}
	}
	else
	{
		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return EAP_STATUS_RETURN(m_am_tools, eap_status_process_general_error);
	}

	// - - - - - - - - - - - - - - - - - - - - - - - -

	eap_type_aka_authentication_vector_c * const copy_authentication_vector = authentication_vector->copy();
	if (copy_authentication_vector == 0
		|| copy_authentication_vector->get_is_valid() == false)
	{
		delete copy_authentication_vector;
		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return EAP_STATUS_RETURN(m_am_tools, status);
	}

	set_authentication_vector(copy_authentication_vector);

	// - - - - - - - - - - - - - - - - - - - - - - - -

	status = send_challenge_response_message(
		&K_aut);
	if (status != eap_status_ok)
	{
		return EAP_STATUS_RETURN(m_am_tools, status);
	}

	// Store keys.
	status = get_XKEY()->set_copy_of_buffer(&XKEY);
	if (status != eap_status_ok)
	{
		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return EAP_STATUS_RETURN(m_am_tools, status);
	}
	status = get_K_encr()->set_copy_of_buffer(&K_encr);
	if (status != eap_status_ok)
	{
		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return EAP_STATUS_RETURN(m_am_tools, status);
	}
	status = get_K_aut()->set_copy_of_buffer(&K_aut);
	if (status != eap_status_ok)
	{
		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return EAP_STATUS_RETURN(m_am_tools, status);
	}
	status = get_master_session_key()->set_copy_of_buffer(&master_session_key);
	if (status != eap_status_ok)
	{
		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return EAP_STATUS_RETURN(m_am_tools, status);
	}

	// In order to use re-authentication, the client and the server need to
	// store the following values: original XKEY, K_aut, K_encr, latest
	// counter value and the next re-authentication identity.
	status = m_am_type_aka->store_reauth_parameters(
		&XKEY,
		&K_aut,
		&K_encr,
		EAP_TYPE_AKA_INITIAL_REAUTH_COUNTER);
	if (status != eap_status_ok)
	{
		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return EAP_STATUS_RETURN(m_am_tools, status);
	}

	// Authentication was successful.
	if (m_use_result_indication == true)
	{
		// This version waits result indication.
		if (m_authentication_type == AKA_AUTHENTICATION_TYPE_REAUTHENTICATION)
		{
			EAP_TRACE_DEBUG(
				m_am_tools, 
				TRACE_FLAGS_DEFAULT|TRACE_TEST_VECTORS, 
				(EAPL("EAP_type_AKA: %s, re-authentication OK, waits result indication\n"),
				(m_is_client == true) ? "client": "server"));
		}
		else if (m_authentication_type == AKA_AUTHENTICATION_TYPE_FULL_AUTH)
		{
			if (m_identity_type == AKA_IDENTITY_TYPE_PSEUDONYM_ID)
			{
				EAP_TRACE_DEBUG(
					m_am_tools, 
					TRACE_FLAGS_DEFAULT|TRACE_TEST_VECTORS, 
					(EAPL("EAP_type_AKA: %s, full authentication OK, pseudonym identity, waits result indication\n"),
					(m_is_client == true) ? "client": "server"));
			}
			else if (m_identity_type == AKA_IDENTITY_TYPE_IMSI_ID)
			{
				EAP_TRACE_DEBUG(
					m_am_tools, 
					TRACE_FLAGS_DEFAULT|TRACE_TEST_VECTORS, 
					(EAPL("EAP_type_AKA: %s, full authentication OK, IMSI identity, waits result indication\n"),
					(m_is_client == true) ? "client": "server"));
			}
			else if (m_identity_type == AKA_IDENTITY_TYPE_RE_AUTH_ID)
			{
				EAP_TRACE_DEBUG(
					m_am_tools, 
					TRACE_FLAGS_DEFAULT|TRACE_TEST_VECTORS, 
					(EAPL("EAP_type_AKA: %s, full authentication OK, Re-auth identity, waits result indication\n"),
					(m_is_client == true) ? "client": "server"));
			}
			else
			{
				EAP_TRACE_ERROR(
					m_am_tools,
					TRACE_FLAGS_AKA_ERROR|TRACE_TEST_VECTORS,
					(EAPL("ERROR: EAP_type_AKA: %s, full authentication OK, unknown identity\n"),
					(m_is_client == true) ? "client": "server"));

				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
				return EAP_STATUS_RETURN(m_am_tools, eap_status_authentication_failure);
			}
		}
		else
		{
			EAP_TRACE_ERROR(
				m_am_tools,
				TRACE_FLAGS_AKA_ERROR|TRACE_TEST_VECTORS,
				(EAPL("ERROR: EAP_type_AKA: %s, unknown authentication OK, unknown identity\n"),
				(m_is_client == true) ? "client": "server"));

			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
			return EAP_STATUS_RETURN(m_am_tools, eap_status_authentication_failure);
		}

		set_state(eap_type_aka_state_waiting_for_notification_request_success);
	}
	else if (m_wait_eap_success_packet == false)
	{
		// This version does not wait EAP-Success message.
		if (get_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_process_general_error);
		}

		// Here we swap the addresses.
		eap_am_network_id_c receive_network_id(m_am_tools,
			get_send_network_id()->get_destination_id(),
			get_send_network_id()->get_source_id(),
			get_send_network_id()->get_type());

		// This version does not wait EAP-Success message.
		status = finish_successful_authentication(
			&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);
		}
	}
	else
	{
		// This version waits the EAP-Success message.

		if (m_authentication_type == AKA_AUTHENTICATION_TYPE_REAUTHENTICATION)
		{
			EAP_TRACE_DEBUG(
				m_am_tools, 
				TRACE_FLAGS_DEFAULT|TRACE_TEST_VECTORS, 
				(EAPL("EAP_type_AKA: %s, re-authentication OK, waits EAP-Success\n"),
				(m_is_client == true) ? "client": "server"));
		}
		else if (m_authentication_type == AKA_AUTHENTICATION_TYPE_FULL_AUTH)
		{
			if (m_identity_type == AKA_IDENTITY_TYPE_PSEUDONYM_ID)
			{
				EAP_TRACE_DEBUG(
					m_am_tools, 
					TRACE_FLAGS_DEFAULT|TRACE_TEST_VECTORS, 
					(EAPL("EAP_type_AKA: %s, full authentication OK, pseudonym identity, waits EAP-Success\n"),
					(m_is_client == true) ? "client": "server"));
			}
			else if (m_identity_type == AKA_IDENTITY_TYPE_IMSI_ID)
			{
				EAP_TRACE_DEBUG(
					m_am_tools, 
					TRACE_FLAGS_DEFAULT|TRACE_TEST_VECTORS, 
					(EAPL("EAP_type_AKA: %s, full authentication OK, IMSI identity, waits EAP-Success\n"),
					(m_is_client == true) ? "client": "server"));
			}
			else if (m_identity_type == AKA_IDENTITY_TYPE_RE_AUTH_ID)
			{
				EAP_TRACE_DEBUG(
					m_am_tools, 
					TRACE_FLAGS_DEFAULT|TRACE_TEST_VECTORS, 
					(EAPL("EAP_type_AKA: %s, full authentication OK, Re-auth identity, waits EAP-Success\n"),
					(m_is_client == true) ? "client": "server"));
			}
			else
			{
				EAP_TRACE_ERROR(
					m_am_tools,
					TRACE_FLAGS_AKA_ERROR|TRACE_TEST_VECTORS,
					(EAPL("ERROR: EAP_type_AKA: %s, full authentication OK, unknown identity, waits EAP-Success\n"),
					(m_is_client == true) ? "client": "server"));

				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
				return EAP_STATUS_RETURN(m_am_tools, eap_status_authentication_failure);
			}
		}
		else
		{
			EAP_TRACE_ERROR(
				m_am_tools,
				TRACE_FLAGS_AKA_ERROR|TRACE_TEST_VECTORS,
				(EAPL("ERROR: EAP_type_AKA: %s, unknown authentication OK, unknown identity, waits EAP-Success\n"),
				(m_is_client == true) ? "client": "server"));

			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
			return EAP_STATUS_RETURN(m_am_tools, eap_status_authentication_failure);
		}

		set_state(eap_type_aka_state_waiting_for_success);
		status = eap_status_ok;
	}

	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
	return EAP_STATUS_RETURN(m_am_tools, status);
}

//--------------------------------------------------

//
EAP_FUNC_EXPORT eap_status_e eap_type_aka_c::send_aka_identity_response_message(
	const eap_am_network_id_c * const receive_network_id,
	const u8_t received_eap_identifier,
	const aka_payload_AT_type_e include_identity_to_aka_identity_response,
	const eap_variable_data_c * const automatic_realm)
{
	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);

	EAP_TRACE_DEBUG(
		m_am_tools, 
		TRACE_FLAGS_DEFAULT, 
		(EAPL("    send: handler 0x%08x, AKA-type %10s, %s, state %2d=%s\n"),
		this,
		EAPL("aka_subtype_Identity"),
		(get_is_client()) ? EAPL("client") : EAPL("server"),
		get_state(),
		get_state_string()
		));

	eap_buf_chain_wr_c aka_initial_reply(
		eap_write_buffer, 
		m_am_tools, 
		EAP_MAX_LOCAL_PACKET_BUFFER_LENGTH);

	if (aka_initial_reply.get_is_valid() == false)
	{
		EAP_TRACE_DEBUG(m_am_tools, TRACE_FLAGS_DEFAULT, (EAPL("packet buffer corrupted.\n")));
		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
	}

	if (m_is_client == true
		&& m_client_responds_retransmitted_packets == true)
	{
		// We do not wan't lower layers do re-transmissions behalf of us.
		// This means AKA does process every re-transmitted EAP-Request.
		aka_initial_reply.set_do_packet_retransmission(false);
	}

	EAP_ASSERT_ALWAYS(EAP_TYPE_AKA_LOCAL_PACKET_BUFFER_LENGTH >= (m_aka_header_offset+m_trailer_length));
	u32_t packet_buffer_free = EAP_TYPE_AKA_LOCAL_PACKET_BUFFER_LENGTH-m_trailer_length;
	u32_t packet_buffer_offset = 0u;

	if (m_aka_header_offset+m_MTU < packet_buffer_free)
	{
		packet_buffer_free = m_aka_header_offset+m_MTU;
	}

	aka_header_c aka_response(
		m_am_tools,
		aka_initial_reply.get_data_offset(
			m_aka_header_offset,
			(packet_buffer_free-m_aka_header_offset)),
		(packet_buffer_free-m_aka_header_offset));

	if (aka_response.get_is_valid() == false)
	{
		EAP_TRACE_DEBUG(
			m_am_tools, 
			TRACE_FLAGS_DEFAULT, 
			(EAPL("packet_send: packet buffer corrupted.\n")));
		return EAP_STATUS_RETURN(m_am_tools, eap_status_header_corrupted);
	}

	// Here we swap the addresses.
	eap_am_network_id_c send_network_id(m_am_tools,
		receive_network_id->get_destination_id(),
		receive_network_id->get_source_id(),
		receive_network_id->get_type());

	aka_response.reset_header(
		packet_buffer_free-m_aka_header_offset,
		m_use_eap_expanded_type);
	aka_response.set_length(
		static_cast<u16_t>(packet_buffer_free-m_aka_header_offset),
		m_use_eap_expanded_type);
	aka_response.set_code(eap_code_response);
	aka_response.set_identifier(received_eap_identifier);
	aka_response.set_type(
		eap_type_aka,
		m_use_eap_expanded_type);

	aka_response.set_subtype(aka_subtype_Identity);

	EAP_TRACE_DEBUG(
		m_am_tools, 
		TRACE_FLAGS_DEFAULT, 
		(EAPL("eap_type_aka_c::send_aka_identity_response_message(0x%08x): include_identity_to_start_response %d=%s.\n"),
		 this,
		 include_identity_to_aka_identity_response,
		 aka_payload_AT_header_c::get_payload_AT_string(include_identity_to_aka_identity_response)));

	update_buffer_indexes(
		EAP_TYPE_AKA_LOCAL_PACKET_BUFFER_LENGTH,
		m_aka_header_offset+aka_response.get_header_length(),
		&packet_buffer_offset,
		&packet_buffer_free);

	// - - - - - - - - - - - - - - - - - - - - - - - -

	u32_t aka_data_free = packet_buffer_free;
	u32_t aka_data_offset = 0u;
	eap_status_e status = eap_status_process_general_error;

	// - - - - - - - - - - - - - - - - - - - - - - - -

	if (m_identity_type == AKA_IDENTITY_TYPE_RE_AUTH_ID)
	{
		// Re-authentication identity MUST be removed after first use.
		// EAP-AKA AM should remove re-authentication identity from favourite place.

		EAP_TRACE_DEBUG(
			m_am_tools, 
			TRACE_FLAGS_DEFAULT, 
			(EAPL("eap_type_aka_c::send_aka_identity_response_message(0x%08x): Resets re-authentication identity from database.\n"),
			 this));

		status = m_am_type_aka->store_reauthentication_id(
			&m_send_network_id,
			0);
		if (status != eap_status_ok)
		{
			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
			return EAP_STATUS_RETURN(m_am_tools, status);
		}

		// Do not reset the local copy of re-authentocation identity.
	}


	if (include_identity_to_aka_identity_response == aka_payload_AT_PERMANENT_ID_REQ)
	{
		m_authentication_type = AKA_AUTHENTICATION_TYPE_FULL_AUTH;
		set_identity_type(AKA_IDENTITY_TYPE_IMSI_ID);

		EAP_TRACE_DEBUG(
			m_am_tools, 
			TRACE_FLAGS_DEFAULT, 
			(EAPL("eap_type_aka_c::send_aka_identity_response_message(0x%08x): Selects full authentication and IMSI identity.\n"),
			 this));
	}
	else if (include_identity_to_aka_identity_response == aka_payload_AT_FULLAUTH_ID_REQ)
	{
		eap_variable_data_c * const pseudonym = get_pseudonym();

		if (pseudonym->get_is_valid_data() == true)
		{
			// We have pseudonym.
			m_authentication_type = AKA_AUTHENTICATION_TYPE_FULL_AUTH;
			set_identity_type(AKA_IDENTITY_TYPE_PSEUDONYM_ID);

			EAP_TRACE_DEBUG(
				m_am_tools, 
				TRACE_FLAGS_DEFAULT, 
				(EAPL("eap_type_aka_c::send_aka_identity_response_message(0x%08x): Selects full authentication and pseudonym identity.\n"),
				 this));
		}
		else // if (pseudonym->get_is_valid() == false)
		{
			// Because no pseudonym is available we must use IMSI.
			m_authentication_type = AKA_AUTHENTICATION_TYPE_FULL_AUTH;
			set_identity_type(AKA_IDENTITY_TYPE_IMSI_ID);

			EAP_TRACE_DEBUG(
				m_am_tools, 
				TRACE_FLAGS_DEFAULT, 
				(EAPL("eap_type_aka_c::send_aka_identity_response_message(0x%08x): Selects full authentication and IMSI identity.\n"),
				 this));
		}
	}
	else if (include_identity_to_aka_identity_response == aka_payload_AT_ANY_ID_REQ)
	{
		eap_variable_data_c * const reauthentication_identity = get_reauthentication_identity();
		eap_variable_data_c * const pseudonym = get_pseudonym();

		if (reauthentication_identity->get_is_valid_data() == true)
		{
			// We have reauthentication identity. Re-authentication identity is NAI.
			m_authentication_type = AKA_AUTHENTICATION_TYPE_REAUTHENTICATION;
			set_identity_type(AKA_IDENTITY_TYPE_RE_AUTH_ID);

			EAP_TRACE_DEBUG(
				m_am_tools, 
				TRACE_FLAGS_DEFAULT, 
				(EAPL("eap_type_aka_c::send_aka_identity_response_message(0x%08x): Selects fast re-authentication and re-authentication identity.\n"),
				 this));
		}
		else if (pseudonym->get_is_valid_data() == true)
		{
			// We have pseudonym.
			m_authentication_type = AKA_AUTHENTICATION_TYPE_FULL_AUTH;
			set_identity_type(AKA_IDENTITY_TYPE_PSEUDONYM_ID);

			EAP_TRACE_DEBUG(
				m_am_tools, 
				TRACE_FLAGS_DEFAULT, 
				(EAPL("eap_type_aka_c::send_aka_identity_response_message(0x%08x): Selects full authentication and pseudonym identity.\n"),
				 this));
		}
		else if (get_IMSI()->get_is_valid_data() == true)
		{
			// Because no pseudonym is available we must use IMSI.
			m_authentication_type = AKA_AUTHENTICATION_TYPE_FULL_AUTH;
			set_identity_type(AKA_IDENTITY_TYPE_IMSI_ID);

			EAP_TRACE_DEBUG(
				m_am_tools, 
				TRACE_FLAGS_DEFAULT, 
				(EAPL("eap_type_aka_c::send_aka_identity_response_message(0x%08x): Selects full authentication and IMSI identity.\n"),
				 this));
		}
		else
		{
			// ERROR, no identity available.
			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_eap_identity);
		}
	}


	if (include_identity_to_aka_identity_response != aka_payload_NONE)
	{
		if (m_identity_type == AKA_IDENTITY_TYPE_IMSI_ID)
		{
			eap_variable_data_c NAI(m_am_tools);
			if (NAI.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 = NAI.init(1u);
			if (status != eap_status_ok)
			{
				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
				return EAP_STATUS_RETURN(m_am_tools, status);
			}
			NAI.set_is_valid();

			status = generate_nai(
				&NAI,
				m_use_manual_realm,
				get_nai_realm(),
				get_IMSI(),
				true,
				get_IMSI(),
				automatic_realm,
				m_length_of_mnc);
			if (status != eap_status_ok)
			{
				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
				return EAP_STATUS_RETURN(m_am_tools, status);
			}

			status = add_pseudonym_or_imsi_payload(
				&aka_response,
				EAP_TYPE_AKA_LOCAL_PACKET_BUFFER_LENGTH,
				aka_response.get_header_length(),
				&aka_data_offset,
				&aka_data_free,
				&packet_buffer_free,
				&packet_buffer_offset,
				&NAI,
				aka_payload_AT_IDENTITY);
			if (status != eap_status_ok)
			{
				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
				return EAP_STATUS_RETURN(m_am_tools, status);
			}

			// Save used NAI.
			status = get_NAI()->set_copy_of_buffer(&NAI);
			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("eap_type_aka_c::send_aka_identity_response_message(): Client sends IMSI identity.\n")));
		}
		else if (m_identity_type == AKA_IDENTITY_TYPE_PSEUDONYM_ID)
		{
			eap_variable_data_c * const pseudonym = get_pseudonym();

			if (pseudonym->get_is_valid_data() == true)
			{
				// We have pseudonym.

				eap_variable_data_c NAI(m_am_tools);
				if (NAI.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 = NAI.init(1u);
				if (status != eap_status_ok)
				{
					EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
					return EAP_STATUS_RETURN(m_am_tools, status);
				}
				NAI.set_is_valid();

				status = generate_nai(
					&NAI,
					m_use_manual_realm,
					get_nai_realm(),
					pseudonym,
					false,
					get_IMSI(),
					automatic_realm,
					m_length_of_mnc);
				if (status != eap_status_ok)
				{
					EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
					return EAP_STATUS_RETURN(m_am_tools, status);
				}

				status = add_pseudonym_or_imsi_payload(
					&aka_response,
					EAP_TYPE_AKA_LOCAL_PACKET_BUFFER_LENGTH,
					aka_response.get_header_length(),
					&aka_data_offset,
					&aka_data_free,
					&packet_buffer_free,
					&packet_buffer_offset,
					&NAI,
					aka_payload_AT_IDENTITY);
				if (status != eap_status_ok)
				{
					EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
					return EAP_STATUS_RETURN(m_am_tools, status);
				}

				// Save used NAI.
				status = get_NAI()->set_copy_of_buffer(&NAI);
				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("eap_type_aka_c::send_aka_identity_response_message(): Client sends pseudonym identity.\n")));
			}
			else // if (pseudonym->get_is_valid() == false)
			{
				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
				return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_eap_identity);
			}
		}
		else if (m_identity_type == AKA_IDENTITY_TYPE_RE_AUTH_ID)
		{
			eap_variable_data_c * const reauthentication_identity = get_reauthentication_identity();

			if (reauthentication_identity->get_is_valid_data() == true)
			{
				// We have reauthentication identity. Re-authentication identity is NAI.
				status = add_pseudonym_or_imsi_payload(
					&aka_response,
					EAP_TYPE_AKA_LOCAL_PACKET_BUFFER_LENGTH,
					aka_response.get_header_length(),
					&aka_data_offset,
					&aka_data_free,
					&packet_buffer_free,
					&packet_buffer_offset,
					reauthentication_identity,
					aka_payload_AT_IDENTITY);
				if (status != eap_status_ok)
				{
					EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
					return EAP_STATUS_RETURN(m_am_tools, status);
				}

				// Save used NAI.
				status = get_NAI()->set_copy_of_buffer(reauthentication_identity);
				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("eap_type_aka_c::send_aka_identity_response_message(): Client sends re-authentication identity.\n")));
			}
			else
			{
				// ERROR, no identity available.
				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
				return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_eap_identity);
			}
		}
	}

	// - - - - - - - - - - - - - - - - - - - - - - - -

	aka_response.set_data_length(
		aka_data_offset,
		m_use_eap_expanded_type);
	aka_initial_reply.set_data_length(packet_buffer_offset);

	EAP_ASSERT_ALWAYS(
		m_aka_header_offset+aka_response.get_header_length()+aka_response.get_data_length()
		== packet_buffer_offset);

	// - - - - - - - - - - - - - - - - - - - - - - - -

	status = checkcode_save_message_client(
		aka_response.get_header_buffer(
			aka_response.get_header_length()
			+aka_response.get_data_length()),
		aka_response.get_header_length()
		+aka_response.get_data_length(),
		0);
	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(
		&send_network_id,
		&aka_initial_reply,
		m_aka_header_offset,
		aka_response.get_header_length()+aka_response.get_data_length(),
		EAP_TYPE_AKA_LOCAL_PACKET_BUFFER_LENGTH
		);

	if (status == eap_status_ok)
	{
		if (m_authentication_type == AKA_AUTHENTICATION_TYPE_REAUTHENTICATION)
		{
			set_state(eap_type_aka_state_waiting_for_reauth_request);
		}
		else
		{
			set_state(eap_type_aka_state_waiting_for_challenge_request);
		}
	}

	// - - - - - - - - - - - - - - - - - - - - - - - -

	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
	return EAP_STATUS_RETURN(m_am_tools, status);
}

//--------------------------------------------------

//
EAP_FUNC_EXPORT eap_status_e eap_type_aka_c::send_aka_synchronization_failure_response_message(
	const eap_type_aka_authentication_vector_c * const authentication_vector
	)
{
	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);

	EAP_TRACE_DEBUG(
		m_am_tools, 
		TRACE_FLAGS_DEFAULT, 
		(EAPL("send: handler 0x%08x, AKA-type %10s, %s, state %2d=%s\n"),
		this,
		EAPL("send_synchronization_failure_response_message"),
		(get_is_client()) ? EAPL("client") : EAPL("server"),
		get_state(),
		get_state_string()
		));

	eap_buf_chain_wr_c eap_notification_packet(
		eap_write_buffer, 
		m_am_tools, 
		EAP_MAX_LOCAL_PACKET_BUFFER_LENGTH);

	if (eap_notification_packet.get_is_valid() == false)
	{
		EAP_TRACE_DEBUG(
			m_am_tools, 
			TRACE_FLAGS_DEFAULT, 
			(EAPL("packet buffer corrupted.\n")));
		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
	}

	if (m_is_client == true
		&& m_client_responds_retransmitted_packets == true)
	{
		// We do not wan't lower layers do re-transmissions behalf of us.
		// This means AKA does process every re-transmitted EAP-Request.
		eap_notification_packet.set_do_packet_retransmission(false);
	}

	EAP_ASSERT_ALWAYS(EAP_TYPE_AKA_LOCAL_PACKET_BUFFER_LENGTH >= (m_aka_header_offset+m_trailer_length));
	u32_t packet_buffer_free = EAP_TYPE_AKA_LOCAL_PACKET_BUFFER_LENGTH-m_trailer_length;
	u32_t packet_buffer_offset = 0u;

	if (m_aka_header_offset+m_MTU < packet_buffer_free)
	{
		packet_buffer_free = m_aka_header_offset+m_MTU;
	}

	aka_header_c aka_response(
		m_am_tools,
		eap_notification_packet.get_data_offset(
			m_aka_header_offset,
			(packet_buffer_free-m_aka_header_offset)),
		(packet_buffer_free-m_aka_header_offset));

	if (aka_response.get_is_valid() == false)
	{
		EAP_TRACE_DEBUG(
			m_am_tools, 
			TRACE_FLAGS_DEFAULT, 
			(EAPL("packet_send: packet buffer corrupted.\n")));
		return EAP_STATUS_RETURN(m_am_tools, eap_status_process_general_error);
	}

	aka_response.reset_header(
		static_cast<u16_t>(packet_buffer_free-m_aka_header_offset),
		m_use_eap_expanded_type);
	aka_response.set_length(
		static_cast<u16_t>(packet_buffer_free-m_aka_header_offset),
		m_use_eap_expanded_type);
	aka_response.set_code(eap_code_response);
	aka_response.set_identifier(static_cast<u8_t>(get_last_eap_identifier()));
	aka_response.set_type(
		eap_type_aka,
		m_use_eap_expanded_type);
	aka_response.set_subtype(aka_subtype_Synchronization_Failure);


	update_buffer_indexes(
		EAP_TYPE_AKA_LOCAL_PACKET_BUFFER_LENGTH,
		m_aka_header_offset+aka_response.get_header_length(),
		&packet_buffer_offset,
		&packet_buffer_free);

	// - - - - - - - - - - - - - - - - - - - - - - - -

	u32_t aka_data_free = packet_buffer_free;
	u32_t aka_data_offset = 0u;
	eap_status_e status = eap_status_process_general_error;

	// - - - - - - - - - - - - - - - - - - - - - - - -

	eap_variable_data_c orig_K_aut(m_am_tools);
	if (orig_K_aut.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 = add_AUTS_payload(
		&aka_response,
		EAP_TYPE_AKA_LOCAL_PACKET_BUFFER_LENGTH,
		aka_response.get_header_length(),
		&aka_data_offset,
		&aka_data_free,
		&packet_buffer_free,
		&packet_buffer_offset,
		authentication_vector->get_AUTS(),
		aka_payload_AT_AUTS);
	if (status != eap_status_ok)
	{
		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return EAP_STATUS_RETURN(m_am_tools, status);
	}
	
	aka_response.set_data_length(
		aka_data_offset,
		m_use_eap_expanded_type);
	eap_notification_packet.set_data_length(packet_buffer_offset);
	
	EAP_ASSERT_ALWAYS(
		m_aka_header_offset+aka_response.get_header_length()+aka_response.get_data_length()
		== packet_buffer_offset);
	
	// - - - - - - - - - - - - - - - - - - - - - - - -

	eap_notification_packet.set_data_length(packet_buffer_offset);

	EAP_TRACE_DATA_DEBUG(
		m_am_tools,
		TRACE_FLAGS_DEFAULT,
		(EAPL("send_aka_synchronization_failure_response_message(): aka_subtype_Synchronization_Failure packet"),
		aka_response.get_header_buffer(aka_response.get_length()),
		aka_response.get_length()));

	status = packet_send(
		get_send_network_id(),
		&eap_notification_packet,
		m_aka_header_offset,
		aka_response.get_length(),
		EAP_TYPE_AKA_LOCAL_PACKET_BUFFER_LENGTH
		);

	if (status == eap_status_ok)
	{
		set_state(eap_type_aka_state_waiting_for_challenge_request);
	}

	// - - - - - - - - - - - - - - - - - - - - - - - -

	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
	return EAP_STATUS_RETURN(m_am_tools, status);
}


//--------------------------------------------------

//
EAP_FUNC_EXPORT eap_status_e eap_type_aka_c::send_aka_authentication_reject_response_message(
	const eap_type_aka_authentication_vector_c * const /*authentication_vector*/
	)
{
	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);

	EAP_TRACE_DEBUG(
		m_am_tools, 
		TRACE_FLAGS_DEFAULT, 
		(EAPL("send: handler 0x%08x, AKA-type %10s, %s, state %2d=%s\n"),
		this,
		EAPL("send_authentication_reject_response_message"),
		(get_is_client()) ? EAPL("client") : EAPL("server"),
		get_state(),
		get_state_string()
		));

	eap_buf_chain_wr_c eap_notification_packet(
		eap_write_buffer, 
		m_am_tools, 
		EAP_MAX_LOCAL_PACKET_BUFFER_LENGTH);

	if (eap_notification_packet.get_is_valid() == false)
	{
		EAP_TRACE_DEBUG(
			m_am_tools, 
			TRACE_FLAGS_DEFAULT, 
			(EAPL("packet buffer corrupted.\n")));
		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
	}

	if (m_is_client == true
		&& m_client_responds_retransmitted_packets == true)
	{
		// We do not wan't lower layers do re-transmissions behalf of us.
		// This means AKA does process every re-transmitted EAP-Request.
		eap_notification_packet.set_do_packet_retransmission(false);
	}

	EAP_ASSERT_ALWAYS(EAP_TYPE_AKA_LOCAL_PACKET_BUFFER_LENGTH >= (m_aka_header_offset+m_trailer_length));
	u32_t packet_buffer_free = EAP_TYPE_AKA_LOCAL_PACKET_BUFFER_LENGTH-m_trailer_length;
	u32_t packet_buffer_offset = 0u;

	if (m_aka_header_offset+m_MTU < packet_buffer_free)
	{
		packet_buffer_free = m_aka_header_offset+m_MTU;
	}

	aka_header_c aka_response(
		m_am_tools,
		eap_notification_packet.get_data_offset(
			m_aka_header_offset,
			(packet_buffer_free-m_aka_header_offset)),
		(packet_buffer_free-m_aka_header_offset));

	if (aka_response.get_is_valid() == false)
	{
		EAP_TRACE_DEBUG(
			m_am_tools, 
			TRACE_FLAGS_DEFAULT, 
			(EAPL("packet_send: packet buffer corrupted.\n")));
		return EAP_STATUS_RETURN(m_am_tools, eap_status_process_general_error);
	}

	aka_response.reset_header(
		static_cast<u16_t>(packet_buffer_free-m_aka_header_offset),
		m_use_eap_expanded_type);
	aka_response.set_length(
		static_cast<u16_t>(packet_buffer_free-m_aka_header_offset),
		m_use_eap_expanded_type);
	aka_response.set_code(eap_code_response);
	aka_response.set_identifier(static_cast<u8_t>(get_last_eap_identifier()));
	aka_response.set_type(
		eap_type_aka,
		m_use_eap_expanded_type);
	aka_response.set_subtype(aka_subtype_Authentication_Reject);


	update_buffer_indexes(
		EAP_TYPE_AKA_LOCAL_PACKET_BUFFER_LENGTH,
		m_aka_header_offset+aka_response.get_header_length(),
		&packet_buffer_offset,
		&packet_buffer_free);

	// - - - - - - - - - - - - - - - - - - - - - - - -

	u32_t aka_data_offset = 0u;
	eap_status_e status = eap_status_process_general_error;

	// - - - - - - - - - - - - - - - - - - - - - - - -

	aka_response.set_data_length(
		aka_data_offset,
		m_use_eap_expanded_type);
	eap_notification_packet.set_data_length(packet_buffer_offset);

	EAP_TRACE_DATA_DEBUG(
		m_am_tools,
		TRACE_FLAGS_DEFAULT,
		(EAPL("send_aka_authentication_reject_response_message: aka_subtype_Authentication_Reject packet"),
		aka_response.get_header_buffer(aka_response.get_length()),
		aka_response.get_length()));

	status = packet_send(
		get_send_network_id(),
		&eap_notification_packet,
		m_aka_header_offset,
		aka_response.get_length(),
		EAP_TYPE_AKA_LOCAL_PACKET_BUFFER_LENGTH
		);

	if (status == eap_status_ok)
	{
	}

	// - - - - - - - - - - - - - - - - - - - - - - - -

	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
	return EAP_STATUS_RETURN(m_am_tools, status);
}


//--------------------------------------------------

//
EAP_FUNC_EXPORT eap_status_e eap_type_aka_c::send_aka_notification_response(
	const eap_aka_notification_codes_e notification_code,
	const bool add_at_counter_attribute)
{
	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);

	EAP_TRACE_DEBUG(
		m_am_tools, 
		TRACE_FLAGS_DEFAULT, 
		(EAPL("send: handler 0x%08x, AKA-type %10s, %s, state %2d=%s\n"),
		this,
		EAPL("send_aka_notification_response"),
		(get_is_client()) ? EAPL("client") : EAPL("server"),
		get_state(),
		get_state_string()
		));

	eap_buf_chain_wr_c eap_notification_packet(
		eap_write_buffer, 
		m_am_tools, 
		EAP_MAX_LOCAL_PACKET_BUFFER_LENGTH);

	if (eap_notification_packet.get_is_valid() == false)
	{
		EAP_TRACE_DEBUG(
			m_am_tools, 
			TRACE_FLAGS_DEFAULT, 
			(EAPL("packet buffer corrupted.\n")));
		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
	}

	if (m_is_client == true
		&& m_client_responds_retransmitted_packets == true)
	{
		// We do not wan't lower layers do re-transmissions behalf of us.
		// This means AKA does process every re-transmitted EAP-Request.
		eap_notification_packet.set_do_packet_retransmission(false);
	}

	EAP_ASSERT_ALWAYS(EAP_TYPE_AKA_LOCAL_PACKET_BUFFER_LENGTH >= (m_aka_header_offset+m_trailer_length));
	u32_t packet_buffer_free = EAP_TYPE_AKA_LOCAL_PACKET_BUFFER_LENGTH-m_trailer_length;
	u32_t packet_buffer_offset = 0u;

	if (m_aka_header_offset+m_MTU < packet_buffer_free)
	{
		packet_buffer_free = m_aka_header_offset+m_MTU;
	}

	aka_header_c aka_response(
		m_am_tools,
		eap_notification_packet.get_data_offset(
			m_aka_header_offset,
			(packet_buffer_free-m_aka_header_offset)),
		(packet_buffer_free-m_aka_header_offset));

	if (aka_response.get_is_valid() == false)
	{
		EAP_TRACE_DEBUG(
			m_am_tools, 
			TRACE_FLAGS_DEFAULT, 
			(EAPL("packet_send: packet buffer corrupted.\n")));
		return EAP_STATUS_RETURN(m_am_tools, eap_status_process_general_error);
	}

	aka_response.reset_header(
		static_cast<u16_t>(packet_buffer_free-m_aka_header_offset),
		m_use_eap_expanded_type);
	aka_response.set_length(
		static_cast<u16_t>(packet_buffer_free-m_aka_header_offset),
		m_use_eap_expanded_type);
	aka_response.set_code(eap_code_response);
	aka_response.set_identifier(static_cast<u8_t>(get_last_eap_identifier()));
	aka_response.set_type(
		eap_type_aka,
		m_use_eap_expanded_type);
	aka_response.set_subtype(aka_subtype_Notification);


	update_buffer_indexes(
		EAP_TYPE_AKA_LOCAL_PACKET_BUFFER_LENGTH,
		m_aka_header_offset+aka_response.get_header_length(),
		&packet_buffer_offset,
		&packet_buffer_free);

	// - - - - - - - - - - - - - - - - - - - - - - - -

	u32_t aka_data_free = packet_buffer_free;
	u32_t aka_data_offset = 0u;
	eap_status_e status = eap_status_process_general_error;

	// - - - - - - - - - - - - - - - - - - - - - - - -

	eap_variable_data_c * K_aut = 0;

	eap_variable_data_c orig_K_aut(m_am_tools);
	if (orig_K_aut.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 (get_aka_notification_code_P_bit(notification_code) == false)
	{
		u8_t *MAC_data = 0;
		u32_t MAC_data_length = 0u;
		eap_type_aka_MAC_attributes_c MAC_attributes;

		if (add_at_counter_attribute == true)
		{
			// If EAP-Request/AKA/Notification is used on fast a re-authentication
			// exchange, and if the P bit in AT_NOTIFICATION is set to zero, then
			// AT_COUNTER is used for replay protection. In this case, the
			// AT_ENCR_DATA and AT_IV attributes MUST be included, and the
			// encapsulated plaintext attributes MUST include the AT_COUNTER
			// attribute. The counter value included in AT_COUNTER MUST be the same
			// as in the EAP-Request/AKA/Re-authentication packet on the same fast
			// re-authentication exchange.

			// - - - - - - - - - - - - - - - - - - - - - - - -

			eap_variable_data_c orig_XKEY(m_am_tools);
			eap_variable_data_c orig_K_encr(m_am_tools);

			if (orig_XKEY.get_is_valid() == false
				|| orig_K_encr.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 reauth_counter = 0u;

			// In order to use re-authentication, the client and the server need to
			// store the following values: original XKEY, K_aut, K_encr, latest
			// counter value and the next re-authentication identity.
			status = m_am_type_aka->query_reauth_parameters(
				&orig_XKEY,
				&orig_K_aut,
				&orig_K_encr,
				&reauth_counter);
			if (status != eap_status_ok)
			{
				restore_saved_previous_state();
				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
				return EAP_STATUS_RETURN(m_am_tools, status);
			}

			K_aut = &orig_K_aut;

			// -   -   -   -   -   -   -   -   -   -   -   -

			crypto_aes_c aes(m_am_tools);

			if (aes.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_am_type_aka->generate_encryption_IV(
				get_IV()->get_payload_buffer(),
				aes.get_block_size());
			if (status != eap_status_ok)
			{
				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
				return EAP_STATUS_RETURN(m_am_tools, status);
			}


			status = add_variable_payload(
				&aka_response,
				EAP_TYPE_AKA_LOCAL_PACKET_BUFFER_LENGTH,
				aka_response.get_header_length(),
				&aka_data_offset,
				&aka_data_free,
				&packet_buffer_free,
				&packet_buffer_offset,
				get_IV()->get_payload_buffer(),
				aka_payload_AT_IV);
			if (status != eap_status_ok)
			{
				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
				return EAP_STATUS_RETURN(m_am_tools, status);
			}


			// Encrypted data.
			{
				aka_payload_AT_header_c gp_encrypted_data(
					m_am_tools,
					aka_response.get_data_offset(
						aka_data_offset, 
						aka_data_free),
					aka_data_free);
				if (aka_response.get_is_valid() == false)
				{
					EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
					return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
				}

				// Initialize the length of encrypted data to maximum length.
				// Later this will be set to correct length.
				gp_encrypted_data.reset_header(aka_payload_AT_header_c::get_max_payload_data_length());
				gp_encrypted_data.set_current_payload(aka_payload_AT_ENCR_DATA);

				update_payload_indexes(
					EAP_TYPE_AKA_LOCAL_PACKET_BUFFER_LENGTH,
					aka_response.get_header_length(),
					gp_encrypted_data.get_header_length(),
					&aka_data_offset,
					&aka_data_free,
					&packet_buffer_offset,
					&packet_buffer_free);

				u32_t encrypted_data_offset_begin = packet_buffer_offset;

				// -   -   -   -   -   -   -   -   -   -   -   -

				status = add_counter_payload(
					&aka_response,
					EAP_TYPE_AKA_LOCAL_PACKET_BUFFER_LENGTH,
					aka_response.get_header_length(),
					&aka_data_offset,
					&aka_data_free,
					&packet_buffer_free,
					&packet_buffer_offset,
					static_cast<u16_t>(reauth_counter));
				if (status != eap_status_ok)
				{
					EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
					return EAP_STATUS_RETURN(m_am_tools, status);
				}

				// -   -   -   -   -   -   -   -   -   -   -   -

				status = add_padding_payload(
					&aka_response,
					EAP_TYPE_AKA_LOCAL_PACKET_BUFFER_LENGTH,
					aka_response.get_header_length(),
					&aka_data_offset,
					&aka_data_free,
					&packet_buffer_free,
					&packet_buffer_offset,
					(packet_buffer_offset - encrypted_data_offset_begin) // Length of the plain text.
					);
				if (status != eap_status_ok)
				{
					EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
					return EAP_STATUS_RETURN(m_am_tools, status);
				}

				gp_encrypted_data.set_data_length(
					static_cast<u16_t>(packet_buffer_offset - encrypted_data_offset_begin));

				// -   -   -   -   -   -   -   -   -   -   -   -

				status = encrypt_DATA_payload(
					gp_encrypted_data.get_data(gp_encrypted_data.get_data_length()),
					gp_encrypted_data.get_data_length(),
					get_IV()->get_payload_buffer(),
					&orig_K_encr);
				if (status != eap_status_ok)
				{
					EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
					return EAP_STATUS_RETURN(m_am_tools, status);
				}

				EAP_AKA_TRACE_PAYLOAD("Payload added", &gp_encrypted_data);
			}

		}
		else
		{
			K_aut = get_K_aut();
		} // if (add_counter_payload == true)

		// -   -   -   -   -   -   -   -   -   -   -   -

		// Add AT_MAC attribute.
		status = add_mac_payload(
			&aka_response,
			EAP_TYPE_AKA_LOCAL_PACKET_BUFFER_LENGTH,
			aka_response.get_header_length(),
			&aka_data_offset,
			&aka_data_free,
			&packet_buffer_free,
			&packet_buffer_offset,
			&MAC_data,
			&MAC_data_length);
		if (status != eap_status_ok)
		{
			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
			return EAP_STATUS_RETURN(m_am_tools, status);
		}

		// -   -   -   -   -   -   -   -   -   -   -   -

		aka_response.set_data_length(
			aka_data_offset,
			m_use_eap_expanded_type);
		eap_notification_packet.set_data_length(packet_buffer_offset);

		EAP_ASSERT_ALWAYS(
			m_aka_header_offset+aka_response.get_header_length()+aka_response.get_data_length()
			== packet_buffer_offset);

		// -   -   -   -   -   -   -   -   -   -   -   -

		MAC_attributes.init(
			MAC_data,
			MAC_data_length,
			aka_response.get_header_buffer(aka_response.get_length()),
			aka_response.get_length());

		status = create_message_authentication_code(
			&MAC_attributes,
			aka_response.get_subtype(),
			aka_response.get_code(),
			K_aut);
		if (status != eap_status_ok)
		{
			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
			return EAP_STATUS_RETURN(m_am_tools, status);
		}
	}
	else
	{
		aka_response.set_data_length(
			aka_data_offset,
			m_use_eap_expanded_type);
		eap_notification_packet.set_data_length(packet_buffer_offset);

		EAP_ASSERT_ALWAYS(
			m_aka_header_offset+aka_response.get_header_length()+aka_response.get_data_length()
			== packet_buffer_offset);
	}

	// - - - - - - - - - - - - - - - - - - - - - - - -

	eap_notification_packet.set_data_length(packet_buffer_offset);

	EAP_TRACE_DATA_DEBUG(
		m_am_tools,
		TRACE_FLAGS_DEFAULT,
		(EAPL("send_aka_notification_response: aka_subtype_Notification packet"),
		aka_response.get_header_buffer(aka_response.get_length()),
		aka_response.get_length()));

	status = packet_send(
		get_send_network_id(),
		&eap_notification_packet,
		m_aka_header_offset,
		aka_response.get_length(),
		EAP_TYPE_AKA_LOCAL_PACKET_BUFFER_LENGTH
		);

	if (status == eap_status_ok)
	{
	}

	// - - - - - - - - - - - - - - - - - - - - - - - -

	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
	return EAP_STATUS_RETURN(m_am_tools, status);
}


//--------------------------------------------------

//
EAP_FUNC_EXPORT eap_status_e eap_type_aka_c::send_aka_client_error_response()
{
	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);

	EAP_TRACE_DEBUG(
		m_am_tools, 
		TRACE_FLAGS_AKA_ERROR, 
		(EAPL("ERROR: send: handler 0x%08x, AKA-type %10s, %s, state %2d=%s\n"),
		this,
		EAPL("send_aka_client_error_response"),
		(get_is_client()) ? EAPL("client") : EAPL("server"),
		get_state(),
		get_state_string()
		));

	eap_buf_chain_wr_c eap_client_error_packet(
		eap_write_buffer, 
		m_am_tools, 
		EAP_MAX_LOCAL_PACKET_BUFFER_LENGTH);

	if (eap_client_error_packet.get_is_valid() == false)
	{
		EAP_TRACE_DEBUG(
			m_am_tools, 
			TRACE_FLAGS_DEFAULT, 
			(EAPL("packet buffer corrupted.\n")));
		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
	}

	if (m_is_client == true
		&& m_client_responds_retransmitted_packets == true)
	{
		// We do not wan't lower layers do re-transmissions behalf of us.
		// This means AKA does process every re-transmitted EAP-Request.
		eap_client_error_packet.set_do_packet_retransmission(false);
	}

	EAP_ASSERT_ALWAYS(EAP_TYPE_AKA_LOCAL_PACKET_BUFFER_LENGTH >= (m_aka_header_offset+m_trailer_length));
	u32_t packet_buffer_free = EAP_TYPE_AKA_LOCAL_PACKET_BUFFER_LENGTH-m_trailer_length;
	u32_t packet_buffer_offset = 0u;

	if (m_aka_header_offset+m_MTU < packet_buffer_free)
	{
		packet_buffer_free = m_aka_header_offset+m_MTU;
	}

	aka_header_c aka_response(
		m_am_tools,
		eap_client_error_packet.get_data_offset(
			m_aka_header_offset,
			(packet_buffer_free-m_aka_header_offset)),
		(packet_buffer_free-m_aka_header_offset));

	if (aka_response.get_is_valid() == false)
	{
		EAP_TRACE_DEBUG(
			m_am_tools, 
			TRACE_FLAGS_DEFAULT, 
			(EAPL("packet_send: packet buffer corrupted.\n")));
		return EAP_STATUS_RETURN(m_am_tools, eap_status_process_general_error);
	}

	aka_response.reset_header(
		static_cast<u16_t>(packet_buffer_free-m_aka_header_offset),
		m_use_eap_expanded_type);
	aka_response.set_length(
		static_cast<u16_t>(packet_buffer_free-m_aka_header_offset),
		m_use_eap_expanded_type);
	aka_response.set_code(eap_code_response);
	aka_response.set_identifier(static_cast<u8_t>(get_last_eap_identifier()));
	aka_response.set_type(
		eap_type_aka,
		m_use_eap_expanded_type);
	aka_response.set_subtype(aka_subtype_Client_Error);


	update_buffer_indexes(
		EAP_TYPE_AKA_LOCAL_PACKET_BUFFER_LENGTH,
		m_aka_header_offset+aka_response.get_header_length(),
		&packet_buffer_offset,
		&packet_buffer_free);

	// - - - - - - - - - - - - - - - - - - - - - - - -

	u32_t aka_data_free = packet_buffer_free;
	u32_t aka_data_offset = 0u;

	eap_status_e status = add_client_error_payload(
		&aka_response,
		EAP_TYPE_AKA_LOCAL_PACKET_BUFFER_LENGTH,
		aka_response.get_header_length(),
		&aka_data_offset,
		&aka_data_free,
		&packet_buffer_free,
		&packet_buffer_offset,
		m_client_error_code);
	if (status != eap_status_ok)
	{
		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return EAP_STATUS_RETURN(m_am_tools, status);
	}

	// - - - - - - - - - - - - - - - - - - - - - - - -

	aka_response.set_data_length(
		aka_data_offset,
		m_use_eap_expanded_type);
	eap_client_error_packet.set_data_length(packet_buffer_offset);

	EAP_ASSERT_ALWAYS(
		m_aka_header_offset+aka_response.get_header_length()+aka_response.get_data_length()
		== packet_buffer_offset);

	status = eap_status_process_general_error;

	// - - - - - - - - - - - - - - - - - - - - - - - -

	eap_client_error_packet.set_data_length(packet_buffer_offset);

	EAP_TRACE_DATA_DEBUG(m_am_tools, TRACE_FLAGS_DEFAULT, (EAPL("    send: EAP-Response/Client-Error packet"),
		aka_response.get_header_buffer(aka_response.get_length()),
		aka_response.get_length()));

	status = packet_send(
		get_send_network_id(),
		&eap_client_error_packet,
		m_aka_header_offset,
		aka_response.get_length(),
		EAP_TYPE_AKA_LOCAL_PACKET_BUFFER_LENGTH
		);

	if (status == eap_status_ok)
	{
	}

	// - - - - - - - - - - - - - - - - - - - - - - - -

	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
	return EAP_STATUS_RETURN(m_am_tools, status);
}


//--------------------------------------------------

//
EAP_FUNC_EXPORT eap_status_e eap_type_aka_c::send_challenge_response_message(
		eap_variable_data_c * const K_aut)
{
	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);

	EAP_TRACE_DEBUG(
		m_am_tools, 
		TRACE_FLAGS_DEFAULT, 
		(EAPL("    send: handler 0x%08x, AKA-type %10s, %s, state %2d=%s\n"),
		this,
		EAPL("aka_subtype_Challenge"),
		(get_is_client()) ? EAPL("client") : EAPL("server"),
		get_state(),
		get_state_string()
		));


	eap_variable_data_c checkcode_digest(m_am_tools);
	if (checkcode_digest.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 = eap_status_process_general_error;

	status = checkcode_final(
		&checkcode_digest);
	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 aka_initial_reply(
		eap_write_buffer, 
		m_am_tools, 
		EAP_MAX_LOCAL_PACKET_BUFFER_LENGTH);

	if (aka_initial_reply.get_is_valid() == false)
	{
		EAP_TRACE_DEBUG(m_am_tools, TRACE_FLAGS_DEFAULT, (EAPL("packet buffer corrupted.\n")));
		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
	}

	if (m_is_client == true
		&& m_client_responds_retransmitted_packets == true)
	{
		// We do not wan't lower layers do re-transmissions behalf of us.
		// This means AKA does process every re-transmitted EAP-Request.
		aka_initial_reply.set_do_packet_retransmission(false);
	}

	EAP_ASSERT_ALWAYS(EAP_TYPE_AKA_LOCAL_PACKET_BUFFER_LENGTH >= (m_aka_header_offset+m_trailer_length));
	u32_t packet_buffer_free = EAP_TYPE_AKA_LOCAL_PACKET_BUFFER_LENGTH-m_trailer_length;
	u32_t packet_buffer_offset = 0u;

	if (m_aka_header_offset+m_MTU < packet_buffer_free)
	{
		packet_buffer_free = m_aka_header_offset+m_MTU;
	}

	aka_header_c aka_response(
		m_am_tools,
		aka_initial_reply.get_data_offset(
			m_aka_header_offset,
			(packet_buffer_free-m_aka_header_offset)),
		(packet_buffer_free-m_aka_header_offset));

	if (aka_response.get_is_valid() == false)
	{
		EAP_TRACE_DEBUG(m_am_tools, TRACE_FLAGS_DEFAULT, (EAPL("packet_send: packet buffer corrupted.\n")));
		return EAP_STATUS_RETURN(m_am_tools, eap_status_header_corrupted);
	}


	aka_response.reset_header(
		packet_buffer_free,
		m_use_eap_expanded_type);
	aka_response.set_length(
		static_cast<u16_t>(packet_buffer_free-m_aka_header_offset),
		m_use_eap_expanded_type);
	aka_response.set_code(eap_code_response);
	aka_response.set_identifier(get_last_eap_identifier());
	aka_response.set_type(
		eap_type_aka,
		m_use_eap_expanded_type);
	aka_response.set_subtype(aka_subtype_Challenge);

	update_buffer_indexes(
		EAP_TYPE_AKA_LOCAL_PACKET_BUFFER_LENGTH,
		m_aka_header_offset+aka_response.get_header_length(),
		&packet_buffer_offset,
		&packet_buffer_free);

	// - - - - - - - - - - - - - - - - - - - - - - - -

	EAP_TRACE_DEBUG(m_am_tools, TRACE_FLAGS_DEFAULT, (EAPL("eap_type_aka_c::send_challenge_response_message(0x%08x).\n"),
		this));

	// - - - - - - - - - - - - - - - - - - - - - - - -

	u32_t aka_data_free = packet_buffer_free;
	u32_t aka_data_offset = 0u;

	u8_t *MAC_data = 0;
	u32_t MAC_data_length = 0u;
	eap_type_aka_MAC_attributes_c MAC_attributes;

	// - - - - - - - - - - - - - - - - - - - - - - - -

	if (m_use_result_indication == true)
	{
		// We support use of protected success indications.
		status = add_simple_payload(
			&aka_response,
			EAP_TYPE_AKA_LOCAL_PACKET_BUFFER_LENGTH,
			aka_response.get_header_length(),
			&aka_data_offset,
			&aka_data_free,
			&packet_buffer_free,
			&packet_buffer_offset,
			aka_payload_AT_RESULT_IND);
		if (status != eap_status_ok)
		{
			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
			return EAP_STATUS_RETURN(m_am_tools, status);
		}
	}

	// -   -   -   -   -   -   -   -   -   -   -   -

	status = add_variable_payload(
		&aka_response,
		EAP_TYPE_AKA_LOCAL_PACKET_BUFFER_LENGTH,
		aka_response.get_header_length(),
		&aka_data_offset,
		&aka_data_free,
		&packet_buffer_free,
		&packet_buffer_offset,
		&checkcode_digest,
		aka_payload_AT_CHECKCODE);
	if (status != eap_status_ok)
	{
		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return EAP_STATUS_RETURN(m_am_tools, status);
	}

	// -   -   -   -   -   -   -   -   -   -   -   -

	status = add_RES_payload(
		&aka_response,
		EAP_TYPE_AKA_LOCAL_PACKET_BUFFER_LENGTH,
		aka_response.get_header_length(),
		&aka_data_offset,
		&aka_data_free,
		&packet_buffer_free,
		&packet_buffer_offset,
		get_authentication_vector()->get_RES(),
		aka_payload_AT_RES);
	if (status != eap_status_ok)
	{
		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return EAP_STATUS_RETURN(m_am_tools, status);
	}

	// -   -   -   -   -   -   -   -   -   -   -   -

	status = add_mac_payload(
		&aka_response,
		EAP_TYPE_AKA_LOCAL_PACKET_BUFFER_LENGTH,
		aka_response.get_header_length(),
		&aka_data_offset,
		&aka_data_free,
		&packet_buffer_free,
		&packet_buffer_offset,
		&MAC_data,
		&MAC_data_length);
	if (status != eap_status_ok)
	{
		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return EAP_STATUS_RETURN(m_am_tools, status);
	}

	// - - - - - - - - - - - - - - - - - - - - - - - -

	aka_response.set_data_length(
		aka_data_offset,
		m_use_eap_expanded_type);
	aka_initial_reply.set_data_length(packet_buffer_offset);

	EAP_ASSERT_ALWAYS(
		m_aka_header_offset+aka_response.get_header_length()+aka_response.get_data_length()
		== packet_buffer_offset);

	// -   -   -   -   -   -   -   -   -   -   -   -

	MAC_attributes.init(
		MAC_data,
		MAC_data_length,
		aka_response.get_header_buffer(aka_response.get_length()),
		aka_response.get_length());

	status = create_message_authentication_code(
		&MAC_attributes,
		aka_response.get_subtype(),
		aka_response.get_code(),
		K_aut);
	if (status != eap_status_ok)
	{
		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return EAP_STATUS_RETURN(m_am_tools, status);
	}

	// - - - - - - - - - - - - - - - - - - - - - - - -

	aka_response.set_data_length(
		aka_data_offset,
		m_use_eap_expanded_type);
	aka_initial_reply.set_data_length(packet_buffer_offset);

	EAP_ASSERT_ALWAYS(
		m_aka_header_offset+aka_response.get_header_length()+aka_response.get_data_length()
		== packet_buffer_offset);

	// - - - - - - - - - - - - - - - - - - - - - - - -

	status = packet_send(
		get_send_network_id(),
		&aka_initial_reply,
		m_aka_header_offset,
		aka_response.get_header_length()+aka_response.get_data_length(),
		EAP_TYPE_AKA_LOCAL_PACKET_BUFFER_LENGTH
		);

	// - - - - - - - - - - - - - - - - - - - - - - - -

	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
	return EAP_STATUS_RETURN(m_am_tools, status);
}

//--------------------------------------------------

//
EAP_FUNC_EXPORT eap_status_e eap_type_aka_c::send_reauthentication_response_message(
	const eap_variable_data_c * const orig_XKEY,
	const eap_variable_data_c * const orig_K_aut,
	const eap_variable_data_c * const orig_K_encr,
	const eap_variable_data_c * const reauth_username,
	const eap_variable_data_c * const reauth_nonce_s,
	const u16_t reauth_counter,
	const u8_t eap_identifier,
	const bool include_at_counter_too_small)
{
	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);

	EAP_TRACE_DEBUG(
		m_am_tools, 
		TRACE_FLAGS_DEFAULT, 
		(EAPL("    send: handler 0x%08x, AKA-subtype %10s, %s, state %2d=%s\n"),
		this,
		EAPL("aka_subtype_Re_authentication"),
		EAPL("client"),
		get_state(),
		get_state_string()
		));


	eap_variable_data_c checkcode_digest(m_am_tools);
	if (checkcode_digest.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 = eap_status_process_general_error;

	status = checkcode_final(
		&checkcode_digest);
	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 request_packet(
		eap_write_buffer, 
		m_am_tools, 
		EAP_MAX_LOCAL_PACKET_BUFFER_LENGTH);

	if (request_packet.get_is_valid() == false)
	{
		EAP_TRACE_DEBUG(m_am_tools, TRACE_FLAGS_DEFAULT, (EAPL("packet buffer corrupted.\n")));
		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
	}

	if (m_is_client == true
		&& m_client_responds_retransmitted_packets == true)
	{
		// We do not wan't lower layers do re-transmissions behalf of us.
		// This means AKA does process every re-transmitted EAP-Request.
		request_packet.set_do_packet_retransmission(false);
	}

	EAP_ASSERT_ALWAYS(EAP_TYPE_AKA_LOCAL_PACKET_BUFFER_LENGTH >= (m_aka_header_offset+m_trailer_length));
	u32_t packet_buffer_free = EAP_TYPE_AKA_LOCAL_PACKET_BUFFER_LENGTH-m_trailer_length;
	u32_t packet_buffer_offset = 0u;

	if (m_aka_header_offset+m_MTU < packet_buffer_free)
	{
		packet_buffer_free = m_aka_header_offset+m_MTU;
	}

	aka_header_c aka_response(
		m_am_tools,
		request_packet.get_data_offset(
			m_aka_header_offset,
			(packet_buffer_free-m_aka_header_offset)),
		(packet_buffer_free-m_aka_header_offset));

	if (aka_response.get_is_valid() == false)
	{
		EAP_TRACE_DEBUG(
			m_am_tools, 
			TRACE_FLAGS_DEFAULT, 
			(EAPL("send_reauthentication_response_message: packet buffer corrupted.\n")));
		return EAP_STATUS_RETURN(m_am_tools, eap_status_header_corrupted);
	}

	aka_response.reset_header(
		packet_buffer_free-m_aka_header_offset,
		m_use_eap_expanded_type);
	aka_response.set_length(
		static_cast<u16_t>(packet_buffer_free-m_aka_header_offset),
		m_use_eap_expanded_type);
	aka_response.set_code(eap_code_response);
	aka_response.set_identifier(eap_identifier);
	aka_response.set_type(
		eap_type_aka,
		m_use_eap_expanded_type);
	aka_response.set_subtype(aka_subtype_Re_authentication);

	update_buffer_indexes(
		EAP_TYPE_AKA_LOCAL_PACKET_BUFFER_LENGTH,
		m_aka_header_offset+aka_response.get_header_length(),
		&packet_buffer_offset,
		&packet_buffer_free);

	// - - - - - - - - - - - - - - - - - - - - - - - -

	EAP_TRACE_DEBUG(
		m_am_tools, 
		TRACE_FLAGS_DEFAULT, 
		(EAPL("eap_type_aka_c::send_reauthentication_response_message(0x%08x).\n"),
		this));

	// - - - - - - - - - - - - - - - - - - - - - - - -

	u32_t aka_data_free = packet_buffer_free;
	u32_t aka_data_offset = 0u;

	eap_variable_data_c master_session_key(m_am_tools);
	if (master_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 = generate_reauth_shared_secred_keys(
		EAP_TYPE_AKA_KEYMAT_SIZE,
		orig_XKEY,
		reauth_counter,
		reauth_username,
		reauth_nonce_s,
		&master_session_key);
	if (status != eap_status_ok)
	{
		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return EAP_STATUS_RETURN(m_am_tools, status);
	}

	// -   -   -   -   -   -   -   -   -   -   -   -

	if (m_use_result_indication == true)
	{
		// We support use of protected success indications.
		status = add_simple_payload(
			&aka_response,
			EAP_TYPE_AKA_LOCAL_PACKET_BUFFER_LENGTH,
			aka_response.get_header_length(),
			&aka_data_offset,
			&aka_data_free,
			&packet_buffer_free,
			&packet_buffer_offset,
			aka_payload_AT_RESULT_IND);
		if (status != eap_status_ok)
		{
			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
			return EAP_STATUS_RETURN(m_am_tools, status);
		}
	}

	// -   -   -   -   -   -   -   -   -   -   -   -

	status = add_variable_payload(
		&aka_response,
		EAP_TYPE_AKA_LOCAL_PACKET_BUFFER_LENGTH,
		aka_response.get_header_length(),
		&aka_data_offset,
		&aka_data_free,
		&packet_buffer_free,
		&packet_buffer_offset,
		&checkcode_digest,
		aka_payload_AT_CHECKCODE);
	if (status != eap_status_ok)
	{
		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return EAP_STATUS_RETURN(m_am_tools, status);
	}

	// -   -   -   -   -   -   -   -   -   -   -   -

	crypto_aes_c aes(m_am_tools);

	if (aes.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_am_type_aka->generate_encryption_IV(
		get_IV()->get_payload_buffer(),
		aes.get_block_size());
	if (status != eap_status_ok)
	{
		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return EAP_STATUS_RETURN(m_am_tools, status);
	}


	status = add_variable_payload(
		&aka_response,
		EAP_TYPE_AKA_LOCAL_PACKET_BUFFER_LENGTH,
		aka_response.get_header_length(),
		&aka_data_offset,
		&aka_data_free,
		&packet_buffer_free,
		&packet_buffer_offset,
		get_IV()->get_payload_buffer(),
		aka_payload_AT_IV);
	if (status != eap_status_ok)
	{
		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return EAP_STATUS_RETURN(m_am_tools, status);
	}


	u8_t *MAC_data = 0;
	u32_t MAC_data_length = 0u;
	eap_type_aka_MAC_attributes_c MAC_attributes;

	// Encrypted data.
	{
		aka_payload_AT_header_c gp_encrypted_data(
			m_am_tools,
			aka_response.get_data_offset(
				aka_data_offset, 
				aka_data_free),
			aka_data_free);
		if (gp_encrypted_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);
		}

		// Initialize the length of encrypted data to maximum length.
		// Later this will be set to correct length.
		gp_encrypted_data.reset_header(aka_payload_AT_header_c::get_max_payload_data_length());
		gp_encrypted_data.set_current_payload(aka_payload_AT_ENCR_DATA);

		update_payload_indexes(
			EAP_TYPE_AKA_LOCAL_PACKET_BUFFER_LENGTH,
			aka_response.get_header_length(),
			gp_encrypted_data.get_header_length(),
			&aka_data_offset,
			&aka_data_free,
			&packet_buffer_offset,
			&packet_buffer_free);

		u32_t encrypted_data_offset_begin = packet_buffer_offset;


		// -   -   -   -   -   -   -   -   -   -   -   -

		if (include_at_counter_too_small == true)
		{
			status = add_simple_payload(
				&aka_response,
				EAP_TYPE_AKA_LOCAL_PACKET_BUFFER_LENGTH,
				aka_response.get_header_length(),
				&aka_data_offset,
				&aka_data_free,
				&packet_buffer_free,
				&packet_buffer_offset,
				aka_payload_AT_COUNTER_TOO_SMALL);
			if (status != eap_status_ok)
			{
				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
				return EAP_STATUS_RETURN(m_am_tools, status);
			}
		}

		// -   -   -   -   -   -   -   -   -   -   -   -

		status = add_counter_payload(
			&aka_response,
			EAP_TYPE_AKA_LOCAL_PACKET_BUFFER_LENGTH,
			aka_response.get_header_length(),
			&aka_data_offset,
			&aka_data_free,
			&packet_buffer_free,
			&packet_buffer_offset,
			reauth_counter);
		if (status != eap_status_ok)
		{
			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
			return EAP_STATUS_RETURN(m_am_tools, status);
		}

		// -   -   -   -   -   -   -   -   -   -   -   -

		status = add_padding_payload(
			&aka_response,
			EAP_TYPE_AKA_LOCAL_PACKET_BUFFER_LENGTH,
			aka_response.get_header_length(),
			&aka_data_offset,
			&aka_data_free,
			&packet_buffer_free,
			&packet_buffer_offset,
			(packet_buffer_offset - encrypted_data_offset_begin) // Length of the plain text.
			);
		if (status != eap_status_ok)
		{
			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
			return EAP_STATUS_RETURN(m_am_tools, status);
		}

		gp_encrypted_data.set_data_length(
			static_cast<u16_t>(packet_buffer_offset - encrypted_data_offset_begin));

		// -   -   -   -   -   -   -   -   -   -   -   -

		status = encrypt_DATA_payload(
			gp_encrypted_data.get_data(gp_encrypted_data.get_data_length()),
			gp_encrypted_data.get_data_length(),
			get_IV()->get_payload_buffer(),
			orig_K_encr);
		if (status != eap_status_ok)
		{
			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
			return EAP_STATUS_RETURN(m_am_tools, status);
		}

		EAP_AKA_TRACE_PAYLOAD("Payload added", &gp_encrypted_data);
	}



	// -   -   -   -   -   -   -   -   -   -   -   -

	status = add_mac_payload(
		&aka_response,
		EAP_TYPE_AKA_LOCAL_PACKET_BUFFER_LENGTH,
		aka_response.get_header_length(),
		&aka_data_offset,
		&aka_data_free,
		&packet_buffer_free,
		&packet_buffer_offset,
		&MAC_data,
		&MAC_data_length);
	if (status != eap_status_ok)
	{
		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return EAP_STATUS_RETURN(m_am_tools, status);
	}

	// -   -   -   -   -   -   -   -   -   -   -   -

	aka_response.set_data_length(
		aka_data_offset,
		m_use_eap_expanded_type);
	request_packet.set_data_length(packet_buffer_offset);

	EAP_ASSERT_ALWAYS(
		m_aka_header_offset+aka_response.get_header_length()+aka_response.get_data_length()
		== packet_buffer_offset);

	// -   -   -   -   -   -   -   -   -   -   -   -

	MAC_attributes.init(
		MAC_data,
		MAC_data_length,
		aka_response.get_header_buffer(aka_response.get_length()),
		aka_response.get_length());

	status = create_message_authentication_code(
		&MAC_attributes,
		aka_response.get_subtype(),
		aka_response.get_code(),
		orig_K_aut);
	if (status != eap_status_ok)
	{
		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return EAP_STATUS_RETURN(m_am_tools, status);
	}

	// - - - - - - - - - - - - - - - - - - - - - - - -

	aka_response.set_data_length(
		aka_data_offset,
		m_use_eap_expanded_type);
	request_packet.set_data_length(packet_buffer_offset);

	EAP_ASSERT_ALWAYS(
		m_aka_header_offset+aka_response.get_header_length()+aka_response.get_data_length()
		== packet_buffer_offset);

	// - - - - - - - - - - - - - - - - - - - - - - - -

	EAP_TRACE_DEBUG(
		m_am_tools, 
		TRACE_FLAGS_DEFAULT, 
		(EAPL("eap_type_aka_c::send_reauthentication_request_message()\n")));

	status = packet_send(
		get_send_network_id(),
		&request_packet,
		m_aka_header_offset,
		aka_response.get_header_length()+aka_response.get_data_length(),
		EAP_TYPE_AKA_LOCAL_PACKET_BUFFER_LENGTH
		);

	if (status == eap_status_ok)
	{
		if (include_at_counter_too_small == false)
		{
			status = get_master_session_key()->set_copy_of_buffer(&master_session_key);
			if (status != eap_status_ok)
			{
				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
				return EAP_STATUS_RETURN(m_am_tools, status);
			}
		}
	}
	else if (include_at_counter_too_small == true)
	{
		// The full authentication must follow.
		m_authentication_type = AKA_AUTHENTICATION_TYPE_FULL_AUTH;
	}

	// - - - - - - - - - - - - - - - - - - - - - - - -

	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
	return EAP_STATUS_RETURN(m_am_tools, status);
}

//--------------------------------------------------

//
EAP_FUNC_EXPORT eap_status_e eap_type_aka_c::decrypt_DATA_payload(
	aka_payloads_c * const p_aka_payloads,
	const eap_variable_data_c * const encryption_key
	)
{
	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);

	eap_status_e status = eap_status_process_general_error;

	crypto_aes_c aes(m_am_tools);
	crypto_cbc_c cbc_aes(m_am_tools, &aes, false);

	if (cbc_aes.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 aes_key_length = aes.get_key_length();
	if (encryption_key->get_data_length() < aes_key_length)
	{
		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return EAP_STATUS_RETURN(m_am_tools, eap_status_key_error);
	}

	if (p_aka_payloads->get_ENCR_DATA()->get_data_length() == 0
		|| (p_aka_payloads->get_ENCR_DATA()->get_data_length() % aes_key_length) != 0)
	{
		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return EAP_STATUS_RETURN(m_am_tools, eap_status_key_error);
	}

	if (p_aka_payloads->get_IV()->get_payload_included() == false
		|| p_aka_payloads->get_IV()->get_data_length() == 0u
		|| encryption_key->get_is_valid_data() == false
		|| encryption_key->get_data_length() == 0u)
	{
		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return EAP_STATUS_RETURN(m_am_tools, eap_status_key_error);
	}

	if (p_aka_payloads->get_ENCR_DATA()->get_payload_included() == false
		|| p_aka_payloads->get_ENCR_DATA()->get_data_length() == 0u)
	{
		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_data_payload);
	}


	status = cbc_aes.set_decryption_key(
		p_aka_payloads->get_IV()->get_data(p_aka_payloads->get_IV()->get_data_length()),
		p_aka_payloads->get_IV()->get_data_length(),
		encryption_key->get_data(encryption_key->get_data_length()),
		aes_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 = cbc_aes.decrypt_data(
		p_aka_payloads->get_ENCR_DATA()->get_data(p_aka_payloads->get_ENCR_DATA()->get_data_length()),
		p_aka_payloads->get_ENCR_DATA()->get_data_length());

	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
	return EAP_STATUS_RETURN(m_am_tools, status);
}

//--------------------------------------------------

EAP_FUNC_EXPORT eap_status_e eap_type_aka_c::handle_DATA_payload(
	const aka_subtype_e subtype,
	aka_payloads_c * const p_aka_payloads
	)
{
	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);

	eap_status_e status = eap_status_process_general_error;

	if (p_aka_payloads->get_ENCR_DATA() == 0
		|| p_aka_payloads->get_ENCR_DATA()->get_data_length() == 0u)
	{
		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
	}

	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

	u32_t state_payload_buffer_length = p_aka_payloads->get_ENCR_DATA()->get_data_length();
	const aka_payload_AT_header_c gp_data_payload(
		m_am_tools,
		p_aka_payloads->get_ENCR_DATA()->get_data(state_payload_buffer_length),
		state_payload_buffer_length);

	if (gp_data_payload.get_is_valid() == false)
	{
		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return EAP_STATUS_RETURN(m_am_tools, eap_status_header_corrupted);
	}

	status = parse_aka_payload(
		&gp_data_payload,
		&state_payload_buffer_length,
		p_aka_payloads,
		subtype);
	if (status != eap_status_ok)
	{
		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return EAP_STATUS_RETURN(m_am_tools, status);
	}

	if (p_aka_payloads->get_NEXT_PSEUDONYM()->get_payload_included() == true)
	{
		if (p_aka_payloads->get_padding_payload()->get_payload_included() == true
			&& p_aka_payloads->get_padding_payload()->get_data_length() > 0u)
		{
			crypto_aes_c aes(m_am_tools);
			crypto_cbc_c cbc_aes(m_am_tools, &aes, false);

			if (cbc_aes.get_is_valid() == false)
			{
				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
				return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
			}

			// Check the padding bytes.
			// Must be filled with zero (0x00) bytes.
			status = cbc_aes.check_padding_bytes(
				p_aka_payloads->get_padding_payload()->get_data(p_aka_payloads->get_padding_payload()->get_data_length()),
				p_aka_payloads->get_padding_payload()->get_data_length(),
				0ul);
			if (status != eap_status_ok)
			{
				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
				return EAP_STATUS_RETURN(m_am_tools, status);
			}
		}

		// AKA AM could store pseudonym to favourite place.
		status = m_am_type_aka->store_pseudonym_id(
			get_send_network_id(),
			p_aka_payloads->get_NEXT_PSEUDONYM()->get_payload_buffer());
		if (status != eap_status_ok)
		{
			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
			return EAP_STATUS_RETURN(m_am_tools, status);
		}
	}

	if (p_aka_payloads->get_NEXT_REAUTH_ID()->get_payload_included() == true)
	{
		if (p_aka_payloads->get_padding_payload()->get_payload_included() == true
			&& p_aka_payloads->get_padding_payload()->get_data_length() > 0u)
		{
			crypto_aes_c aes(m_am_tools);
			crypto_cbc_c cbc_aes(m_am_tools, &aes, false);

			if (cbc_aes.get_is_valid() == false)
			{
				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
				return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
			}

			// Check the padding bytes.
			// Must be filled with zero (0x00) bytes.
			status = cbc_aes.check_padding_bytes(
				p_aka_payloads->get_padding_payload()->get_data(p_aka_payloads->get_padding_payload()->get_data_length()),
				p_aka_payloads->get_padding_payload()->get_data_length(),
				0ul);
			if (status != eap_status_ok)
			{
				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
				return EAP_STATUS_RETURN(m_am_tools, status);
			}
		}

		// AKA AM could store pseudonym to favourite place.
		status = m_am_type_aka->store_reauthentication_id(
			get_send_network_id(),
			p_aka_payloads->get_NEXT_REAUTH_ID()->get_payload_buffer());
		if (status != eap_status_ok)
		{
			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
			return EAP_STATUS_RETURN(m_am_tools, status);
		}
	}
	else
	{
		// AKA AM should remove pseudonym from favourite place.

		EAP_TRACE_DEBUG(
			m_am_tools, 
			TRACE_FLAGS_DEFAULT, 
			(EAPL("eap_type_aka_c::handle_DATA_payload(0x%08x): Resets re-authentication identity from database.\n"),
			 this));

		status = m_am_type_aka->store_reauthentication_id(
			get_send_network_id(),
			0);
		if (status != eap_status_ok)
		{
			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
			return EAP_STATUS_RETURN(m_am_tools, status);
		}

		get_reauthentication_identity()->reset();
	}

	status = eap_status_ok;

	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
	return EAP_STATUS_RETURN(m_am_tools, status);
}


//--------------------------------------------------

//
EAP_FUNC_EXPORT eap_status_e eap_type_aka_c::handle_aka_identity_request_message(
	const eap_am_network_id_c * const receive_network_id,
	aka_header_c * const received_aka,
	const u32_t /* aka_packet_length */,
	aka_payloads_c * const p_aka_payloads)
{
	eap_status_e status = eap_status_process_general_error;

	if (get_state() == eap_type_aka_state_waiting_for_aka_identity_request
		|| get_state() == eap_type_aka_state_pseydonym_waiting_for_aka_identity_request
		|| get_state() == eap_type_aka_state_imsi_waiting_for_aka_identity_request
		|| get_state() == eap_type_aka_state_waiting_for_challenge_request
		|| get_state() == eap_type_aka_state_waiting_for_reauth_request)
	{
		// This could be first or retransmission request.

		// Checks the payloads existence.
		if (p_aka_payloads->check_payloads(
				aka_payloads_c::eap_aka_payload_status_must_not_be, // nonce_s
				aka_payloads_c::eap_aka_payload_status_must_not_be, // MAC
				aka_payloads_c::eap_aka_payload_status_must_not_be, // ENCR_DATA
				aka_payloads_c::eap_aka_payload_status_must_not_be, // IDENTITY
				aka_payloads_c::eap_aka_payload_status_must_not_be, // padding
				aka_payloads_c::eap_aka_payload_status_must_not_be, // n_RANDs
				aka_payloads_c::eap_aka_payload_status_must_not_be, // AUTN
				aka_payloads_c::eap_aka_payload_status_must_not_be, // AUTS
				aka_payloads_c::eap_aka_payload_status_must_not_be, // RES
				aka_payloads_c::eap_aka_payload_status_optional,    // PERMANENT_ID_REQ
				aka_payloads_c::eap_aka_payload_status_optional,    // FULLAUTH_ID_REQ
				aka_payloads_c::eap_aka_payload_status_optional,    // ANY_ID_REQ
				aka_payloads_c::eap_aka_payload_status_must_not_be, // IV
				aka_payloads_c::eap_aka_payload_status_must_not_be, // NEXT_PSEUDONYM
				aka_payloads_c::eap_aka_payload_status_must_not_be, // NEXT_REAUTH_ID
				aka_payloads_c::eap_aka_payload_status_must_not_be, // NOTIFICATION
				aka_payloads_c::eap_aka_payload_status_must_not_be, // COUNTER
				aka_payloads_c::eap_aka_payload_status_must_not_be, // COUNTER_TOO_SMALL
				aka_payloads_c::eap_aka_payload_status_must_not_be, // CLIENT_ERROR_CODE
				aka_payloads_c::eap_aka_payload_status_must_not_be, // RESULT_IND
				aka_payloads_c::eap_aka_payload_status_must_not_be  // CHECKCODE
				) == true
			)
		{
			// NOTE, this message is unauthenticated. Anybody could sent this message.

			aka_payload_AT_type_e include_identity_to_aka_identity_response = aka_payload_NONE;


			if ( get_state() == eap_type_aka_state_imsi_waiting_for_aka_identity_request
					|| get_state() == eap_type_aka_state_waiting_for_challenge_request
					|| get_state() == eap_type_aka_state_waiting_for_aka_identity_request
					|| get_state() == eap_type_aka_state_pseydonym_waiting_for_aka_identity_request
					|| get_state() == eap_type_aka_state_waiting_for_reauth_request)
			{
				if ((p_aka_payloads->get_PERMANENT_ID_REQ()->get_payload_included() == true
						&& p_aka_payloads->get_FULLAUTH_ID_REQ()->get_payload_included() == true)
					|| (p_aka_payloads->get_PERMANENT_ID_REQ()->get_payload_included() == true
						&& p_aka_payloads->get_ANY_ID_REQ()->get_payload_included() == true)
					|| (p_aka_payloads->get_FULLAUTH_ID_REQ()->get_payload_included() == true
						&& p_aka_payloads->get_ANY_ID_REQ()->get_payload_included() == true))
				{
					// Only one of these is allowed.
					EAP_TRACE_DEBUG(
						m_am_tools, 
						TRACE_FLAGS_AKA_ERROR, 
						(EAPL("ERROR: eap_type_aka_c::handle_aka_identity_request_message(0x%08x), illegal payloads.\n"),
						this));
					EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
					return EAP_STATUS_RETURN(m_am_tools, eap_status_header_corrupted);
				}
				else if (p_aka_payloads->get_PERMANENT_ID_REQ()->get_payload_included() == true)
				{
					EAP_TRACE_DEBUG(
						m_am_tools, 
						TRACE_FLAGS_DEFAULT, 
						(EAPL("eap_type_aka_c::handle_aka_identity_request_message ")
						 EAPL("aka_payload_AT_PERMANENT_ID_REQ\n")));
					// Note, this is optional, included only when pseudonym decode failed in server.
					include_identity_to_aka_identity_response = aka_payload_AT_PERMANENT_ID_REQ;
				}
				else if (p_aka_payloads->get_FULLAUTH_ID_REQ()->get_payload_included() == true)
				{
					EAP_TRACE_DEBUG(
						m_am_tools, 
						TRACE_FLAGS_DEFAULT, 
						(EAPL("eap_type_aka_c::handle_aka_identity_request_message ")
						 EAPL("aka_payload_AT_FULLAUTH_ID_REQ\n")));
					// Note, this is optional, included only when reauthentication identity decode failed in server.
					include_identity_to_aka_identity_response = aka_payload_AT_FULLAUTH_ID_REQ;
				}
				else if (p_aka_payloads->get_ANY_ID_REQ()->get_payload_included() == true)
				{
					EAP_TRACE_DEBUG(
						m_am_tools, 
						TRACE_FLAGS_DEFAULT, 
						(EAPL("eap_type_aka_c::handle_aka_identity_request_message ")
						 EAPL("aka_payload_AT_ANY_ID_REQ\n")));
					// Note, this is optional, included only when server have no identity of client.
					include_identity_to_aka_identity_response = aka_payload_AT_ANY_ID_REQ;
				}
			}

			set_include_identity_to_aka_identity_response(include_identity_to_aka_identity_response);

			if (include_identity_to_aka_identity_response == aka_payload_NONE
				&& (get_identity() == 0
				 || get_identity()->get_is_valid_data() == false))
			{
				// We must query the previous sent EAP-Identity from EAP_Core.
				// The EAP_Core saves the sent EAP-Identity when the EAP-Identity is
				// sent to the network.
				// Previous EAP-type was NOT this instance. EAP-Identity was queried from other instance.
				status = get_type_partner()->get_saved_eap_identity(get_identity());
				if (status != eap_status_ok)
				{
					// We do not have the identity server accepted anymore.
					EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
					return EAP_STATUS_RETURN(m_am_tools, status);
				}

				status = get_NAI()->set_copy_of_buffer(get_identity());
				if (status != eap_status_ok)
				{
					EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
					return EAP_STATUS_RETURN(m_am_tools, status);
				}
			}


			status = checkcode_save_message_client(
				received_aka->get_header_buffer(
					received_aka->get_header_length()
					+received_aka->get_data_length()),
				received_aka->get_header_length()
				+received_aka->get_data_length(),
				p_aka_payloads);
			if (status != eap_status_ok)
			{
				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
				return EAP_STATUS_RETURN(m_am_tools, status);
			}

			if (include_identity_to_aka_identity_response == aka_payload_AT_ANY_ID_REQ
				&& (get_reauthentication_identity()->get_is_valid_data() == false
					&& get_pseudonym()->get_is_valid_data() == false
					&& get_IMSI()->get_is_valid_data() == false
					&& m_automatic_realm_read == false))
			{
				eap_variable_data_c IMSI(m_am_tools);
				eap_variable_data_c pseudonym(m_am_tools);
				eap_variable_data_c reauthentication_identity(m_am_tools);

				if (IMSI.get_is_valid() == false
					|| pseudonym.get_is_valid() == false
					|| reauthentication_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);
				}

				EAP_TRACE_DEBUG(
					m_am_tools, 
					TRACE_FLAGS_DEFAULT, 
					(EAPL("eap_type_aka_c::handle_aka_identity_request_message ")
					 EAPL("aka_payload_AT_ANY_ID_REQ\n")));

				status = query_AKA_IMSI_or_pseudonym_or_reauthentication_id(
					&IMSI,
					&pseudonym,
					&reauthentication_identity,
					&m_automatic_realm,
					&m_length_of_mnc,
					false,
					include_identity_to_aka_identity_response,
					eap_type_aka_complete_aka_identity_request,
					received_aka->get_identifier());

				if (status == eap_status_pending_request)
				{
					// This is pending query, that will be completed by complete_AKA_IMSI_or_pseudonym_or_reauthentication_id_query() call.
					EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
					return EAP_STATUS_RETURN(m_am_tools, status);
				}
				else if (status == eap_status_completed_request)
				{
					// This is already completed by complete_AKA_IMSI_or_pseudonym_or_reauthentication_id_query() call.
					EAP_TRACE_DEBUG(
						m_am_tools, 
						TRACE_FLAGS_DEFAULT, 
						(EAPL("eap_type_aka_c::handle_aka_identity_request_message(): ")
						 EAPL("completed returns eap_type_aka_state_variable_e %d=%s.\n"),
						 get_state(),
						 get_state_string()));

					status = eap_status_ok;
					
					EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
					return EAP_STATUS_RETURN(m_am_tools, status);
				}
				else if (status != eap_status_ok)
				{
					// This is the error case.

					EAP_TRACE_DEBUG(
						m_am_tools, 
						TRACE_FLAGS_DEFAULT, 
						(EAPL("eap_type_aka_c::handle_aka_identity_request_message(): ")
						 EAPL("error returns eap_type_aka_state_variable_e %d=%s.\n"),
						 get_state(),
						 get_state_string()));
					
					EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
					return EAP_STATUS_RETURN(m_am_tools, status);
				}
				
				if (reauthentication_identity.get_is_valid_data() == true)
				{
					EAP_TRACE_DEBUG(
						m_am_tools, 
						TRACE_FLAGS_DEFAULT, 
						(EAPL("eap_type_aka_c::handle_aka_identity_request_message ")
						 EAPL("reauthentication_identity\n")));

					status = get_reauthentication_identity()->set_copy_of_buffer(&reauthentication_identity);
					if (status != eap_status_ok)
					{
						EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
						return EAP_STATUS_RETURN(m_am_tools, status);
					}

					status = store_identity(get_reauthentication_identity(), 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 (pseudonym.get_is_valid_data() == true)
				{
					EAP_TRACE_DEBUG(
						m_am_tools, 
						TRACE_FLAGS_DEFAULT, 
						(EAPL("eap_type_aka_c::handle_aka_identity_request_message pseudonym\n")));

					get_reauthentication_identity()->reset();
					status = get_pseudonym()->set_copy_of_buffer(&pseudonym);
					if (status != eap_status_ok)
					{
						EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
						return EAP_STATUS_RETURN(m_am_tools, status);
					}

					status = store_identity(get_pseudonym(), 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 (IMSI.get_is_valid_data() == true)
				{
					EAP_TRACE_DEBUG(
						m_am_tools, 
						TRACE_FLAGS_DEFAULT, 
						(EAPL("eap_type_aka_c::handle_aka_identity_request_message IMSI\n")));

					get_reauthentication_identity()->reset();
					get_pseudonym()->reset();
					status = get_IMSI()->set_copy_of_buffer(&IMSI);
					if (status != eap_status_ok)
					{
						EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
						return EAP_STATUS_RETURN(m_am_tools, status);
					}

					status = store_identity(get_IMSI(), true);
					if (status != eap_status_ok)
					{
						EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
						return EAP_STATUS_RETURN(m_am_tools, status);
					}
				}
			}
			else if (include_identity_to_aka_identity_response == aka_payload_AT_FULLAUTH_ID_REQ
					&& (get_pseudonym()->get_is_valid_data() == false
						&& get_IMSI()->get_is_valid_data() == false
						&& m_automatic_realm_read == false))
			{
				eap_variable_data_c IMSI(m_am_tools);
				eap_variable_data_c pseudonym(m_am_tools);
				eap_variable_data_c reauthentication_identity(m_am_tools);

				if (IMSI.get_is_valid() == false
					|| pseudonym.get_is_valid() == false
					|| reauthentication_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);
				}

				EAP_TRACE_DEBUG(
					m_am_tools, 
					TRACE_FLAGS_DEFAULT, 
					(EAPL("eap_type_aka_c::handle_aka_identity_request_message ")
					 EAPL("aka_payload_AT_FULLAUTH_ID_REQ\n")));

				status = query_AKA_IMSI_or_pseudonym_or_reauthentication_id(
					&IMSI,
					&pseudonym,
					&reauthentication_identity,
					&m_automatic_realm,
					&m_length_of_mnc,
					false,
					include_identity_to_aka_identity_response,
					eap_type_aka_complete_aka_identity_request,
					received_aka->get_identifier());

				if (status == eap_status_pending_request)
				{
					// This is pending query, that will be completed by complete_AKA_IMSI_or_pseudonym_or_reauthentication_id_query() call.
					EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
					return EAP_STATUS_RETURN(m_am_tools, status);
				}
				else if (status == eap_status_completed_request)
				{
					// This is already completed by complete_AKA_IMSI_or_pseudonym_or_reauthentication_id_query() call.
					EAP_TRACE_DEBUG(
						m_am_tools, 
						TRACE_FLAGS_DEFAULT, 
						(EAPL("eap_type_aka_c::handle_aka_identity_request_message(): ")
						 EAPL("completed returns eap_type_aka_state_variable_e %d=%s.\n"),
						 get_state(),
						 get_state_string()));

					status = eap_status_ok;

					EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
					return EAP_STATUS_RETURN(m_am_tools, status);
				}
				else if (status != eap_status_ok)
				{
					// This is the error case.

					EAP_TRACE_DEBUG(
						m_am_tools, 
						TRACE_FLAGS_DEFAULT, 
						(EAPL("eap_type_aka_c::handle_aka_identity_request_message(): ")
						 EAPL("error returns eap_type_aka_state_variable_e %d=%s.\n"),
						 get_state(),
						 get_state_string()));
					
					EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
					return EAP_STATUS_RETURN(m_am_tools, status);
				}

				if (pseudonym.get_is_valid_data() == true)
				{
					EAP_TRACE_DEBUG(
						m_am_tools, 
						TRACE_FLAGS_DEFAULT, 
						(EAPL("eap_type_aka_c::handle_aka_identity_request_message pseudonym\n")));

					get_reauthentication_identity()->reset();
					status = get_pseudonym()->set_copy_of_buffer(&pseudonym);
					if (status != eap_status_ok)
					{
						EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
						return EAP_STATUS_RETURN(m_am_tools, status);
					}

					status = store_identity(get_pseudonym(), 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 (IMSI.get_is_valid_data() == true)
				{
					EAP_TRACE_DEBUG(
						m_am_tools, 
						TRACE_FLAGS_DEFAULT, 
						(EAPL("eap_type_aka_c::handle_aka_identity_request_message IMSI\n")));

					get_reauthentication_identity()->reset();
					get_pseudonym()->reset();
					status = get_IMSI()->set_copy_of_buffer(&IMSI);
					if (status != eap_status_ok)
					{
						EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
						return EAP_STATUS_RETURN(m_am_tools, status);
					}

					status = store_identity(get_IMSI(), true);
					if (status != eap_status_ok)
					{
						EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
						return EAP_STATUS_RETURN(m_am_tools, status);
					}
				}
			}
			else if (include_identity_to_aka_identity_response == aka_payload_AT_PERMANENT_ID_REQ
					&& get_IMSI()->get_is_valid_data() == false
					&& m_automatic_realm_read == false)
			{
				eap_variable_data_c IMSI(m_am_tools);
				eap_variable_data_c pseudonym(m_am_tools);
				eap_variable_data_c reauthentication_identity(m_am_tools);

				if (IMSI.get_is_valid() == false
					|| pseudonym.get_is_valid() == false
					|| reauthentication_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);
				}

				EAP_TRACE_DEBUG(
					m_am_tools, 
					TRACE_FLAGS_DEFAULT, 
					(EAPL("eap_type_aka_c::handle_aka_identity_request_message ")
					 EAPL("aka_payload_AT_PERMANENT_ID_REQ\n")));

				status = query_AKA_IMSI_or_pseudonym_or_reauthentication_id(
					&IMSI,
					&pseudonym,
					&reauthentication_identity,
					&m_automatic_realm,
					&m_length_of_mnc,
					false,
					include_identity_to_aka_identity_response,
					eap_type_aka_complete_aka_identity_request,
					received_aka->get_identifier());

				if (status == eap_status_pending_request)
				{
					// This is pending query, that will be completed by complete_AKA_IMSI_or_pseudonym_or_reauthentication_id_query() call.
					EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
					return EAP_STATUS_RETURN(m_am_tools, status);
				}
				else if (status == eap_status_completed_request)
				{
					// This is already completed by complete_AKA_IMSI_or_pseudonym_or_reauthentication_id_query() call.
					EAP_TRACE_DEBUG(
						m_am_tools, 
						TRACE_FLAGS_DEFAULT, 
						(EAPL("eap_type_aka_c::handle_aka_identity_request_message(): ")
						 EAPL("completed returns eap_type_aka_state_variable_e %d=%s.\n"),
						 get_state(),
						 get_state_string()));
					
					status = eap_status_ok;

					EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
					return EAP_STATUS_RETURN(m_am_tools, status);
				}
				else if (status != eap_status_ok)
				{
					// This is the error case.

					EAP_TRACE_DEBUG(
						m_am_tools, 
						TRACE_FLAGS_DEFAULT, 
						(EAPL("eap_type_aka_c::handle_aka_identity_request_message(): ")
						 EAPL("error returns eap_type_aka_state_variable_e %d=%s.\n"),
						 get_state(),
						 get_state_string()));
					
					EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
					return EAP_STATUS_RETURN(m_am_tools, status);
				}

				if (IMSI.get_is_valid_data() == true)
				{
					EAP_TRACE_DEBUG(
						m_am_tools, 
						TRACE_FLAGS_DEFAULT, 
						(EAPL("eap_type_aka_c::handle_aka_identity_request_message IMSI\n")));

					get_reauthentication_identity()->reset();
					get_pseudonym()->reset();
					status = get_IMSI()->set_copy_of_buffer(&IMSI);
					if (status != eap_status_ok)
					{
						EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
						return EAP_STATUS_RETURN(m_am_tools, status);
					}

					status = store_identity(get_IMSI(), true);
					if (status != eap_status_ok)
					{
						EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
						return EAP_STATUS_RETURN(m_am_tools, status);
					}
				}
				else
				{
					EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
					return EAP_STATUS_RETURN(m_am_tools, status);
				}
			}


			save_current_state();
			set_state(eap_type_aka_state_analyse_aka_identity_request);

			EAP_TRACE_DEBUG(
				m_am_tools, 
				TRACE_FLAGS_DEFAULT, 
				(EAPL("eap_type_aka_c::handle_aka_identity_request_message(0x%08x).\n"),
				 this));

			status = send_aka_identity_response_message(
				receive_network_id,
				received_aka->get_identifier(),
				include_identity_to_aka_identity_response,
				&m_automatic_realm);

			if (status != eap_status_ok)
			{
				restore_saved_previous_state();
				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
				return EAP_STATUS_RETURN(m_am_tools, status);
			}
		}
		else
		{
			// Not correct AKA-payloads are included.
			EAP_TRACE_DEBUG(
				m_am_tools, 
				TRACE_FLAGS_AKA_ERROR, 
				(EAPL("ERROR: eap_type_aka_c::handle_aka_identity_request_message(1): ")
				 EAPL("Not correct AKA-payloads are included in eap_type_aka_state_variable_e %d=%s.\n"),
				 get_state(),
				 get_state_string()));
			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
			return EAP_STATUS_RETURN(m_am_tools, eap_status_header_corrupted);
		}
	}
	else
	{
		// Wrong message in this state.
		EAP_TRACE_DEBUG(
			m_am_tools, 
			TRACE_FLAGS_AKA_ERROR, 
			(EAPL("ERROR: eap_type_aka_c::handle_aka_identity_request_message(): ")
			 EAPL("Wrong message %d=%s in eap_type_aka_state_variable_e %d=%s.\n"),
			 received_aka->get_subtype(),
			 received_aka->get_subtype_string(),
			 get_state(),
			 get_state_string()));
		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_FUNC_EXPORT eap_status_e eap_type_aka_c::handle_aka_notification_request_message_reauthentication(
	const eap_am_network_id_c * const receive_network_id,
	aka_header_c * const received_aka,
	const u32_t /*aka_packet_length*/,
	aka_payloads_c * const p_aka_payloads)
{
	eap_status_e status = eap_status_process_general_error;

	EAP_TRACE_DEBUG(
		m_am_tools, 
		TRACE_FLAGS_DEFAULT, 
		(EAPL("send: handler 0x%08x, AKA-type %10s, %s, state %2d=%s\n"),
		 this,
		 EAPL("handle_aka_notification_request_message_reauthentication"),
		 (get_is_client()) ? EAPL("client") : EAPL("server"),
		 get_state(),
		 get_state_string()
		 ));

	// The second most significant bit of the notification code is called
	// the Phase bit (P bit). It specifies at which phase of the EAP-AKA
	// exchange the notification can be used.
	if (get_aka_notification_code_P_bit(m_aka_notification_code) == false
		&& (get_state() == eap_type_aka_state_waiting_for_success
			|| get_state() == eap_type_aka_state_success
			|| get_state() == eap_type_aka_state_waiting_for_notification_request_success)
		)
	{
		// If the P bit is set to zero,
		// the notification can only be used after a successful EAP/AKA/
		// Challenge round in full authentication or a successful EAP/AKA/
		// Re-authentication round in reautentication. A re-authentication round
		// is considered successful only if the peer has successfully verified
		// AT_MAC and AT_COUNTER attributes, and does not include the
		// AT_COUNTER_TOO_SMALL attribute in EAP-Response/AKA/Re-authentication.

		// The AT_MAC attribute MUST beincluded if the P bit of the notification
		// code in AT_NOTIFICATION is set to zero, and MUST NOT be included in
		// cases when the P bit is set to one. The P bit is discussed in Section
		// 4.4.
		if (p_aka_payloads->check_payloads(
				aka_payloads_c::eap_aka_payload_status_must_not_be, // nonce_s
				aka_payloads_c::eap_aka_payload_status_must_be,     // MAC
				aka_payloads_c::eap_aka_payload_status_must_be,     // ENCR_DATA
				aka_payloads_c::eap_aka_payload_status_must_not_be, // IDENTITY
				aka_payloads_c::eap_aka_payload_status_must_not_be, // padding
				aka_payloads_c::eap_aka_payload_status_must_not_be, // n_RANDs
				aka_payloads_c::eap_aka_payload_status_must_not_be, // AUTN
				aka_payloads_c::eap_aka_payload_status_must_not_be, // AUTS
				aka_payloads_c::eap_aka_payload_status_must_not_be, // RES
				aka_payloads_c::eap_aka_payload_status_must_not_be, // PERMANENT_ID_REQ
				aka_payloads_c::eap_aka_payload_status_must_not_be, // FULLAUTH_ID_REQ
				aka_payloads_c::eap_aka_payload_status_must_not_be, // ANY_ID_REQ
				aka_payloads_c::eap_aka_payload_status_must_be,     // IV
				aka_payloads_c::eap_aka_payload_status_must_not_be, // NEXT_PSEUDONYM
				aka_payloads_c::eap_aka_payload_status_must_not_be, // NEXT_REAUTH_ID
				aka_payloads_c::eap_aka_payload_status_must_be,     // NOTIFICATION
				aka_payloads_c::eap_aka_payload_status_must_not_be, // COUNTER
				aka_payloads_c::eap_aka_payload_status_must_not_be, // COUNTER_TOO_SMALL
				aka_payloads_c::eap_aka_payload_status_must_not_be, // CLIENT_ERROR_CODE
				aka_payloads_c::eap_aka_payload_status_must_not_be, // RESULT_IND
				aka_payloads_c::eap_aka_payload_status_must_not_be  // CHECKCODE
				) == true
			)
		{
			eap_variable_data_c orig_XKEY(m_am_tools);
			eap_variable_data_c orig_K_aut(m_am_tools);
			eap_variable_data_c orig_K_encr(m_am_tools);

			if (orig_XKEY.get_is_valid() == false
				|| orig_K_aut.get_is_valid() == false
				|| orig_K_encr.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 reauth_counter = 0u;

			// In order to use re-authentication, the client and the server need to
			// store the following values: original XKEY, K_aut, K_encr, latest
			// counter value and the next re-authentication identity.
			status = m_am_type_aka->query_reauth_parameters(
				&orig_XKEY,
				&orig_K_aut,
				&orig_K_encr,
				&reauth_counter);
			if (status != eap_status_ok)
			{
				restore_saved_previous_state();
				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
				return EAP_STATUS_RETURN(m_am_tools, status);
			}

			status = check_message_authentication_code(
				&orig_K_aut,
				p_aka_payloads,
				received_aka,
				received_aka->get_length());
			if (status != eap_status_ok)
			{
				(void) initialize_error_message(status);

				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
				return EAP_STATUS_RETURN(m_am_tools, status);
			}

			bool authentication_ok = true;

			// Decrypt and parse encrypted payload.
			{
				aka_payloads_c * const l_aka_payloads = new aka_payloads_c(m_am_tools);
				eap_automatic_variable_c<aka_payloads_c> l_aka_payloads_automatic(m_am_tools, l_aka_payloads);

				if (l_aka_payloads == 0
					|| l_aka_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 = decrypt_DATA_payload(
					p_aka_payloads,
					&orig_K_encr);
				if (status != eap_status_ok)
				{
					restore_saved_previous_state();
					EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
					return EAP_STATUS_RETURN(m_am_tools, status);
				}

				u32_t state_payload_buffer_length = p_aka_payloads->get_ENCR_DATA()->get_data_length();
				const aka_payload_AT_header_c gp_data_payload(
					m_am_tools,
					p_aka_payloads->get_ENCR_DATA()->get_data(state_payload_buffer_length),
					state_payload_buffer_length);

				status = parse_aka_payload(
					&gp_data_payload,
					&state_payload_buffer_length,
					l_aka_payloads,
					received_aka->get_subtype());
				if (status != eap_status_ok)
				{
					restore_saved_previous_state();
					EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
					return EAP_STATUS_RETURN(m_am_tools, status);
				}


				if (l_aka_payloads->get_COUNTER()->get_payload_included() == false)
				{
					restore_saved_previous_state();
					EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
					return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
				}

				u32_t counter = l_aka_payloads->get_COUNTER()->get_original_header()->get_reserved();

				if (counter == reauth_counter)
				{
					EAP_TRACE_DEBUG(
						m_am_tools, 
						TRACE_FLAGS_DEFAULT, 
						(EAPL("eap_type_aka_c::handle_aka_notification_request_message_reauthentication(): ")
						EAPL("reauth counter %d OK, %d=%s.\n"),
						reauth_counter,
						get_state(),
						get_state_string()));
				}
				else
				{
					EAP_TRACE_DEBUG(
						m_am_tools, 
						TRACE_FLAGS_AKA_ERROR, 
						(EAPL("ERROR: eap_type_aka_c::handle_aka_notification_request_message_reauthentication(): ")
						EAPL("reauth counter %d wrong, should be %d, %d=%s.\n"),
						counter,
						reauth_counter,
						get_state(),
						get_state_string()));

					authentication_ok = false;
				}
			}

			// The most significant bit is called the Failure bit (F bit).
			// The F bit specifies whether the notification implies failure.
			if (authentication_ok == false
				|| get_aka_notification_code_F_bit(m_aka_notification_code) == false)
			{
				// The code values with the F bit set to zero (code values 0...32767)
				// are used on unsuccessful cases.
				// The receipt of a notification code from this range implies failed EAP
				// exchange, so the peer can use the notification as a failure indication.

				set_state(eap_type_aka_state_failure);

				// This will terminate session immediately.
				(void) get_type_partner()->set_session_timeout(0ul);
			}


			// EAP-Response/AKA-Notification must be send
			// before finish_successful_authentication() call.
			status = send_aka_notification_response(
				m_aka_notification_code,
				true);
			if (status != eap_status_ok)
			{
				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
				return EAP_STATUS_RETURN(m_am_tools, status);
			}


			if (authentication_ok == true
				&& get_aka_notification_code_F_bit(m_aka_notification_code) == true)
			{
				// The receipt of a notification code with the F bit set to one (values
				// 32768...65536) does not imply failure. Notification code 32768 has
				// been reserved as a general notification code to indicate successful
				// authentication.
				if (m_aka_notification_code == eap_aka_notification_F_set_no_P_user_authenticated)
				{
					if (m_wait_eap_success_packet == false)
					{
						status = finish_successful_authentication(
							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);
						}
					}
					else
					{
						// This version waits the EAP-Success message.
						set_state(eap_type_aka_state_waiting_for_success);
						status = eap_status_ok;

						EAP_TRACE_DEBUG(
							m_am_tools, 
							TRACE_FLAGS_DEFAULT|TRACE_TEST_VECTORS, 
							(EAPL("EAP_type_AKA: %s, re-authentication OK, waits EAP-Success\n"),
							 (m_is_client == true) ? "client": "server"));
					}
				}
			}

		}
		else
		{
			// Not correct AKA-payloads are included.
			EAP_TRACE_DEBUG(
				m_am_tools, 
				TRACE_FLAGS_AKA_ERROR, 
				(EAPL("ERROR: eap_type_aka_c::handle_aka_notification_request_message(1): ")
				EAPL("Not correct AKA-payloads are included in eap_type_aka_state_variable_e %d=%s.\n"),
				get_state(),
				get_state_string()));

			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
			return EAP_STATUS_RETURN(m_am_tools, eap_status_authentication_failure);
		}
	}
	else if (get_aka_notification_code_P_bit(m_aka_notification_code) == true
		&& (get_state() == eap_type_aka_state_waiting_for_identity_request
			|| get_state() == eap_type_aka_state_pending_identity_query
			|| get_state() == eap_type_aka_state_waiting_for_aka_identity_request
			|| get_state() == eap_type_aka_state_imsi_waiting_for_aka_identity_request
			|| get_state() == eap_type_aka_state_pseydonym_waiting_for_aka_identity_request
			|| get_state() == eap_type_aka_state_analyse_aka_identity_request
			|| get_state() == eap_type_aka_state_waiting_for_challenge_request
			|| get_state() == eap_type_aka_state_analyses_challenge_request
			|| get_state() == eap_type_aka_state_pending_kc_sres_query
			|| get_state() == eap_type_aka_state_waiting_for_reauth_request
			|| get_state() == eap_type_aka_state_analyses_reauthentication_request
			|| get_state() == eap_type_aka_state_waiting_for_success
			|| get_state() == eap_type_aka_state_success
			|| get_state() == eap_type_aka_state_waiting_for_notification_request_success
			|| get_state() == eap_type_aka_state_failure))
	{
		// If the P bit is set to one, the notification can only by used before
		// the EAP/AKA/Challenge round in full authentication, or before the
		// EAP/AKA/Re-authentication round in reauthentication.

		// The most significant bit is called the Failure bit (F bit).
		// The F bit specifies whether the notification implies failure.
		if (get_aka_notification_code_F_bit(m_aka_notification_code) == false)
		{
			// The code values with the F bit set to zero (code values 0...32767)
			// are used on unsuccessful cases.
			// The receipt of a notification code from this range implies failed EAP
			// exchange, so the peer can use the notification as a failure indication.
		}
		else //if (get_aka_notification_code_F_bit(m_aka_notification_code) == true)
		{
			// The receipt of a notification code with the F bit set to one (values
			// 32768...65536) does not imply failure. Notification code 32768 has
			// been reserved as a general notification code to indicate successful
			// authentication.
		}

		status = initialize_notification_message();
		if (status != eap_status_ok)
		{
			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
			return EAP_STATUS_RETURN(m_am_tools, status);
		}
	}
	else
	{
		// Wrong message in this state.
		EAP_TRACE_DEBUG(
			m_am_tools, 
			TRACE_FLAGS_AKA_ERROR, 
			(EAPL("ERROR: eap_type_aka_c::handle_aka_notification_request_message(): ")
			 EAPL("Wrong message %d=%s in eap_type_aka_state_variable_e %d=%s.\n"),
			 received_aka->get_subtype(),
			 received_aka->get_subtype_string(),
			 get_state(),
			 get_state_string()));

		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_FUNC_EXPORT eap_status_e eap_type_aka_c::handle_aka_notification_request_message_full_authentication(
	const eap_am_network_id_c * const receive_network_id,
	aka_header_c * const received_aka,
	const u32_t /*aka_packet_length*/,
	aka_payloads_c * const p_aka_payloads)
{
	eap_status_e status = eap_status_process_general_error;

	EAP_TRACE_DEBUG(
		m_am_tools, 
		TRACE_FLAGS_DEFAULT, 
		(EAPL("send: handler 0x%08x, AKA-type %10s, %s, state %2d=%s\n"),
		 this,
		 EAPL("handle_aka_notification_request_message_full_authentication"),
		 (get_is_client()) ? EAPL("client") : EAPL("server"),
		 get_state(),
		 get_state_string()
		 ));

	// The second most significant bit of the notification code is called
	// the Phase bit (P bit). It specifies at which phase of the EAP-AKA
	// exchange the notification can be used.
	if (get_aka_notification_code_P_bit(m_aka_notification_code) == false
		&& (get_state() == eap_type_aka_state_waiting_for_success
			|| get_state() == eap_type_aka_state_success
			|| get_state() == eap_type_aka_state_waiting_for_notification_request_success)
		)
	{
		// If the P bit is set to zero,
		// the notification can only be used after a successful EAP/AKA/
		// Challenge round in full authentication or a successful EAP/AKA/
		// Re-authentication round in reautentication. A re-authentication round
		// is considered successful only if the peer has successfully verified
		// AT_MAC and AT_COUNTER attributes, and does not include the
		// AT_COUNTER_TOO_SMALL attribute in EAP-Response/AKA/Re-authentication.

		// The AT_MAC attribute MUST beincluded if the P bit of the notification
		// code in AT_NOTIFICATION is set to zero, and MUST NOT be included in
		// cases when the P bit is set to one. The P bit is discussed in Section
		// 4.4.
		if (p_aka_payloads->check_payloads(
				aka_payloads_c::eap_aka_payload_status_must_not_be, // nonce_s
				aka_payloads_c::eap_aka_payload_status_must_be,     // MAC
				aka_payloads_c::eap_aka_payload_status_must_not_be, // ENCR_DATA
				aka_payloads_c::eap_aka_payload_status_must_not_be, // IDENTITY
				aka_payloads_c::eap_aka_payload_status_must_not_be, // padding
				aka_payloads_c::eap_aka_payload_status_must_not_be, // n_RANDs
				aka_payloads_c::eap_aka_payload_status_must_not_be, // AUTN
				aka_payloads_c::eap_aka_payload_status_must_not_be, // AUTS
				aka_payloads_c::eap_aka_payload_status_must_not_be, // RES
				aka_payloads_c::eap_aka_payload_status_must_not_be, // PERMANENT_ID_REQ
				aka_payloads_c::eap_aka_payload_status_must_not_be, // FULLAUTH_ID_REQ
				aka_payloads_c::eap_aka_payload_status_must_not_be, // ANY_ID_REQ
				aka_payloads_c::eap_aka_payload_status_must_not_be, // IV
				aka_payloads_c::eap_aka_payload_status_must_not_be, // NEXT_PSEUDONYM
				aka_payloads_c::eap_aka_payload_status_must_not_be, // NEXT_REAUTH_ID
				aka_payloads_c::eap_aka_payload_status_must_be,     // NOTIFICATION
				aka_payloads_c::eap_aka_payload_status_must_not_be, // COUNTER
				aka_payloads_c::eap_aka_payload_status_must_not_be, // COUNTER_TOO_SMALL
				aka_payloads_c::eap_aka_payload_status_must_not_be, // CLIENT_ERROR_CODE
				aka_payloads_c::eap_aka_payload_status_optional,    // RESULT_IND
				aka_payloads_c::eap_aka_payload_status_must_not_be  // CHECKCODE
				) == true
			)
		{
			status = check_message_authentication_code(
				get_K_aut(),
				p_aka_payloads,
				received_aka,
				received_aka->get_length());
			if (status != eap_status_ok)
			{
				(void) initialize_error_message(status);

				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
				return EAP_STATUS_RETURN(m_am_tools, status);
			}

			if (m_use_result_indication == false
				&& m_aka_notification_code == eap_aka_notification_F_set_no_P_user_authenticated)
			{
				// ERROR: We did not require result indication and server send it to us.
				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
				return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
			}

			// The most significant bit is called the Failure bit (F bit).
			// The F bit specifies whether the notification implies failure.
			if (get_aka_notification_code_F_bit(m_aka_notification_code) == false)
			{
				// The code values with the F bit set to zero (code values 0...32767)
				// are used on unsuccessful cases.
				// The receipt of a notification code from this range implies failed EAP
				// exchange, so the peer can use the notification as a failure indication.

				set_state(eap_type_aka_state_failure);

				// This will terminate session immediately.
				(void) get_type_partner()->set_session_timeout(0ul);
			}
			else //if (get_aka_notification_code_F_bit(m_aka_notification_code) == true)
			{
				// The receipt of a notification code with the F bit set to one (values
				// 32768...65536) does not imply failure. Notification code 32768 has
				// been reserved as a general notification code to indicate successful
				// authentication.
				if (m_aka_notification_code == eap_aka_notification_F_set_no_P_user_authenticated)
				{
					if (m_wait_eap_success_packet == false)
					{
						status = finish_successful_authentication(
							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);
						}
					}
					else
					{
						// This version waits the EAP-Success message.
						set_state(eap_type_aka_state_waiting_for_success);
						status = eap_status_ok;

						EAP_TRACE_DEBUG(
							m_am_tools, 
							TRACE_FLAGS_DEFAULT|TRACE_TEST_VECTORS, 
							(EAPL("EAP_type_AKA: %s, full-authentication OK, waits EAP-Success\n"),
							 (m_is_client == true) ? "client": "server"));
					}
				}
			}

			status = send_aka_notification_response(
				m_aka_notification_code,
				false);
			if (status != eap_status_ok)
			{
				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
				return EAP_STATUS_RETURN(m_am_tools, status);
			}
		}
		else
		{
			// Not correct AKA-payloads are included.
			EAP_TRACE_DEBUG(
				m_am_tools, 
				TRACE_FLAGS_AKA_ERROR, 
				(EAPL("ERROR: eap_type_aka_c::handle_aka_notification_request_message(1): ")
				EAPL("Not correct AKA-payloads are included in eap_type_aka_state_variable_e %d=%s.\n"),
				get_state(),
				get_state_string()));

			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
			return EAP_STATUS_RETURN(m_am_tools, eap_status_authentication_failure);
		}
	}
	else if (get_aka_notification_code_P_bit(m_aka_notification_code) == true
		&& (get_state() == eap_type_aka_state_waiting_for_identity_request
			|| get_state() == eap_type_aka_state_pending_identity_query
			|| get_state() == eap_type_aka_state_waiting_for_aka_identity_request
			|| get_state() == eap_type_aka_state_imsi_waiting_for_aka_identity_request
			|| get_state() == eap_type_aka_state_pseydonym_waiting_for_aka_identity_request
			|| get_state() == eap_type_aka_state_analyse_aka_identity_request
			|| get_state() == eap_type_aka_state_waiting_for_challenge_request
			|| get_state() == eap_type_aka_state_analyses_challenge_request
			|| get_state() == eap_type_aka_state_pending_kc_sres_query
			|| get_state() == eap_type_aka_state_waiting_for_reauth_request
			|| get_state() == eap_type_aka_state_analyses_reauthentication_request
			|| get_state() == eap_type_aka_state_waiting_for_success
			|| get_state() == eap_type_aka_state_success
			|| get_state() == eap_type_aka_state_waiting_for_notification_request_success
			|| get_state() == eap_type_aka_state_failure))
	{
		// If the P bit is set to one, the notification can only by used before
		// the EAP/AKA/Challenge round in full authentication, or before the
		// EAP/AKA/Re-authentication round in reauthentication.

		// The most significant bit is called the Failure bit (F bit).
		// The F bit specifies whether the notification implies failure.
		if (get_aka_notification_code_F_bit(m_aka_notification_code) == false)
		{
			// The code values with the F bit set to zero (code values 0...32767)
			// are used on unsuccessful cases.
			// The receipt of a notification code from this range implies failed EAP
			// exchange, so the peer can use the notification as a failure indication.
		}
		else //if (get_aka_notification_code_F_bit(m_aka_notification_code) == true)
		{
			// The receipt of a notification code with the F bit set to one (values
			// 32768...65536) does not imply failure. Notification code 32768 has
			// been reserved as a general notification code to indicate successful
			// authentication.
		}

		status = initialize_notification_message();
		if (status != eap_status_ok)
		{
			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
			return EAP_STATUS_RETURN(m_am_tools, status);
		}
	}
	else
	{
		// Wrong message in this state.
		EAP_TRACE_DEBUG(
			m_am_tools, 
			TRACE_FLAGS_AKA_ERROR, 
			(EAPL("ERROR: eap_type_aka_c::handle_aka_notification_request_message(): ")
			 EAPL("Wrong message %d=%s in eap_type_aka_state_variable_e %d=%s.\n"),
			 received_aka->get_subtype(),
			 received_aka->get_subtype_string(),
			 get_state(),
			 get_state_string()));

		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_FUNC_EXPORT eap_status_e eap_type_aka_c::handle_aka_notification_request_message(
	const eap_am_network_id_c * const receive_network_id,
	aka_header_c * const received_aka,
	const u32_t aka_packet_length,
	aka_payloads_c * const p_aka_payloads)
{
	eap_status_e status = eap_status_process_general_error;

	EAP_UNREFERENCED_PARAMETER(received_aka);
	EAP_UNREFERENCED_PARAMETER(aka_packet_length);
	EAP_UNREFERENCED_PARAMETER(receive_network_id);

	if (get_state() == eap_type_aka_state_waiting_for_identity_request
		|| get_state() == eap_type_aka_state_pending_identity_query
		|| get_state() == eap_type_aka_state_waiting_for_aka_identity_request
		|| get_state() == eap_type_aka_state_imsi_waiting_for_aka_identity_request
		|| get_state() == eap_type_aka_state_pseydonym_waiting_for_aka_identity_request
		|| get_state() == eap_type_aka_state_analyse_aka_identity_request
		|| get_state() == eap_type_aka_state_waiting_for_challenge_request
		|| get_state() == eap_type_aka_state_analyses_challenge_request
		|| get_state() == eap_type_aka_state_pending_kc_sres_query
		|| get_state() == eap_type_aka_state_waiting_for_notification_request_success
		|| get_state() == eap_type_aka_state_waiting_for_success
		|| get_state() == eap_type_aka_state_waiting_for_reauth_request
		|| get_state() == eap_type_aka_state_analyses_reauthentication_request
		|| get_state() == eap_type_aka_state_success
		|| get_state() == eap_type_aka_state_failure
	)
	{
		// This could be first or retransmission request.

		// Checks the payloads existence.
		if (p_aka_payloads->check_payloads(
				aka_payloads_c::eap_aka_payload_status_must_not_be, // nonce_s
				aka_payloads_c::eap_aka_payload_status_optional,    // MAC
				aka_payloads_c::eap_aka_payload_status_optional,    // ENCR_DATA
				aka_payloads_c::eap_aka_payload_status_must_not_be, // IDENTITY
				aka_payloads_c::eap_aka_payload_status_must_not_be, // padding
				aka_payloads_c::eap_aka_payload_status_must_not_be, // n_RANDs
				aka_payloads_c::eap_aka_payload_status_must_not_be, // AUTN
				aka_payloads_c::eap_aka_payload_status_must_not_be, // AUTS
				aka_payloads_c::eap_aka_payload_status_must_not_be, // RES
				aka_payloads_c::eap_aka_payload_status_must_not_be, // PERMANENT_ID_REQ
				aka_payloads_c::eap_aka_payload_status_must_not_be, // FULLAUTH_ID_REQ
				aka_payloads_c::eap_aka_payload_status_must_not_be, // ANY_ID_REQ
				aka_payloads_c::eap_aka_payload_status_optional,    // IV
				aka_payloads_c::eap_aka_payload_status_must_not_be, // NEXT_PSEUDONYM
				aka_payloads_c::eap_aka_payload_status_must_not_be, // NEXT_REAUTH_ID
				aka_payloads_c::eap_aka_payload_status_must_be,     // NOTIFICATION
				aka_payloads_c::eap_aka_payload_status_must_not_be, // COUNTER
				aka_payloads_c::eap_aka_payload_status_must_not_be, // COUNTER_TOO_SMALL
				aka_payloads_c::eap_aka_payload_status_must_not_be, // CLIENT_ERROR_CODE
				aka_payloads_c::eap_aka_payload_status_optional,    // RESULT_IND
				aka_payloads_c::eap_aka_payload_status_must_not_be  // CHECKCODE
				) == true
			)
		{
			// NOTE, this message is unauthenticated. Anybody could sent this message.

			// We store the received notification code. This will be handled after session timeouts.
			m_aka_notification_code =
				static_cast<eap_aka_notification_codes_e>(
					p_aka_payloads->get_NOTIFICATION()->get_original_header()->get_reserved());

			save_current_state();

			set_last_eap_identifier(received_aka->get_identifier());

			EAP_TRACE_DEBUG(
				m_am_tools, 
				TRACE_FLAGS_DEFAULT, 
				(EAPL("eap_type_aka_c::handle_aka_notification_request_message(0x%08x), ")
				 EAPL("notification_code = 0x%04x, F bit %d, P bit %d, state %d=%s.\n"),
				 this,
				 m_aka_notification_code,
				 get_aka_notification_code_F_bit(m_aka_notification_code),
				 get_aka_notification_code_P_bit(m_aka_notification_code),
				 get_state(),
				 get_state_string()));

			if (m_authentication_type == AKA_AUTHENTICATION_TYPE_REAUTHENTICATION)
			{
				status = handle_aka_notification_request_message_reauthentication(
					receive_network_id,
					received_aka,
					aka_packet_length,
					p_aka_payloads);
				if (status != eap_status_ok)
				{
					EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
					return EAP_STATUS_RETURN(m_am_tools, status);
				}
			}
			else
			{
				status = handle_aka_notification_request_message_full_authentication(
					receive_network_id,
					received_aka,
					aka_packet_length,
					p_aka_payloads);
				if (status != eap_status_ok)
				{
					EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
					return EAP_STATUS_RETURN(m_am_tools, status);
				}
			}
		}
		else
		{
			// Not correct AKA-payloads are included.
			EAP_TRACE_DEBUG(
				m_am_tools, 
				TRACE_FLAGS_AKA_ERROR, 
				(EAPL("ERROR: eap_type_aka_c::handle_aka_notification_request_message(1): ")
				EAPL("Not correct AKA-payloads are included in eap_type_aka_state_variable_e %d=%s.\n"),
				get_state(),
				get_state_string()));
			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
			return EAP_STATUS_RETURN(m_am_tools, eap_status_authentication_failure);
		}
	}
	else
	{
		// Wrong message in this state.
		EAP_TRACE_DEBUG(
			m_am_tools, 
			TRACE_FLAGS_AKA_ERROR, 
			(EAPL("ERROR: eap_type_aka_c::handle_aka_notification_request_message(): ")
			 EAPL("Wrong message %d=%s in eap_type_aka_state_variable_e %d=%s.\n"),
			 received_aka->get_subtype(),
			 received_aka->get_subtype_string(),
			 get_state(),
			 get_state_string()));
		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_FUNC_EXPORT eap_status_e eap_type_aka_c::handle_challenge_request_message(
	const eap_am_network_id_c * const /* receive_network_id */,
	aka_header_c * const received_aka,
	const u32_t aka_packet_length,
	aka_payloads_c * const p_aka_payloads)
{
	eap_status_e status = eap_status_process_general_error;

	if (get_state() == eap_type_aka_state_waiting_for_challenge_request
		|| get_state() == eap_type_aka_state_waiting_for_reauth_request // Server could start full authentication.
		|| (get_state() == eap_type_aka_state_waiting_for_success && m_use_result_indication == false)
		|| (get_state() == eap_type_aka_state_waiting_for_notification_request_success && m_use_result_indication == true))
	{
		// This could be first or retransmission request.

		// Checks the payloads existence.
		if (p_aka_payloads->check_payloads(
				aka_payloads_c::eap_aka_payload_status_must_not_be, // nonce_s
				aka_payloads_c::eap_aka_payload_status_must_be,     // MAC
				aka_payloads_c::eap_aka_payload_status_optional,    // ENCR_DATA
				aka_payloads_c::eap_aka_payload_status_must_not_be, // IDENTITY
				aka_payloads_c::eap_aka_payload_status_must_not_be, // padding
				aka_payloads_c::eap_aka_payload_status_must_be,     // n_RANDs
				aka_payloads_c::eap_aka_payload_status_must_be,     // AUTN
				aka_payloads_c::eap_aka_payload_status_must_not_be, // AUTS
				aka_payloads_c::eap_aka_payload_status_must_not_be, // RES
				aka_payloads_c::eap_aka_payload_status_must_not_be, // PERMANENT_ID_REQ
				aka_payloads_c::eap_aka_payload_status_must_not_be, // FULLAUTH_ID_REQ
				aka_payloads_c::eap_aka_payload_status_must_not_be, // ANY_ID_REQ
				aka_payloads_c::eap_aka_payload_status_optional,    // IV
				aka_payloads_c::eap_aka_payload_status_must_not_be, // NEXT_PSEUDONYM
				aka_payloads_c::eap_aka_payload_status_must_not_be, // NEXT_REAUTH_ID
				aka_payloads_c::eap_aka_payload_status_must_not_be, // NOTIFICATION
				aka_payloads_c::eap_aka_payload_status_must_not_be, // COUNTER
				aka_payloads_c::eap_aka_payload_status_must_not_be, // COUNTER_TOO_SMALL
				aka_payloads_c::eap_aka_payload_status_must_not_be, // CLIENT_ERROR_CODE
				aka_payloads_c::eap_aka_payload_status_optional,    // RESULT_IND
				aka_payloads_c::eap_aka_payload_status_optional     // CHECKCODE
				) == true
			)
		{
			if (p_aka_payloads->get_RESULT_IND()->get_payload_included() == true)
			{
				m_use_result_indication = m_allow_use_result_indication;
			}
			else
			{
				m_use_result_indication = false;
			}

			if (p_aka_payloads->get_IV()->get_payload_included() == true
			|| p_aka_payloads->get_ENCR_DATA()->get_payload_included() == true)
			{
				if (p_aka_payloads->get_IV()->get_payload_included() == true
				&& p_aka_payloads->get_ENCR_DATA()->get_payload_included() == true)
				{
					// OK
				}
				else
				{
					// All of these must be included
					// or none of these must be included.
					return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
				}
			}

			// The whole EAP packet must copied.
			// The MAC is checked later after n*Kc and n*SRES is get from AKA.
			status = get_saved_EAP_packet()->set_copy_of_buffer(
				received_aka->get_header_buffer(aka_packet_length),
				aka_packet_length);
			if (status != eap_status_ok)
			{
				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
				return EAP_STATUS_RETURN(m_am_tools, status);
			}

			if (p_aka_payloads->get_CHECKCODE()->get_payload_included() == true)
			{
				// Save AT_CHECKCODE.
				status = m_received_checkcode.set_copy_of_buffer(
					p_aka_payloads->get_CHECKCODE()->get_payload_buffer());
				if (status != eap_status_ok)
				{
					EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
					return EAP_STATUS_RETURN(m_am_tools, status);
				}
			}

			if (get_state() == eap_type_aka_state_waiting_for_reauth_request)
			{
				// Server started a full authentication.
				m_authentication_type = AKA_AUTHENTICATION_TYPE_FULL_AUTH;
			}

			set_last_eap_identifier(received_aka->get_identifier());

			save_current_state();
			set_state(eap_type_aka_state_analyses_challenge_request);

			// - - - - - - - - - - - - - - - - - - - - - - - -

			eap_variable_data_c CK(m_am_tools);
			eap_variable_data_c IK(m_am_tools);
			eap_variable_data_c RES(m_am_tools);

			if (CK.get_is_valid() == false
				|| IK.get_is_valid() == false
				|| RES.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_type_aka_authentication_vector_c authentication_vector(m_am_tools);

			if (authentication_vector.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 = authentication_vector.get_RAND()->set_copy_of_buffer(
				p_aka_payloads->get_RAND()->get_payload_buffer());
			if (status != eap_status_ok)
			{
				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
				return EAP_STATUS_RETURN(m_am_tools, status);
			}

			status = authentication_vector.get_AUTN()->set_copy_of_buffer(
				p_aka_payloads->get_AUTN()->get_payload_buffer());
			if (status != eap_status_ok)
			{
				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
				return EAP_STATUS_RETURN(m_am_tools, status);
			}

			status = query_AKA_RES(
				&authentication_vector);
			if (status == eap_status_pending_request)
			{
				// Request will be completed later using complete_AKA_RES_query() function.
				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
				return EAP_STATUS_RETURN(m_am_tools, eap_status_ok);
			}
			else if (status == eap_status_completed_request)
			{
				// Request was already completed by AM using complete_AKA_RES_query() function.
				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
				return EAP_STATUS_RETURN(m_am_tools, eap_status_ok);
			}
			else if (status == eap_status_success)
			{
				// eap_status_success means the authentication was successful.
				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
				return EAP_STATUS_RETURN(m_am_tools, status);
			}
			else if (status == eap_status_ok)
			{
				// The query_AKA_RES() function call is synchronous.
				// We must call process_AKA_kc_sres().
			}
			else
			{
				// This is an error case.
				restore_saved_previous_state();
				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
				return EAP_STATUS_RETURN(m_am_tools, status);
			}

			// eap_status_ok status value means n_kc and n_sres were read but not processed.
			// Next we must call process_AKA_kc_sres().

			// - - - - - - - - - - - - - - - - - - - - - - - -

			status = process_AKA_kc_sres(
				&authentication_vector);

			if (status != eap_status_ok
				&& status != eap_status_success)
			{
				(void) initialize_error_message(status);

				restore_saved_previous_state();
			}

			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
			return EAP_STATUS_RETURN(m_am_tools, status);
		}
		else
		{
			// Not correct AKA-payloads are included.
			EAP_TRACE_DEBUG(
				m_am_tools, 
				TRACE_FLAGS_AKA_ERROR, 
				(EAPL("ERROR: eap_type_aka_c::handle_challenge_request_message(6): ")
				 EAPL("Not correct AKA-payloads are included in eap_type_aka_state_variable_e %d=%s.\n"),
				 get_state(),
				 get_state_string()));
			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
			return EAP_STATUS_RETURN(m_am_tools, eap_status_header_corrupted);
		}
	}
	else if (get_state() == eap_type_aka_state_pending_kc_sres_query)
	{
		// This is re-transmitted EAP-Request/AKA/Challenge.
		// We dischard it quietly.
		EAP_TRACE_DEBUG(
			m_am_tools, 
			TRACE_FLAGS_DEFAULT, 
			(EAPL("WARNING: eap_type_aka_c::handle_challenge_request_message(): ")
			 EAPL("Re-transmitted message %d=%s dropped in eap_type_aka_state_variable_e %d=%s.\n"),
			 received_aka->get_subtype(),
			 received_aka->get_subtype_string(),
			 get_state(),
			 get_state_string()));
		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return EAP_STATUS_RETURN(m_am_tools, eap_status_drop_packet_quietly);
	}
	else
	{
		// Wrong message in this state.
		EAP_TRACE_DEBUG(
			m_am_tools, 
			TRACE_FLAGS_AKA_ERROR, 
			(EAPL("ERROR: eap_type_aka_c::handle_challenge_request_message(): ")
			 EAPL("Wrong message %d=%s in eap_type_aka_state_variable_e %d=%s.\n"),
			 received_aka->get_subtype(),
			 received_aka->get_subtype_string(),
			 get_state(),
			 get_state_string()));
		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return EAP_STATUS_RETURN(m_am_tools, eap_status_header_corrupted);
	}
}

//--------------------------------------------------

//
EAP_FUNC_EXPORT eap_status_e eap_type_aka_c::handle_reauthentication_request_message(
	const eap_am_network_id_c * const receive_network_id,
	aka_header_c * const received_aka,
	const u32_t aka_packet_length,
	aka_payloads_c * const p_aka_payloads)
{
	eap_status_e status = eap_status_process_general_error;

	if (get_state() == eap_type_aka_state_waiting_for_reauth_request
		|| (get_state() == eap_type_aka_state_waiting_for_success && m_use_result_indication == false)
		|| (get_state() == eap_type_aka_state_waiting_for_notification_request_success && m_use_result_indication == true))
	{
		// This could be first or retransmission request.

		// Checks the payloads existence.
		if (p_aka_payloads->check_payloads(
				aka_payloads_c::eap_aka_payload_status_must_not_be, // nonce_s
				aka_payloads_c::eap_aka_payload_status_must_be,     // MAC
				aka_payloads_c::eap_aka_payload_status_must_be,     // ENCR_DATA
				aka_payloads_c::eap_aka_payload_status_must_not_be, // IDENTITY
				aka_payloads_c::eap_aka_payload_status_must_not_be, // padding
				aka_payloads_c::eap_aka_payload_status_must_not_be, // n_RANDs
				aka_payloads_c::eap_aka_payload_status_must_not_be, // AUTN
				aka_payloads_c::eap_aka_payload_status_must_not_be, // AUTS
				aka_payloads_c::eap_aka_payload_status_must_not_be, // RES
				aka_payloads_c::eap_aka_payload_status_must_not_be, // PERMANENT_ID_REQ
				aka_payloads_c::eap_aka_payload_status_must_not_be, // FULLAUTH_ID_REQ
				aka_payloads_c::eap_aka_payload_status_must_not_be, // ANY_ID_REQ
				aka_payloads_c::eap_aka_payload_status_must_be,     // IV
				aka_payloads_c::eap_aka_payload_status_must_not_be, // NEXT_PSEUDONYM
				aka_payloads_c::eap_aka_payload_status_must_not_be, // NEXT_REAUTH_ID
				aka_payloads_c::eap_aka_payload_status_must_not_be, // NOTIFICATION
				aka_payloads_c::eap_aka_payload_status_must_not_be, // COUNTER
				aka_payloads_c::eap_aka_payload_status_must_not_be, // COUNTER_TOO_SMALL
				aka_payloads_c::eap_aka_payload_status_must_not_be, // CLIENT_ERROR_CODE
				aka_payloads_c::eap_aka_payload_status_optional,    // RESULT_IND
				aka_payloads_c::eap_aka_payload_status_optional     // CHECKCODE
				) == true
			)
		{

			set_last_eap_identifier(received_aka->get_identifier());

			save_current_state();
			set_state(eap_type_aka_state_analyses_reauthentication_request);

			// - - - - - - - - - - - - - - - - - - - - - - - -

			eap_variable_data_c orig_XKEY(m_am_tools);
			eap_variable_data_c orig_K_aut(m_am_tools);
			eap_variable_data_c orig_K_encr(m_am_tools);

			if (orig_XKEY.get_is_valid() == false
				|| orig_K_aut.get_is_valid() == false
				|| orig_K_encr.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 reauth_counter = 0u;
			// In order to use re-authentication, the client and the server need to
			// store the following values: original XKEY, K_aut, K_encr, latest
			// counter value and the next re-authentication identity.
			status = m_am_type_aka->query_reauth_parameters(
				&orig_XKEY,
				&orig_K_aut,
				&orig_K_encr,
				&reauth_counter);
			if (status != eap_status_ok)
			{
				restore_saved_previous_state();
				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
				return EAP_STATUS_RETURN(m_am_tools, status);
			}

			status = check_message_authentication_code(
				&orig_K_aut,
				p_aka_payloads,
				received_aka,
				aka_packet_length);
			if (status != eap_status_ok)
			{
				(void) initialize_error_message(status);

				restore_saved_previous_state();
				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
				return EAP_STATUS_RETURN(m_am_tools, status);
			}

			if (p_aka_payloads->get_CHECKCODE()->get_payload_included() == true)
			{
				// Verify AT_CHECKCODE.
				status = checkcode_verify(
					p_aka_payloads->get_CHECKCODE()->get_payload_buffer());
				if (status != eap_status_ok)
				{
					EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
					return EAP_STATUS_RETURN(m_am_tools, status);
				}
			}

			if (p_aka_payloads->get_RESULT_IND()->get_payload_included() == true)
			{
				m_use_result_indication = m_allow_use_result_indication;
			}
			else
			{
				m_use_result_indication = false;
			}

			// Decrypt and parse encrypted payload.
			if (p_aka_payloads->get_IV()->get_payload_included() == true
			|| p_aka_payloads->get_ENCR_DATA()->get_payload_included() == true)
			{
				if (p_aka_payloads->get_IV()->get_payload_included() == true
				&& p_aka_payloads->get_ENCR_DATA()->get_payload_included() == true
				&& p_aka_payloads->get_MAC()->get_payload_included() == true)
				{
					aka_payloads_c * const l_aka_payloads = new aka_payloads_c(m_am_tools);
					eap_automatic_variable_c<aka_payloads_c> l_aka_payloads_automatic(m_am_tools, l_aka_payloads);

					if (l_aka_payloads == 0
						|| l_aka_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);
					}

					bool include_at_counter_too_small = false;

					status = decrypt_DATA_payload(
						p_aka_payloads,
						&orig_K_encr);
					if (status != eap_status_ok)
					{
						restore_saved_previous_state();
						EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
						return EAP_STATUS_RETURN(m_am_tools, status);
					}

					u32_t state_payload_buffer_length = p_aka_payloads->get_ENCR_DATA()->get_data_length();
					const aka_payload_AT_header_c gp_data_payload(
						m_am_tools,
						p_aka_payloads->get_ENCR_DATA()->get_data(state_payload_buffer_length),
						state_payload_buffer_length);

					status = parse_aka_payload(
						&gp_data_payload,
						&state_payload_buffer_length,
						l_aka_payloads,
						received_aka->get_subtype());
					if (status != eap_status_ok)
					{
						restore_saved_previous_state();
						EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
						return EAP_STATUS_RETURN(m_am_tools, status);
					}


					if (l_aka_payloads->get_COUNTER()->get_payload_included() == false
						|| l_aka_payloads->get_NONCE_S()->get_payload_included() == false)
					{
						restore_saved_previous_state();
						EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
						return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
					}

					u32_t counter = l_aka_payloads->get_COUNTER()->get_original_header()->get_reserved();

					if (m_aka_test_version == true
						&& m_fail_reauthentication_counter_check == true)
					{
						--counter;
					}

					if (get_saved_previous_state() == eap_type_aka_state_waiting_for_reauth_request)
					{
						if (counter < reauth_counter)
						{
							// When the client detects that the
							// counter value is not fresh, it includes the AT_COUNTER_TOO_SMALL
							// attribute in EAP-Response/AKA/Re-authentication. This attribute
							// doesn't contain any data but it is a request for the server to
							// initiate full authentication. In this case, the client MUST ignore
							// the contents of the server's AT_NEXT_REAUTH_ID attribute.
							include_at_counter_too_small = true;
							l_aka_payloads->get_NEXT_REAUTH_ID()->reset();
						}
					}
					else if (get_saved_previous_state() == eap_type_aka_state_waiting_for_success
						|| get_state() == eap_type_aka_state_waiting_for_notification_request_success)
					{
						// This is retransmission request.
						if (counter+1ul < reauth_counter)
						{
							// When the client detects that the
							// counter value is not fresh, it includes the AT_COUNTER_TOO_SMALL
							// attribute in EAP-Response/AKA/Re-authentication. This attribute
							// doesn't contain any data but it is a request for the server to
							// initiate full authentication. In this case, the client MUST ignore
							// the contents of the server's AT_NEXT_REAUTH_ID attribute.
							include_at_counter_too_small = true;
							l_aka_payloads->get_NEXT_REAUTH_ID()->reset();
						}
					}

					if (l_aka_payloads->get_NEXT_REAUTH_ID()->get_payload_included() == true)
					{
						// Save next re-authentication identity.
						// AKA AM could store pseudonym to favourite place.
						status = m_am_type_aka->store_reauthentication_id(
							get_send_network_id(),
							l_aka_payloads->get_NEXT_REAUTH_ID()->get_payload_buffer());
						if (status != eap_status_ok)
						{
							restore_saved_previous_state();
							EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
							return EAP_STATUS_RETURN(m_am_tools, status);
						}
					}
					else
					{
						// AKA AM should remove re-authentiction ID from favourite place.

						EAP_TRACE_DEBUG(
							m_am_tools, 
							TRACE_FLAGS_DEFAULT, 
							(EAPL("eap_type_aka_c::handle_reauthentication_request_message(0x%08x): Resets re-authentication identity from database.\n"),
							 this));

						status = m_am_type_aka->store_reauthentication_id(
							get_send_network_id(),
							0);
						if (status != eap_status_ok)
						{
							EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
							return EAP_STATUS_RETURN(m_am_tools, status);
						}

						get_reauthentication_identity()->reset();
					}

					status = get_NONCE_S()->set_copy_of_buffer(
						l_aka_payloads->get_NONCE_S()->get_payload_buffer());
					if (status != eap_status_ok)
					{
						restore_saved_previous_state();
						EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
						return EAP_STATUS_RETURN(m_am_tools, status);
					}

					status = send_reauthentication_response_message(
						&orig_XKEY,
						&orig_K_aut,
						&orig_K_encr,
						get_NAI(),
						l_aka_payloads->get_NONCE_S()->get_payload_buffer(),
						static_cast<u16_t>(counter),
						received_aka->get_identifier(),
						include_at_counter_too_small);
					if (status != eap_status_ok)
					{
						restore_saved_previous_state();
						EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
						return EAP_STATUS_RETURN(m_am_tools, status);
					}

					// OK, server re-authenticates correcly.

					if (include_at_counter_too_small == true)
					{
						// Re-authentication failed.
						// We will expect full authentication.
						set_state(eap_type_aka_state_waiting_for_aka_identity_request);
						status = eap_status_ok;
					}
					else
					{
						// Authentication was successful.
						if (m_use_result_indication == true)
						{
							// This version waits the result indication.
							set_state(eap_type_aka_state_waiting_for_notification_request_success);
							status = eap_status_ok;

							EAP_TRACE_DEBUG(
								m_am_tools, 
								TRACE_FLAGS_DEFAULT|TRACE_TEST_VECTORS, 
								(EAPL("EAP_type_AKA: %s, re-authentication OK, waits result indication\n"),
								 (m_is_client == true) ? "client": "server"));
						}
						else if (m_wait_eap_success_packet == false)
						{
							// This version does not wait EAP-Success message.
							status = finish_successful_authentication(
								receive_network_id);
							if (status != eap_status_ok)
							{
								restore_saved_previous_state();
								EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
								return EAP_STATUS_RETURN(m_am_tools, status);
							}
						}
						else
						{
							// This version waits the EAP-Success message.
							set_state(eap_type_aka_state_waiting_for_success);
							status = eap_status_ok;

							EAP_TRACE_DEBUG(
								m_am_tools, 
								TRACE_FLAGS_DEFAULT|TRACE_TEST_VECTORS, 
								(EAPL("EAP_type_AKA: %s, re-authentication OK, waits EAP-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);
				}
				else
				{
					// All of these must be included
					// or none of these must be included.
					restore_saved_previous_state();
					return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_payload);
				}
			}
			else
			{
				// Not correct AKA-payloads are included.
				restore_saved_previous_state();
				EAP_TRACE_DEBUG(
					m_am_tools, 
					TRACE_FLAGS_AKA_ERROR, 
					(EAPL("ERROR: eap_type_aka_c::handle_reauthentication_request_message(6): ")
					 EAPL("Not correct AKA-payloads are included in eap_type_aka_state_variable_e %d=%s.\n"),
					 get_state(),
					 get_state_string()));
				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
				return EAP_STATUS_RETURN(m_am_tools, eap_status_header_corrupted);
			}
		}
		else
		{
			// Not correct AKA-payloads are included.
			EAP_TRACE_DEBUG(
				m_am_tools, 
				TRACE_FLAGS_AKA_ERROR, 
				(EAPL("ERROR: eap_type_aka_c::handle_reauthentication_request_message(6): ")
				 EAPL("Not correct AKA-payloads are included in eap_type_aka_state_variable_e %d=%s.\n"),
				 get_state(),
				 get_state_string()));
			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
			return EAP_STATUS_RETURN(m_am_tools, eap_status_header_corrupted);
		}
	}
	else
	{
		// Wrong message in this state.
		EAP_TRACE_DEBUG(
			m_am_tools, 
			TRACE_FLAGS_AKA_ERROR, 
			(EAPL("ERROR: eap_type_aka_c::handle_reauthentication_request_message(): ")
			 EAPL("Wrong message %d=%s in eap_type_aka_state_variable_e %d=%s.\n"),
			 received_aka->get_subtype(),
			 received_aka->get_subtype_string(),
			 get_state(),
			 get_state_string()));
		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return EAP_STATUS_RETURN(m_am_tools, eap_status_header_corrupted);
	}
}

//--------------------------------------------------

//
eap_status_e eap_type_aka_c::handle_eap_identity_query(
	const eap_am_network_id_c * const send_network_id,
	eap_variable_data_c * const p_identity,
	const u8_t eap_identifier,
	const eap_variable_data_c * const IMSI,
	const eap_variable_data_c * const pseudonym,
	const eap_variable_data_c * const reauthentication_identity,
	const eap_variable_data_c * const automatic_realm, ///< If this is not used, do not add any data to this parameter.
	const u32_t length_of_mnc,
	const bool must_be_synchronous)
{
	const eap_variable_data_c *current_identity = 0;

	eap_variable_data_c manual_identity(m_am_tools);

	if (manual_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);
	}

	bool IMSI_is_used = false;
	bool reauth_id_is_used = false;
	eap_status_e status = eap_status_process_general_error;

	if (get_state() != eap_type_aka_state_pending_identity_query)
	{
		// Authentication is state is wrong. Cannot continue.
		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return EAP_STATUS_RETURN(m_am_tools, eap_status_wrong_gsmsim_state);
	}

	if (must_be_synchronous == true
		&& p_identity == 0)
	{
		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return EAP_STATUS_RETURN(m_am_tools, eap_status_process_general_error);
	}

	if (IMSI == 0
		|| IMSI->get_is_valid_data() == false
		|| IMSI->get_data_length() < EAP_TYPE_AKA_MINIMUM_IMSI_LENGTH)
	{
		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_eap_identity);
	}


	// We must save queried re-authentication and pseudonym identity.
	if (reauthentication_identity != 0
		&& reauthentication_identity->get_is_valid_data() == true)
	{
		status = get_reauthentication_identity()->set_copy_of_buffer(reauthentication_identity);
		if (status != eap_status_ok)
		{
			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
			return EAP_STATUS_RETURN(m_am_tools, status);
		}
	}

	if (pseudonym != 0
		&& pseudonym->get_is_valid_data() == true)
	{
		status = get_pseudonym()->set_copy_of_buffer(pseudonym);
		if (status != eap_status_ok)
		{
			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
			return EAP_STATUS_RETURN(m_am_tools, status);
		}
	}


	if (m_use_manual_username == true
		&& m_manual_username.get_is_valid_data() == true
		&& m_use_manual_realm == true
		&& m_manual_realm.get_is_valid_data() == true)
	{
		// Here manual username could be zero or more bytes in length.
		EAP_TRACE_DEBUG(
			m_am_tools, 
			TRACE_FLAGS_DEFAULT, 
			(EAPL("eap_type_aka_c::handle_eap_identity_query(): Client sends manual username and realm.\n")));

		status = manual_identity.set_copy_of_buffer(&m_manual_username);
		if (status != eap_status_ok)
		{
			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
			return EAP_STATUS_RETURN(m_am_tools, status);
		}

		current_identity = &manual_identity;
		IMSI_is_used = false;
	}
	else if (m_use_manual_username == true
			 && m_manual_username.get_is_valid_data() == true
			 && m_use_manual_realm == false)
	{
		// We will send manual username with automatic realm.
		EAP_TRACE_DEBUG(
			m_am_tools, 
			TRACE_FLAGS_DEFAULT, 
			(EAPL("eap_type_aka_c::handle_eap_identity_query(): Client sends manual username and automatic realm.\n")));

		status = manual_identity.set_copy_of_buffer(&m_manual_username);
		if (status != eap_status_ok)
		{
			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
			return EAP_STATUS_RETURN(m_am_tools, status);
		}
		
		current_identity = &manual_identity;
		IMSI_is_used = false;
	}
	else if (reauthentication_identity != 0
		&& reauthentication_identity->get_is_valid_data() == true)
	{
		EAP_TRACE_DEBUG(
			m_am_tools, 
			TRACE_FLAGS_DEFAULT, 
			(EAPL("eap_type_aka_c::handle_eap_identity_query(): Client sends re-authentication identity.\n")));

		current_identity = reauthentication_identity;
		IMSI_is_used = false;
		reauth_id_is_used = true;

		status = get_reauthentication_identity()->set_copy_of_buffer(reauthentication_identity);
		if (status != eap_status_ok)
		{
			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
			return EAP_STATUS_RETURN(m_am_tools, status);
		}
	}
	else if (pseudonym != 0
		&& pseudonym->get_is_valid_data() == true)
	{
		EAP_TRACE_DEBUG(
			m_am_tools, 
			TRACE_FLAGS_DEFAULT, 
			(EAPL("eap_type_aka_c::handle_eap_identity_query(): Client sends pseudonym username.\n")));

		current_identity = pseudonym;
		IMSI_is_used = false;

		status = get_pseudonym()->set_copy_of_buffer(pseudonym);
		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_use_random_identity_on_eap_identity_response == true)
	{
		// Generate random username.
		EAP_TRACE_DEBUG(
			m_am_tools, 
			TRACE_FLAGS_DEFAULT, 
			(EAPL("eap_type_aka_c::handle_eap_identity_query(): Client sends random username.\n")));

		const u32_t EAP_TLS_PEAP_RANDOM_IDENTITY_LENGTH = 16;

		eap_variable_data_c random_username(m_am_tools);
		eap_variable_data_c random_bytes(m_am_tools);

		if (random_username.get_is_valid() == false
			|| random_bytes.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 = random_bytes.init(EAP_TLS_PEAP_RANDOM_IDENTITY_LENGTH);
		if (status != eap_status_ok)
		{
			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
			return EAP_STATUS_RETURN(m_am_tools, status);
		}
		random_bytes.set_is_valid();
		random_bytes.set_data_length(EAP_TLS_PEAP_RANDOM_IDENTITY_LENGTH);
		
		status = m_am_tools->get_crypto()->get_rand_bytes(
			random_bytes.get_data(random_bytes.get_data_length()),
			random_bytes.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);
		}
		
		// Change first byte to 'r' to make it different than the default IMSI prefix '1'.
		// This will cause the first identity char 'S' after ascii armor conversion.
		*random_bytes.get_data(1ul) = static_cast<u8_t>('r');

		const u32_t EAP_AKA_RANDOM_IDENTITY_BUFFER_LENGTH
			= 3ul+(EAP_TLS_PEAP_RANDOM_IDENTITY_LENGTH+1u)*4u/3u;
		
		status = random_username.init(EAP_AKA_RANDOM_IDENTITY_BUFFER_LENGTH);
		if (status != eap_status_ok)
		{
			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
			return EAP_STATUS_RETURN(m_am_tools, status);
		}

		random_username.set_is_valid();
		random_username.set_data_length(EAP_AKA_RANDOM_IDENTITY_BUFFER_LENGTH);
		
		u32_t random_identity_length = random_username.get_data_length();
		
		status = m_am_tools->convert_bytes_to_ascii_armor(
			random_bytes.get_data_offset(0u, random_bytes.get_data_length()),
			random_bytes.get_data_length(),
			random_username.get_data_offset(0u, random_username.get_data_length()),
			&random_identity_length);
		if (status != eap_status_ok)
		{
			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
			return EAP_STATUS_RETURN(m_am_tools, status);
		}
		random_username.set_data_length(random_identity_length);
		
		status = manual_identity.set_copy_of_buffer(&random_username);
		if (status != eap_status_ok)
		{
			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
			return EAP_STATUS_RETURN(m_am_tools, status);
		}

		current_identity = &manual_identity;
		IMSI_is_used = false;
	}
	else if (IMSI != 0
		&& IMSI->get_is_valid_data() == true
		&& IMSI->get_data_length() >= EAP_TYPE_AKA_MINIMUM_IMSI_LENGTH)
	{
		EAP_TRACE_DEBUG(
			m_am_tools, 
			TRACE_FLAGS_DEFAULT, 
			(EAPL("eap_type_aka_c::handle_eap_identity_query(): Client sends IMSI username.\n")));

		current_identity = IMSI;
		IMSI_is_used = true;
	}

	if (current_identity != 0)
	{
		status = get_IMSI()->set_copy_of_buffer(IMSI);
		if (status != eap_status_ok)
		{
			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
			return EAP_STATUS_RETURN(m_am_tools, status);
		}

		status = store_identity(current_identity, IMSI_is_used);
		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("current_identity"),
			current_identity->get_data(current_identity->get_data_length()),
			current_identity->get_data_length()));

		eap_variable_data_c NAI(m_am_tools);

		if (NAI.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 (reauth_id_is_used == true)
		{
			// Re-authentication identity is NAI.
			status = NAI.set_copy_of_buffer(current_identity);
			if (status != eap_status_ok)
			{
				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
				return EAP_STATUS_RETURN(m_am_tools, status);
			}
		}
		else
		{
			status = generate_nai(
				&NAI,
				m_use_manual_realm,
				get_nai_realm(),
				current_identity,
				IMSI_is_used,
				get_IMSI(),
				automatic_realm,
				length_of_mnc);
		}

		if (status == eap_status_ok)
		{
			status = get_NAI()->set_copy_of_buffer(&NAI);
			if (status != eap_status_ok)
			{
				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
				return EAP_STATUS_RETURN(m_am_tools, status);
			}

			if (must_be_synchronous == false)
			{
				status = get_type_partner()->complete_eap_identity_query(
					send_network_id,
					get_NAI(),
					eap_identifier);
				if (status == eap_status_ok)
				{
					if (p_identity != 0)
					{
						status = p_identity->set_copy_of_buffer(get_NAI());
					}

					if (status == eap_status_ok)
					{
						status = eap_status_completed_request;
					}
				}		
			}
			else
			{
				status = p_identity->set_copy_of_buffer(get_NAI());
			}

			if (status == eap_status_ok
				|| status == eap_status_completed_request)
			{
				if (m_use_random_identity_on_eap_identity_response == true)
				{
					m_authentication_type = AKA_AUTHENTICATION_TYPE_NONE;
					set_identity_type(AKA_IDENTITY_TYPE_NONE);
					set_state(eap_type_aka_state_waiting_for_aka_identity_request);

					EAP_TRACE_DEBUG(
						m_am_tools, 
						TRACE_FLAGS_DEFAULT, 
						(EAPL("eap_type_aka_c::handle_eap_identity_query(0x%08x): Selects none.\n"),
						 this));
				}
				else if (reauthentication_identity != 0
					&& reauthentication_identity->get_is_valid_data() == true)
				{
					m_authentication_type = AKA_AUTHENTICATION_TYPE_REAUTHENTICATION;
					set_identity_type(AKA_IDENTITY_TYPE_RE_AUTH_ID);
					set_state(eap_type_aka_state_waiting_for_reauth_request);

					EAP_TRACE_DEBUG(
						m_am_tools, 
						TRACE_FLAGS_DEFAULT, 
						(EAPL("eap_type_aka_c::handle_eap_identity_query(0x%08x): Selects fast re-authentication and re-authentication identity.\n"),
						 this));
				}
				else if (pseudonym != 0
					&& pseudonym->get_is_valid_data() == true)
				{
					m_authentication_type = AKA_AUTHENTICATION_TYPE_FULL_AUTH;
					set_identity_type(AKA_IDENTITY_TYPE_PSEUDONYM_ID);
					set_state(eap_type_aka_state_waiting_for_challenge_request);

					EAP_TRACE_DEBUG(
						m_am_tools, 
						TRACE_FLAGS_DEFAULT, 
						(EAPL("eap_type_aka_c::handle_eap_identity_query(0x%08x): Selects full authentication and pseudonym identity.\n"),
						 this));
				}
				else
				{
					if (m_use_manual_username == true)
					{
						m_authentication_type = AKA_AUTHENTICATION_TYPE_NONE;
						set_identity_type(AKA_IDENTITY_TYPE_NONE);
						set_state(eap_type_aka_state_waiting_for_aka_identity_request);

						EAP_TRACE_DEBUG(
							m_am_tools, 
							TRACE_FLAGS_DEFAULT, 
							(EAPL("eap_type_aka_c::handle_eap_identity_query(0x%08x): Selects none.\n"),
							 this));
					}
					else
					{
						m_authentication_type = AKA_AUTHENTICATION_TYPE_FULL_AUTH;
						set_identity_type(AKA_IDENTITY_TYPE_IMSI_ID);
						set_state(eap_type_aka_state_waiting_for_challenge_request);

						EAP_TRACE_DEBUG(
							m_am_tools, 
							TRACE_FLAGS_DEFAULT, 
							(EAPL("eap_type_aka_c::handle_eap_identity_query(0x%08x): Selects full authentication and IMSI identity.\n"),
							 this));
					}
				}
			}
		}
	}
	else
	{
		status = eap_status_illegal_nai;
	}

	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
	return EAP_STATUS_RETURN(m_am_tools, status);
}

//--------------------------------------------------

//
eap_status_e eap_type_aka_c::query_eap_identity(
	const bool must_be_synchronous,
	eap_variable_data_c * const identity,
	const eap_am_network_id_c * const receive_network_id,
	const u8_t eap_identifier)
{
	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);

	eap_variable_data_c IMSI(m_am_tools);
	eap_variable_data_c pseudonym(m_am_tools);
	eap_variable_data_c reauthentication_identity(m_am_tools);

	if (IMSI.get_is_valid() == false
		|| pseudonym.get_is_valid() == false
		|| reauthentication_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);
	}

	// Here we swap the addresses.
	eap_am_network_id_c send_network_id(m_am_tools,
		receive_network_id->get_destination_id(),
		receive_network_id->get_source_id(),
		receive_network_id->get_type());

	eap_status_e status = eap_status_process_general_error;

	if (get_state() == eap_type_aka_state_none)
	{
		status = new_handler(
			receive_network_id,
			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_DEBUG(
		m_am_tools, 
		TRACE_FLAGS_DEFAULT, 
		(EAPL("eap_type_aka_c::query_eap_identity(): eap_type_aka_state_variable_e %d=%s.\n"),
		get_state(),
		get_state_string()));

	if (get_state() == eap_type_aka_state_waiting_for_identity_request
		|| (m_aka_test_version == true
			&& get_state() == eap_type_aka_state_success)) // This one is for testing purposes.
	{
		set_last_eap_identifier(eap_identifier);

		eap_variable_data_c automatic_realm(m_am_tools);
		if (automatic_realm.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 = query_AKA_IMSI_or_pseudonym_or_reauthentication_id(
			&IMSI,
			&pseudonym,
			&reauthentication_identity,
			&automatic_realm,
			&m_length_of_mnc,
			must_be_synchronous,
			aka_payload_AT_ANY_ID_REQ,
			eap_type_aka_complete_query_eap_identity,
			eap_identifier);
		if (status == eap_status_pending_request)
		{
			// This is pending query, that will be completed by complete_AKA_IMSI_or_pseudonym_or_reauthentication_id_query() call.
			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
			return EAP_STATUS_RETURN(m_am_tools, status);
		}
		else if (status == eap_status_completed_request)
		{
			// This is already completed by complete_AKA_IMSI_or_pseudonym_or_reauthentication_id_query() call.
			EAP_TRACE_DEBUG(
				m_am_tools, 
				TRACE_FLAGS_DEFAULT, 
				(EAPL("eap_type_aka_c::query_eap_identity(): ")
				 EAPL("completed returns eap_type_aka_state_variable_e %d=%s.\n"),
				 get_state(),
				 get_state_string()));

			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
			return EAP_STATUS_RETURN(m_am_tools, status);
		}
		else if (status != eap_status_ok)
		{
			// This is the error case.
			restore_saved_previous_state();

			EAP_TRACE_DEBUG(
				m_am_tools, 
				TRACE_FLAGS_DEFAULT, 
				(EAPL("eap_type_aka_c::query_eap_identity(): ")
				 EAPL("error returns eap_type_aka_state_variable_e %d=%s.\n"),
				 get_state(),
				 get_state_string()));

			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
			return EAP_STATUS_RETURN(m_am_tools, status);
		}
		else // status == eap_status_ok
		{
			status = handle_eap_identity_query(
				&send_network_id,
				identity,
				eap_identifier,
				&IMSI,
				&pseudonym,
				&reauthentication_identity,
				&automatic_realm,
				m_length_of_mnc,
				must_be_synchronous);

			if (status != eap_status_ok
				&& status != eap_status_completed_request)
			{
				restore_saved_previous_state();
			}

			EAP_TRACE_DEBUG(
				m_am_tools, 
				TRACE_FLAGS_DEFAULT, 
				(EAPL("eap_type_aka_c::query_eap_identity(): handle_eap_identity_query() ")
				 EAPL("returns eap_type_aka_state_variable_e %d=%s.\n"),
				 get_state(),
				 get_state_string()));

			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
			return EAP_STATUS_RETURN(m_am_tools, status);
		}
	}
	else if (get_state() == eap_type_aka_state_waiting_for_aka_identity_request
		|| get_state() == eap_type_aka_state_pseydonym_waiting_for_aka_identity_request
		|| get_state() == eap_type_aka_state_imsi_waiting_for_aka_identity_request
		|| get_state() == eap_type_aka_state_waiting_for_reauth_request)
	{
		// This is re-transmission request. We do not change our state.
		// Just send EAP-Identity again.
		if (get_NAI()->get_is_valid_data() == true)
		{
			status = identity->set_copy_of_buffer(get_NAI());

			EAP_TRACE_DEBUG(
				m_am_tools, 
				TRACE_FLAGS_DEFAULT, 
				(EAPL("eap_type_aka_c::query_eap_identity(): handle_eap_identity_query() ")
				 EAPL("returns eap_type_aka_state_variable_e %d=%s.\n"),
				 get_state(),
				 get_state_string()));
		}
		else
		{
			EAP_TRACE_DEBUG(
				m_am_tools, 
				TRACE_FLAGS_AKA_ERROR, 
				(EAPL("ERROR: eap_type_aka_c::query_eap_identity(): ")
				 EAPL("EAP-Request/Identity cannot be completed, identity (NAI) ")
				 EAPL("is missing. in eap_type_aka_state_variable_e %d=%s.\n"),
				 get_state(),
				 get_state_string()));
			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);
	}
	else if (get_state() == eap_type_aka_state_pending_identity_query)
	{
		EAP_TRACE_DEBUG(
			m_am_tools, 
			TRACE_FLAGS_DEFAULT, 
			(EAPL("eap_type_aka_c::query_eap_identity(): ignores re-transmitted query_eap_identity()")
			 EAPL("in eap_type_aka_state_variable_e %d=%s.\n"),
			 get_state(),
			 get_state_string()));
		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return EAP_STATUS_RETURN(m_am_tools, eap_status_pending_request);
	}


	// Wrong message in this state.
	EAP_TRACE_DEBUG(
		m_am_tools, 
		TRACE_FLAGS_AKA_ERROR, 
		(EAPL("WARNING: eap_type_aka_c::query_eap_identity(): ")
		 EAPL("Wrong message EAP-Request/Identity in eap_type_aka_state_variable_e %d=%s.\n"),
		 get_state(),
		 get_state_string()));		
	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
	return EAP_STATUS_RETURN(m_am_tools, eap_status_header_corrupted);
}

//--------------------------------------------------

EAP_FUNC_EXPORT eap_status_e eap_type_aka_c::set_initial_eap_identifier(
	const eap_am_network_id_c * const receive_network_id,
	const u8_t initial_identifier)
{
	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);

	eap_status_e status = new_handler(
		receive_network_id,
		false);
	if (status != eap_status_ok)
	{
		EAP_TRACE_DEBUG(
			m_am_tools, 
			TRACE_FLAGS_AKA_ERROR, 
			(EAPL("ERROR: eap_type_aka_c::aka_packet_process(): ")
			 EAPL("new_handler() failed, status %d.\n"),
			 status));
		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return EAP_STATUS_RETURN(m_am_tools, status);
	}

	set_last_eap_identifier(static_cast<u8_t>(initial_identifier-1u));

	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
	return EAP_STATUS_RETURN(m_am_tools, status);
}

//--------------------------------------------------

EAP_FUNC_EXPORT eap_status_e eap_type_aka_c::eap_acknowledge(
	const eap_am_network_id_c * const receive_network_id)
{
	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);

	eap_status_e status(eap_status_ok);

	if (get_state() == eap_type_aka_state_waiting_for_success
		&& m_wait_eap_success_packet == true)
	{
		status = finish_successful_authentication(
			receive_network_id);
	}
	else
	{
		EAP_TRACE_DEBUG(
			m_am_tools, 
			TRACE_FLAGS_DEFAULT, 
			(EAPL("ignored eap_acknowledge(): state %d=%s, is client %d\n"),
			get_state(),
			get_state_string(),
			(m_is_client == true)));
	}

	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
	return EAP_STATUS_RETURN(m_am_tools, status);
}

//--------------------------------------------------

#if defined(_DEBUG) && defined(USE_EAP_AKA_TEST_VECTORS)
	#include "read_rand_and_triplets.h"
#endif //#if defined(_DEBUG) && defined(USE_EAP_AKA_TEST_VECTORS)


//
EAP_FUNC_EXPORT eap_status_e eap_type_aka_c::generate_nonce(
	const u32_t nonce_size,
	eap_variable_data_c * const nonce)
{
	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);

	eap_status_e status = nonce->init(nonce_size);
	if (status != eap_status_ok)
	{
		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return EAP_STATUS_RETURN(m_am_tools, status);
	}
	nonce->set_is_valid();
	nonce->set_data_length(nonce_size);

#if defined(_DEBUG) && defined(USE_EAP_AKA_TEST_VECTORS)
	if (m_nonce_mt_file.get_is_valid() == true)
	{
		status = read_rand_from_file(m_am_tools, &m_nonce_mt_file, nonce);
		if (status != eap_status_ok)
		{
			status = m_am_tools->get_crypto()->get_rand_bytes(
				nonce->get_data(nonce->get_data_length()),
				nonce->get_data_length());
		}
	}
	else
#endif //#if defined(_DEBUG) && defined(USE_EAP_AKA_TEST_VECTORS)
	{
		status = m_am_tools->get_crypto()->get_rand_bytes(
			nonce->get_data(nonce->get_data_length()),
			nonce->get_data_length());
	}

	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
	return EAP_STATUS_RETURN(m_am_tools, status);
}

//--------------------------------------------------



// End.
