/*
* Copyright (c) 2001-2006 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of the License "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:  EAP and WLAN authentication protocols.
*
*/

/*
* %version: %
*/

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



#include "eap_type_tls_peap_header.h"
#include "eap_header_string.h"

/** @file */

EAP_FUNC_EXPORT eap_tls_peap_header_c::~eap_tls_peap_header_c()
{
}

// 
EAP_FUNC_EXPORT eap_tls_peap_header_c::eap_tls_peap_header_c(
	abs_eap_am_tools_c * const tools,
	u8_t * const header_begin,
	const u32_t header_buffer_length)
	: eap_header_base_c(tools, header_begin, header_buffer_length)
	, m_am_tools(tools)
{
}

EAP_FUNC_EXPORT eap_code_value_e eap_tls_peap_header_c::get_eap_code() const
{
	return eap_header_base_c::get_code();
}

EAP_FUNC_EXPORT u8_t eap_tls_peap_header_c::get_eap_identifier() const
{
	return eap_header_base_c::get_identifier();
}

EAP_FUNC_EXPORT u16_t eap_tls_peap_header_c::get_eap_length() const
{
	return eap_header_base_c::get_length();
}

EAP_FUNC_EXPORT eap_type_value_e eap_tls_peap_header_c::get_eap_type() const
{
	return eap_header_base_c::get_type();
}

EAP_FUNC_EXPORT u16_t eap_tls_peap_header_c::get_data_length() const
{
	if (get_flag_tls_length_included() == true
		&& get_eap_length() > static_cast<u16_t>(get_header_length()))
	{
		return static_cast<u16_t>(get_eap_length()-static_cast<u16_t>(get_header_length()));
	}
	else if (get_flag_tls_length_included() == false
			 && get_eap_length() > static_cast<u16_t>(get_header_length()))
	{
		return static_cast<u16_t>(get_eap_length()-static_cast<u16_t>(get_header_length()));
	}
	else
	{
		return 0;
	}
}

EAP_FUNC_EXPORT u32_t eap_tls_peap_header_c::get_tls_min_header_length() const
{
	return eap_header_base_c::get_header_length()
		+ eap_header_base_c::get_type_field_length()
		+ m_tls_length_delta_offset;
}

EAP_FUNC_EXPORT u32_t eap_tls_peap_header_c::get_tls_max_header_length()
{
	return eap_header_base_c::get_header_length()
		+ eap_header_base_c::get_expanded_type_field_length()
		+ m_tls_length_delta_offset
		+ TLS_MESSAGE_LENGTH_FIELD_SIZE;
}

EAP_FUNC_EXPORT u32_t eap_tls_peap_header_c::get_header_length() const
{
	u32_t length = get_tls_min_header_length();

	if (get_flag_tls_length_included() == true)
	{
		return length+TLS_MESSAGE_LENGTH_FIELD_SIZE;
	}
	else
	{
		return length;
	}
}

EAP_FUNC_EXPORT u32_t eap_tls_peap_header_c::get_start_offset_of_data() const
{
	return get_header_length();
}

EAP_FUNC_EXPORT u8_t * eap_tls_peap_header_c::get_data_offset(
	abs_eap_am_tools_c * const m_am_tools,
	const u32_t offset,
	const u32_t contignuous_bytes) const
{
	EAP_UNREFERENCED_PARAMETER(m_am_tools);

	u32_t data_length = get_data_length(); // Here is removed optional TLS message length.

	if (data_length >= offset+contignuous_bytes)
	{
		// get_header_length() handles optional TLS message length field.
		u32_t offset_of_data = get_start_offset_of_data();
		u8_t * const data = get_header_offset(offset_of_data, offset+contignuous_bytes);
		if (data != 0)
		{
			return data+offset; // Data begins after the header.
		}
		else
		{
			return 0;
		}
	}
	else
	{
		EAP_ASSERT_ALWAYS(get_data_length() > 0u);
	}
	return 0;
}


EAP_FUNC_EXPORT u8_t * eap_tls_peap_header_c::get_data(
	abs_eap_am_tools_c * const m_am_tools,
	const u32_t contignuous_bytes) const
{
	return get_data_offset(m_am_tools, 0u, contignuous_bytes);
}

