eapol/eapol_framework/eapol_common/type/mschapv2/core/eap_type_mschapv2_client.cpp
changeset 0 c8830336c852
child 2 1c7bc153c08e
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/eapol/eapol_framework/eapol_common/type/mschapv2/core/eap_type_mschapv2_client.cpp	Thu Dec 17 08:47:43 2009 +0200
@@ -0,0 +1,1753 @@
+/*
+* Copyright (c) 2001-2006 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of the License "Eclipse Public License v1.0"
+* which accompanies this distribution, and is available
+* at the URL "http://www.eclipse.org/legal/epl-v10.html".
+*
+* Initial Contributors:
+* Nokia Corporation - initial contribution.
+*
+* Contributors:
+*
+* Description:  EAP and WLAN authentication protocols.
+*
+*/
+
+
+// This is enumeration of EAPOL source code.
+#if defined(USE_EAP_MINIMUM_RELEASE_TRACES)
+	#undef EAP_FILE_NUMBER_ENUM
+	#define EAP_FILE_NUMBER_ENUM 94 
+	#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_base_type.h"
+#include "eap_state_notification.h"
+#include "eap_type_mschapv2.h"
+#include "eap_am_type_mschapv2.h"
+#include "eap_buffer.h"
+#include "eap_master_session_key.h"
+#include "eap_network_id_selector.h"
+#include "eap_tlv_message_data.h"
+#include "eap_config.h"
+
+
+eap_status_e eap_type_mschapv2_c::client_packet_process(
+	eap_header_wr_c * const received_eap,
+	const u32_t eap_packet_length)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("eap_type_mschapv2_c::client_packet_process(): this = 0x%08x, m_session.get_state()=%d\n"),
+		this,
+		m_session.get_state()));
+
+	eap_status_e status = eap_status_drop_packet_quietly;
+
+	m_identifier = received_eap->get_identifier();
+
+	if (received_eap->get_type() == eap_type_identity)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_drop_packet_quietly);
+	}
+
+	if (received_eap->get_code() == eap_code_failure)
+	{
+		if (m_session.is_valid_state(eap_type_mschapv2_state_failure) == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_drop_packet_quietly);
+		}
+
+		status = finish_unsuccessful_authentication(false);
+
+		m_session.set_state(eap_type_mschapv2_state_failure);
+	}
+	else if (received_eap->get_code() == eap_code_success)
+	{
+		if (m_session.is_valid_state(eap_type_mschapv2_state_success) == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_drop_packet_quietly);
+		}
+
+		eap_master_session_key_c key(
+			m_am_tools,
+			eap_type_mschapv2);
+		if (key.get_is_valid() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+		}
+
+		status = generate_session_key(&key);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = get_type_partner()->packet_data_crypto_keys(&m_send_network_id, &key);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		// Check wait for eap success
+
+		status = finish_successful_authentication();
+
+		m_session.set_state(eap_type_mschapv2_state_success);
+	}
+	else if (received_eap->get_code() == eap_code_request) // Request
+	{
+		// MsChapV2
+		if (received_eap->get_type() == eap_type_mschapv2)
+		{
+			status = client_mschapv2_packet_process(
+				received_eap,
+				eap_packet_length);			    
+		}
+	}
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+eap_status_e eap_type_mschapv2_c::client_mschapv2_packet_process(
+	eap_header_wr_c * const received_eap,
+	const u32_t eap_packet_length)
+{	
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("eap_type_mschapv2_c::client_mschapv2_packet_process(): this = 0x%08x\n"),
+		this));
+
+	eap_status_e status = eap_status_drop_packet_quietly;
+
+	if (eap_packet_length <= eap_header_base_c::get_header_length())
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_drop_packet_quietly);
+	}
+	else if (eap_packet_length == received_eap->get_data_length())
+	{
+		// MsChapV2 packet without any data, unspecified packet?
+		// lets finish unsuccesfully?
+		if (m_session.is_valid_state(eap_type_mschapv2_state_failure) == true)
+		{
+			status = finish_unsuccessful_authentication(false);
+		}
+
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	const u32_t type_data_length = received_eap->get_type_data_length();
+
+	if (received_eap->get_type_data_offset(0, type_data_length) == 0)
+	{
+		// Not enough data.
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_drop_packet_quietly);
+	}
+
+	mschapv2_header_c mschapv2_header(
+		m_am_tools,
+		received_eap->get_type_data_offset(0, type_data_length),
+		type_data_length);
+
+	if (mschapv2_header.check_header() != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_drop_packet_quietly);
+	}
+
+	switch (mschapv2_header.get_opcode())
+	{
+	case mschapv2_opcode_challenge:
+		if (m_session.is_valid_state(eap_type_mschapv2_state_challenge_request) == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_drop_packet_quietly);
+		}
+		status = client_handle_challenge_request(mschapv2_header);
+		break;
+
+	case mschapv2_opcode_success:
+		if (m_session.is_valid_state(eap_type_mschapv2_state_success_request) == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_drop_packet_quietly);
+		}
+		status = client_handle_success_request(mschapv2_header);
+		break;
+
+	case mschapv2_opcode_failure:
+		if (m_session.is_valid_state(eap_type_mschapv2_state_failure_request) == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_drop_packet_quietly);
+		}
+		status = client_handle_failure_request(mschapv2_header);
+		break;
+
+	default:
+		break;
+	}
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+eap_status_e eap_type_mschapv2_c::client_handle_challenge_request(mschapv2_header_c &challenge_request)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("eap_type_mschapv2_c::client_handle_challenge_request(): this = 0x%08x\n"),
+		this));
+
+	eap_status_e status = eap_status_drop_packet_quietly;
+
+	if (challenge_request.get_data_length() < mschapv2_challenge_c::get_header_minimum_size())
+	{ 
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	m_mschapv2id = challenge_request.get_mschapv2_id();
+
+	mschapv2_challenge_c challenge_payload(
+		m_am_tools,
+		challenge_request.get_data(),
+		challenge_request.get_data_length());
+
+	if (challenge_payload.check_header() != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_drop_packet_quietly);
+	}
+
+	EAP_TRACE_DATA_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("client_handle_challenge_request(): mschapv2id"),
+		 &m_mschapv2id,
+		 sizeof(m_mschapv2id)));
+
+	EAP_TRACE_DATA_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("client_handle_challenge_request(): m_authenticator_challenge"),
+		 challenge_payload.get_challenge(),
+		 EAP_MSCHAPV2_AUTHENTICATOR_CHALLENGE_SIZE));
+
+	m_am_tools->memmove(
+		m_authenticator_challenge,
+		challenge_payload.get_challenge(),
+		EAP_MSCHAPV2_AUTHENTICATOR_CHALLENGE_SIZE);
+
+#if defined(USE_FAST_EAP_TYPE)
+	if (m_use_EAP_FAST_challenge == true)
+	{
+		// Note the received authenticator challenge is zero bytes in EAP-FAST.
+		if (m_server_EAP_FAST_challenge.get_is_valid_data() == false
+			|| m_server_EAP_FAST_challenge.get_data_length() != EAP_MSCHAPV2_AUTHENTICATOR_CHALLENGE_SIZE)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_authentication_failure);
+		}
+
+		m_am_tools->memmove(
+			m_authenticator_challenge,
+			m_server_EAP_FAST_challenge.get_data(),
+			EAP_MSCHAPV2_AUTHENTICATOR_CHALLENGE_SIZE);
+
+		EAP_TRACE_DATA_DEBUG(
+			m_am_tools,
+			TRACE_FLAGS_DEFAULT,
+			(EAPL("client_handle_challenge_request(): EAP-FAST m_authenticator_challenge"),
+			 m_authenticator_challenge,
+			 EAP_MSCHAPV2_AUTHENTICATOR_CHALLENGE_SIZE));
+	}
+#endif //#if defined(USE_FAST_EAP_TYPE)
+
+	// If no identity, ask it
+	if (!m_identity_asked
+		&& (m_password_prompt_enabled
+			|| m_username_utf8.get_is_valid_data() == false))
+	{
+		// Prefill username with identity if it is possible
+		if (m_username_utf8.get_is_valid_data() == false)
+		{
+			status = get_type_partner()->get_saved_eap_identity(&m_username_utf8);
+		}
+
+		// Open username/password dialog
+		status = m_am_type_mschapv2->show_username_password_dialog(
+			m_username_utf8,
+			m_password_utf8,
+			m_password_prompt_enabled,
+			false);
+	}
+	else
+	{
+		status = client_send_challenge_response();
+	}
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+eap_status_e eap_type_mschapv2_c::client_send_challenge_response()
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("eap_type_mschapv2_c::client_send_challenge_response(): this = 0x%08x\n"),
+		this));
+
+	eap_status_e status = m_rand.add_rand_seed_hw_ticks();
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+#if defined(USE_FAST_EAP_TYPE)
+	if (m_use_EAP_FAST_challenge == true
+		&& m_client_EAP_FAST_challenge.get_is_valid_data() == true
+		&& m_client_EAP_FAST_challenge.get_data_length() == EAP_MSCHAPV2_PEER_CHALLENGE_SIZE)
+	{
+		m_am_tools->memmove(
+			m_peer_challenge,
+			m_client_EAP_FAST_challenge.get_data(),
+			EAP_MSCHAPV2_PEER_CHALLENGE_SIZE);
+	}
+	else
+#endif //#if defined(USE_FAST_EAP_TYPE)
+	{
+		status = m_rand.get_rand_bytes(m_peer_challenge, EAP_MSCHAPV2_PEER_CHALLENGE_SIZE);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+
+	crypto_nt_hash_c nt_hash(m_am_tools);
+	if (nt_hash.get_is_valid() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	if (m_do_wrong_password_tests == true)
+	{
+		u8_t crap_from_stack;
+		status = m_password_utf8.add_data(&crap_from_stack, sizeof(crap_from_stack));
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+
+	status = nt_hash.nt_password_hash(
+		&m_password_utf8,
+		&m_password_hash,
+		EAP_MSCHAPV2_MD4_DIGEST_SIZE);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = nt_hash.hash_nt_password_hash(
+		&m_password_hash,
+		&m_password_hash_hash,
+		EAP_MSCHAPV2_MD4_DIGEST_SIZE);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	EAP_TRACE_DATA_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("EAP_type_MSCHAPV2: client_send_challenge_response(), m_password_utf8"),
+		m_password_utf8.get_data(),
+		m_password_utf8.get_data_length()));
+
+	EAP_TRACE_DATA_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("EAP_type_MSCHAPV2: client_send_challenge_response(), m_authenticator_challenge"),
+		m_authenticator_challenge,
+		EAP_MSCHAPV2_AUTHENTICATOR_CHALLENGE_SIZE));
+
+	EAP_TRACE_DATA_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("EAP_type_MSCHAPV2: client_send_challenge_response(), m_peer_challenge"),
+		m_peer_challenge,
+		EAP_MSCHAPV2_PEER_CHALLENGE_SIZE));
+
+	EAP_TRACE_DATA_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("EAP_type_MSCHAPV2: client_send_challenge_response(), m_password_hash"),
+		m_password_hash.get_data(),
+		m_password_hash.get_data_length()));
+
+	EAP_TRACE_DATA_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("EAP_type_MSCHAPV2: client_send_challenge_response(), m_username_utf8"),
+		m_username_utf8.get_data(),
+		m_username_utf8.get_data_length()));
+
+	EAP_TRACE_DATA_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("EAP_type_MSCHAPV2: client_send_challenge_response(): m_authenticator_challenge"),
+		 m_authenticator_challenge,
+		 EAP_MSCHAPV2_AUTHENTICATOR_CHALLENGE_SIZE));
+
+	status = generate_nt_response(
+		m_authenticator_challenge,
+		m_peer_challenge,
+		m_username_utf8.get_data(),
+		m_username_utf8.get_data_length(),
+		&m_password_hash,
+		m_nt_response);
+	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("EAP_type_MSCHAPV2: client_send_challenge_response(), m_nt_response:"),
+		m_nt_response,
+		EAP_MSCHAPV2_NT_RESPONSE_SIZE));
+
+	// Send response
+
+	const u32_t type_data_length = EAP_MSCHAPV2_HEADER_SIZE // OpCode, MS-CHAPv2-ID and MS-Length
+		+ mschapv2_response_c::get_header_minimum_size()
+		+ m_username_utf8.get_data_length();
+
+	const u32_t packet_length
+		= eap_header_base_c::get_type_data_start_offset(m_use_eap_expanded_type)
+		+ type_data_length;
+
+	eap_buf_chain_wr_c * packet = create_send_packet(packet_length);
+	if (!packet)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	eap_header_base_c eap_header(
+		m_am_tools,
+		packet->get_data_offset(m_offset, packet_length),
+		packet_length);
+	if (eap_header.get_is_valid() == false)
+	{
+		delete packet;
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+	}
+	eap_header.set_code(eap_code_response);
+	eap_header.set_identifier(m_identifier);
+	eap_header.set_length(
+		static_cast<u16_t>(packet_length),
+		m_use_eap_expanded_type);
+	eap_header.set_type(
+		eap_type_mschapv2,
+		m_use_eap_expanded_type);
+
+	mschapv2_header_c mschapv2_header(
+		m_am_tools,
+		eap_header.get_type_data_offset(0, eap_header.get_type_data_length()),
+		eap_header.get_type_data_length());
+
+	mschapv2_header.set_opcode(mschapv2_opcode_response);
+	mschapv2_header.set_mschapv2_id(m_mschapv2id);
+	mschapv2_header.set_ms_length(static_cast<u16_t>(type_data_length));
+
+	mschapv2_response_c response(
+		m_am_tools,
+		mschapv2_header.get_data(),
+		mschapv2_header.get_data_length());
+	if (response.get_is_valid() == false)
+	{
+		delete packet;
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	status = response.set_constants();
+	if (status != eap_status_ok)
+	{
+		delete packet;
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+
+#if defined(USE_FAST_EAP_TYPE)
+	if (m_use_EAP_FAST_challenge == true)
+	{
+		// EAP-FAST sends peer challenge containing only zeroes.
+		response.set_peer_challenge(EAP_MSCHAPV2_ZERO_CHALLENGE);
+	}
+	else
+#endif //#if defined(USE_FAST_EAP_TYPE)
+	{
+		response.set_peer_challenge(m_peer_challenge);
+	}
+
+	response.set_nt_response(m_nt_response);
+
+	// Copy username to end of response packet
+	u32_t username_ascii_size = m_username_utf8.get_data_length();
+	response.set_name(m_username_utf8.get_data(username_ascii_size));
+
+	// Send
+	status = packet_send(packet, packet_length);
+
+	delete packet;
+
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	m_session.set_state(eap_type_mschapv2_state_challenge_request);
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, eap_status_ok);
+}
+
+eap_status_e eap_type_mschapv2_c::client_handle_success_request(mschapv2_header_c & success_request)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("eap_type_mschapv2_c::client_handle_success_request(): this = 0x%08x\n"),
+		this));
+
+	eap_status_e status = eap_status_drop_packet_quietly;
+
+	if (success_request.get_data_length() < EAP_MSCHAPV2_SUCCESS_REQUEST_MESSAGE_MIN_LENGTH) // Check this out
+	{ 
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	m_mschapv2id = success_request.get_mschapv2_id();
+
+	u8_t * success_payload = success_request.get_data();
+	bool response_check;
+
+	eap_variable_data_c new_password_hash(m_am_tools);
+	eap_variable_data_c new_password_hash_hash(m_am_tools);
+
+	EAP_TRACE_DATA_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("client_handle_success_request(): m_authenticator_challenge"),
+		 m_authenticator_challenge,
+		 EAP_MSCHAPV2_AUTHENTICATOR_CHALLENGE_SIZE));
+
+	EAP_TRACE_DATA_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("client_handle_success_request(): m_peer_challenge"),
+		 m_peer_challenge,
+		 EAP_MSCHAPV2_PEER_CHALLENGE_SIZE));
+
+	EAP_TRACE_DATA_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("client_handle_success_request(): m_nt_response"),
+		 m_nt_response,
+		 EAP_MSCHAPV2_NT_RESPONSE_SIZE));
+
+	if (m_session.get_state() == eap_type_mschapv2_state_change_password_request)
+	{
+		// Here we need to generate password hashes from new_password
+
+		crypto_nt_hash_c nt_hash(m_am_tools);
+		if (nt_hash.get_is_valid() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+		}
+
+		status = nt_hash.nt_password_hash(
+			&m_password_utf8,
+			&new_password_hash,
+			EAP_MSCHAPV2_MD4_DIGEST_SIZE);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = nt_hash.hash_nt_password_hash(
+			&new_password_hash,
+			&new_password_hash_hash,
+			EAP_MSCHAPV2_MD4_DIGEST_SIZE);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = check_authenticator_response(
+			&new_password_hash_hash,
+			m_nt_response,
+			m_peer_challenge,
+			m_authenticator_challenge,
+			m_username_utf8.get_data(),
+			m_username_utf8.get_data_length(),
+			success_payload,
+			response_check);
+	}
+	else
+	{
+		status = check_authenticator_response(
+			&m_password_hash_hash,
+			m_nt_response,
+			m_peer_challenge,
+			m_authenticator_challenge,
+			m_username_utf8.get_data(),
+			m_username_utf8.get_data_length(),
+			success_payload,
+			response_check);
+	}
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	if (response_check)
+	{
+		EAP_TRACE_DEBUG(
+			m_am_tools,
+			TRACE_FLAGS_DEFAULT,
+			(EAPL("EAP_type_MSCHAPV2: client_handle_success_request(): mschapv2_opcode_success, authenticator_response correct\n")));
+
+		// Check if earlier state was password change
+		if (m_session.get_state() == eap_type_mschapv2_state_change_password_request)
+		{
+			if (m_password_prompt_enabled == false)
+			{
+				// Save new password
+				status = m_am_type_mschapv2->update_username_password();
+			}
+
+			// Save new hashes
+			status = m_password_hash.set_copy_of_buffer(&new_password_hash);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			status = m_password_hash_hash.set_copy_of_buffer(&new_password_hash_hash);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+		}
+
+		// Send success response
+
+		// Return value will be not checked. If fails we still try to finish authentication
+		status = send_success_failure_response(true);
+
+		if (m_wait_eap_success == false)
+		{
+			status = finish_successful_authentication();
+			m_session.set_state(eap_type_mschapv2_state_success);
+		}
+		else
+		{
+			m_session.set_state(eap_type_mschapv2_state_success_request);
+		}
+	}
+	else // drop
+	{
+		EAP_TRACE_DEBUG(
+			m_am_tools,
+			TRACE_FLAGS_DEFAULT,
+			(EAPL("EAP_type_MSCHAPV2: client_handle_success_request(): mschapv2_opcode_success, authenticator_response incorrect\n")));
+
+		status = eap_status_drop_packet_quietly;
+	}
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+eap_status_e eap_type_mschapv2_c::client_handle_failure_request(mschapv2_header_c & failure_request)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("eap_type_mschapv2_c::client_handle_failure_request(): this = 0x%08x\n"),
+		this));
+
+	eap_status_e status = eap_status_drop_packet_quietly;
+
+	u32_t buffer_left = failure_request.get_data_length();
+	if (buffer_left < EAP_MSCHAPV2_FAILURE_MIN_SIZE)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_drop_packet_quietly);
+	}
+
+	m_mschapv2id = failure_request.get_mschapv2_id();
+
+	const u8_t * buffer_ptr = failure_request.get_data();
+
+	// "E="
+	if (*buffer_ptr != 'E' || *(buffer_ptr + 1) != '=')
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_drop_packet_quietly);
+	}
+	buffer_ptr += 2;
+	buffer_left -= 2;
+
+	while (buffer_left && *buffer_ptr == '0')
+	{
+		buffer_ptr++;
+		buffer_left--;
+	}
+
+	if (buffer_left == 0)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_drop_packet_quietly);
+	}
+
+	const u8_t * error_code_str = buffer_ptr;
+
+	while (buffer_left && *buffer_ptr != ' ')
+	{
+		if (*buffer_ptr < '0' || *buffer_ptr > '9')
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_drop_packet_quietly);
+		}
+		buffer_ptr++;
+		buffer_left--;
+	}
+
+	u32_t error_code_str_len = buffer_ptr - error_code_str;
+	if (buffer_left < 43 || error_code_str_len != 3 || *buffer_ptr != ' ') // " R=x C=<32 bytes> V=x", min 43 bytes
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_drop_packet_quietly);
+	}
+	buffer_ptr++;
+	buffer_left--;
+
+	// "R="
+	if (*buffer_ptr != 'R' || *(buffer_ptr + 1) != '=')
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_drop_packet_quietly);
+	}
+
+	bool retry_allowed = true; // value = 1
+	if (*(buffer_ptr + 2) == '0')
+	{
+		retry_allowed = false;
+	}
+	else if (*(buffer_ptr + 2) != '1')
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_drop_packet_quietly);
+	}
+	buffer_ptr += 4;
+	buffer_left -= 4;
+
+	// "C="
+	if (*buffer_ptr != 'C' || *(buffer_ptr + 1) != '=')
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_drop_packet_quietly);
+	}
+	buffer_ptr += 2;
+	buffer_left -= 2;
+
+	const u8_t * challenge_str = buffer_ptr;
+	u32_t i;
+	for (i = 0; i < EAP_MSCHAPV2_AUTHENTICATOR_CHALLENGE_SIZE * 2; i++)
+	{
+		if ((*(buffer_ptr + i) >= '0' && *(buffer_ptr + i) <= '9')
+			||
+			(*(buffer_ptr + i) >= 'A' && *(buffer_ptr + i) <= 'F'))
+		{
+			continue;
+		}
+		break;
+	}
+	if (i != EAP_MSCHAPV2_AUTHENTICATOR_CHALLENGE_SIZE * 2)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_drop_packet_quietly);
+	}
+
+	buffer_ptr += EAP_MSCHAPV2_AUTHENTICATOR_CHALLENGE_SIZE * 2;
+	buffer_left -= EAP_MSCHAPV2_AUTHENTICATOR_CHALLENGE_SIZE * 2;
+
+	if (*buffer_ptr != ' '
+		|| *(buffer_ptr + 1) != 'V' // " V="
+		|| *(buffer_ptr + 2) != '=')
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_drop_packet_quietly);
+	}
+	buffer_ptr += 3;
+	buffer_left -= 3;
+
+	while (buffer_left && *buffer_ptr == '0')
+	{
+		buffer_ptr++;
+		buffer_left--;
+	}
+
+	if (buffer_left == 0
+		|| *buffer_ptr != '3') // Version must be 3
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_drop_packet_quietly);
+	}
+
+	u32_t error_code = 
+		(*error_code_str - '0') * 100
+		+ (*(error_code_str + 1) - '0') * 10
+		+ (*(error_code_str + 2) - '0');
+	m_error_code = error_code;
+
+	{
+		u32_t length = EAP_MSCHAPV2_AUTHENTICATOR_CHALLENGE_SIZE;
+
+		status = m_am_tools->convert_hex_ascii_to_bytes(
+			challenge_str,
+			EAP_MSCHAPV2_AUTHENTICATOR_CHALLENGE_SIZE * 2,
+			m_authenticator_challenge,
+			&length);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		if (length != EAP_MSCHAPV2_AUTHENTICATOR_CHALLENGE_SIZE)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_drop_packet_quietly);
+		}
+
+		EAP_TRACE_DATA_DEBUG(
+			m_am_tools,
+			TRACE_FLAGS_DEFAULT,
+			(EAPL("EAP_type_MSCHAPV2: client_handle_failure_request(): client got auth_challenge:"),
+			m_authenticator_challenge,
+			EAP_MSCHAPV2_AUTHENTICATOR_CHALLENGE_SIZE));
+
+		EAP_TRACE_DATA_DEBUG(
+			m_am_tools,
+			TRACE_FLAGS_DEFAULT,
+			(EAPL("EAP_type_MSCHAPV2: client_handle_failure_request(): client has peer_challenge:"),
+			m_peer_challenge,
+			EAP_MSCHAPV2_PEER_CHALLENGE_SIZE));
+	}
+
+#if defined(USE_USER_NOTIFICATIONS)
+	// Notification to user
+	eap_variable_data_c string(m_am_tools);
+	status = m_am_type_mschapv2->read_auth_failure_string(static_cast<eap_mschapv2_error_e> (m_error_code), string);
+	if (status == eap_status_ok)
+	{
+		eap_state_notification_c notification(
+			m_am_tools,
+			&m_send_network_id,
+			m_is_client,
+			eap_state_notification_eap,
+			eap_protocol_layer_general, // This must be used with eap_general_state_show_notification_string.
+			eap_type_mschapv2,
+			eap_state_none,
+			eap_general_state_show_notification_string,
+			m_identifier,
+			false);
+		notification.set_notification_string(&string, true);
+		get_type_partner()->state_notification(&notification);
+	}
+#endif //#if defined(USE_USER_NOTIFICATIONS)
+
+	switch (m_error_code)
+	{
+
+	case EAP_MSCHAPV2_ERROR_RESTRICTED_LOGON_HOURS:
+	case EAP_MSCHAPV2_ERROR_ACCT_DISABLED:
+	case EAP_MSCHAPV2_ERROR_NO_DIALIN_PERMISSION:
+
+		// Notification to user
+		if (m_error_code == EAP_MSCHAPV2_ERROR_RESTRICTED_LOGON_HOURS)
+		{
+			send_error_notification(eap_status_restricted_logon_hours);
+		}
+		else if (m_error_code == EAP_MSCHAPV2_ERROR_ACCT_DISABLED)
+		{
+			send_error_notification(eap_status_account_disabled);
+		}
+		else if (m_error_code == EAP_MSCHAPV2_ERROR_NO_DIALIN_PERMISSION)
+		{
+			send_error_notification(eap_status_no_dialin_permission);
+		}
+
+		// Send failure response
+		status = send_success_failure_response(false); // failure
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+		break;
+
+	case EAP_MSCHAPV2_ERROR_AUTHENTICATION_FAILURE:
+
+		EAP_TRACE_DEBUG(
+			m_am_tools,
+			TRACE_FLAGS_DEFAULT,
+			(EAPL("EAP_type_MSCHAPV2: client_handle_failure_request(): EAP_MSCHAPV2_ERROR_AUTHENTICATION_FAILURE\n")));
+
+#if defined(EAP_USE_TTLS_PLAIN_MS_CHAP_V2_HACK)
+		EAP_TRACE_DEBUG(
+			m_am_tools,
+			TRACE_FLAGS_DEFAULT,
+			(EAPL("EAP_type_MSCHAPV2: client_handle_failure_request(): m_use_implicit_challenge = %d\n"),
+			m_use_implicit_challenge));
+#endif //#if defined(EAP_USE_TTLS_PLAIN_MS_CHAP_V2_HACK)
+
+		if (retry_allowed == true
+#if defined(EAP_USE_TTLS_PLAIN_MS_CHAP_V2_HACK)
+			// Note, retry is supported only inside EAP-MsChapv2.
+			// Note, inside TTLS/plain-MsChapv2 the retry is not supported. Authentication will fail immediately.
+			&& m_use_implicit_challenge == false
+#endif //#if defined(EAP_USE_TTLS_PLAIN_MS_CHAP_V2_HACK)
+			)
+		{
+			m_is_pending = true;
+
+			// Prompt for username/password
+			status = m_am_type_mschapv2->show_username_password_dialog(
+				m_username_utf8,
+				m_password_utf8,
+				m_password_prompt_enabled,
+				false);
+		}
+		else
+		{
+			// Notification to user
+			send_error_notification(eap_status_authentication_failure);
+
+			// Send failure response
+			status = send_success_failure_response(false);
+		}
+		break;
+
+	case EAP_MSCHAPV2_ERROR_PASSWD_EXPIRED:
+	case EAP_MSCHAPV2_ERROR_CHANGING_PASSWORD:
+
+		if (m_error_code == EAP_MSCHAPV2_ERROR_PASSWD_EXPIRED)
+		{
+			EAP_TRACE_DEBUG(
+				m_am_tools,
+				TRACE_FLAGS_DEFAULT,
+				(EAPL("EAP_type_MSCHAPV2: client_handle_failure_request(): EAP_MSCHAPV2_ERROR_PASSWD_EXPIRED\n")));
+		}
+		else if (m_error_code == EAP_MSCHAPV2_ERROR_CHANGING_PASSWORD)
+		{
+			EAP_TRACE_DEBUG(
+				m_am_tools,
+				TRACE_FLAGS_DEFAULT,
+				(EAPL("EAP_type_MSCHAPV2: client_handle_failure_request(): EAP_MSCHAPV2_ERROR_CHANGING_PASSWORD\n")));
+		}
+
+#if defined(EAP_USE_TTLS_PLAIN_MS_CHAP_V2_HACK)
+		EAP_TRACE_DEBUG(
+			m_am_tools,
+			TRACE_FLAGS_DEFAULT,
+			(EAPL("EAP_type_MSCHAPV2: client_handle_failure_request(): m_use_implicit_challenge = %d\n"),
+			m_use_implicit_challenge));
+#endif //#if defined(EAP_USE_TTLS_PLAIN_MS_CHAP_V2_HACK)
+
+		if (
+#if defined(EAP_USE_TTLS_PLAIN_MS_CHAP_V2_HACK)
+			m_use_implicit_challenge == false
+			&&
+#endif //#if defined(EAP_USE_TTLS_PLAIN_MS_CHAP_V2_HACK)
+				(m_error_code == EAP_MSCHAPV2_ERROR_PASSWD_EXPIRED 
+				|| (m_error_code == EAP_MSCHAPV2_ERROR_CHANGING_PASSWORD && retry_allowed)))
+		{
+			m_old_password_utf8.set_copy_of_buffer(&m_password_utf8);
+
+			m_is_pending = true;
+
+			m_session.set_state(eap_type_mschapv2_state_failure_request);
+
+			// Prompt for password change
+			status = m_am_type_mschapv2->show_change_password_dialog(
+				m_username_utf8,
+				m_old_password_utf8,
+				m_password_utf8,
+				m_password_prompt_enabled);
+
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+		else // Retry not allowed
+		{
+			send_error_notification(eap_status_password_expired);
+
+			status = send_success_failure_response(false);
+
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+			// Show message without retry
+		}
+		break;
+
+	default:
+		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_mschapv2_c::eap_acknowledge(
+	const eap_am_network_id_c * const /* receive_network_id */)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("eap_type_mschapv2_c::eap_acknowledge(): this = 0x%08x\n"),
+		this));
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, eap_status_not_supported);
+}
+
+EAP_FUNC_EXPORT eap_status_e eap_type_mschapv2_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_TRACE_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("eap_type_mschapv2_c::set_initial_eap_identifier(): this = 0x%08x\n"),
+		this));
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, eap_status_ok);
+}
+
+EAP_FUNC_EXPORT eap_status_e eap_type_mschapv2_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_TRACE_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("eap_type_mschapv2_c::query_eap_identity(): this = 0x%08x\n"),
+		this));
+
+	if (m_session.is_valid_state(eap_type_mschapv2_state_identity_request) == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_drop_packet_quietly);
+	}
+
+	// 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());
+
+	m_identifier = eap_identifier;
+
+	eap_status_e status;
+
+	EAP_TRACE_DEBUG(m_am_tools, 
+		TRACE_FLAGS_DEFAULT, (
+		EAPL("eap_type_mschapv2_c::query_eap_identity: m_identity_asked=%d, m_password_prompt_enabled=%d\n"),
+		m_identity_asked, m_password_prompt_enabled));
+
+	EAP_TRACE_DEBUG(m_am_tools, 
+		TRACE_FLAGS_DEFAULT, (
+		EAPL("eap_type_mschapv2_c::query_eap_identity: user name length=%d, PW length=%d, m_is_pending=%d\n"),
+		m_username_utf8.get_data_length(), m_password_utf8.get_data_length(), m_is_pending));
+
+	if (m_identity_asked == false && // Only at first time username/password dialog is shown if needed
+		(m_username_utf8.get_is_valid_data() == false
+        || m_username_utf8.get_data_length() == 0
+		|| m_password_prompt_enabled == true))
+	{
+		// Ask username and password
+
+		EAP_TRACE_DATA_DEBUG(
+			m_am_tools,
+			TRACE_FLAGS_DEFAULT,
+			(EAPL("EAP_type_MSCHAPV2: query_eap_identity(): m_username_utf8:"),
+			m_username_utf8.get_data(),
+			m_username_utf8.get_data_length()));
+
+		EAP_TRACE_DATA_DEBUG(
+			m_am_tools,
+			TRACE_FLAGS_DEFAULT,
+			(EAPL("EAP_type_MSCHAPV2: query_eap_identity(): m_password_utf8:"),
+			 m_password_utf8.get_data(),
+			 m_password_utf8.get_data_length()));
+
+		EAP_TRACE_DEBUG(
+			m_am_tools,
+			TRACE_FLAGS_DEFAULT,
+			(EAPL("EAP_type_MSCHAPV2: query_eap_identity(): password length %d\n"),
+			m_password_utf8.get_buffer_length()));
+
+		if (m_is_pending == false)
+		{
+			m_is_pending = true;
+			status = m_am_type_mschapv2->show_username_password_dialog(
+				m_username_utf8,
+				m_password_utf8,
+				m_password_prompt_enabled,
+				true);
+			if (status == eap_status_pending_request
+				|| status == eap_status_completed_request)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+		}
+		else
+		{
+			EAP_TRACE_DEBUG(
+				m_am_tools,
+				TRACE_FLAGS_DEFAULT,
+				(EAPL("EAP_type_MSCHAPV2: query_eap_identity(): identity query is already pending!.\n")));
+
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_pending_request);
+		}
+	}
+
+	m_identity_asked = true;
+
+	// If this is not the first query, use already stored identity information for reply
+	status = identity->set_copy_of_buffer(&m_username_utf8);
+
+	EAP_TRACE_DATA_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("EAP_type_MSCHAPV2: query_eap_identity(): identity:"),
+		identity->get_data(),
+		identity->get_data_length()));
+
+	m_session.set_state(eap_type_mschapv2_state_identity_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_mschapv2_c::complete_eap_identity_query()
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DATA_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("EAP_type_MSCHAPV2: complete_eap_identity_query() m_username_utf8:"),
+		m_username_utf8.get_data(),
+		m_username_utf8.get_data_length()));
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("EAP_type_MSCHAPV2: complete_eap_identity_query(), password length %d\n"),
+		m_password_utf8.get_buffer_length()));
+
+	m_identity_asked = true;
+
+	m_session.set_state(eap_type_mschapv2_state_identity_request);
+
+	eap_status_e status = get_type_partner()->complete_eap_identity_query(
+		&m_send_network_id,
+		&m_username_utf8,
+		m_identifier);
+	if (status != eap_status_ok)
+	{
+		m_session.set_state(eap_type_mschapv2_state_failure);
+	}
+
+	m_is_pending = false;
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+eap_status_e eap_type_mschapv2_c::send_success_failure_response(bool is_success_response)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("eap_type_mschapv2_c::send_success_failure_response(): this = 0x%08x\n"),
+		this));
+
+	eap_status_e status = eap_status_allocation_error;
+   
+	u32_t packet_length
+		= EAP_MSCHAPV2_OPCODE_SIZE // OpCode is the only payload
+		+ eap_header_base_c::get_type_data_start_offset(m_use_eap_expanded_type);
+
+	eap_buf_chain_wr_c * packet = create_send_packet(packet_length);
+	if (!packet)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	eap_header_base_c eap_header(
+		m_am_tools,
+		packet->get_data_offset(m_offset, packet_length),
+		packet_length);
+	if (eap_header.get_is_valid() == false)
+	{
+		delete packet;
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+	}
+	eap_header.set_code(eap_code_response);
+	eap_header.set_identifier(m_identifier);
+	eap_header.set_length(
+		static_cast<u16_t>(packet_length),
+		m_use_eap_expanded_type);
+	eap_header.set_type(
+		eap_type_mschapv2,
+		m_use_eap_expanded_type);
+
+	mschapv2_header_c mschapv2_header(
+		m_am_tools,
+		eap_header.get_type_data_offset(0, eap_header.get_type_data_length()),
+		eap_header.get_type_data_length());
+
+	if (is_success_response)
+	{
+		mschapv2_header.set_opcode(mschapv2_opcode_success);
+		m_session.set_state(eap_type_mschapv2_state_success_request);
+	}
+	else
+	{
+		mschapv2_header.set_opcode(mschapv2_opcode_failure);
+		m_session.set_state(eap_type_mschapv2_state_failure_request);
+	}
+
+	status = packet_send(packet, packet_length);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("EAP_type_MSCHAPV2: send_success_failure_response():\n")));
+
+	delete packet;
+
+	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_mschapv2_c::complete_failure_retry_response()
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("eap_type_mschapv2_c::complete_failure_retry_response(): this = 0x%08x\n"),
+		this));
+
+	m_is_pending = false;
+
+	eap_status_e status = m_rand.add_rand_seed_hw_ticks();
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = m_rand.add_rand_seed(
+		m_password_utf8.get_data(),
+		m_password_utf8.get_data_length()); 
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+#if defined(USE_FAST_EAP_TYPE)
+	if (m_use_EAP_FAST_challenge == true
+		&& m_client_EAP_FAST_challenge.get_is_valid_data() == true
+		&& m_client_EAP_FAST_challenge.get_data_length() == EAP_MSCHAPV2_PEER_CHALLENGE_SIZE)
+	{
+		m_am_tools->memmove(
+			m_peer_challenge,
+			m_client_EAP_FAST_challenge.get_data(),
+			EAP_MSCHAPV2_PEER_CHALLENGE_SIZE);
+	}
+	else
+#endif //#if defined(USE_FAST_EAP_TYPE)
+	{
+		status = m_rand.get_rand_bytes(
+			m_peer_challenge,
+			EAP_MSCHAPV2_PEER_CHALLENGE_SIZE);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+
+	crypto_nt_hash_c nt_hash(m_am_tools);
+	if (nt_hash.get_is_valid() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	status = nt_hash.nt_password_hash(
+		&m_password_utf8,
+		&m_password_hash,
+		EAP_MSCHAPV2_MD4_DIGEST_SIZE);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = nt_hash.hash_nt_password_hash(
+		&m_password_hash,
+		&m_password_hash_hash,
+		EAP_MSCHAPV2_MD4_DIGEST_SIZE);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	EAP_TRACE_DATA_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("EAP_type_MSCHAPV2: complete_failure_retry_response(), m_password_utf8"),
+		m_password_utf8.get_data(),
+		m_password_utf8.get_data_length()));
+
+	EAP_TRACE_DATA_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("EAP_type_MSCHAPV2: complete_failure_retry_response(), m_authenticator_challenge"),
+		m_authenticator_challenge,
+		EAP_MSCHAPV2_AUTHENTICATOR_CHALLENGE_SIZE));
+
+	EAP_TRACE_DATA_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("EAP_type_MSCHAPV2: complete_failure_retry_response(), m_peer_challenge"),
+		m_peer_challenge,
+		EAP_MSCHAPV2_PEER_CHALLENGE_SIZE));
+
+	EAP_TRACE_DATA_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("EAP_type_MSCHAPV2: complete_failure_retry_response(), m_password_hash"),
+		m_password_hash.get_data(),
+		m_password_hash.get_data_length()));
+
+	EAP_TRACE_DATA_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("EAP_type_MSCHAPV2: complete_failure_retry_response(), m_username_utf8"),
+		m_username_utf8.get_data(),
+		m_username_utf8.get_data_length()));
+
+	EAP_TRACE_DATA_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("EAP_type_MSCHAPV2: complete_failure_retry_response(): m_authenticator_challenge"),
+		 m_authenticator_challenge,
+		 EAP_MSCHAPV2_AUTHENTICATOR_CHALLENGE_SIZE));
+
+
+	status = generate_nt_response(
+		m_authenticator_challenge,
+		m_peer_challenge,
+		m_username_utf8.get_data(),
+		m_username_utf8.get_data_length(),
+		&m_password_hash,
+		m_nt_response);
+	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("EAP_type_MSCHAPV2: complete_failure_retry_response(), m_nt_response:"),
+		m_nt_response,
+		EAP_MSCHAPV2_NT_RESPONSE_SIZE));
+
+	// Send response
+
+	const u32_t type_data_length = EAP_MSCHAPV2_HEADER_SIZE // OpCode, MS-CHAPv2-ID and MS-Length
+		+ mschapv2_response_c::get_header_minimum_size()
+		+ m_username_utf8.get_data_length();
+
+	const u32_t packet_length = eap_header_base_c::get_type_data_start_offset(m_use_eap_expanded_type)
+		+ type_data_length;
+
+	eap_buf_chain_wr_c * packet = create_send_packet(packet_length);
+
+	if (!packet)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+	
+
+	eap_header_base_c eap_header(
+		m_am_tools,
+		packet->get_data_offset(m_offset, packet_length),
+		packet_length);
+	if (eap_header.get_is_valid() == false)
+	{
+		delete packet;
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+	}
+	eap_header.set_code(eap_code_response);
+	eap_header.set_identifier(m_identifier);
+	eap_header.set_length(
+		static_cast<u16_t>(packet_length),
+		m_use_eap_expanded_type);
+	eap_header.set_type(
+		eap_type_mschapv2,
+		m_use_eap_expanded_type);
+
+	mschapv2_header_c mschapv2_header(
+		m_am_tools,
+		eap_header.get_type_data_offset(0, eap_header.get_type_data_length()),
+		eap_header.get_type_data_length());
+
+	mschapv2_header.set_opcode(mschapv2_opcode_response);
+	mschapv2_header.set_mschapv2_id(++m_mschapv2id);
+	mschapv2_header.set_ms_length(static_cast<u16_t>(type_data_length));
+
+	mschapv2_response_c response(
+		m_am_tools,
+		mschapv2_header.get_data(),
+		mschapv2_header.get_data_length());
+	if (response.get_is_valid() == false)
+	{
+		delete packet;
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	status = response.set_constants();
+	if (status != eap_status_ok)
+	{
+		delete packet;
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	response.set_nt_response(m_nt_response);
+	response.set_peer_challenge(m_peer_challenge);
+	response.set_name(m_username_utf8.get_data());
+
+	// Send
+	status = packet_send(packet, packet_length);
+	delete packet;
+
+	m_session.set_state(eap_type_mschapv2_state_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_mschapv2_c::complete_change_password_query()
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("eap_type_mschapv2_c::complete_change_password_query(): this = 0x%08x\n"),
+		this));
+
+	eap_status_e status = eap_status_process_general_error;
+
+	m_is_pending = false;
+
+	status = m_rand.add_rand_seed_hw_ticks();
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = m_rand.add_rand_seed(
+		m_password_utf8.get_data(),
+		m_password_utf8.get_data_length()); 
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+#if defined(USE_FAST_EAP_TYPE)
+	if (m_use_EAP_FAST_challenge == true
+		&& m_client_EAP_FAST_challenge.get_is_valid_data() == true
+		&& m_client_EAP_FAST_challenge.get_data_length() == EAP_MSCHAPV2_PEER_CHALLENGE_SIZE)
+	{
+		m_am_tools->memmove(
+			m_peer_challenge,
+			m_client_EAP_FAST_challenge.get_data(),
+			EAP_MSCHAPV2_PEER_CHALLENGE_SIZE);
+	}
+	else
+#endif //#if defined(USE_FAST_EAP_TYPE)
+	{
+		status = m_rand.get_rand_bytes(m_peer_challenge, EAP_MSCHAPV2_PEER_CHALLENGE_SIZE);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+
+	u8_t * encrypted_pw_block = new u8_t[sizeof(pw_block_s)];
+	if (!encrypted_pw_block)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	eap_variable_data_c new_password_hash(m_am_tools);
+
+	crypto_nt_hash_c nt_hash(m_am_tools);
+	if (nt_hash.get_is_valid() == false)
+	{
+		delete [] encrypted_pw_block;
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	status = nt_hash.nt_password_hash(
+		&m_password_utf8,
+		&new_password_hash,
+		EAP_MSCHAPV2_MD4_DIGEST_SIZE);
+	if (status != eap_status_ok)
+	{
+		delete [] encrypted_pw_block;
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = new_password_encrypted_with_old_nt_password_hash(
+		&m_password_utf8,
+		&m_password_hash,
+		encrypted_pw_block);
+	if (status != eap_status_ok)
+	{
+		delete [] encrypted_pw_block;
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	eap_variable_data_c encrypted_pw_hash(m_am_tools);
+
+	status = old_nt_password_hash_encrypted_with_new_nt_password_hash(
+		&new_password_hash,
+		&m_password_hash,
+		&encrypted_pw_hash);
+	if (status != eap_status_ok)
+	{
+		delete [] encrypted_pw_block;
+		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("complete_change_password_query(): m_authenticator_challenge"),
+		 m_authenticator_challenge,
+		 EAP_MSCHAPV2_AUTHENTICATOR_CHALLENGE_SIZE));
+
+	status = generate_nt_response(
+		m_authenticator_challenge,
+		m_peer_challenge,
+		m_username_utf8.get_data(),
+		m_username_utf8.get_data_length(),
+		&new_password_hash,
+		m_nt_response);
+	if (status != eap_status_ok)
+	{
+		delete [] encrypted_pw_block;
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	const u32_t type_data_length = EAP_MSCHAPV2_HEADER_SIZE // OpCode, MS-CHAPv2-ID and MS-Length
+		+ mschapv2_change_password_c::get_header_minimum_size();
+
+	const u32_t packet_length
+		= eap_header_base_c::get_type_data_start_offset(m_use_eap_expanded_type)
+		+ type_data_length;
+
+
+	eap_buf_chain_wr_c * packet = create_send_packet(packet_length);
+	if (!packet)
+	{
+		delete [] encrypted_pw_block;
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	eap_header_base_c eap_header(
+		m_am_tools,
+		packet->get_data_offset(m_offset, packet_length),
+		packet_length);
+	if (eap_header.get_is_valid() == false)
+	{
+		delete [] encrypted_pw_block;
+		delete packet;
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+	}
+	eap_header.set_code(eap_code_response);
+	eap_header.set_identifier(m_identifier);
+	eap_header.set_length(
+		static_cast<u16_t>(packet_length),
+		m_use_eap_expanded_type);
+	eap_header.set_type(
+		eap_type_mschapv2,
+		m_use_eap_expanded_type);
+
+	mschapv2_header_c mschapv2_header(
+		m_am_tools,
+		eap_header.get_type_data_offset(0, eap_header.get_type_data_length()),
+		eap_header.get_type_data_length());
+
+	mschapv2_header.set_opcode(mschapv2_opcode_change_password);
+	mschapv2_header.set_mschapv2_id(static_cast<u8_t>((++m_mschapv2id)));
+	mschapv2_header.set_ms_length(static_cast<u16_t>(type_data_length));
+
+	mschapv2_change_password_c response(
+		m_am_tools,
+		mschapv2_header.get_data(),
+		mschapv2_header.get_data_length());
+	if (response.get_is_valid() == false)
+	{
+		delete [] encrypted_pw_block;
+		delete packet;
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	status = response.set_constants();
+	if (status != eap_status_ok)
+	{
+		delete [] encrypted_pw_block;
+		delete packet;
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	response.set_encrypted_pw_block(encrypted_pw_block);
+	delete [] encrypted_pw_block;
+	response.set_encrypted_hash(encrypted_pw_hash.get_data());
+	response.set_peer_challenge(m_peer_challenge);
+	response.set_nt_response(m_nt_response);
+
+	// Send change password response
+	status = packet_send(packet, packet_length);
+	delete packet;
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	m_session.set_state(eap_type_mschapv2_state_change_password_request);
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+// End of file.