eapol/eapol_framework/eapol_common/common/eap_crypto_api.cpp
changeset 0 c8830336c852
child 2 1c7bc153c08e
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/eapol/eapol_framework/eapol_common/common/eap_crypto_api.cpp	Thu Dec 17 08:47:43 2009 +0200
@@ -0,0 +1,6097 @@
+/*
+* 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 22 
+	#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_crypto_api.h"
+
+//--------------------------------------------------
+
+// This is simple optimization.
+const u32_t EAP_CRYPTO_API_SHA1_DIGEST_BUFFER_BYTE_SIZE = 20ul;
+const u32_t EAP_CRYPTO_API_SHA1_BLOCK_BYTE_SIZE = 64ul;
+
+const u32_t EAP_CRYPTO_API_SHA_256_DIGEST_BUFFER_BYTE_SIZE = 32ul;
+const u32_t EAP_CRYPTO_API_SHA_256_BLOCK_BYTE_SIZE = 64ul;
+
+//--------------------------------------------------
+//--------------------------------------------------
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT abs_crypto_cbc_block_algorithm_c::~abs_crypto_cbc_block_algorithm_c()
+{
+}
+
+
+EAP_FUNC_EXPORT abs_crypto_block_algorithm_c::~abs_crypto_block_algorithm_c()
+{
+}
+
+EAP_FUNC_EXPORT abs_crypto_stream_algorithm_c::~abs_crypto_stream_algorithm_c()
+{
+}
+
+EAP_FUNC_EXPORT abs_crypto_hash_algorithm_c::~abs_crypto_hash_algorithm_c()
+{
+}
+
+EAP_FUNC_EXPORT abs_crypto_hmac_algorithm_c::~abs_crypto_hmac_algorithm_c()
+{
+}
+
+//--------------------------------------------------
+//--------------------------------------------------
+//--------------------------------------------------
+
+
+EAP_FUNC_EXPORT crypto_hmac_c::~crypto_hmac_c()
+{
+	hmac_cleanup();
+
+	delete m_key;
+	m_key = 0;
+
+	delete m_ipad;
+	m_ipad = 0;
+
+	delete m_opad;
+	m_opad = 0;
+}
+
+EAP_FUNC_EXPORT crypto_hmac_c::crypto_hmac_c(
+	abs_eap_am_tools_c * const tools,
+	abs_crypto_hash_algorithm_c * const crypto_hash_algorithm,
+	const bool free_crypto_hash_algorithm)
+	: m_am_tools(tools)
+	, m_crypto_hash_algorithm(crypto_hash_algorithm)
+	, m_key(0)
+	, m_ipad(0)
+	, m_opad(0)
+	, m_is_valid(false)
+	, m_free_crypto_hash_algorithm(free_crypto_hash_algorithm)
+{
+	m_key = new eap_variable_data_c(m_am_tools);
+	if (m_key == 0)
+	{
+		return;
+	}
+
+	m_ipad = new eap_variable_data_c(m_am_tools);
+	if (m_ipad == 0)
+	{
+		return;
+	}
+
+	m_opad = new eap_variable_data_c(m_am_tools);
+	if (m_opad == 0)
+	{
+		return;
+	}
+
+	set_is_valid();
+}
+
+EAP_FUNC_EXPORT void crypto_hmac_c::set_is_valid()
+{
+	m_is_valid = true;
+}
+
+EAP_FUNC_EXPORT bool crypto_hmac_c::get_is_valid()
+{
+	return m_is_valid;
+}
+
+EAP_FUNC_EXPORT u32_t crypto_hmac_c::get_digest_length()
+{
+	return m_crypto_hash_algorithm->get_digest_length();
+}
+
+EAP_FUNC_EXPORT eap_status_e crypto_hmac_c::initialize_pad(
+	eap_variable_data_c * const p_pad,
+	const u8_t pad_value)
+{
+	if (get_is_valid() == false)
+	{
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	eap_status_e status = p_pad->set_buffer_length(
+		m_crypto_hash_algorithm->get_block_size());
+	if (status != eap_status_ok)
+	{
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+	p_pad->set_data_length(m_crypto_hash_algorithm->get_block_size());
+
+	u32_t ind = 0ul;
+	u8_t * key = m_key->get_data(m_key->get_data_length());
+	u8_t * ipad = p_pad->get_data(p_pad->get_data_length());
+	
+	for (ind = 0ul; ind < m_key->get_data_length(); ind++)
+	{
+		ipad[ind] = static_cast<u8_t>(key[ind] ^ pad_value);
+	}
+	
+	for (ind = m_key->get_data_length()
+			 ; ind < m_crypto_hash_algorithm->get_block_size(); ind++)
+	{
+		ipad[ind] = pad_value;
+	}
+
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+EAP_FUNC_EXPORT eap_status_e crypto_hmac_c::hmac_set_key(
+	const eap_variable_data_c * const hmac_key)
+{
+	if (hmac_key == 0
+		|| hmac_key->get_is_valid_data() == false
+		|| get_is_valid() == false)
+	{
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+	}
+
+	eap_status_e status = eap_status_process_general_error;
+
+	status = m_crypto_hash_algorithm->hash_init();
+	if (status != eap_status_ok)
+	{
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	if (hmac_key->get_data_length()
+		> m_crypto_hash_algorithm->get_block_size())
+	{
+		status = m_crypto_hash_algorithm->hash_update(
+			hmac_key->get_data(hmac_key->get_data_length()),
+			hmac_key->get_data_length());
+		if (status != eap_status_ok)
+		{
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = m_key->set_buffer_length(
+			m_crypto_hash_algorithm->get_digest_length());
+		if (status != eap_status_ok)
+		{
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+		m_key->set_data_length(m_crypto_hash_algorithm->get_digest_length());
+
+		u32_t md_length = m_key->get_data_length();
+
+		status = m_crypto_hash_algorithm->hash_final(
+			m_key->get_data(m_key->get_data_length()),
+			&md_length);
+		if (status != eap_status_ok)
+		{
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		EAP_ASSERT(md_length == m_key->get_data_length());
+	}
+	else
+	{
+		status = m_key->set_copy_of_buffer(hmac_key);
+		if (status != eap_status_ok)
+		{
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+
+	// - - - - - - - - - - - - - - - - - - - -
+	// Initialize inner pad.
+
+	{
+		static const u8_t EAP_HMAC_IPAD = 0x36;
+
+		status = initialize_pad(
+			m_ipad,
+			EAP_HMAC_IPAD);
+		if (status != eap_status_ok)
+		{
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+
+	// - - - - - - - - - - - - - - - - - - - -
+	// Initialize outer pad.
+
+	{
+		static const u8_t EAP_HMAC_OPAD = 0x5C;
+
+		status = initialize_pad(
+			m_opad,
+			EAP_HMAC_OPAD);
+		if (status != eap_status_ok)
+		{
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+
+	// - - - - - - - - - - - - - - - - - - - -
+
+	status = m_crypto_hash_algorithm->hash_init();
+	if (status != eap_status_ok)
+	{
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = m_crypto_hash_algorithm->hash_update(
+		m_ipad->get_data(m_ipad->get_data_length()),
+		m_ipad->get_data_length());
+	if (status != eap_status_ok)
+	{
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+EAP_FUNC_EXPORT eap_status_e crypto_hmac_c::hmac_update(
+	const void * const data,
+	const u32_t data_length)
+{
+	if (get_is_valid() == false)
+	{
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+	}
+
+	eap_status_e status = m_crypto_hash_algorithm->hash_update(
+		data,
+		data_length);
+	if (status != eap_status_ok)
+	{
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+EAP_FUNC_EXPORT eap_status_e crypto_hmac_c::hmac_final(
+	void * const message_digest,
+	u32_t *md_length_or_null)
+{
+	if (get_is_valid() == false)
+	{
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+	}
+
+	eap_variable_data_c idigest(m_am_tools);
+
+	eap_status_e status = idigest.set_buffer_length(
+		m_crypto_hash_algorithm->get_digest_length());
+	if (status != eap_status_ok)
+	{
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+	idigest.set_data_length(m_crypto_hash_algorithm->get_digest_length());
+
+	u32_t md_length = idigest.get_data_length();
+
+	status = m_crypto_hash_algorithm->hash_final(
+		idigest.get_data(idigest.get_data_length()),
+		&md_length);
+	if (status != eap_status_ok)
+	{
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	EAP_ASSERT(md_length == m_crypto_hash_algorithm->get_digest_length());
+
+	// - - - - - - - - - - - - - - - - - - - - - - - -
+
+	status = m_crypto_hash_algorithm->hash_init();
+	if (status != eap_status_ok)
+	{
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = m_crypto_hash_algorithm->hash_update(
+		m_opad->get_data(m_opad->get_data_length()),
+		m_opad->get_data_length());
+	if (status != eap_status_ok)
+	{
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = m_crypto_hash_algorithm->hash_update(
+		idigest.get_data(idigest.get_data_length()),
+		idigest.get_data_length());
+	if (status != eap_status_ok)
+	{
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = m_crypto_hash_algorithm->hash_final(
+		message_digest,
+		md_length_or_null);
+	if (status != eap_status_ok)
+	{
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	if (md_length_or_null != 0)
+	{
+		EAP_ASSERT(*md_length_or_null
+				   == m_crypto_hash_algorithm->get_digest_length());
+	}
+
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+
+EAP_FUNC_EXPORT eap_status_e crypto_hmac_c::hmac_128_final(
+	void * const message_digest,
+	u32_t *md_length_or_null)
+{
+	if (message_digest == 0)
+	{
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+	}
+
+	u32_t tmp_length = get_digest_length();
+	eap_variable_data_c tmp_digest(m_am_tools);
+	if (tmp_digest.get_is_valid() == false)
+	{
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+	eap_status_e status = tmp_digest.set_buffer_length(tmp_length);
+	if (status != eap_status_ok)
+	{
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = hmac_final(
+		tmp_digest.get_buffer(tmp_digest.get_buffer_length()),
+		&tmp_length);
+	if (status != eap_status_ok
+		|| tmp_length != tmp_digest.get_buffer_length())
+	{
+		if (md_length_or_null != 0)
+		{
+			*md_length_or_null = 0;
+		}
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+	else
+	{
+		status = tmp_digest.set_data_length(tmp_length);
+		if (status != eap_status_ok)
+	    {
+			return EAP_STATUS_RETURN(m_am_tools, status);
+	    }
+
+		if (md_length_or_null != 0)
+		{
+			*md_length_or_null = HMAC_SHA1_128_SIZE;
+		}
+
+		m_am_tools->memmove(
+			message_digest,
+			tmp_digest.get_data(HMAC_SHA1_128_SIZE),
+			HMAC_SHA1_128_SIZE);
+
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+}
+
+EAP_FUNC_EXPORT eap_status_e crypto_hmac_c::hmac_cleanup()
+{
+	if (m_key != 0)
+	{
+		m_key->reset();
+	}
+
+	if (m_ipad != 0)
+	{
+		m_ipad->reset();
+	}
+
+	if (m_opad != 0)
+	{
+		m_opad->reset();
+	}
+
+	if (m_free_crypto_hash_algorithm == true)
+	{
+		delete m_crypto_hash_algorithm;
+	}
+	m_crypto_hash_algorithm = 0;
+
+	m_is_valid = false;
+
+	return EAP_STATUS_RETURN(m_am_tools, eap_status_ok);
+}
+
+//--------------------------------------------------
+//--------------------------------------------------
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT crypto_cbc_c::~crypto_cbc_c()
+{
+	reset();
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT crypto_cbc_c::crypto_cbc_c(
+	abs_eap_am_tools_c * const tools,
+	abs_crypto_block_algorithm_c * const crypto_block_algorithm,
+	const bool free_crypto_block_algorithm)
+	: m_am_tools(tools)
+	, m_crypto_block_algorithm(crypto_block_algorithm)
+	, m_is_valid(false)
+	, m_tmp_IV(0)
+	, m_encr_offset(0ul)
+	, m_encr_dispatch(0)
+	, m_encr_hold(0)
+	, m_saved_in_buffer(0)
+	, m_saved_out_buffer(0)
+	, m_iv_buffer_1(0)
+	, m_iv_buffer_2(0)
+	, m_free_crypto_block_algorithm(free_crypto_block_algorithm)
+{
+	m_tmp_IV = new eap_variable_data_c(m_am_tools);
+	m_encr_dispatch = new u8_t *[m_crypto_block_algorithm->get_block_size()];
+	m_encr_hold = new u8_t [m_crypto_block_algorithm->get_block_size()];
+	m_saved_in_buffer = new u8_t [m_crypto_block_algorithm->get_block_size()];
+	m_saved_out_buffer = new u8_t [m_crypto_block_algorithm->get_block_size()];
+	m_iv_buffer_1 = new u8_t [m_crypto_block_algorithm->get_block_size()];
+	m_iv_buffer_2 = new u8_t [m_crypto_block_algorithm->get_block_size()];
+
+	if (m_tmp_IV == 0
+		|| m_encr_dispatch == 0
+		|| m_encr_hold == 0
+		|| m_saved_in_buffer == 0
+		|| m_saved_out_buffer == 0
+		|| m_iv_buffer_1 == 0
+		|| m_iv_buffer_2 == 0)
+	{
+		reset();
+	}
+	else
+	{
+		set_is_valid();
+	}
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT void crypto_cbc_c::reset()
+{
+	m_is_valid = false;
+
+	if (m_tmp_IV != 0)
+	{
+		m_tmp_IV->reset();
+		delete m_tmp_IV;
+		m_tmp_IV = 0;
+	}
+
+	if (m_crypto_block_algorithm != 0)
+	{
+		if (m_encr_dispatch != 0)
+		{
+			m_am_tools->memset(
+				m_encr_dispatch,
+				0,
+				m_crypto_block_algorithm->get_block_size());
+		}
+
+		if (m_encr_hold != 0)
+		{
+			m_am_tools->memset(
+				m_encr_hold,
+				0,
+				m_crypto_block_algorithm->get_block_size());
+		}
+	}
+
+	delete [] m_encr_dispatch;
+	m_encr_dispatch = 0;
+
+	delete [] m_encr_hold;
+	m_encr_hold = 0;
+
+	delete [] m_saved_in_buffer;
+	m_saved_in_buffer = 0;
+
+	delete [] m_saved_out_buffer;
+	m_saved_out_buffer = 0;
+
+	delete [] m_iv_buffer_1;
+	m_iv_buffer_1 = 0;
+
+	delete [] m_iv_buffer_2;
+	m_iv_buffer_2 = 0;
+
+	if (m_free_crypto_block_algorithm == true)
+	{
+		delete m_crypto_block_algorithm;
+	}
+	m_crypto_block_algorithm = 0;
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT void crypto_cbc_c::cbc_xor_block(
+	const void * const cipher_IV,
+	void * const data_block,
+	const u32_t block_size,
+	const u32_t key_length)
+{
+	EAP_UNREFERENCED_PARAMETER(block_size);
+	EAP_UNREFERENCED_PARAMETER(key_length);
+	EAP_ASSERT((block_size % m_crypto_block_algorithm->get_block_size()) == 0);
+	EAP_ASSERT((key_length % m_crypto_block_algorithm->get_key_length()) == 0);
+
+#if defined(USE_EAP_64_BIT_MULTIPLICATION)
+	if ((reinterpret_cast<u32_t>(cipher_IV) % sizeof(u64_t)) == 0
+		&& (reinterpret_cast<u32_t>(data_block) % sizeof(u64_t)) == 0)
+	{
+		const u64_t * const pIV = static_cast<const u64_t *>(cipher_IV);
+		u64_t * const pdata = static_cast<u64_t *>(data_block);
+
+		for (u32_t ind = 0u; ind < block_size/sizeof(u64_t); ind++)
+		{
+			pdata[ind] ^= pIV[ind];
+		}
+	}
+	else
+#endif //#if defined(USE_EAP_64_BIT_MULTIPLICATION)
+	if ((reinterpret_cast<u32_t>(cipher_IV) % sizeof(u32_t)) == 0
+		&& (reinterpret_cast<u32_t>(data_block) % sizeof(u32_t)) == 0)
+	{
+		const u32_t * const pIV = static_cast<const u32_t *>(cipher_IV);
+		u32_t * const pdata = static_cast<u32_t *>(data_block);
+
+		for (u32_t ind = 0u; ind < block_size/sizeof(u32_t); ind++)
+		{
+			pdata[ind] ^= pIV[ind];
+		}
+	}
+	else if ((reinterpret_cast<u32_t>(cipher_IV) % sizeof(u16_t)) == 0
+		&& (reinterpret_cast<u32_t>(data_block) % sizeof(u16_t)) == 0)
+	{
+		const u16_t * const pIV = static_cast<const u16_t *>(cipher_IV);
+		u16_t * const pdata = static_cast<u16_t *>(data_block);
+
+		for (u32_t ind = 0u; ind < block_size/sizeof(u16_t); ind++)
+		{
+			pdata[ind] ^= pIV[ind];
+		}
+	}
+	else
+	{
+		const u8_t * const pIV = static_cast<const u8_t *>(cipher_IV);
+		u8_t * const pdata = static_cast<u8_t *>(data_block);
+
+		for (u32_t ind = 0u; ind < block_size/sizeof(u8_t); ind++)
+		{
+			pdata[ind] ^= pIV[ind];
+		}
+	}
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT void crypto_cbc_c::cbc_copy_block(
+	void * const target,
+	const void * const source,
+	const u32_t block_size,
+	const u32_t key_length)
+{
+	EAP_UNREFERENCED_PARAMETER(key_length);
+	EAP_ASSERT((block_size % m_crypto_block_algorithm->get_block_size()) == 0);
+	EAP_ASSERT((key_length % m_crypto_block_algorithm->get_key_length()) == 0);
+
+#if defined(USE_EAP_64_BIT_MULTIPLICATION)
+	if ((reinterpret_cast<u32_t>(source) % sizeof(u64_t)) == 0
+		&& (reinterpret_cast<u32_t>(target) % sizeof(u64_t)) == 0)
+	{
+		const u64_t * const p_source
+			= static_cast<const u64_t *>(source);
+		u64_t * const p_target = static_cast<u64_t *>(target);
+
+		for (u32_t ind = 0u; ind < block_size/sizeof(u64_t); ind++)
+		{
+			p_target[ind] = p_source[ind];
+		}
+	}
+	else
+#endif //#if defined(USE_EAP_64_BIT_MULTIPLICATION)
+	if ((reinterpret_cast<u32_t>(source) % sizeof(u32_t)) == 0
+		&& (reinterpret_cast<u32_t>(target) % sizeof(u32_t)) == 0)
+	{
+		const u32_t * const p_source
+			= static_cast<const u32_t *>(source);
+		u32_t * const p_target = static_cast<u32_t *>(target);
+
+		for (u32_t ind = 0u; ind < block_size/sizeof(u32_t); ind++)
+		{
+			p_target[ind] = p_source[ind];
+		}
+	}
+	else if ((reinterpret_cast<u32_t>(source) % sizeof(u16_t)) == 0
+		&& (reinterpret_cast<u32_t>(target) % sizeof(u16_t)) == 0)
+	{
+		const u16_t * const p_source
+			= static_cast<const u16_t *>(source);
+		u16_t * const p_target = static_cast<u16_t *>(target);
+
+		for (u32_t ind = 0u; ind < block_size/sizeof(u16_t); ind++)
+		{
+			p_target[ind] = p_source[ind];
+		}
+	}
+	else
+	{
+		const u8_t * const p_source = static_cast<const u8_t *>(source);
+		u8_t * const p_target = static_cast<u8_t *>(target);
+
+		for (u32_t ind = 0u; ind < block_size/sizeof(u8_t); ind++)
+		{
+			p_target[ind] = p_source[ind];
+		}
+	}
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT void crypto_cbc_c::set_is_valid()
+{
+	m_is_valid = true;
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT bool crypto_cbc_c::get_is_valid()
+{
+	return m_is_valid;
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT const eap_variable_data_c * crypto_cbc_c::get_tmp_IV()
+{
+	return m_tmp_IV;
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT bool crypto_cbc_c::get_encrypts()
+{
+	return m_crypto_block_algorithm->get_encrypts();
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT u32_t crypto_cbc_c::get_key_length()
+{
+	return m_crypto_block_algorithm->get_key_length();
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT u32_t crypto_cbc_c::get_block_size()
+{
+	return m_crypto_block_algorithm->get_block_size();
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT u32_t crypto_cbc_c::aligned_data_length(
+	u32_t data_length)
+{
+	const u32_t fill_bytes = data_length
+		% (m_crypto_block_algorithm->get_block_size());
+
+	// NOTE, this will always add padding bytes (1...BLOCK_SIZE).
+	data_length += ((m_crypto_block_algorithm->get_block_size())-fill_bytes);
+
+	return data_length;
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e crypto_cbc_c::add_padding_bytes(
+	void * const buffer,
+	const u32_t buffer_length,
+	const u8_t padding_byte)
+{
+	u8_t * const p_buffer = static_cast<u8_t *>(buffer);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("CBC: encrypt_function: crypto_cbc_c::add_padding_bytes(): buffer=0x%08x, buffer_length=%d, padding_byte=0x%02x\n"),
+		buffer,
+		buffer_length,
+		padding_byte));
+
+	for(u32_t ind = 0; ind < buffer_length; ind++)
+	{
+		p_buffer[ind] = padding_byte;
+	}
+
+	EAP_TRACE_DATA_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("crypto_cbc_c::add_padding_bytes()"),
+		 p_buffer,
+		 buffer_length));
+
+	return EAP_STATUS_RETURN(m_am_tools, eap_status_ok);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e crypto_cbc_c::check_padding_bytes(
+	const void * const buffer,
+	const u32_t buffer_length,
+	const u8_t padding_byte)
+{
+	const u8_t * const p_buffer = static_cast<const u8_t *>(buffer);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("CBC: encrypt_function: crypto_cbc_c::check_padding_bytes(): buffer=0x%08x, buffer_length=%d, padding_byte=0x%02x\n"),
+		buffer,
+		buffer_length,
+		padding_byte));
+
+	EAP_TRACE_DATA_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("crypto_cbc_c::check_padding_bytes()"),
+		 p_buffer,
+		 buffer_length));
+
+	for(u32_t ind = 0; ind < buffer_length; ind++)
+	{
+		if (p_buffer[ind] != padding_byte)
+		{
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_padding);
+		}
+	}
+
+	return EAP_STATUS_RETURN(m_am_tools, eap_status_ok);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e crypto_cbc_c::set_encryption_key(
+	const void * const encryption_IV,
+	const u32_t encryption_IV_length,
+	const void * const key,
+	const u32_t key_length)
+{
+	eap_status_e status = m_tmp_IV->set_copy_of_buffer(
+		encryption_IV, encryption_IV_length);
+	if (status != eap_status_ok)
+	{
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	return m_crypto_block_algorithm->set_encryption_key(
+		key,
+		key_length);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e crypto_cbc_c::set_decryption_key(
+	const void * const encryption_IV,
+	const u32_t encryption_IV_length,
+	const void * const key,
+	const u32_t key_length)
+{
+	eap_status_e status = m_tmp_IV->set_copy_of_buffer(
+		encryption_IV, encryption_IV_length);
+	if (status != eap_status_ok)
+	{
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	return m_crypto_block_algorithm->set_decryption_key(
+		key,
+		key_length);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e crypto_cbc_c::internal_encrypt_data(
+	const void * const data_in,
+	void * const data_out,
+	const u32_t data_length)
+{
+	if ((data_length % m_crypto_block_algorithm->get_block_size()) != 0)
+	{
+		return EAP_STATUS_RETURN(
+			m_am_tools,
+			eap_status_data_length_not_aligned_to_block_size);
+	}
+
+	const u8_t * const p_data_in = static_cast<const u8_t *>(data_in);
+	u8_t * const p_data_out = static_cast<u8_t *>(data_out);
+
+	EAP_TRACE_DATA_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_EAP_AM_CRYPTO,
+		(EAPL("encrypt data_in"),
+		data_in, data_length));
+
+	cbc_copy_block(
+		m_saved_in_buffer,
+		data_in,
+		m_crypto_block_algorithm->get_block_size(),
+		m_crypto_block_algorithm->get_key_length());
+
+	cbc_xor_block(
+		m_tmp_IV->get_data(m_tmp_IV->get_data_length()),
+		m_saved_in_buffer,
+		m_crypto_block_algorithm->get_block_size(),
+		m_crypto_block_algorithm->get_key_length());
+
+	u32_t ind;
+	eap_status_e status = eap_status_ok;
+
+	for (ind = 0u; ind < data_length
+			 ; ind += m_crypto_block_algorithm->get_block_size())
+	{
+		if (ind > 0u)
+		{
+			cbc_copy_block(
+				m_saved_in_buffer,
+				p_data_in+ind,
+				m_crypto_block_algorithm->get_block_size(),
+				m_crypto_block_algorithm->get_key_length());
+
+			cbc_xor_block(
+				p_data_out+(ind-m_crypto_block_algorithm->get_block_size()),
+				m_saved_in_buffer,
+				m_crypto_block_algorithm->get_block_size(),
+				m_crypto_block_algorithm->get_key_length());
+		}
+
+		status = m_crypto_block_algorithm->encrypt_block(
+			m_saved_in_buffer,
+			m_saved_out_buffer,
+			m_crypto_block_algorithm->get_block_size());
+		if (status != eap_status_ok)
+		{
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		cbc_copy_block(
+			p_data_out+ind,
+			m_saved_out_buffer,
+			m_crypto_block_algorithm->get_block_size(),
+			m_crypto_block_algorithm->get_key_length());
+
+	}
+
+	// Save the last block.
+	status = m_tmp_IV->set_copy_of_buffer(
+		p_data_out+(ind-m_crypto_block_algorithm->get_block_size()),
+		m_crypto_block_algorithm->get_block_size());
+
+	EAP_TRACE_DATA_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_EAP_AM_CRYPTO,
+		(EAPL("encrypt data_out"),
+		 data_out,
+		 data_length));
+
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e crypto_cbc_c::encrypt_data(
+	const void * const data_in,
+	void * const data_out,
+	const u32_t data_length)
+{
+	EAP_ASSERT(data_in != data_out);
+
+	eap_status_e status = internal_encrypt_data(
+		data_in,
+		data_out,
+		data_length);
+
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e crypto_cbc_c::encrypt_data(
+	void * const data_in_out,
+	const u32_t data_length)
+{
+
+	eap_status_e status = internal_encrypt_data(
+		data_in_out,
+		data_in_out,
+		data_length);
+
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e crypto_cbc_c::internal_decrypt_data(
+	const void * const data_in,
+	void * const data_out,
+	const u32_t data_length)
+{
+	u8_t *saved_iv_buffer = m_iv_buffer_1;
+	u8_t *encrypted_buffer = m_iv_buffer_2;
+
+	if ((data_length % m_crypto_block_algorithm->get_block_size()) != 0u)
+	{
+		return EAP_STATUS_RETURN(
+			m_am_tools,
+			eap_status_data_length_not_aligned_to_block_size);
+	}
+
+	EAP_TRACE_DATA_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_EAP_AM_CRYPTO,
+		(EAPL("decrypt data_in"),
+		 data_in,
+		 data_length));
+
+	const u8_t * const p_data_in = static_cast<const u8_t *>(data_in);
+	u8_t * const p_data_out = static_cast<u8_t *>(data_out);
+
+	cbc_copy_block(
+		saved_iv_buffer,
+		m_tmp_IV->get_data(m_tmp_IV->get_data_length()),
+		m_crypto_block_algorithm->get_block_size(),
+		m_crypto_block_algorithm->get_key_length());
+
+	u32_t ind;
+	eap_status_e status = eap_status_ok;
+
+	for (ind = 0u; ind < data_length
+			 ; ind += m_crypto_block_algorithm->get_block_size())
+	{
+		cbc_copy_block(
+			encrypted_buffer,
+			p_data_in+ind,
+			m_crypto_block_algorithm->get_block_size(),
+			m_crypto_block_algorithm->get_key_length());
+
+		cbc_copy_block(
+			m_saved_in_buffer,
+			p_data_in+ind,
+			m_crypto_block_algorithm->get_block_size(),
+			m_crypto_block_algorithm->get_key_length());
+
+		status = m_crypto_block_algorithm->decrypt_block(
+			m_saved_in_buffer,
+			m_saved_out_buffer,
+			m_crypto_block_algorithm->get_block_size());
+		if (status != eap_status_ok)
+		{
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		cbc_xor_block(
+			saved_iv_buffer,
+			m_saved_out_buffer,
+			m_crypto_block_algorithm->get_block_size(),
+			m_crypto_block_algorithm->get_key_length());
+
+		cbc_copy_block(
+			p_data_out+ind,
+			m_saved_out_buffer,
+			m_crypto_block_algorithm->get_block_size(),
+			m_crypto_block_algorithm->get_key_length());
+
+		u8_t *tmp = encrypted_buffer;
+		encrypted_buffer = saved_iv_buffer;
+		saved_iv_buffer = tmp;
+	}
+
+	EAP_TRACE_DATA_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_EAP_AM_CRYPTO,
+		(EAPL("decrypt data_out"),
+		 data_out,
+		 data_length));
+
+	// Save the last block.
+	status = m_tmp_IV->set_copy_of_buffer(
+		saved_iv_buffer,
+		m_crypto_block_algorithm->get_block_size());
+
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e crypto_cbc_c::decrypt_data(
+	const void * const data_in,
+	void * const data_out,
+	const u32_t data_length)
+{
+	EAP_ASSERT(data_in != data_out);
+
+	eap_status_e status = internal_decrypt_data(
+		data_in,
+		data_out,
+		data_length);
+
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e crypto_cbc_c::decrypt_data(
+	void * const data_in_out,
+	const u32_t data_length)
+{
+
+	eap_status_e status = internal_decrypt_data(
+		data_in_out,
+		data_in_out,
+		data_length);
+
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e crypto_cbc_c::update_non_aligned(
+	const void * const msg_in,
+	void * const msg_out,
+	const u32_t msg_size)
+{
+	EAP_ASSERT_ALWAYS(msg_in != msg_out);
+
+	const u8_t *inp = reinterpret_cast<const u8_t *>(msg_in);
+	u8_t *oup = static_cast<u8_t *>(msg_out);
+	i32_t len = static_cast<i32_t>(msg_size);
+	i32_t tail;
+	eap_status_e status = eap_status_process_general_error;
+
+	if (m_encr_offset)
+	{
+		//
+		// Non aligned handling
+		//
+		while (len > 0
+			&& m_encr_offset
+			   < static_cast<i32_t>(m_crypto_block_algorithm->get_block_size()))
+		{
+			m_encr_dispatch[m_encr_offset] = oup++;
+			m_encr_hold[m_encr_offset++] = *inp++;
+			len--;
+		}
+
+		if (m_encr_offset == static_cast<i32_t>(m_crypto_block_algorithm->get_block_size()))
+		{
+			// Got full block, flush out
+			if (m_crypto_block_algorithm->get_encrypts() == true)
+			{
+				status = encrypt_data(
+					m_encr_hold,
+					m_crypto_block_algorithm->get_block_size());
+			}
+			else
+			{
+				status = decrypt_data(
+					m_encr_hold,
+					m_crypto_block_algorithm->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);
+			}
+
+			for (m_encr_offset = 0
+					 ; m_encr_offset
+					 < static_cast<i32_t>(m_crypto_block_algorithm->get_block_size())
+					 ; ++m_encr_offset)
+			{
+				*(m_encr_dispatch[m_encr_offset]) = m_encr_hold[m_encr_offset];
+			}
+			m_encr_offset = 0;
+		}
+		// Either len == 0 or (encrypt->encr_offset) == 0, or both!!!
+	}
+
+	tail = len % m_crypto_block_algorithm->get_block_size();
+	len -= tail;
+	if (len > 0)
+	{
+		if (m_crypto_block_algorithm->get_encrypts() == true)
+		{
+			status = encrypt_data(
+				inp,
+				oup,
+				len);
+		}
+		else
+		{
+			status = decrypt_data(
+				inp,
+				oup,
+				len);
+		}
+
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+
+	if (tail > 0)
+	{
+		oup += len;
+		inp += len;
+		while (m_encr_offset < tail)
+		{
+			m_encr_dispatch[m_encr_offset] = oup++;
+			m_encr_hold[m_encr_offset++] = *inp++;
+		}
+		status = eap_status_ok;
+	}
+
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e crypto_cbc_c::update_non_aligned(
+	void * const msg_in_out,
+	const u32_t msg_size)
+{
+	u8_t *msg = static_cast<u8_t *>(msg_in_out);
+	i32_t len = static_cast<i32_t>(msg_size);
+	i32_t tail;
+	eap_status_e status = eap_status_process_general_error;
+
+	if (m_encr_offset)
+	{
+		//
+		// Non aligned handling
+		//
+		while (len > 0
+			&& m_encr_offset
+			   < static_cast<i32_t>(m_crypto_block_algorithm->get_block_size()))
+		{
+			m_encr_dispatch[m_encr_offset] = msg;
+			m_encr_hold[m_encr_offset++] = *msg++;
+			len--;
+		}
+
+		if (m_encr_offset == static_cast<i32_t>(m_crypto_block_algorithm->get_block_size()))
+		{
+			// Got full block, flush out
+			if (m_crypto_block_algorithm->get_encrypts() == true)
+			{
+				status = encrypt_data(
+					m_encr_hold,
+					m_crypto_block_algorithm->get_block_size());
+			}
+			else
+			{
+				status = decrypt_data(
+					m_encr_hold,
+					m_crypto_block_algorithm->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);
+			}
+
+			for (m_encr_offset = 0
+					 ; m_encr_offset
+					 < static_cast<i32_t>(m_crypto_block_algorithm->get_block_size())
+					 ; ++m_encr_offset)
+			{
+				*(m_encr_dispatch[m_encr_offset]) = m_encr_hold[m_encr_offset];
+			}
+			m_encr_offset = 0;
+		}
+		// Either len == 0 or (encrypt->encr_offset) == 0, or both!!!
+	}
+
+	tail = len % m_crypto_block_algorithm->get_block_size();
+	len -= tail;
+	if (len > 0)
+	{
+		if (m_crypto_block_algorithm->get_encrypts() == true)
+		{
+			status = encrypt_data(
+				msg,
+				len);
+		}
+		else
+		{
+			status = decrypt_data(
+				msg,
+				len);
+		}
+
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+
+	if (tail > 0)
+	{
+		msg += len;
+		while (m_encr_offset < tail)
+		{
+			m_encr_dispatch[m_encr_offset] = msg;
+			m_encr_hold[m_encr_offset++] = *msg++;
+		}
+	}
+
+	return EAP_STATUS_RETURN(m_am_tools, eap_status_ok);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e crypto_cbc_c::finalize_non_aligned()
+{
+	eap_status_e status = eap_status_ok;
+
+	if (m_encr_offset > 0)
+	{
+		if ((m_encr_offset
+			 % m_crypto_block_algorithm->get_block_size()) != 0u)
+		{
+			EAP_ASSERT_ALWAYS(
+				(m_encr_offset
+				 % m_crypto_block_algorithm->get_block_size()) == 0u);
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_data_length_not_aligned_to_block_size);
+		}
+
+		i32_t ind;
+
+		/* Unfinished business... flush it out! */
+		if (m_crypto_block_algorithm->get_encrypts() == true)
+		{
+			status = encrypt_data(
+				m_encr_hold,
+				m_crypto_block_algorithm->get_block_size());
+		}
+		else
+		{
+			status = decrypt_data(
+				m_encr_hold,
+				m_crypto_block_algorithm->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);
+		}
+
+		for (ind = 0u; ind < m_encr_offset; ++ind)
+		{
+			*(m_encr_dispatch[ind]) = m_encr_hold[ind];
+		}
+		m_encr_offset = 0;	/* (should not be needed, but... ) */
+	}
+
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+//--------------------------------------------------
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT crypto_aes_c::~crypto_aes_c()
+{
+	if (m_aes_context.get_is_valid_data() == true)
+	{
+		m_am_tools->get_crypto()->aes_cleanup(&m_aes_context);
+	}
+	m_aes_context.reset();
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT crypto_aes_c::crypto_aes_c(abs_eap_am_tools_c * const tools)
+	: m_am_tools(tools)
+	, m_is_valid(false)
+	, m_aes_context(tools)
+	, m_block_size(0u)
+	, m_encrypt(false)
+{
+	m_block_size = m_am_tools->get_crypto()->aes_block_size();
+
+	m_aes_context.reset();
+
+	set_is_valid();
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT bool crypto_aes_c::get_encrypts()
+{
+	return m_encrypt;
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT void crypto_aes_c::set_is_valid()
+{
+	m_is_valid = true;
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT bool crypto_aes_c::get_is_valid()
+{
+	return m_is_valid;
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT u32_t crypto_aes_c::get_key_length()
+{
+	return m_am_tools->get_crypto()->aes_key_length();
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT u32_t crypto_aes_c::get_block_size()
+{
+	return m_block_size;
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e crypto_aes_c::set_encryption_key(
+	const void * const key,
+	const u32_t key_length)
+{
+	m_encrypt = true;
+
+	return m_am_tools->get_crypto()->aes_set_encryption_key(
+		&m_aes_context, static_cast<const u8_t *>(key), key_length);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e crypto_aes_c::set_decryption_key(
+	const void * const key,
+	const u32_t key_length)
+{
+	m_encrypt = false;
+
+	return m_am_tools->get_crypto()->aes_set_decryption_key(
+		&m_aes_context, static_cast<const u8_t *>(key), key_length);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e crypto_aes_c::encrypt_block(
+	const void * const data_in,
+	void * const data_out,
+	const u32_t data_length)
+{
+	EAP_ASSERT_TOOLS(m_am_tools, ((reinterpret_cast<u32_t>(data_in) % 4) == 0));
+	EAP_ASSERT_TOOLS(m_am_tools, ((reinterpret_cast<u32_t>(data_out) % 4) == 0));
+
+	return m_am_tools->get_crypto()->aes_encrypt_block(
+		&m_aes_context,
+		static_cast<const u8_t *>(data_in),
+		static_cast<u8_t *>(data_out),
+		data_length);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e crypto_aes_c::decrypt_block(
+	const void * const data_in,
+	void * const data_out,
+	const u32_t data_length)
+{
+	EAP_ASSERT_TOOLS(m_am_tools, ((reinterpret_cast<u32_t>(data_in) % 4) == 0));
+	EAP_ASSERT_TOOLS(m_am_tools, ((reinterpret_cast<u32_t>(data_out) % 4) == 0));
+
+	return m_am_tools->get_crypto()->aes_decrypt_block(
+		&m_aes_context,
+		static_cast<const u8_t *>(data_in),
+		static_cast<u8_t *>(data_out), data_length);
+}
+
+//--------------------------------------------------
+//--------------------------------------------------
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT crypto_3des_ede_c::~crypto_3des_ede_c()
+{
+	if (m_context.get_is_valid_data() == true)
+	{
+		m_am_tools->get_crypto()->cleanup_3des_ede(&m_context);
+	}
+	m_context.reset();
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT crypto_3des_ede_c::crypto_3des_ede_c(
+	abs_eap_am_tools_c * const tools)
+	: m_am_tools(tools)
+	, m_is_valid(false)
+	, m_context(tools)
+	, m_block_size(0u)
+	, m_encrypt(false)
+{
+	m_block_size = m_am_tools->get_crypto()->block_size_3des_ede();
+
+	m_context.reset();
+
+	set_is_valid();
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT bool crypto_3des_ede_c::get_encrypts()
+{
+	return m_encrypt;
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT void crypto_3des_ede_c::set_is_valid()
+{
+	m_is_valid = true;
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT bool crypto_3des_ede_c::get_is_valid()
+{
+	return m_is_valid;
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT u32_t crypto_3des_ede_c::get_key_length()
+{
+	return m_am_tools->get_crypto()->key_length_3des_ede();
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT u32_t crypto_3des_ede_c::get_block_size()
+{
+	return m_block_size;
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e crypto_3des_ede_c::set_encryption_key(
+	const void * const key,
+	const u32_t key_length)
+{
+	m_encrypt = true;
+
+	return m_am_tools->get_crypto()->set_encryption_key_3des_ede(
+		&m_context, static_cast<const u8_t *>(key), key_length);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e crypto_3des_ede_c::set_decryption_key(
+	const void * const key,
+	const u32_t key_length)
+{
+	m_encrypt = false;
+
+	return m_am_tools->get_crypto()->set_decryption_key_3des_ede(
+		&m_context, static_cast<const u8_t *>(key), key_length);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e crypto_3des_ede_c::encrypt_block(
+	const void * const data_in,
+	void * const data_out,
+	const u32_t data_length)
+{
+	EAP_ASSERT_TOOLS(m_am_tools, ((reinterpret_cast<u32_t>(data_in) % 4) == 0));
+	EAP_ASSERT_TOOLS(m_am_tools, ((reinterpret_cast<u32_t>(data_out) % 4) == 0));
+
+	return m_am_tools->get_crypto()->encrypt_block_3des_ede(
+		&m_context,
+		static_cast<const u8_t *>(data_in),
+		static_cast<u8_t *>(data_out),
+		data_length);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e crypto_3des_ede_c::decrypt_block(
+	const void * const data_in,
+	void * const data_out,
+	const u32_t data_length)
+{
+	EAP_ASSERT_TOOLS(m_am_tools, ((reinterpret_cast<u32_t>(data_in) % 4) == 0));
+	EAP_ASSERT_TOOLS(m_am_tools, ((reinterpret_cast<u32_t>(data_out) % 4) == 0));
+
+	return m_am_tools->get_crypto()->decrypt_block_3des_ede(
+		&m_context,
+		static_cast<const u8_t *>(data_in),
+		static_cast<u8_t *>(data_out),
+		data_length);
+}
+
+//--------------------------------------------------
+//--------------------------------------------------
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT crypto_aes_wrap_c::~crypto_aes_wrap_c()
+{
+}
+
+EAP_FUNC_EXPORT crypto_aes_wrap_c::crypto_aes_wrap_c(abs_eap_am_tools_c * const tools)
+	: m_am_tools(tools)
+	  , m_aes(tools)
+	  , m_key(tools)
+	  , m_encrypt(false)
+	  , m_is_valid(false)
+{
+	if (m_aes.get_is_valid() == false)
+	{
+		return;
+	}
+
+	if (m_key.get_is_valid() == false)
+	{
+		return;
+	}
+
+	set_is_valid();
+}
+
+EAP_FUNC_EXPORT void crypto_aes_wrap_c::set_is_valid()
+{
+	m_is_valid = true;
+}
+
+EAP_FUNC_EXPORT bool crypto_aes_wrap_c::get_is_valid()
+{
+	return m_is_valid;
+}
+
+EAP_FUNC_EXPORT bool crypto_aes_wrap_c::get_encrypts()
+{
+	return m_encrypt;
+}
+
+EAP_FUNC_EXPORT u32_t crypto_aes_wrap_c::get_key_length()
+{
+	return m_aes.get_key_length();
+}
+
+EAP_FUNC_EXPORT u32_t crypto_aes_wrap_c::get_block_size()
+{
+	return EAP_CRYPTO_AES_WRAP_BLOCK_SIZE;
+}
+
+EAP_FUNC_EXPORT eap_status_e crypto_aes_wrap_c::set_encryption_key(
+	const void * const key,
+	const u32_t key_length)
+{
+	eap_status_e status = m_key.set_copy_of_buffer(key, key_length);
+
+	m_encrypt = true;
+
+	EAP_TRACE_DATA_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_EAP_AM_CRYPTO,
+		(EAPL("AES-Wrap enc key"),
+		 m_key.get_data(),
+		 m_key.get_data_length()));
+
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+EAP_FUNC_EXPORT eap_status_e crypto_aes_wrap_c::set_decryption_key(
+	const void * const key,
+	const u32_t key_length)
+{
+	eap_status_e status = m_key.set_copy_of_buffer(key, key_length);
+
+	m_encrypt = false;
+
+	EAP_TRACE_DATA_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_EAP_AM_CRYPTO,
+		(EAPL("AES-Wrap dec key"),
+		 m_key.get_data(),
+		 m_key.get_data_length()));
+
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+EAP_FUNC_EXPORT eap_status_e crypto_aes_wrap_c::add_padding_bytes(
+	void * const buffer,
+	const u32_t buffer_length)
+{
+	if (buffer == 0
+		|| buffer_length == 0)
+	{
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+	}
+
+	u8_t * data = static_cast<u8_t *>(buffer);
+	
+	data[0] = 0xdd;
+
+	for (u32_t ind = 1ul; ind < buffer_length; ind++)
+	{
+		data[ind] = 0x00;
+	}
+
+	return EAP_STATUS_RETURN(m_am_tools, eap_status_ok);
+}
+
+EAP_FUNC_EXPORT eap_status_e crypto_aes_wrap_c::encrypt_block(
+	const void * const data_in,
+	const u32_t data_in_length,
+	void * const data_out,
+	const u32_t data_out_length)
+{
+#if !defined(DISABLE_CRYPTO_AES_WRAP)
+	/*
+	  Inputs:  Plaintext, n 64-bit values {P1, P2, ..., Pn}, and
+	           Key, K (the KEK).
+	  Outputs: Ciphertext, (n+1) 64-bit values {C0, C1, ..., Cn}.
+	*/
+	if (data_in == 0
+		|| data_out == 0
+		|| (data_in_length % get_block_size()) != 0
+		|| data_out_length < data_in_length+get_block_size())
+	{
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+	}
+
+	EAP_TRACE_DATA_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_EAP_AM_CRYPTO,
+		(EAPL("plain data"),
+		 data_in,
+		 data_in_length));
+
+	/*
+	  1) Initialize variables.
+   
+	  Set A = IV, an initial value (see 2.2.3)
+	  For i = 1 to n
+	      R[i] = P[i]
+	*/
+
+	u64_t A_integrity_reg;
+	EAP_ASSERT_TOOLS(m_am_tools, sizeof(A_integrity_reg) == sizeof(EAP_CRYPTO_AES_WRAP_DEFAULT_INITIAL_IV));
+	m_am_tools->memmove(&A_integrity_reg, EAP_CRYPTO_AES_WRAP_DEFAULT_INITIAL_IV, sizeof(A_integrity_reg));
+
+	eap_variable_data_c Register(m_am_tools);
+
+	eap_status_e status = Register.set_copy_of_buffer(data_in, data_in_length);
+	if (status != eap_status_ok)
+	{
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	/*
+	  2) Calculate intermediate values.
+
+	  For j = 0 to 5
+	      For i=1 to n
+		      B = AES(K, A | R[i])
+			  A = MSB(64, B) ^ t where t = (n*j)+i
+			  R[i] = LSB(64, B)
+	*/
+
+	u64_t n_block_count = Register.get_data_length() / 8ul;
+	u64_t B_tmp_buffer[2];
+	eap_variable_data_c data(m_am_tools);
+
+	status = data.set_buffer_length(sizeof(A_integrity_reg) + EAP_CRYPTO_AES_WRAP_BLOCK_SIZE);
+	if (status != eap_status_ok)
+	{
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	for (u64_t j_ind = 0ul; j_ind <= 5ul; j_ind++)
+	{
+		for (u64_t i_ind = 1ul; i_ind <= n_block_count; i_ind++)
+		{
+			EAP_TRACE_DEBUG(
+				m_am_tools,
+				TRACE_FLAGS_EAP_AM_CRYPTO,
+				(EAPL("\n")));
+
+			EAP_TRACE_DEBUG(
+				m_am_tools,
+				TRACE_FLAGS_EAP_AM_CRYPTO,
+				(EAPL("crypto_aes_wrap_c::encrypt_block(): j_ind %d, i_ind %d\n"),
+				 j_ind,
+				 i_ind));
+
+			eap_status_e status = m_aes.set_encryption_key(
+				m_key.get_data(),
+				m_key.get_data_length());
+			if (status != eap_status_ok)
+			{
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			EAP_TRACE_DATA_DEBUG(
+				m_am_tools,
+				TRACE_FLAGS_EAP_AM_CRYPTO,
+				(EAPL("In A_integrity_reg"),
+				 &A_integrity_reg,
+				 sizeof(A_integrity_reg)));
+
+			status = data.set_copy_of_buffer(&A_integrity_reg, sizeof(A_integrity_reg));
+			if (status != eap_status_ok)
+			{
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			u8_t * const Ri_reg = Register.get_data_offset(
+				static_cast<u32_t>((i_ind-1ul))*static_cast<u32_t>(EAP_CRYPTO_AES_WRAP_BLOCK_SIZE),
+				static_cast<u32_t>(EAP_CRYPTO_AES_WRAP_BLOCK_SIZE));
+			if (Ri_reg == 0)
+			{
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+			}
+
+			EAP_TRACE_DATA_DEBUG(
+				m_am_tools,
+				TRACE_FLAGS_EAP_AM_CRYPTO,
+				(EAPL("         In Ri_reg"),
+				 Ri_reg,
+				 static_cast<u32_t>(EAP_CRYPTO_AES_WRAP_BLOCK_SIZE)));
+
+			status = data.add_data(Ri_reg, EAP_CRYPTO_AES_WRAP_BLOCK_SIZE);
+			if (status != eap_status_ok)
+			{
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			EAP_ASSERT_TOOLS(m_am_tools, sizeof(B_tmp_buffer) == data.get_data_length());
+
+			status = m_aes.encrypt_block(
+				data.get_data(),
+				B_tmp_buffer,
+				data.get_data_length());
+			if (status != eap_status_ok)
+			{
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			EAP_TRACE_DATA_DEBUG(
+				m_am_tools,
+				TRACE_FLAGS_EAP_AM_CRYPTO,
+				(EAPL("  Enc B_tmp_buffer"),
+				 B_tmp_buffer,
+				 sizeof(B_tmp_buffer)));
+
+			u64_t counter = m_am_tools->multiply_u64(n_block_count, j_ind) + i_ind;
+
+			A_integrity_reg = eap_htonll(
+				m_am_tools->xor_u64(
+					eap_ntohll(*B_tmp_buffer),
+					counter));
+
+			m_am_tools->memmove(Ri_reg, B_tmp_buffer+1, sizeof(u64_t));
+		}
+	}
+
+	/*
+	  3) Output the results.
+
+	  Set C[0] = A
+	  For i = 1 to n
+	      C[i] = R[i]
+	*/
+	
+	u8_t * const C = static_cast<u8_t *>(data_out);
+
+	m_am_tools->memmove(C, &A_integrity_reg, EAP_CRYPTO_AES_WRAP_BLOCK_SIZE);
+
+	for (u32_t i_ind = 1ul; i_ind <= n_block_count; i_ind++)
+	{
+		u32_t offset = static_cast<u32_t>(i_ind)*static_cast<u32_t>(EAP_CRYPTO_AES_WRAP_BLOCK_SIZE);
+
+		u8_t * const Ri_reg = Register.get_data_offset(
+			static_cast<u32_t>((i_ind-1ul))*static_cast<u32_t>(EAP_CRYPTO_AES_WRAP_BLOCK_SIZE),
+			static_cast<u32_t>(EAP_CRYPTO_AES_WRAP_BLOCK_SIZE));
+		if (Ri_reg == 0)
+		{
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+		}
+
+		m_am_tools->memmove((C + offset), Ri_reg, EAP_CRYPTO_AES_WRAP_BLOCK_SIZE);
+	}
+
+	EAP_TRACE_DATA_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_EAP_AM_CRYPTO,
+		(EAPL("encrypted data"),
+		 data_out,
+		 data_out_length));
+
+	return EAP_STATUS_RETURN(m_am_tools, eap_status_ok);
+
+#else // !defined(DISABLE_CRYPTO_AES_WRAP)
+	return EAP_STATUS_RETURN(m_am_tools, eap_status_not_supported);
+#endif
+
+}
+
+EAP_FUNC_EXPORT eap_status_e crypto_aes_wrap_c::decrypt_block(
+	const void * const data_in,
+	const u32_t data_in_length,
+	void * const data_out,
+	const u32_t data_out_length)
+{
+#if !defined(DISABLE_CRYPTO_AES_WRAP)
+	/*
+	  Inputs:  Ciphertext, (n+1) 64-bit values {C0, C1, ..., Cn}, and
+	           Key, K (the KEK).
+	  Outputs: Plaintext, n 64-bit values {P0, P1, K, Pn}.
+	*/
+	if (data_in == 0
+		|| data_out == 0
+		|| (data_in_length % get_block_size()) != 0
+		|| data_out_length < data_in_length-get_block_size())
+	{
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+	}
+
+	EAP_TRACE_DATA_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_EAP_AM_CRYPTO,
+		(EAPL("encrypted data"),
+		 data_in,
+		 data_in_length));
+
+	/*
+	  1) Initialize variables.
+
+	  Set A = C[0]
+	  For i = 1 to n
+	      R[i] = C[i]
+	*/
+	u64_t A_integrity_reg(0ul);
+	const u8_t * const cipher_text = static_cast<const u8_t *>(data_in);
+
+	m_am_tools->memmove(&A_integrity_reg, cipher_text, EAP_CRYPTO_AES_WRAP_BLOCK_SIZE);
+
+	eap_variable_data_c Register(m_am_tools);
+
+	eap_status_e status = Register.set_copy_of_buffer(
+		(cipher_text + static_cast<u32_t>(EAP_CRYPTO_AES_WRAP_BLOCK_SIZE)),
+		data_in_length - static_cast<u32_t>(EAP_CRYPTO_AES_WRAP_BLOCK_SIZE));
+	if (status != eap_status_ok)
+	{
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	/*
+	  2) Compute intermediate values.
+
+	  For j = 5 to 0
+	      For i = n to 1
+		      B = AES-1(K, (A ^ t) | R[i]) where t = n*j+i
+			  A = MSB(64, B)
+			  R[i] = LSB(64, B)
+	*/
+
+	u64_t n_block_count = Register.get_data_length() / 8ul;
+	u64_t B_tmp_buffer[2];
+	eap_variable_data_c data(m_am_tools);
+
+	status = data.set_buffer_length(sizeof(A_integrity_reg) + EAP_CRYPTO_AES_WRAP_BLOCK_SIZE);
+	if (status != eap_status_ok)
+	{
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	u64_t j_ind(5ul);
+
+	for(;;)
+	{
+		for (u64_t i_ind = n_block_count; i_ind >= 1; i_ind--)
+		{
+			EAP_TRACE_DEBUG(
+				m_am_tools,
+				TRACE_FLAGS_EAP_AM_CRYPTO,
+				(EAPL("\n")));
+
+			EAP_TRACE_DEBUG(
+				m_am_tools,
+				TRACE_FLAGS_EAP_AM_CRYPTO,
+				(EAPL("crypto_aes_wrap_c::decrypt_block(): j_ind %d, i_ind %d\n"),
+				 j_ind,
+				 i_ind));
+
+			eap_status_e status = m_aes.set_decryption_key(
+				m_key.get_data(),
+				m_key.get_data_length());
+			if (status != eap_status_ok)
+			{
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			u64_t counter = m_am_tools->multiply_u64(n_block_count, j_ind) + i_ind;
+
+			EAP_TRACE_DATA_DEBUG(
+				m_am_tools,
+				TRACE_FLAGS_EAP_AM_CRYPTO,
+				(EAPL("In A_integrity_reg"),
+				 &A_integrity_reg,
+				 sizeof(A_integrity_reg)));
+
+			A_integrity_reg = eap_htonll(
+				m_am_tools->xor_u64(
+					eap_ntohll(A_integrity_reg),
+					counter));
+
+			status = data.set_copy_of_buffer(&A_integrity_reg, sizeof(A_integrity_reg));
+			if (status != eap_status_ok)
+			{
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			u8_t * const Ri_reg = Register.get_data_offset(
+				static_cast<u32_t>((i_ind-1ul))*static_cast<u32_t>(EAP_CRYPTO_AES_WRAP_BLOCK_SIZE),
+				static_cast<u32_t>(EAP_CRYPTO_AES_WRAP_BLOCK_SIZE));
+			if (Ri_reg == 0)
+			{
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+			}
+
+			EAP_TRACE_DATA_DEBUG(
+				m_am_tools,
+				TRACE_FLAGS_EAP_AM_CRYPTO,
+				(EAPL("         In Ri_reg"),
+				 Ri_reg,
+				 EAP_CRYPTO_AES_WRAP_BLOCK_SIZE));
+
+			status = data.add_data(Ri_reg, EAP_CRYPTO_AES_WRAP_BLOCK_SIZE);
+			if (status != eap_status_ok)
+			{
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			EAP_ASSERT_TOOLS(m_am_tools, sizeof(B_tmp_buffer) == data.get_data_length());
+
+			status = m_aes.decrypt_block(
+				data.get_data(),
+				B_tmp_buffer,
+				data.get_data_length());
+			if (status != eap_status_ok)
+			{
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			EAP_TRACE_DATA_DEBUG(
+				m_am_tools,
+				TRACE_FLAGS_EAP_AM_CRYPTO,
+				(EAPL("  Dec B_tmp_buffer"),
+				 B_tmp_buffer,
+				 sizeof(B_tmp_buffer)));
+
+			A_integrity_reg = *B_tmp_buffer;
+
+			m_am_tools->memmove(Ri_reg, B_tmp_buffer+1, sizeof(u64_t));
+		}
+		
+		if (j_ind == 0)
+		{
+			// Terminates the loop.
+			break;
+		}
+
+		// Decrementation must be after the termination test,
+		// because we need to run the loop also with j_ind value 0.
+		--j_ind;
+	}
+
+	/*
+	  3) Output results.
+
+	  If A is an appropriate initial value (see 2.2.3),
+	  Then
+	      For i = 1 to n
+	          P[i] = R[i]
+	  Else
+	      Return an error
+	*/
+
+	EAP_TRACE_DATA_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_EAP_AM_CRYPTO,
+		(EAPL("plain data"),
+		 Register.get_data(),
+		 Register.get_data_length()));
+
+	if (m_am_tools->memcmp(&A_integrity_reg, EAP_CRYPTO_AES_WRAP_DEFAULT_INITIAL_IV, sizeof(A_integrity_reg)) == 0)
+	{
+		// OK
+		m_am_tools->memmove(data_out, Register.get_data(), Register.get_data_length());
+
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_ok);
+	}
+	else
+	{
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_authentication_failure);
+	}
+
+#else // !defined(DISABLE_CRYPTO_AES_WRAP)
+
+	return EAP_STATUS_RETURN(m_am_tools, eap_status_not_supported);
+
+#endif
+
+}
+
+//--------------------------------------------------
+//--------------------------------------------------
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT crypto_random_c::~crypto_random_c()
+{
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT crypto_random_c::crypto_random_c(
+	abs_eap_am_tools_c * const tools)
+	: m_am_tools(tools)
+	, m_is_valid(false)
+{
+	set_is_valid();
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT void crypto_random_c::set_is_valid()
+{
+	m_is_valid = true;
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT bool crypto_random_c::get_is_valid()
+{
+	return m_is_valid;
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e crypto_random_c::get_rand_bytes(
+	void * const buffer,
+	const u32_t count)
+{
+	return m_am_tools->get_crypto()->get_rand_bytes(
+		static_cast<u8_t *>(buffer), count);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e crypto_random_c::get_rand_bytes(
+	eap_variable_data_c * const buffer,
+	const u32_t count)
+{
+	if (buffer == 0
+		|| buffer->get_is_valid() == false)
+	{
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+	}
+
+	eap_status_e status = buffer->init(count);
+	if (status != eap_status_ok)
+	{
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = buffer->set_data_length(count);
+	if (status != eap_status_ok)
+	{
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = get_rand_bytes(
+		buffer->get_data(),
+		buffer->get_data_length());
+	if (status != eap_status_ok)
+	{
+		buffer->reset();
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT u32_t crypto_random_c::get_rand_integer(
+	const u32_t minimum,
+	const u32_t maximum)
+{
+	const u32_t MAX_U32 = ~(static_cast<u32_t>(0ul));
+
+	u32_t random = 0ul;
+
+	if ((maximum - minimum) == MAX_U32 )
+	{
+		eap_status_e status = get_rand_bytes(&random, sizeof(random));
+		if (status != eap_status_ok)
+		{
+			return 0;
+		}
+	}
+	else
+	{
+		u32_t frame = ((maximum+1)-minimum);
+		u32_t randomized = 0;
+		u32_t max_value = MAX_U32 - (MAX_U32 % frame);
+
+		do
+		{
+			eap_status_e status = get_rand_bytes(&randomized, sizeof(randomized));
+			if (status != eap_status_ok)
+			{
+				return 0;
+			}
+		}
+		while(randomized >= max_value);
+		
+		random = ((randomized % frame) + minimum);
+	}
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_EAP_AM_CRYPTO,
+		(EAPL("RANDOM u32_t %lu\n"),
+		 random));
+	
+	return random;
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e crypto_random_c::add_rand_seed(
+	const void * const buffer,
+	const u32_t count)
+{
+	return m_am_tools->get_crypto()->add_rand_seed(
+		static_cast<const u8_t *>(buffer), count);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e crypto_random_c::add_rand_seed_hw_ticks()
+{
+	return m_am_tools->get_crypto()->add_rand_seed_hw_ticks();
+}
+
+//--------------------------------------------------
+//--------------------------------------------------
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT crypto_sha_256_c::~crypto_sha_256_c()
+{
+	hash_cleanup();
+	m_sha_256_context.reset();
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT crypto_sha_256_c::crypto_sha_256_c(abs_eap_am_tools_c * const tools)
+	: m_am_tools(tools)
+	, m_sha_256_context(tools)
+	, m_is_valid(false)
+{
+	set_is_valid();
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e crypto_sha_256_c::copy_context(
+	const eap_variable_data_c * const sha_256_context)
+{
+	if (sha_256_context == 0
+		|| sha_256_context->get_is_valid_data() == false)
+	{
+		set_is_invalid();
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+	}
+
+	eap_status_e status = m_am_tools->get_crypto()->sha_256_copy_context(
+		&m_sha_256_context,
+		sha_256_context);
+	if (status != eap_status_ok)
+	{
+		set_is_invalid();
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT void crypto_sha_256_c::set_is_invalid()
+{
+	m_is_valid = false;
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT void crypto_sha_256_c::set_is_valid()
+{
+	m_is_valid = true;
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT bool crypto_sha_256_c::get_is_valid()
+{
+	return m_is_valid;
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT u32_t crypto_sha_256_c::get_digest_length()
+{
+	// This is simple optimization.
+	return EAP_CRYPTO_API_SHA_256_DIGEST_BUFFER_BYTE_SIZE;
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT u32_t crypto_sha_256_c::get_block_size()
+{
+	// This is simple optimization.
+	return EAP_CRYPTO_API_SHA_256_BLOCK_BYTE_SIZE;
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e crypto_sha_256_c::hash_init()
+{
+	eap_status_e status = m_am_tools->get_crypto()->sha_256_init(&m_sha_256_context);
+	if (m_sha_256_context.get_is_valid_data() == false)
+	{
+		set_is_invalid();
+		if (status == eap_status_ok)
+		{
+			status = eap_status_allocation_error;
+		}
+	}
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e crypto_sha_256_c::hash_update(
+	const void * const data,
+	const u32_t data_length)
+{
+	eap_status_e status = m_am_tools->get_crypto()->sha_256_update(
+		&m_sha_256_context,
+		static_cast<const u8_t *>(data),
+		data_length);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e crypto_sha_256_c::hash_final(
+	void * const message_digest,
+	u32_t *md_length_or_null)
+{
+	eap_status_e status = m_am_tools->get_crypto()->sha_256_final(
+		&m_sha_256_context,
+		static_cast<u8_t *>(message_digest),
+		md_length_or_null);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e crypto_sha_256_c::hash_cleanup()
+{
+	if (m_sha_256_context.get_is_valid_data() == true)
+	{
+		m_am_tools->get_crypto()->sha_256_cleanup(&m_sha_256_context);
+	}
+	
+	return EAP_STATUS_RETURN(m_am_tools, eap_status_ok);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT abs_crypto_hash_algorithm_c * crypto_sha_256_c::copy()
+{
+	crypto_sha_256_c * const new_context = new crypto_sha_256_c(m_am_tools);
+	if (new_context == 0)
+	{
+		return 0;
+	}
+
+	eap_status_e status = new_context->copy_context(&m_sha_256_context);
+	if (status != eap_status_ok)
+	{
+		delete new_context;
+		return 0;
+	}
+
+	return new_context;
+}
+
+//--------------------------------------------------
+//--------------------------------------------------
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT crypto_sha1_c::~crypto_sha1_c()
+{
+	hash_cleanup();
+	m_sha1_context.reset();
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT crypto_sha1_c::crypto_sha1_c(abs_eap_am_tools_c * const tools)
+	: m_am_tools(tools)
+	, m_sha1_context(tools)
+	, m_is_valid(false)
+{
+	set_is_valid();
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e crypto_sha1_c::copy_context(
+	const eap_variable_data_c * const sha1_context)
+{
+	if (sha1_context == 0
+		|| sha1_context->get_is_valid_data() == false)
+	{
+		set_is_invalid();
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+	}
+
+	eap_status_e status = m_am_tools->get_crypto()->sha1_copy_context(
+		&m_sha1_context,
+		sha1_context);
+	if (status != eap_status_ok)
+	{
+		set_is_invalid();
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT void crypto_sha1_c::set_is_invalid()
+{
+	m_is_valid = false;
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT void crypto_sha1_c::set_is_valid()
+{
+	m_is_valid = true;
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT bool crypto_sha1_c::get_is_valid()
+{
+	return m_is_valid;
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT u32_t crypto_sha1_c::get_digest_length()
+{
+	// This is simple optimization.
+	return EAP_CRYPTO_API_SHA1_DIGEST_BUFFER_BYTE_SIZE;
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT u32_t crypto_sha1_c::get_block_size()
+{
+	// This is simple optimization.
+	return EAP_CRYPTO_API_SHA1_BLOCK_BYTE_SIZE;
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e crypto_sha1_c::hash_init()
+{
+	eap_status_e status = m_am_tools->get_crypto()->sha1_init(&m_sha1_context);
+	if (m_sha1_context.get_is_valid_data() == false)
+	{
+		set_is_invalid();
+		if (status == eap_status_ok)
+		{
+			status = eap_status_allocation_error;
+		}
+	}
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e crypto_sha1_c::hash_update(
+	const void * const data,
+	const u32_t data_length)
+{
+	eap_status_e status = m_am_tools->get_crypto()->sha1_update(
+		&m_sha1_context,
+		static_cast<const u8_t *>(data),
+		data_length);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e crypto_sha1_c::hash_final(
+	void * const message_digest,
+	u32_t *md_length_or_null)
+{
+	eap_status_e status = m_am_tools->get_crypto()->sha1_final(
+		&m_sha1_context,
+		static_cast<u8_t *>(message_digest),
+		md_length_or_null);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e crypto_sha1_c::hash_cleanup()
+{
+	if (m_sha1_context.get_is_valid_data() == true)
+	{
+		m_am_tools->get_crypto()->sha1_cleanup(&m_sha1_context);
+	}
+	
+	return EAP_STATUS_RETURN(m_am_tools, eap_status_ok);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT abs_crypto_hash_algorithm_c * crypto_sha1_c::copy()
+{
+	crypto_sha1_c * const new_context = new crypto_sha1_c(m_am_tools);
+	if (new_context == 0)
+	{
+		return 0;
+	}
+
+	eap_status_e status = new_context->copy_context(&m_sha1_context);
+	if (status != eap_status_ok)
+	{
+		delete new_context;
+		return 0;
+	}
+
+	return new_context;
+}
+
+//--------------------------------------------------
+//--------------------------------------------------
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT
+crypto_ephemeral_diffie_hellman_c::~crypto_ephemeral_diffie_hellman_c()
+{
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT
+crypto_ephemeral_diffie_hellman_c::crypto_ephemeral_diffie_hellman_c(
+	abs_eap_am_tools_c * const tools)
+	: m_am_tools(tools)
+	, m_is_valid(false)
+{
+	set_is_valid();
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT void crypto_ephemeral_diffie_hellman_c::set_is_valid()
+{
+	m_is_valid = true;
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT bool crypto_ephemeral_diffie_hellman_c::get_is_valid()
+{
+	return m_is_valid;
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e
+crypto_ephemeral_diffie_hellman_c::generate_diffie_hellman_keys(
+	eap_variable_data_c * const own_private_dh_key,
+	eap_variable_data_c * const own_public_dh_key,
+	const void * const prime,
+	const u32_t prime_length,
+	const void * const group_generator,
+	const u32_t group_generator_length)
+{
+	return m_am_tools->get_crypto()->generate_diffie_hellman_keys(
+		own_private_dh_key,
+		own_public_dh_key,
+		static_cast<const u8_t *>(prime),
+		prime_length,
+		static_cast<const u8_t *>(group_generator),
+		group_generator_length);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e
+crypto_ephemeral_diffie_hellman_c::generate_g_power_to_xy(
+	const eap_variable_data_c * const own_private_dh_key,
+	const eap_variable_data_c * const peer_public_dh_key,
+	eap_variable_data_c * const shared_dh_key,
+	const void * const prime,
+	const u32_t prime_length,
+	const void * const group_generator,
+	const u32_t group_generator_length)
+{
+	return m_am_tools->get_crypto()->generate_g_power_to_xy(
+		own_private_dh_key,
+		peer_public_dh_key,
+		shared_dh_key,
+		static_cast<const u8_t *>(prime),
+		prime_length,
+		static_cast<const u8_t *>(group_generator),
+		group_generator_length);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e
+crypto_ephemeral_diffie_hellman_c::dh_cleanup(
+	const eap_variable_data_c * const dh_context)
+{
+	return m_am_tools->get_crypto()->dh_cleanup(
+		dh_context);
+}
+
+//--------------------------------------------------
+//--------------------------------------------------
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT crypto_md5_c::~crypto_md5_c()
+{
+	hash_cleanup();
+	m_md5_context.reset();
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT crypto_md5_c::crypto_md5_c(abs_eap_am_tools_c * const tools)
+	: m_am_tools(tools)
+	, m_md5_context(tools)
+	, m_is_valid(false)
+{
+	set_is_valid();
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e crypto_md5_c::copy_context(
+	const eap_variable_data_c * const md5_context)
+{
+	if (md5_context == 0
+		|| md5_context->get_is_valid_data() == false)
+	{
+		set_is_invalid();
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+	}
+
+	eap_status_e status = m_am_tools->get_crypto()->md5_copy_context(
+		&m_md5_context,
+		md5_context);
+	if (status != eap_status_ok)
+	{
+		set_is_invalid();
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT void crypto_md5_c::set_is_invalid()
+{
+	m_is_valid = false;
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT void crypto_md5_c::set_is_valid()
+{
+	m_is_valid = true;
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT bool crypto_md5_c::get_is_valid()
+{
+	return m_is_valid;
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT u32_t crypto_md5_c::get_digest_length()
+{
+	return m_am_tools->get_crypto()->get_md5_digest_length(&m_md5_context);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT u32_t crypto_md5_c::get_block_size()
+{
+	return m_am_tools->get_crypto()->get_md5_block_size(&m_md5_context);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e crypto_md5_c::hash_init()
+{
+	eap_status_e status = m_am_tools->get_crypto()->md5_init(&m_md5_context);
+
+	if (m_md5_context.get_is_valid_data() == false)
+	{
+		set_is_invalid();
+		if (status == eap_status_ok)
+		{
+			status = eap_status_allocation_error;
+		}
+	}
+
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e crypto_md5_c::hash_update(
+	const void * const data,
+	const u32_t data_length)
+{
+	eap_status_e status = m_am_tools->get_crypto()->md5_update(
+		&m_md5_context,
+		static_cast<const u8_t *>(data),
+		data_length);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e crypto_md5_c::hash_final(
+	void * const message_digest,
+	u32_t *md_length_or_null)
+{
+	eap_status_e status = m_am_tools->get_crypto()->md5_final(
+		&m_md5_context,
+		static_cast<u8_t *>(message_digest),
+		md_length_or_null);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e crypto_md5_c::hash_cleanup()
+{
+	if (m_md5_context.get_is_valid_data() == true)
+	{
+		m_am_tools->get_crypto()->md5_cleanup(&m_md5_context);
+	}
+	return EAP_STATUS_RETURN(m_am_tools, eap_status_ok);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT abs_crypto_hash_algorithm_c * crypto_md5_c::copy()
+{
+	crypto_md5_c * const new_context = new crypto_md5_c(m_am_tools);
+	if (new_context == 0)
+	{
+		return 0;
+	}
+
+	eap_status_e status = new_context->copy_context(&m_md5_context);
+	if (status != eap_status_ok)
+	{
+		delete new_context;
+		return 0;
+	}
+
+	return new_context;
+}
+
+//--------------------------------------------------
+//--------------------------------------------------
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT crypto_md4_c::~crypto_md4_c()
+{
+	hash_cleanup();
+	m_md4_context.reset();
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT crypto_md4_c::crypto_md4_c(abs_eap_am_tools_c * const tools)
+	: m_am_tools(tools)
+	, m_md4_context(tools)
+	, m_is_valid(false)
+{
+	set_is_valid();
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e crypto_md4_c::copy_context(
+	const eap_variable_data_c * const md4_context)
+{
+	if (md4_context == 0
+		|| md4_context->get_is_valid_data() == false)
+	{
+		set_is_invalid();
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+	}
+
+	eap_status_e status = m_am_tools->get_crypto()->md4_copy_context(
+		&m_md4_context,
+		md4_context);
+	if (status != eap_status_ok)
+	{
+		set_is_invalid();
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT void crypto_md4_c::set_is_invalid()
+{
+	m_is_valid = false;
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT void crypto_md4_c::set_is_valid()
+{
+	m_is_valid = true;
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT bool crypto_md4_c::get_is_valid()
+{
+	return m_is_valid;
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT u32_t crypto_md4_c::get_digest_length()
+{
+	return m_am_tools->get_crypto()->get_md4_digest_length(&m_md4_context);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT u32_t crypto_md4_c::get_block_size()
+{
+	return m_am_tools->get_crypto()->get_md4_block_size(&m_md4_context);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e crypto_md4_c::hash_init()
+{
+	// Cleanup the previous context.
+	hash_cleanup();
+
+	eap_status_e status = m_am_tools->get_crypto()->md4_init(&m_md4_context);
+
+	if (m_md4_context.get_is_valid_data() == false)
+	{
+		set_is_invalid();
+		if (status == eap_status_ok)
+		{
+			status = eap_status_allocation_error;
+		}
+	}
+
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e crypto_md4_c::hash_update(
+	const void * const data,
+	const u32_t data_length)
+{
+	eap_status_e status = m_am_tools->get_crypto()->md4_update(
+		&m_md4_context,
+		static_cast<const u8_t *>(data),
+		data_length);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e crypto_md4_c::hash_final(
+	void * const message_digest,
+	u32_t *md_length_or_null)
+{
+	eap_status_e status = m_am_tools->get_crypto()->md4_final(
+		&m_md4_context,
+		static_cast<u8_t *>(message_digest),
+		md_length_or_null);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e crypto_md4_c::hash_cleanup()
+{
+	if (m_md4_context.get_is_valid_data() == true)
+	{
+		m_am_tools->get_crypto()->md4_cleanup(&m_md4_context);
+	}
+	return EAP_STATUS_RETURN(m_am_tools, eap_status_ok);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT abs_crypto_hash_algorithm_c * crypto_md4_c::copy()
+{
+	crypto_md4_c * const new_context = new crypto_md4_c(m_am_tools);
+	if (new_context == 0)
+	{
+		return 0;
+	}
+
+	eap_status_e status = new_context->copy_context(&m_md4_context);
+	if (status != eap_status_ok)
+	{
+		delete new_context;
+		return 0;
+	}
+
+	return new_context;
+}
+
+//--------------------------------------------------
+//--------------------------------------------------
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT crypto_rc4_c::~crypto_rc4_c()
+{
+	if (m_rc4_context.get_is_valid_data() == true)
+	{
+		m_am_tools->get_crypto()->rc4_cleanup(&m_rc4_context);
+	}
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT crypto_rc4_c::crypto_rc4_c(abs_eap_am_tools_c * const tools)
+	: m_am_tools(tools)
+	, m_rc4_context(tools)
+	, m_is_valid(false)
+{
+	set_is_valid();
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT void crypto_rc4_c::set_is_invalid()
+{
+	m_is_valid = false;
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT void crypto_rc4_c::set_is_valid()
+{
+	m_is_valid = true;
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT bool crypto_rc4_c::get_is_valid()
+{
+	return m_is_valid;
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e crypto_rc4_c::set_key(
+	const eap_variable_data_c * const key)
+{
+	return m_am_tools->get_crypto()->rc4_set_key(&m_rc4_context, key);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e crypto_rc4_c::discard_stream(
+	const u32_t count_of_discarded_octets)
+{
+	eap_status_e status = eap_status_ok;
+
+	for (u32_t ind = 0ul; ind < count_of_discarded_octets; ind++)
+	{
+		u8_t octet = 0;
+		status = encrypt_data(
+			&octet,
+			sizeof(octet));
+		if (status != eap_status_ok)
+		{
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	} // for()
+
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e crypto_rc4_c::encrypt_data(
+	void * const data_in_out,
+	const u32_t data_length)
+{
+	return m_am_tools->get_crypto()->rc4_encrypt(
+		&m_rc4_context,
+		data_in_out,
+		data_length);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e crypto_rc4_c::encrypt_data(
+	const void * const data_in, 
+	void * const data_out,
+	const u32_t data_length)
+{
+	return m_am_tools->get_crypto()->rc4_encrypt(
+		&m_rc4_context,
+		data_in,
+		data_out,
+		data_length);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e crypto_rc4_c::decrypt_data(
+	void * const data_in_out,
+	const u32_t data_length)
+{
+	return m_am_tools->get_crypto()->rc4_decrypt(
+		&m_rc4_context,
+		data_in_out,
+		data_length);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e crypto_rc4_c::decrypt_data(
+	const void * const data_in, 
+	void * const data_out,
+	const u32_t data_length)
+{
+	return m_am_tools->get_crypto()->rc4_decrypt(
+		&m_rc4_context,
+		data_in,
+		data_out,
+		data_length);
+}
+
+//--------------------------------------------------
+//--------------------------------------------------
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT crypto_tls_base_prf_c::~crypto_tls_base_prf_c()
+{
+	tls_prf_cleanup();
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT crypto_tls_base_prf_c::crypto_tls_base_prf_c(
+	abs_eap_am_tools_c * const tools)
+	: m_am_tools(tools)
+	, m_is_valid(false)
+{
+	set_is_valid();
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT void crypto_tls_base_prf_c::set_is_invalid()
+{
+	m_is_valid = false;
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT void crypto_tls_base_prf_c::set_is_valid()
+{
+	m_is_valid = true;
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT bool crypto_tls_base_prf_c::get_is_valid()
+{
+	return 	m_is_valid;
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e crypto_tls_base_prf_c::tls_prf_A_value(
+	abs_crypto_hmac_algorithm_c * const hash,
+	eap_variable_data_c * const key,
+	eap_variable_data_c * const seed,
+	eap_variable_data_c * const A_output)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	if (hash == 0
+		|| key == 0
+		|| seed == 0
+		|| A_output == 0)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+	}
+
+	// First we initialize A value.
+
+	eap_status_e status = hash->hmac_set_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 = hash->hmac_update(
+		seed->get_data(seed->get_data_length()),
+		seed->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);
+	}
+
+	u32_t hmac_md_length = hash->get_digest_length();
+
+	status = hash->hmac_final(
+		A_output->get_data(hmac_md_length),
+		&hmac_md_length);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e crypto_tls_base_prf_c::tls_prf_one_round(
+	abs_crypto_hmac_algorithm_c * const hash,
+	const eap_variable_data_c * const key,
+	eap_variable_data_c * const A_input,
+	eap_variable_data_c * const seed,
+	void * const out_buffer,
+	const u32_t out_buffer_length)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	if (hash == 0
+		|| key == 0
+		|| A_input == 0
+		|| seed == 0
+		|| out_buffer == 0)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+	}
+
+	eap_status_e status = hash->hmac_set_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 = hash->hmac_update(
+		A_input->get_data(A_input->get_data_length()),
+		A_input->get_data_length());
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+	
+	status = hash->hmac_update(
+		seed->get_data(seed->get_data_length()),
+		seed->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);
+	}
+	
+	u32_t hmac_md_length = hash->get_digest_length();
+
+	if (hmac_md_length > out_buffer_length)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_buffer_too_short);
+	}
+
+	status = hash->hmac_final(
+		out_buffer,
+		&hmac_md_length);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e crypto_tls_base_prf_c::tls_prf_cleanup()
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, eap_status_ok);
+}
+
+//--------------------------------------------------
+//--------------------------------------------------
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT crypto_tls_sha1_prf_c::~crypto_tls_sha1_prf_c()
+{
+	tls_prf_cleanup();
+
+	delete m_sha1_context;
+	m_sha1_context = 0;
+
+	delete m_A_sha1_context;
+	m_A_sha1_context = 0;
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT crypto_tls_sha1_prf_c::crypto_tls_sha1_prf_c(
+	abs_eap_am_tools_c * const tools)
+	: crypto_tls_base_prf_c(tools)
+	, m_am_tools(tools)
+	, m_sha1_context(0)
+	, m_A_sha1_context(0)
+	, m_sha1_key(tools)
+	, m_seed(tools)
+	, m_is_valid(false)
+{
+	{
+		crypto_sha1_c * sha1 = new crypto_sha1_c(m_am_tools);
+		if (sha1 == 0
+			|| sha1->get_is_valid() == false)
+		{
+			delete sha1;
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return;
+		}
+
+		m_sha1_context = new crypto_hmac_c(m_am_tools, sha1, true);
+		if (m_sha1_context == 0
+			|| m_sha1_context->get_is_valid() == false)
+		{
+			delete m_sha1_context;
+			m_sha1_context = 0;
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return;
+		}
+	}
+
+	{
+		crypto_sha1_c * sha1 = new crypto_sha1_c(m_am_tools);
+		if (sha1 == 0
+			|| sha1->get_is_valid() == false)
+		{
+			delete sha1;
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return;
+		}
+
+		m_A_sha1_context = new crypto_hmac_c(m_am_tools, sha1, true);
+		if (m_A_sha1_context == 0
+			|| m_A_sha1_context->get_is_valid() == false)
+		{
+			delete m_A_sha1_context;
+			m_A_sha1_context = 0;
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return;
+		}
+	}
+
+	set_is_valid();
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT void crypto_tls_sha1_prf_c::set_is_invalid()
+{
+	m_is_valid = false;
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT void crypto_tls_sha1_prf_c::set_is_valid()
+{
+	m_is_valid = true;
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT bool crypto_tls_sha1_prf_c::get_is_valid()
+{
+	return 	m_is_valid;
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e crypto_tls_sha1_prf_c::tls_prf_init(
+	const eap_variable_data_c * const secret,
+	const eap_variable_data_c * const label,
+	const eap_variable_data_c * const seed)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	if (secret->get_is_valid_data() == false
+		|| secret->get_data_length() == 0ul)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+	}
+
+	if (label->get_is_valid_data() == false
+		|| label->get_data_length() == 0ul)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+	}
+
+	// NOTE seed could be empty buffer.
+	if (seed->get_is_valid() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	eap_variable_data_c input(m_am_tools);
+
+	eap_status_e status = input.add_data(label);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = input.add_data(seed);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = m_seed.set_copy_of_buffer(&input);
+	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_EAP_AM_CRYPTO,
+		(EAPL("tls_prf_init(): m_seed"),
+		m_seed.get_data(m_seed.get_data_length()),
+		m_seed.get_data_length()));
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	status = m_sha1_key.set_copy_of_buffer(
+		secret);
+	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_EAP_AM_CRYPTO,
+		(EAPL("tls_prf_init(): m_sha1_key"),
+		m_sha1_key.get_data(m_sha1_key.get_data_length()),
+		m_sha1_key.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 crypto_tls_sha1_prf_c::tls_prf_output(
+	void * const pseudo_random_data,
+	const u32_t pseudo_random_data_length)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	if (pseudo_random_data == 0)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+	}
+
+	eap_status_e status = eap_status_not_supported;
+
+	EAP_ASSERT_ALWAYS(get_is_valid() == true);
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	status = m_A_sha1_context->hmac_set_key(&m_sha1_key);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+	
+	status = m_sha1_context->hmac_set_key(&m_sha1_key);
+	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 A_sha1_output(m_am_tools);
+
+	status = A_sha1_output.init(m_A_sha1_context->get_digest_length());
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+	A_sha1_output.set_is_valid();
+	A_sha1_output.set_data_length(A_sha1_output.get_buffer_length());
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	eap_variable_data_c sha1_output(m_am_tools);
+
+	status = sha1_output.init(
+		pseudo_random_data_length+m_sha1_context->get_digest_length());
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+	sha1_output.set_is_valid();
+	sha1_output.set_data_length(sha1_output.get_buffer_length());
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	{
+		u32_t hmac_sha1_md_length = m_sha1_context->get_digest_length();
+		u32_t sha1_count = sha1_output.get_data_length()
+			/ m_sha1_context->get_digest_length();
+
+		// First we initialize A value:
+		// A(0) = seed
+		status = tls_prf_A_value(
+			m_A_sha1_context,
+			&m_sha1_key,
+			&m_seed,
+			&A_sha1_output);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		// out(1) = HMAC_hash(secret, A(1) + seed)
+		status = tls_prf_one_round(
+			m_sha1_context,
+			&m_sha1_key,
+			&A_sha1_output,
+			&m_seed,
+			sha1_output.get_data_offset(0ul, hmac_sha1_md_length),
+			hmac_sha1_md_length);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		for (u32_t ind = 1ul; ind < sha1_count; ind++)
+		{
+			// Next A value is:
+			// A(i) = HMAC_hash(secret, A(i-1)).
+			status = tls_prf_A_value(
+				m_A_sha1_context,
+				&m_sha1_key,
+				&A_sha1_output,
+				&A_sha1_output);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			// out(i) = HMAC_hash(secret, A(i) + seed)
+			status = tls_prf_one_round(
+				m_sha1_context,
+				&m_sha1_key,
+				&A_sha1_output,
+				&m_seed,
+				sha1_output.get_data_offset(
+					ind*hmac_sha1_md_length,
+					hmac_sha1_md_length),
+				hmac_sha1_md_length);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+		} // for()
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	// Final operation is copy sha1_output to output buffer.
+
+	{
+		EAP_TRACE_DATA_DEBUG(
+			m_am_tools,
+			TRACE_FLAGS_EAP_AM_CRYPTO,
+			(EAPL("crypto_tls_sha1_prf_c::tls_prf_output(): sha1_output"),
+			 sha1_output.get_data(pseudo_random_data_length),
+			 pseudo_random_data_length));
+
+		EAP_ASSERT(pseudo_random_data_length <= sha1_output.get_data_length());
+
+		const u8_t * const sha1_buffer = sha1_output.get_data(
+			pseudo_random_data_length);
+		if (sha1_buffer == 0)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+		}
+
+		u8_t * const p_pseudo_random_data
+			= static_cast<u8_t *>(pseudo_random_data);
+
+		m_am_tools->memmove(
+			p_pseudo_random_data,
+			sha1_buffer,
+			pseudo_random_data_length);
+
+		EAP_TRACE_DATA_DEBUG(
+			m_am_tools,
+			TRACE_FLAGS_EAP_AM_CRYPTO,
+			(EAPL("crypto_tls_sha1_prf_c::tls_prf_output(): ")
+			 EAPL("p_pseudo_random_data"),
+			 p_pseudo_random_data,
+			 pseudo_random_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 crypto_tls_sha1_prf_c::tls_prf_cleanup()
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	if (m_sha1_context != 0)
+	{
+		m_sha1_context->hmac_cleanup();
+	}
+
+	if (m_A_sha1_context != 0)
+	{
+		m_A_sha1_context->hmac_cleanup();
+	}
+
+	m_sha1_key.reset();
+	m_seed.reset();
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, eap_status_ok);
+}
+
+//--------------------------------------------------
+//--------------------------------------------------
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT crypto_tls_md5_prf_c::~crypto_tls_md5_prf_c()
+{
+	tls_prf_cleanup();
+
+	delete m_md5_context;
+	m_md5_context = 0;
+
+	delete m_A_md5_context;
+	m_A_md5_context = 0;
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT crypto_tls_md5_prf_c::crypto_tls_md5_prf_c(
+	abs_eap_am_tools_c * const tools)
+	: crypto_tls_base_prf_c(tools)
+	, m_am_tools(tools)
+	, m_md5_context(0)
+	, m_A_md5_context(0)
+	, m_md5_key(tools)
+	, m_seed(tools)
+	, m_is_valid(false)
+{
+	{
+		crypto_md5_c * md5 = new crypto_md5_c(m_am_tools);
+		if (md5 == 0
+			|| md5->get_is_valid() == false)
+		{
+			delete md5;
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return;
+		}
+
+		m_md5_context = new crypto_hmac_c(m_am_tools, md5, true);
+		if (m_md5_context == 0
+			|| m_md5_context->get_is_valid() == false)
+		{
+			delete m_md5_context;
+			m_md5_context = 0;
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return;
+		}
+	}
+
+	{
+		crypto_md5_c * md5 = new crypto_md5_c(m_am_tools);
+		if (md5 == 0
+			|| md5->get_is_valid() == false)
+		{
+			delete md5;
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return;
+		}
+
+		m_A_md5_context = new crypto_hmac_c(m_am_tools, md5, true);
+		if (m_A_md5_context == 0
+			|| m_A_md5_context->get_is_valid() == false)
+		{
+			delete m_A_md5_context;
+			m_A_md5_context = 0;
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return;
+		}
+	}
+
+	set_is_valid();
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT void crypto_tls_md5_prf_c::set_is_invalid()
+{
+	m_is_valid = false;
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT void crypto_tls_md5_prf_c::set_is_valid()
+{
+	m_is_valid = true;
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT bool crypto_tls_md5_prf_c::get_is_valid()
+{
+	return 	m_is_valid;
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e crypto_tls_md5_prf_c::tls_prf_init(
+	const eap_variable_data_c * const secret,
+	const eap_variable_data_c * const label,
+	const eap_variable_data_c * const seed)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	if (secret->get_is_valid_data() == false
+		|| secret->get_data_length() == 0ul)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+	}
+
+	if (label->get_is_valid_data() == false
+		|| label->get_data_length() == 0ul)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+	}
+
+	if (seed->get_is_valid_data() == false
+		|| seed->get_data_length() == 0ul)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	eap_variable_data_c input(m_am_tools);
+
+	eap_status_e status = input.add_data(label);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = input.add_data(seed);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = m_seed.set_copy_of_buffer(&input);
+	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_EAP_AM_CRYPTO,
+		(EAPL("tls_prf_init(): m_seed"),
+		 m_seed.get_data(m_seed.get_data_length()),
+		 m_seed.get_data_length()));
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	status = m_md5_key.set_copy_of_buffer(
+		secret);
+	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_EAP_AM_CRYPTO,
+		(EAPL("tls_prf_init(): m_md5_key"),
+		 m_md5_key.get_data(m_md5_key.get_data_length()),
+		 m_md5_key.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 crypto_tls_md5_prf_c::tls_prf_output(
+	void * const pseudo_random_data,
+	const u32_t pseudo_random_data_length)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	if (pseudo_random_data == 0)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+	}
+
+	eap_status_e status = eap_status_not_supported;
+
+	EAP_ASSERT_ALWAYS(get_is_valid() == true);
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	status = m_A_md5_context->hmac_set_key(&m_md5_key);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+	
+	status = m_md5_context->hmac_set_key(&m_md5_key);
+	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 A_md5_output(m_am_tools);
+
+	status = A_md5_output.init(m_A_md5_context->get_digest_length());
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+	A_md5_output.set_is_valid();
+	A_md5_output.set_data_length(A_md5_output.get_buffer_length());
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	eap_variable_data_c md5_output(m_am_tools);
+
+	status = md5_output.init(
+		pseudo_random_data_length+m_md5_context->get_digest_length());
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+	md5_output.set_is_valid();
+	md5_output.set_data_length(md5_output.get_buffer_length());
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	{
+		u32_t hmac_md5_md_length = m_md5_context->get_digest_length();
+		u32_t md5_count = md5_output.get_data_length()
+			/ m_md5_context->get_digest_length();
+
+		// First we initialize A value:
+		// A(0) = seed
+		status = tls_prf_A_value(
+			m_A_md5_context,
+			&m_md5_key,
+			&m_seed,
+			&A_md5_output);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		// out(1) = HMAC_hash(secret, A(1) + seed)
+		status = tls_prf_one_round(
+			m_md5_context,
+			&m_md5_key,
+			&A_md5_output,
+			&m_seed,
+			md5_output.get_data_offset(0ul, hmac_md5_md_length),
+			hmac_md5_md_length);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		for (u32_t ind = 1ul; ind < md5_count; ind++)
+		{
+			// Next A value is:
+			// A(i) = HMAC_hash(secret, A(i-1)).
+			status = tls_prf_A_value(
+				m_A_md5_context,
+				&m_md5_key,
+				&A_md5_output,
+				&A_md5_output);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			// out(i) = HMAC_hash(secret, A(i) + seed)
+			status = tls_prf_one_round(
+				m_md5_context,
+				&m_md5_key,
+				&A_md5_output,
+				&m_seed,
+				md5_output.get_data_offset(
+					ind*hmac_md5_md_length,
+					hmac_md5_md_length),
+				hmac_md5_md_length);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+		} // for()
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	// Final operation is copy md5_output to output buffer.
+
+	{
+		EAP_TRACE_DATA_DEBUG(
+			m_am_tools,
+			TRACE_FLAGS_EAP_AM_CRYPTO,
+			(EAPL("crypto_tls_md5_prf_c::tls_prf_output(): md5_output"),
+			 md5_output.get_data(pseudo_random_data_length),
+			 pseudo_random_data_length));
+
+		EAP_ASSERT(pseudo_random_data_length <= md5_output.get_data_length());
+
+		const u8_t * const md5_buffer
+			= md5_output.get_data(pseudo_random_data_length);
+		if (md5_buffer == 0)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+		}
+
+		u8_t * const p_pseudo_random_data
+			= static_cast<u8_t *>(pseudo_random_data);
+
+		m_am_tools->memmove(
+			p_pseudo_random_data,
+			md5_buffer,
+			pseudo_random_data_length);
+
+		EAP_TRACE_DATA_DEBUG(
+			m_am_tools,
+			TRACE_FLAGS_EAP_AM_CRYPTO,
+			(EAPL("crypto_tls_md5_prf_c::tls_prf_output(): ")
+			 EAPL("p_pseudo_random_data"),
+			 p_pseudo_random_data,
+			 pseudo_random_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 crypto_tls_md5_prf_c::tls_prf_cleanup()
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	if (m_md5_context != 0)
+	{
+		m_md5_context->hmac_cleanup();
+	}
+
+	if (m_A_md5_context != 0)
+	{
+		m_A_md5_context->hmac_cleanup();
+	}
+
+	m_md5_key.reset();
+	m_seed.reset();
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, eap_status_ok);
+}
+
+//--------------------------------------------------
+//--------------------------------------------------
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT crypto_tls_prf_c::~crypto_tls_prf_c()
+{
+	tls_prf_cleanup();
+
+	delete m_tls_md5_prf;
+	m_tls_md5_prf = 0;
+
+	delete m_tls_sha1_prf;
+	m_tls_sha1_prf = 0;
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT crypto_tls_prf_c::crypto_tls_prf_c(
+	abs_eap_am_tools_c * const tools)
+	: m_am_tools(tools)
+	, m_tls_md5_prf(0)
+	, m_tls_sha1_prf(0)
+	, m_is_valid(false)
+{
+	m_tls_md5_prf = new crypto_tls_md5_prf_c(tools);
+	if (m_tls_md5_prf == 0)
+	{
+		return;
+	}
+
+	m_tls_sha1_prf = new crypto_tls_sha1_prf_c(tools);
+	if (m_tls_sha1_prf == 0)
+	{
+		return;
+	}
+
+	set_is_valid();
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT void crypto_tls_prf_c::set_is_invalid()
+{
+	m_is_valid = false;
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT void crypto_tls_prf_c::set_is_valid()
+{
+	m_is_valid = true;
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT bool crypto_tls_prf_c::get_is_valid()
+{
+	return 	m_is_valid;
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e crypto_tls_prf_c::tls_prf_init(
+	const eap_variable_data_c * const secret,
+	const eap_variable_data_c * const label,
+	const eap_variable_data_c * const seed)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	if (m_tls_md5_prf == 0
+		|| m_tls_sha1_prf == 0)
+	{
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	if (secret->get_is_valid_data() == false
+		|| secret->get_data_length() == 0ul)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+	}
+
+	if (label->get_is_valid_data() == false
+		|| label->get_data_length() == 0ul)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+	}
+
+	if (seed->get_is_valid_data() == false
+		|| seed->get_data_length() == 0ul)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	bool is_odd_key = false;
+	if ((secret->get_data_length() % 2) != 0)
+	{
+		// Odd length, one byte will be shared with both keys.
+		is_odd_key = true;
+	}
+
+	u32_t key_length = secret->get_data_length() / 2ul;
+	u32_t sha1_key_offset = key_length;
+
+	if (is_odd_key == true)
+	{
+		++key_length;
+	}
+
+	eap_variable_data_c md5_secret(m_am_tools);
+	eap_variable_data_c sha1_secret(m_am_tools);
+
+	eap_status_e status = md5_secret.set_buffer(
+		secret->get_data_offset(0ul, key_length),
+		key_length,
+		false,
+		false);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = sha1_secret.set_buffer(
+		secret->get_data_offset(sha1_key_offset, key_length),
+		key_length,
+		false,
+		false);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+
+	status = m_tls_md5_prf->tls_prf_init(&md5_secret, label, seed);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = m_tls_sha1_prf->tls_prf_init(&sha1_secret, label, seed);
+	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 crypto_tls_prf_c::tls_prf_output(
+	void * const pseudo_random_data,
+	const u32_t pseudo_random_data_length)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	eap_status_e status = eap_status_not_supported;
+
+	EAP_ASSERT_ALWAYS(get_is_valid() == true);
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	eap_variable_data_c tls_md5_prf_output(m_am_tools);
+	eap_variable_data_c tls_sha1_prf_output(m_am_tools);
+
+
+	status = tls_md5_prf_output.init(pseudo_random_data_length);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+	tls_md5_prf_output.set_is_valid();
+	tls_md5_prf_output.set_data_length(tls_md5_prf_output.get_buffer_length());
+
+	status = tls_sha1_prf_output.init(pseudo_random_data_length);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+	tls_sha1_prf_output.set_is_valid();
+	tls_sha1_prf_output.set_data_length(
+		tls_sha1_prf_output.get_buffer_length());
+
+
+	status = m_tls_md5_prf->tls_prf_output(
+		tls_md5_prf_output.get_data(tls_md5_prf_output.get_data_length()),
+		tls_md5_prf_output.get_data_length());
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = m_tls_sha1_prf->tls_prf_output(
+		tls_sha1_prf_output.get_data(tls_sha1_prf_output.get_data_length()),
+		tls_sha1_prf_output.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);
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	// Final operation is XOR the md5_output and sha1_output.
+
+	{
+		EAP_TRACE_DATA_DEBUG(
+			m_am_tools,
+			TRACE_FLAGS_EAP_AM_CRYPTO,
+			(EAPL("tls_prf_init(): tls_md5_prf_output"),
+			 tls_md5_prf_output.get_data(tls_md5_prf_output.get_data_length()),
+			 tls_md5_prf_output.get_data_length()));
+
+		EAP_TRACE_DATA_DEBUG(
+			m_am_tools,
+			TRACE_FLAGS_EAP_AM_CRYPTO,
+			(EAPL("tls_prf_init(): tls_sha1_prf_output"),
+			 tls_sha1_prf_output.get_data(
+				 tls_sha1_prf_output.get_data_length()),
+			 tls_sha1_prf_output.get_data_length()));
+
+		EAP_ASSERT(
+			pseudo_random_data_length
+			<= tls_md5_prf_output.get_data_length());
+		EAP_ASSERT(
+			pseudo_random_data_length
+			<= tls_sha1_prf_output.get_data_length());
+
+		const u8_t * const md5_buffer
+			= tls_md5_prf_output.get_data(pseudo_random_data_length);
+		const u8_t * const sha1_buffer
+			= tls_sha1_prf_output.get_data(pseudo_random_data_length);
+		u8_t * const p_pseudo_random_data
+			= static_cast<u8_t *>(pseudo_random_data);
+
+		for (u32_t ind = 0u; ind < pseudo_random_data_length; ind++)
+		{
+			p_pseudo_random_data[ind]
+				= static_cast<u8_t>((md5_buffer[ind] ^ sha1_buffer[ind]));
+		}
+
+		EAP_TRACE_DATA_DEBUG(
+			m_am_tools,
+			TRACE_FLAGS_EAP_AM_CRYPTO,
+			(EAPL("tls_prf_init(): p_pseudo_random_data"),
+			 p_pseudo_random_data,
+			 pseudo_random_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 crypto_tls_prf_c::tls_prf_cleanup()
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	if (m_tls_md5_prf != 0)
+	{
+		m_tls_md5_prf->tls_prf_cleanup();
+	}
+
+	if (m_tls_sha1_prf != 0)
+	{
+		m_tls_sha1_prf->tls_prf_cleanup();
+	}
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, eap_status_ok);
+}
+
+//--------------------------------------------------
+//--------------------------------------------------
+//--------------------------------------------------
+
+
+EAP_FUNC_EXPORT crypto_eap_fast_hmac_sha1_prf_c::~crypto_eap_fast_hmac_sha1_prf_c()
+{
+	t_prf_cleanup();
+
+	delete m_context;
+	m_context = 0;
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT crypto_eap_fast_hmac_sha1_prf_c::crypto_eap_fast_hmac_sha1_prf_c(
+	abs_eap_am_tools_c * const tools)
+	: m_am_tools(tools)
+	, m_context(0)
+	, m_key(tools)
+	, m_S_value(tools)
+	, m_is_valid(false)
+{
+	{
+		crypto_sha1_c * sha1 = new crypto_sha1_c(m_am_tools);
+		if (sha1 == 0
+			|| sha1->get_is_valid() == false)
+		{
+			delete sha1;
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return;
+		}
+
+		m_context = new crypto_hmac_c(m_am_tools, sha1, true);
+		if (m_context == 0
+			|| m_context->get_is_valid() == false)
+		{
+			delete m_context;
+			m_context = 0;
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return;
+		}
+	}
+
+	set_is_valid();
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT void crypto_eap_fast_hmac_sha1_prf_c::set_is_invalid()
+{
+	m_is_valid = false;
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT void crypto_eap_fast_hmac_sha1_prf_c::set_is_valid()
+{
+	m_is_valid = true;
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT bool crypto_eap_fast_hmac_sha1_prf_c::get_is_valid()
+{
+	return 	m_is_valid;
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e crypto_eap_fast_hmac_sha1_prf_c::t_prf_init(
+	const eap_variable_data_c * const key,
+	const eap_variable_data_c * const label,
+	const eap_variable_data_c * const seed)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	if (key->get_is_valid_data() == false
+		|| key->get_data_length() == 0ul)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+	}
+
+	if (label->get_is_valid_data() == false
+		|| label->get_data_length() == 0ul)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+	}
+
+	// NOTE seed could be empty buffer.
+	if (seed != 0
+		&& seed->get_is_valid() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	m_S_value.reset();
+
+	eap_status_e status = m_S_value.set_copy_of_buffer(label);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	u8_t byte_counter(0ul);
+
+	status = m_S_value.add_data(&byte_counter, sizeof(byte_counter));
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	if (seed != 0)
+	{
+		status = m_S_value.add_data(seed);
+		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_EAP_AM_CRYPTO,
+		(EAPL("tls_prf_init(): m_S_value"),
+		m_S_value.get_data(),
+		m_S_value.get_data_length()));
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	status = m_key.set_copy_of_buffer(key);
+	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_EAP_AM_CRYPTO,
+		(EAPL("tls_prf_init(): m_key"),
+		m_key.get_data(),
+		m_key.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 crypto_eap_fast_hmac_sha1_prf_c::t_prf_output(
+	void * const pseudo_random_data,
+	const u16_t pseudo_random_data_length)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	if (pseudo_random_data == 0)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+	}
+
+	eap_status_e status = eap_status_not_supported;
+
+	EAP_ASSERT_ALWAYS(get_is_valid() == true);
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	status = m_context->hmac_set_key(&m_key);
+	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 sha1_output(m_am_tools);
+
+	u32_t required_data = pseudo_random_data_length+m_context->get_digest_length();
+
+	status = sha1_output.init(
+		required_data);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+	sha1_output.set_is_valid();
+	sha1_output.set_data_length(0ul);
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	{
+		u32_t hmac_sha1_md_length = m_context->get_digest_length();
+		u32_t sha1_count = required_data
+			/ m_context->get_digest_length();
+
+		// - - - - - - - - - - - - - - - - - - - - - - - -
+
+		// - - - - - - - - - - - - - - - - - - - - - - - -
+
+		eap_variable_data_c T_value(m_am_tools);
+		if (T_value.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 = T_value.set_buffer_length(hmac_sha1_md_length);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = T_value.set_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);
+		}
+
+		// - - - - - - - - - - - - - - - - - - - - - - - -
+
+		u16_t network_order_pseudo_random_data_length(eap_htons(pseudo_random_data_length));
+		u8_t byte_counter(0ul);
+
+		for (u32_t ind = 0ul; ind < sha1_count; ind++)
+		{
+			++byte_counter;
+
+			// Next A value is:
+			// T(i) = HMAC_hash(key, T(i-1) + S + outputlength + 0x(i)).
+
+			status = m_context->hmac_set_key(&m_key);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			status = m_context->hmac_update(T_value.get_data(), T_value.get_data_length());
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			status = m_context->hmac_update(m_S_value.get_data(), m_S_value.get_data_length());
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			status = m_context->hmac_update(&network_order_pseudo_random_data_length, sizeof(network_order_pseudo_random_data_length));
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			status = m_context->hmac_update(&byte_counter, sizeof(byte_counter));
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			u32_t digest_length(m_context->get_digest_length());
+
+			status = T_value.set_data_length(digest_length);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			status = m_context->hmac_final(
+				T_value.get_data(),
+				&digest_length);
+
+			if (m_context->get_digest_length() != digest_length)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			status = sha1_output.add_data(&T_value);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+		} // for()
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	// Final operation is copy sha1_output to output buffer.
+
+	{
+		EAP_TRACE_DATA_DEBUG(
+			m_am_tools,
+			TRACE_FLAGS_EAP_AM_CRYPTO,
+			(EAPL("crypto_eap_fast_hmac_sha1_prf_c::tls_prf_output(): sha1_output"),
+			 sha1_output.get_data(pseudo_random_data_length),
+			 pseudo_random_data_length));
+
+		EAP_ASSERT(pseudo_random_data_length <= sha1_output.get_data_length());
+
+		const u8_t * const sha1_buffer = sha1_output.get_data(
+			pseudo_random_data_length);
+		if (sha1_buffer == 0)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+		}
+
+		u8_t * const p_pseudo_random_data
+			= static_cast<u8_t *>(pseudo_random_data);
+
+		m_am_tools->memmove(
+			p_pseudo_random_data,
+			sha1_buffer,
+			pseudo_random_data_length);
+
+		EAP_TRACE_DATA_DEBUG(
+			m_am_tools,
+			TRACE_FLAGS_EAP_AM_CRYPTO,
+			(EAPL("crypto_eap_fast_hmac_sha1_prf_c::tls_prf_output(): ")
+			 EAPL("p_pseudo_random_data"),
+			 p_pseudo_random_data,
+			 pseudo_random_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 crypto_eap_fast_hmac_sha1_prf_c::t_prf_cleanup()
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	if (m_context != 0)
+	{
+		m_context->hmac_cleanup();
+	}
+
+	m_key.reset();
+	m_S_value.reset();
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, eap_status_ok);
+}
+
+
+//--------------------------------------------------
+//--------------------------------------------------
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT crypto_rsa_c::~crypto_rsa_c()
+{
+	cleanup();
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT crypto_rsa_c::crypto_rsa_c(abs_eap_am_tools_c * const tools)
+	: m_am_tools(tools)
+	, m_context(tools)
+	, m_is_valid(false)
+{
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT void crypto_rsa_c::set_is_invalid()
+{
+	m_is_valid = false;
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT void crypto_rsa_c::set_is_valid()
+{
+	m_is_valid = true;
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT bool crypto_rsa_c::get_is_valid()
+{
+	return 	m_is_valid;
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e crypto_rsa_c::init()
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	eap_status_e status = m_am_tools->get_crypto()->rsa_init(&m_context);
+
+	if (m_context.get_is_valid_data() == false)
+	{
+		set_is_invalid();
+		if (status == eap_status_ok)
+		{
+			status = eap_status_allocation_error;
+		}
+	}
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e crypto_rsa_c::encrypt_with_public_key(
+	const eap_variable_data_c * const public_rsa_key,
+	const eap_variable_data_c * const input_data,
+	eap_variable_data_c * const output_data)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	eap_status_e status = m_am_tools->get_crypto()
+		->rsa_encrypt_with_public_key(
+			&m_context,
+			public_rsa_key,
+			input_data,
+			output_data);
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e crypto_rsa_c::decrypt_with_public_key(
+	const eap_variable_data_c * const public_rsa_key,
+	const eap_variable_data_c * const input_data,
+	eap_variable_data_c * const output_data)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	eap_status_e status = m_am_tools->get_crypto()
+		->rsa_decrypt_with_public_key(
+			&m_context,
+			public_rsa_key,
+			input_data,
+			output_data);
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e crypto_rsa_c::encrypt_with_private_key(
+	const eap_variable_data_c * const private_rsa_key,
+	const eap_variable_data_c * const input_data,
+	eap_variable_data_c * const output_data)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	eap_status_e status = m_am_tools->get_crypto()
+		->rsa_encrypt_with_private_key(
+			&m_context,
+			private_rsa_key,
+			input_data,
+			output_data);
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e crypto_rsa_c::decrypt_with_private_key(
+	const eap_variable_data_c * const private_rsa_key,
+	const eap_variable_data_c * const input_data,
+	eap_variable_data_c * const output_data)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	eap_status_e status = m_am_tools->get_crypto()
+		->rsa_decrypt_with_private_key(
+			&m_context,
+			private_rsa_key,
+			input_data,
+			output_data);
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e crypto_rsa_c::sign(
+	const eap_variable_data_c * const private_rsa_key,
+	const eap_variable_data_c * const hash,
+	eap_variable_data_c * const signed_hash)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	eap_status_e status = m_am_tools->get_crypto()->rsa_sign(
+		&m_context,
+		private_rsa_key,
+		hash,
+		signed_hash);
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e crypto_rsa_c::verify(
+	const eap_variable_data_c * const public_rsa_key,
+	const eap_variable_data_c * const hash,
+	const eap_variable_data_c * const signed_hash)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	eap_status_e status = m_am_tools->get_crypto()->rsa_verify(
+		&m_context,
+		public_rsa_key,
+		hash,
+		signed_hash);
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e crypto_rsa_c::cleanup()
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	eap_status_e status = m_am_tools->get_crypto()->rsa_cleanup(&m_context);
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+//--------------------------------------------------
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT crypto_dsa_c::~crypto_dsa_c()
+{
+	cleanup();
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT crypto_dsa_c::crypto_dsa_c(abs_eap_am_tools_c * const tools)
+	: m_am_tools(tools)
+	, m_context(tools)
+	, m_is_valid(false)
+{
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT void crypto_dsa_c::set_is_invalid()
+{
+	m_is_valid = false;
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT void crypto_dsa_c::set_is_valid()
+{
+	m_is_valid = true;
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT bool crypto_dsa_c::get_is_valid()
+{
+	return 	m_is_valid;
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e crypto_dsa_c::init()
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	eap_status_e status = m_am_tools->get_crypto()->dsa_init(&m_context);
+
+	if (m_context.get_is_valid_data() == false)
+	{
+		set_is_invalid();
+		if (status == eap_status_ok)
+		{
+			status = eap_status_allocation_error;
+		}
+	}
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e crypto_dsa_c::sign(
+	const eap_variable_data_c * const private_dsa_key,
+	const eap_variable_data_c * const hash,
+	eap_variable_data_c * const signed_hash)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	eap_status_e status = m_am_tools->get_crypto()->dsa_sign(
+		&m_context,
+		private_dsa_key,
+		hash,
+		signed_hash);
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e crypto_dsa_c::verify(
+	const eap_variable_data_c * const public_dsa_key,
+	const eap_variable_data_c * const dsa_param_p,
+	const eap_variable_data_c * const dsa_param_q,
+	const eap_variable_data_c * const dsa_param_g,
+	const eap_variable_data_c * const hash,
+	const eap_variable_data_c * const signed_hash)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	eap_status_e status = m_am_tools->get_crypto()->dsa_verify(
+		&m_context,
+		public_dsa_key,
+		dsa_param_p,
+		dsa_param_q,
+		dsa_param_g,
+		hash,
+		signed_hash);
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e crypto_dsa_c::cleanup()
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	eap_status_e status = m_am_tools->get_crypto()->dsa_cleanup(&m_context);
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+//--------------------------------------------------
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT crypto_wpa_psk_password_hash_c::~crypto_wpa_psk_password_hash_c()
+{	
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT crypto_wpa_psk_password_hash_c::crypto_wpa_psk_password_hash_c(abs_eap_am_tools_c * const tools)
+	: m_am_tools(tools)
+	, m_is_valid(false)
+{
+	set_is_valid();
+}
+
+//--------------------------------------------------
+
+void crypto_wpa_psk_password_hash_c::set_is_invalid()
+{
+	m_is_valid = false;
+}
+
+//--------------------------------------------------
+
+void crypto_wpa_psk_password_hash_c::set_is_valid()
+{
+	m_is_valid = true;
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT bool crypto_wpa_psk_password_hash_c::get_is_valid()
+{
+	return m_is_valid;
+}
+
+//--------------------------------------------------
+
+// These are the functions for calculating PSK from ascii string 
+// taken from 802.11i D3.0
+
+/*
+* password - ascii string up to 63 characters in length
+* ssid - octet string up to 32 octets
+* ssidlength - length of ssid in octets
+* output must be 40 octets in length and outputs 256 bits of key
+*/
+EAP_FUNC_EXPORT eap_status_e crypto_wpa_psk_password_hash_c::password_hash(
+	const eap_variable_data_c * const password,
+	const eap_variable_data_c * const ssid,	
+	eap_variable_data_c * const output,
+	void * object,
+	eap_status_e (*progress_callback)(void*, u32_t))
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+	
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("Hashing WPA PSK ASCII password.\n")));		
+
+	EAP_TRACE_DATA_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("password"),
+		password->get_data(),
+		password->get_data_length()));
+
+	EAP_TRACE_DATA_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("ssid"),
+		ssid->get_data(),
+		ssid->get_data_length()));
+
+	// Buffer must be reset before new HASH is added to it.
+	// This causes the new HASH addition to the begin of the buffer.
+	// This is small optimization that does not free the old buffer.
+	eap_status_e status = output->reset_start_offset_and_data_length();
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	if ((password->get_data_length() > 63) || (ssid->get_data_length() > 32))
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+	}
+	
+	status = password_hash_F(password, ssid, 4096, 1, output, object, progress_callback);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+		
+	status = password_hash_F(password, ssid, 4096, 2, output, object, progress_callback);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	// truncate the buffer to 32 bytes
+	output->set_data_length(WPA_PSK_LENGTH);
+
+	EAP_TRACE_DATA_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("WPA PSK"),
+		output->get_data(),
+		output->get_data_length()));
+
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+eap_status_e crypto_wpa_psk_password_hash_c::password_hash_F(
+	const eap_variable_data_c * const password,
+	const eap_variable_data_c * const ssid,	
+	u32_t iterations,
+	u32_t count,
+	eap_variable_data_c * const output,
+	void * object,
+	eap_status_e (*progress_callback)(void*, u32_t))
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+	eap_status_e status(eap_status_process_general_error);
+	
+	crypto_sha1_c sha1(m_am_tools);
+	
+	status = sha1.hash_init();
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+	
+	u32_t digest_length = sha1.get_digest_length();
+
+	crypto_hmac_c hmac(m_am_tools, &sha1, false);
+
+	u32_t i_ind, j_ind;
+
+	// Check for illegal characters
+	for (i_ind = 0; i_ind < password->get_data_length(); i_ind++)
+	{
+		u8_t * const character = password->get_data_offset(i_ind, 1);
+		if (character == 0)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+		}
+
+		if(!((*character >= 32) && (*character <= 126)))
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+		}
+	}
+
+	eap_variable_data_c digest(m_am_tools);	
+	eap_variable_data_c digest1(m_am_tools);
+
+	status = digest.set_copy_of_buffer(ssid);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	u8_t tmp[] = {
+		static_cast<u8_t>((count>>24) & 0xff),
+		static_cast<u8_t>((count>>16) & 0xff),
+		static_cast<u8_t>((count>>8) & 0xff),
+		static_cast<u8_t>(count & 0xff)
+	};
+
+	status = digest.add_data(tmp, sizeof(tmp));
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = hmac.hmac_set_key(password);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = hmac.hmac_update(
+			digest.get_data(),
+			digest.get_data_length());
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = digest1.set_buffer_length(digest_length);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = hmac.hmac_final(
+		digest1.get_buffer(digest_length),
+			0);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+	digest1.set_data_length(digest_length);
+
+	status = digest.set_buffer_length(digest_length);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+	digest.set_data_length(digest_length);
+
+	/* output = U1 */
+	status = output->add_data(&digest1);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	u8_t * p_output = output->get_data_offset((count - 1) * digest_length, digest_length);
+	if (p_output == 0)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+	}
+	
+	u32_t callback_trigger = 0ul;
+
+	for (i_ind = 1; i_ind < iterations; i_ind++) 
+	{
+		callback_trigger++;
+
+		if (callback_trigger > 400
+			&& progress_callback != 0
+			&& object != 0)
+		{
+			callback_trigger = 0;
+			status = progress_callback(object, 400);		
+			if (status != eap_status_ok)
+			{
+				// This means that the operation was cancelled.
+				EAP_TRACE_DEBUG(
+					m_am_tools,
+					TRACE_FLAGS_DEFAULT,
+					(EAPL("WPA PSK password hashing cancelled.\n")));
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}			
+		}
+
+		status = hmac.hmac_set_key(password);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_DATA_ERROR(
+				m_am_tools,
+				TRACE_FLAGS_DEFAULT, 
+				(EAPL("illegal password:"),
+				password->get_data(),
+				password->get_data_length()));
+
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = hmac.hmac_update(
+			digest1.get_data(),
+			digest1.get_data_length());
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	
+		status = hmac.hmac_final(
+			digest.get_data(sha1.get_digest_length()),
+				0);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		digest.set_data_length(digest_length);
+
+		status = digest1.set_copy_of_buffer(&digest);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+		
+		/* output = output xor Un */		
+		u8_t * p_digest = digest.get_data(digest_length);
+		if (p_digest == 0)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+		}
+
+		for (j_ind = 0; j_ind < digest_length; j_ind++)
+		{
+			p_output[j_ind] ^= p_digest[j_ind];
+		}
+	} // for()
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, eap_status_ok);
+}
+
+
+//--------------------------------------------------
+//--------------------------------------------------
+//--------------------------------------------------
+
+
+EAP_FUNC_EXPORT crypto_nt_hash_c::~crypto_nt_hash_c()
+{	
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT crypto_nt_hash_c::crypto_nt_hash_c(abs_eap_am_tools_c * const tools)
+	: m_am_tools(tools)
+	, m_is_valid(false)
+{
+	set_is_valid();
+}
+
+//--------------------------------------------------
+
+void crypto_nt_hash_c::set_is_invalid()
+{
+	m_is_valid = false;
+}
+
+//--------------------------------------------------
+
+void crypto_nt_hash_c::set_is_valid()
+{
+	m_is_valid = true;
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT bool crypto_nt_hash_c::get_is_valid()
+{
+	return m_is_valid;
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e crypto_nt_hash_c::nt_password_hash(
+	const eap_variable_data_c * const password_utf8,
+	eap_variable_data_c * const password_hash,
+	const u32_t /* digest_size */)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+	if (password_utf8 == 0
+		|| password_hash == 0)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_process_general_error);
+	}
+
+	eap_variable_data_c tmp_password_unicode(m_am_tools);
+	eap_status_e status = m_am_tools->convert_utf8_to_unicode(tmp_password_unicode, *password_utf8);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	// Ms-Chap-V2 wants the byte order of password is little endian
+#if defined(EAP_BIG_ENDIAN)
+	{
+		u32_t password_size = tmp_password_unicode.get_data_length();
+		u8_t * password = tmp_password_unicode.get_data(password_size);
+		u8_t tmp;
+
+		for (i = 0; i < password_size; i += 2)
+		{
+			// Swap bytes
+			tmp = password[i];
+			password[i] = password[i + 1];
+			password[i + 1] = tmp;
+		}
+	}
+#endif // EAP_BIG_ENDIAN
+
+
+	EAP_TRACE_DATA_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("EAP_type_MSCHAPV2: nt_password_hash(), password_unicode"),
+		tmp_password_unicode.get_data(),
+		tmp_password_unicode.get_data_length()));
+
+	crypto_md4_c md4(m_am_tools);
+
+	status = md4.hash_init();
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+	
+	status = md4.hash_update(
+		tmp_password_unicode.get_data(),
+		tmp_password_unicode.get_data_length()); // unicode-chars
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	password_hash->reset();
+
+	status = password_hash->set_buffer_length(md4.get_digest_length());
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = password_hash->set_data_length(md4.get_digest_length());
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+	
+	status = md4.hash_final(password_hash->get_data(md4.get_digest_length()), 0);
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e crypto_nt_hash_c::hash_nt_password_hash(
+	const eap_variable_data_c * const password_hash,
+	eap_variable_data_c * const password_hash_hash,
+	const u32_t digest_size)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+	if (!password_hash
+	|| !password_hash_hash)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_process_general_error);
+	}
+
+	crypto_md4_c md4(m_am_tools);
+
+	eap_status_e status = md4.hash_init();
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = md4.hash_update(password_hash->get_data(digest_size), digest_size);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	password_hash_hash->reset();
+
+	status = password_hash_hash->set_buffer_length(md4.get_digest_length());
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = password_hash_hash->set_data_length(md4.get_digest_length());
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+	
+	status = md4.hash_final(password_hash_hash->get_data(md4.get_digest_length()), 0);
+
+	EAP_TRACE_DATA_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("EAP_type_MSCHAPV2: hash_nt_password_hash(), password_hash_hash"),
+		password_hash_hash->get_data(),
+		password_hash_hash->get_data_length()));
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+/* RFC 3079 */
+
+static const u8_t eap_type_mschapv2_magic1[] = {
+	0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74,
+	0x68, 0x65, 0x20, 0x4d, 0x50, 0x50, 0x45, 0x20, 0x4d,
+	0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x4b, 0x65, 0x79 };
+
+EAP_FUNC_EXPORT eap_status_e crypto_nt_hash_c::get_master_key(
+	const eap_variable_data_c * const in_password_hash_hash,
+	const eap_variable_data_c * const in_nt_response,
+	eap_variable_data_c * const out_master_key,
+	const u32_t in_master_key_length)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("eap_type_mschapv2_c::get_master_key(): this = 0x%08x\n"),
+		this));
+
+	if (in_password_hash_hash == 0
+		|| in_password_hash_hash->get_is_valid_data() == false
+		|| in_nt_response == 0
+		|| in_nt_response->get_is_valid_data() == false
+		|| out_master_key == 0
+		|| out_master_key->get_is_valid() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_process_general_error);
+	}
+
+	crypto_sha1_c sha1(m_am_tools);
+
+	eap_status_e status = sha1.hash_init();
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = sha1.hash_update(
+		in_password_hash_hash->get_data(),
+		in_password_hash_hash->get_data_length());
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = sha1.hash_update(
+		in_nt_response->get_data(),
+		in_nt_response->get_data_length());
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = sha1.hash_update(eap_type_mschapv2_magic1, sizeof(eap_type_mschapv2_magic1));
+	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 digest(m_am_tools);
+	if (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);
+	}
+	
+	status = digest.set_buffer_length(sha1.get_digest_length());
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = digest.set_data_length(sha1.get_digest_length());
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = sha1.hash_final(
+		digest.get_data(), 0);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = out_master_key->set_copy_of_buffer(
+		digest.get_data(in_master_key_length),
+		in_master_key_length);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+static const u8_t eap_type_mschapv2_magic2[] = {
+	0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
+	0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
+	0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
+	0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x6b, 0x65, 0x79,
+	0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73,
+	0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, 0x69, 0x64, 0x65,
+	0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
+	0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
+	0x6b, 0x65, 0x79, 0x2e };
+
+static const u8_t eap_type_mschapv2_magic3[] = {
+	0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
+	0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
+	0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
+	0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
+	0x6b, 0x65, 0x79, 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68,
+	0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73,
+	0x69, 0x64, 0x65, 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73,
+	0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20,
+	0x6b, 0x65, 0x79, 0x2e };
+		
+
+EAP_FUNC_EXPORT eap_status_e crypto_nt_hash_c::get_asymmetric_start_key(
+	const eap_variable_data_c * const in_master_key,
+	eap_variable_data_c * const out_session_key,
+	const u32_t in_session_key_length,
+	const bool in_is_send,
+	const bool in_is_server)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("eap_type_mschapv2_c::get_asymmetric_start_key(): this = 0x%08x\n"),
+		this));
+
+	if (in_master_key == 0
+		|| in_master_key->get_is_valid_data() == false
+		|| out_session_key == 0
+		|| out_session_key->get_is_valid() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_process_general_error);
+	}
+
+	crypto_sha1_c sha1(m_am_tools);
+
+	eap_status_e status = sha1.hash_init();
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+	status = sha1.hash_update(
+		in_master_key->get_data(),
+		in_master_key->get_data_length());
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	const u32_t padding_size = 40;
+
+	eap_variable_data_c padding(m_am_tools);
+	if (padding.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 = padding.set_buffer_length(padding_size);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = padding.set_data_length(padding_size);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	const u32_t padding1_value = 0x00;
+
+	m_am_tools->memset(padding.get_data(), padding1_value, padding.get_data_length()); // padding1
+
+	status = sha1.hash_update(
+		padding.get_data(),
+		padding.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);
+	}
+
+	{
+		const u8_t * magic = eap_type_mschapv2_magic2;
+
+		if (in_is_send == in_is_server)
+		{
+			magic = eap_type_mschapv2_magic3;
+		}
+
+		status = sha1.hash_update(magic, sizeof(eap_type_mschapv2_magic2)); // magic2 and magic3 are both 84 bytes
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+
+	const u32_t padding2_value = 0xf2;
+
+	m_am_tools->memset(padding.get_data(), padding2_value, padding.get_data_length()); // padding2
+
+	status = sha1.hash_update(
+		padding.get_data(),
+		padding.get_data_length());
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	eap_variable_data_c digest(m_am_tools);
+	if (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);
+	}
+
+	status = digest.set_buffer_length(sha1.get_digest_length());
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = digest.set_data_length(sha1.get_digest_length());
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = sha1.hash_final(digest.get_data(), 0);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = out_session_key->set_copy_of_buffer(digest.get_data(in_session_key_length), in_session_key_length);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e crypto_nt_hash_c::get_new_key_from_sha(
+	const eap_variable_data_c * const in_start_key,
+	const eap_variable_data_c * const in_session_key,
+	eap_variable_data_c * const out_interim_key,
+	const u32_t in_interim_key_length)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("eap_type_mschapv2_c::get_new_key_from_sha(): this = 0x%08x\n"),
+		this));
+
+	if (in_start_key == 0
+		|| in_start_key->get_is_valid_data() == false
+		|| in_session_key == 0
+		|| in_session_key->get_is_valid_data() == false
+		|| out_interim_key == 0
+		|| out_interim_key->get_is_valid() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_process_general_error);
+	}
+
+	crypto_sha1_c sha1(m_am_tools);
+
+	eap_status_e status = sha1.hash_init();
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+	status = sha1.hash_update(
+		in_start_key->get_data(),
+		in_start_key->get_data_length());
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	const u32_t padding_size = 40;
+
+	eap_variable_data_c padding(m_am_tools);
+	if (padding.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 = padding.set_buffer_length(padding_size);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = padding.set_data_length(padding_size);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+
+	const u32_t padding1_value = 0x00;
+
+	m_am_tools->memset(padding.get_data(), padding1_value, padding.get_data_length()); // padding1
+
+	status = sha1.hash_update(
+		padding.get_data(),
+		padding.get_data_length());
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = sha1.hash_update(
+		in_session_key->get_data(),
+		in_session_key->get_data_length());
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	const u32_t padding2_value = 0xf2;
+
+	m_am_tools->memset(padding.get_data(), padding2_value, padding.get_data_length()); // padding2
+
+	status = sha1.hash_update(
+		padding.get_data(),
+		padding.get_data_length());
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	eap_variable_data_c digest(m_am_tools);
+	if (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);
+	}
+
+	status = digest.set_buffer_length(sha1.get_digest_length());
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = digest.set_data_length(sha1.get_digest_length());
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = sha1.hash_final(digest.get_data(), 0);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = out_interim_key->set_copy_of_buffer(digest.get_data(in_interim_key_length), in_interim_key_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);
+}
+
+//--------------------------------------------------
+//--------------------------------------------------
+//--------------------------------------------------
+
+void crypto_kd_hmac_sha256_c::set_is_invalid()
+{
+	m_is_valid = false;
+}
+
+//--------------------------------------------------
+
+void crypto_kd_hmac_sha256_c::set_is_valid()
+{
+	m_is_valid = true;
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT crypto_kd_hmac_sha256_c::~crypto_kd_hmac_sha256_c()
+{
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT crypto_kd_hmac_sha256_c::crypto_kd_hmac_sha256_c(abs_eap_am_tools_c * const tools)
+	: m_am_tools(tools)
+	, m_is_valid(false)
+{
+	set_is_valid();
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT bool crypto_kd_hmac_sha256_c::get_is_valid()
+{
+	return m_is_valid;
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e crypto_kd_hmac_sha256_c::expand_key(
+	eap_variable_data_c * const output,
+	const u32_t required_output_size,
+	const eap_variable_data_c * const key,
+	const eap_variable_data_c * const label
+	)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(m_am_tools, TRACE_FLAGS_DEFAULT, (EAPL("\n")));
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("crypto_kd_hmac_sha256_c::expand_key()\n")));
+
+	output->reset();
+
+	crypto_sha_256_c sha_256(m_am_tools);
+	if (sha_256.get_is_valid() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	crypto_hmac_c hmac_sha_256(
+		m_am_tools,
+		&sha_256,
+		false);
+	if (hmac_sha_256.get_is_valid() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	EAP_TRACE_DATA_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("crypto_kd_hmac_sha256_c::expand_key(): key"),
+		key->get_data(),
+		key->get_data_length()));
+
+	EAP_TRACE_DATA_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("crypto_kd_hmac_sha256_c::expand_key(): label"),
+		label->get_data(),
+		label->get_data_length()));
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("crypto_kd_hmac_sha256_c::expand_key(): required_output_size %d\n"),
+		required_output_size));
+
+	const u32_t prf_digest_size(hmac_sha_256.get_digest_length());
+	const u32_t iterations((required_output_size + prf_digest_size - 1) / prf_digest_size);
+
+	eap_status_e status = output->set_buffer_length(iterations * prf_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 = output->set_data_length(iterations * prf_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_variable_data_c input(m_am_tools);
+	if (input.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 = input.set_buffer(label);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+
+	for (u32_t ind = 0ul; ind != iterations; ++ind)
+	{
+		status = hmac_sha_256.hmac_set_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 = hmac_sha_256.hmac_update(
+			input.get_data(),
+			input.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);
+		}
+
+		u32_t md_length(prf_digest_size);
+
+		status = hmac_sha_256.hmac_final(
+			output->get_data_offset(ind*prf_digest_size, prf_digest_size),
+			&md_length);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = input.set_buffer(
+			output->get_data_offset(ind*prf_digest_size, prf_digest_size),
+			md_length,
+			false,
+			false);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	} // for()
+
+	status = output->set_data_length(required_output_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("crypto_kd_hmac_sha256_c::expand_key(): output"),
+		output->get_data(),
+		output->get_data_length()));
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+// End.