EAP_FUNC_EXPORT u32_t eap_tls_peap_header_c::get_tls_flags_offset() const
{
	return eap_header_base_c::get_header_length() + eap_header_base_c::get_type_field_length();
}

EAP_FUNC_EXPORT u32_t eap_tls_peap_header_c::get_tls_length_offset() const
{
	return get_tls_flags_offset() + m_tls_length_delta_offset;
}

EAP_FUNC_EXPORT u8_t * eap_tls_peap_header_c::get_tls_flags() const
{
	u32_t flag_offset(get_tls_flags_offset()+m_flag_delta_offset);

	return get_header_offset(flag_offset, sizeof(u8_t));
}

EAP_FUNC_EXPORT bool eap_tls_peap_header_c::get_tls_flag_bit(const u32_t mask) const
{
	const u8_t * const flag = get_tls_flags();

	if (flag != 0
		&& ((*flag) & mask))
	{
		return true;
	}
	return false;
}

EAP_FUNC_EXPORT u8_t eap_tls_peap_header_c::get_tls_flag_value(const u32_t mask, const u32_t shift) const
{
	const u8_t * const flag = get_tls_flags();

	if (flag != 0)
	{
		return static_cast<u8_t>(((*flag) & mask) >> shift);
	}
	else
	{
		return 0;
	}
}

EAP_FUNC_EXPORT bool eap_tls_peap_header_c::get_flag_tls_length_included() const
{
	return get_tls_flag_bit(m_flag_mask_tls_length_included);
}

EAP_FUNC_EXPORT bool eap_tls_peap_header_c::get_flag_more_fragments() const
{
	return get_tls_flag_bit(m_flag_mask_more_fragments);
}

EAP_FUNC_EXPORT bool eap_tls_peap_header_c::get_flag_start() const
{
	return get_tls_flag_bit(m_flag_mask_start);
}
	
EAP_FUNC_EXPORT u8_t eap_tls_peap_header_c::get_flag_reserved() const
{
	return get_tls_flag_value(m_flag_mask_reserved, m_flag_shift_reserved);
}

EAP_FUNC_EXPORT u8_t eap_tls_peap_header_c::get_flag_version() const
{
	return get_tls_flag_value(m_flag_mask_version, m_flag_shift_version);
}

EAP_FUNC_EXPORT eap_status_e eap_tls_peap_header_c::get_tls_message_length(u32_t * const tls_length) const
{
	if (get_flag_tls_length_included() == false)
	{
		// TLS data length is NOT included.
		*tls_length = 0u;
		return eap_status_ok;
	}
	
	u8_t * const data = get_header_offset(get_tls_length_offset(), sizeof(u32_t));
	if (data != 0)
	{
		u32_t tls_message_length =
			eap_read_u32_t_network_order(data, sizeof(u32_t));
		
		*tls_length = tls_message_length;
		return EAP_STATUS_RETURN(m_am_tools, eap_status_ok);
	}
	else
	{
		return EAP_STATUS_RETURN(m_am_tools, eap_status_header_corrupted);
	}
}


EAP_FUNC_EXPORT eap_status_e eap_tls_peap_header_c::check_header(
	abs_eap_am_tools_c * const tools,
	const eap_type_value_e required_eap_type,
	const bool is_client_when_true,
	const peap_version_e peap_version,
	const bool check_peap_version_when_true) const
{
	eap_status_e status = eap_status_ok;

	if (get_eap_type() != required_eap_type)
	{
		EAP_UNREFERENCED_PARAMETER(tools);
		status = EAP_STATUS_RETURN(tools, eap_status_header_corrupted);
	}
	else if (get_flag_reserved() != static_cast<u16_t>(0ul))
	{
		status = EAP_STATUS_RETURN(tools, eap_status_header_corrupted);
	}
	else if ((get_eap_type() == eap_type_tls
			  || get_eap_type() == eap_type_ttls)
			 && get_flag_version() != static_cast<u16_t>(0ul))
	{
		status = EAP_STATUS_RETURN(tools, eap_status_header_corrupted);
	}
	else if (get_eap_type() == eap_type_peap
			 && check_peap_version_when_true == true
			 && peap_version == peap_version_0_xp
			 && get_flag_version() != static_cast<u16_t>(peap_version_0_xp))
	{
		// In version negotiation this check will fail.
		// Do not add error traces here.
		status = eap_status_no_matching_protocol_version;
	}
	else if (get_eap_type() == eap_type_peap
			 && check_peap_version_when_true == true
			 && peap_version == peap_version_1
			 && get_flag_version() != static_cast<u16_t>(peap_version_1))
	{
		// In version negotiation this check will fail.
		// Do not add error traces here.
		status = eap_status_no_matching_protocol_version;
	}
	else if (get_eap_type() == eap_type_peap
			 && check_peap_version_when_true == true
			 && peap_version == peap_version_2
			 && get_flag_version() != static_cast<u16_t>(peap_version_2))
	{
		// In version negotiation this check will fail.
		// Do not add error traces here.
		status = eap_status_no_matching_protocol_version;
	}
	
	if (status != eap_status_ok)
	{
		EAP_UNREFERENCED_PARAMETER(is_client_when_true);
		
		// In version negotiation this check will fail.
		// Do not add error traces here.
		eap_status_string_c status_string;
		EAP_UNREFERENCED_PARAMETER(status_string);
		EAP_TRACE_DEBUG(
			m_am_tools,
			TRACE_FLAGS_DEFAULT|TRACE_TEST_VECTORS,
			(EAPL("WARNING: EAP_type_TLS_PEAP: check_header(): failed, %s, required_eap_type 0x%08x, ")
			 EAPL("peap_version %d, check_peap_version_when_true %d, get_eap_type() 0x%08x, get_flag_reserved() %d, get_flag_version() %d, status %s\n"),
			 (is_client_when_true == true) ? "client": "server",
			 convert_eap_type_to_u32_t(required_eap_type),
			 peap_version,
			 check_peap_version_when_true,
			 convert_eap_type_to_u32_t(get_eap_type()),
			 get_flag_reserved(),
			 get_flag_version(),
			 status_string.get_status_string(status)));
	}
	
	if (status == eap_status_no_matching_protocol_version)
	{
		// In version negotiation this check will fail.
		// Do not add error traces here.
		return status;
	}
	else
	{
		return EAP_STATUS_RETURN(tools, status);
	}
}

EAP_FUNC_EXPORT eap_const_string eap_tls_peap_header_c::get_code_string() const
{
	return eap_header_string_c::get_eap_code_string(get_eap_code());
}

EAP_FUNC_EXPORT eap_const_string eap_tls_peap_header_c::get_eap_type_string() const
{
	if (get_eap_length() <= eap_header_base_c::get_header_length())
	{
		return EAPL("No EAP-type");
	}

	return eap_header_string_c::get_eap_type_string(get_eap_type());
}

EAP_FUNC_EXPORT void eap_tls_peap_header_c::set_eap_code(const eap_code_value_e p_code)
{
	eap_header_base_c::set_code(p_code);
}

EAP_FUNC_EXPORT void eap_tls_peap_header_c::set_eap_identifier(const u8_t p_identifier)
{
	eap_header_base_c::set_identifier(p_identifier);
}

EAP_FUNC_EXPORT void eap_tls_peap_header_c::set_eap_length(
	const u16_t p_length,
	const bool expanded_type_when_true)
{
	eap_header_base_c::set_length(
		p_length,
		expanded_type_when_true);
}

EAP_FUNC_EXPORT void eap_tls_peap_header_c::set_eap_type(
	const eap_type_value_e p_type,
	const bool expanded_type_when_true)
{
	eap_header_base_c::set_type(p_type, expanded_type_when_true);
}


EAP_FUNC_EXPORT void eap_tls_peap_header_c::set_tls_flag_value(const u8_t value, const u32_t mask, const u32_t shift) const
{
	u8_t *flag = get_tls_flags();

	if (flag != 0)
	{
		(*flag) = static_cast<u8_t>(((*flag) & ~mask) | ((value << shift) & mask));
	}
}

EAP_FUNC_EXPORT void eap_tls_peap_header_c::set_tls_flag_bit(const bool flag, u32_t mask) const
{
	u8_t *p_flag = get_tls_flags();
	
	if (p_flag != 0)
	{
		if (flag == true)
		{
			(*p_flag) = static_cast<u8_t>((*p_flag) | mask);
		}
		else
		{
			(*p_flag) = static_cast<u8_t>((*p_flag) & ~mask);
		}
	}
}

EAP_FUNC_EXPORT void eap_tls_peap_header_c::set_flag_reserved(const u8_t reserved)
{
	set_tls_flag_value(reserved, m_flag_mask_reserved, m_flag_shift_reserved);
}

EAP_FUNC_EXPORT void eap_tls_peap_header_c::set_flag_version(const u8_t version)
{
	set_tls_flag_value(version, m_flag_mask_version, m_flag_shift_version);
}

EAP_FUNC_EXPORT void eap_tls_peap_header_c::set_flag_tls_length_included(const bool tls_length_included)
{
	set_tls_flag_bit(tls_length_included, m_flag_mask_tls_length_included);
}

EAP_FUNC_EXPORT void eap_tls_peap_header_c::set_flag_more_fragments(const bool more_fragments)
{
	set_tls_flag_bit(more_fragments, m_flag_mask_more_fragments);
}

EAP_FUNC_EXPORT void eap_tls_peap_header_c::set_flag_start(const bool start)
{
	set_tls_flag_bit(start, m_flag_mask_start);
}


EAP_FUNC_EXPORT void eap_tls_peap_header_c::set_data_length(
	const u32_t p_data_length,
	const bool expanded_type_when_true)
{
	EAP_ASSERT_ALWAYS(p_data_length+get_header_length() <= 0xffff);
	
	set_eap_length(
		static_cast<u16_t>(p_data_length+get_header_length()),
		expanded_type_when_true);
}

EAP_FUNC_EXPORT void eap_tls_peap_header_c::set_tls_message_length(const u32_t tls_length)
{
	EAP_ASSERT_ALWAYS(get_flag_tls_length_included() == true);

	u8_t * const data = get_header_offset(get_tls_length_offset(), sizeof(u32_t));

	EAP_ASSERT(data != 0);

	data[0] = static_cast<u8_t>((tls_length & 0xff000000) >> 24);
	data[1] = static_cast<u8_t>((tls_length & 0x00ff0000) >> 16);
	data[2] = static_cast<u8_t>((tls_length & 0x0000ff00) >> 8);
	data[3] = static_cast<u8_t>((tls_length & 0x000000ff) >> 0);
}


EAP_FUNC_EXPORT void eap_tls_peap_header_c::reset_header(
	abs_eap_am_tools_c * const m_am_tools,
	const eap_type_value_e required_eap_type,
	const u32_t buffer_length,
	const peap_version_e peap_version,
	const bool expanded_type_when_true)
{
	EAP_UNREFERENCED_PARAMETER(m_am_tools);

	eap_header_base_c::set_length(
		static_cast<u16_t>(buffer_length),
		expanded_type_when_true);

	set_eap_code(eap_code_none);
	set_eap_identifier(0u);
	set_eap_type(
		required_eap_type,
		expanded_type_when_true);

	set_flag_tls_length_included(false);
	set_flag_more_fragments(false);
	set_flag_start(false);
	
	set_flag_reserved(0u);
		
#if defined(USE_FAST_EAP_TYPE)
	if (required_eap_type == eap_type_fast)
	{
		set_flag_version(static_cast<u8_t>(peap_version));
	}
	else
#endif //#if defined(USE_FAST_EAP_TYPE)
	if (required_eap_type == eap_type_peap)
	{
		if (peap_version >= peap_version_0_xp
			&& peap_version <= peap_version_2)
		{
			set_flag_version(static_cast<u8_t>(peap_version));
		}
		else
		{
			EAP_ASSERT_ALWAYS((peap_version >= peap_version_0_xp) && (peap_version <= peap_version_2));
		}
	}
	else
	{
		// All other EAP-methods.
		set_flag_version(0u);
	}
}




// End.
