eapol/eapol_framework/eapol_common/common/asn1_der_type.cpp
changeset 0 c8830336c852
child 2 1c7bc153c08e
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/eapol/eapol_framework/eapol_common/common/asn1_der_type.cpp	Thu Dec 17 08:47:43 2009 +0200
@@ -0,0 +1,1706 @@
+/*
+* Copyright (c) 2008 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.
+*
+*/
+
+
+
+#include "asn1_der_type.h"
+#include "eap_automatic_variable.h"
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT asn1_der_type_c::~asn1_der_type_c()
+{
+	delete m_sub_types;
+	m_sub_types = 0;
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT asn1_der_type_c::asn1_der_type_c(
+	abs_eap_am_tools_c * const tools)
+	: m_am_tools(tools)
+	, m_is_valid(false)
+	, m_index(0u)
+	, m_count_of_sub_types(0u)
+	, m_recursion(0ul)
+	, m_input_data_length(0ul)
+	, m_input_data(0)
+	, m_used_octets(0ul)
+	, m_offset_of_length_field(0ul)
+	, m_offset_of_contents_field(0ul)
+	, m_parent_type(0)
+	, m_sub_types(0)
+{
+	m_is_valid = true;
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT bool asn1_der_type_c::get_is_valid() const
+{
+	return m_is_valid;
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT u32_t asn1_der_type_c::get_index() const
+{
+	return m_index;
+}
+
+//--------------------------------------------------
+
+u32_t asn1_der_type_c::get_input_data_length() const
+{
+	return m_input_data_length;
+}
+
+//--------------------------------------------------
+
+const u8_t * asn1_der_type_c::get_input_data() const
+{
+	return m_input_data;
+}
+
+//--------------------------------------------------
+
+u16_t asn1_der_type_c::get_recursion() const
+{
+	return m_recursion;
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_const_string asn1_der_type_c::get_class_string() const
+{
+
+#if defined(USE_EAP_DEBUG_TRACE)
+
+	asn1_class_e asn1_class = get_class();
+
+	if (asn1_class == asn1_class_universal)
+	{
+		return("U");
+	}
+	else if (asn1_class == asn1_class_application)
+	{
+		return("A");
+	}
+	else if (asn1_class == asn1_class_context_specific)
+	{
+		return("C");
+	}
+	else if (asn1_class == asn1_class_private)
+	{
+		return("P");
+	}
+	else
+#endif //#if defined(USE_EAP_DEBUG_TRACE)
+	{
+		return("<Unknown Class>");
+	}
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_const_string asn1_der_type_c::get_pc_string() const
+{
+
+#if defined(USE_EAP_DEBUG_TRACE)
+
+	asn1_pc_e pc = get_pc();
+
+	if (pc == asn1_pc_primitive)
+	{
+		return("P");
+	}
+	else if (pc == asn1_pc_constructed)
+	{
+		return("C");
+	}
+	else
+#endif //#if defined(USE_EAP_DEBUG_TRACE)
+	{
+		return("<Unknown PC>");
+	}
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_const_string asn1_der_type_c::get_tag_string() const
+{
+
+#if defined(USE_EAP_DEBUG_TRACE)
+
+	asn1_class_e asn1_class = get_class();
+	asn1_tag_e tag = get_tag();
+
+	if (asn1_class == asn1_class_context_specific)
+	{
+		static const eap_char context[][4] = { "[0]", "[1]", "[2]", "[3]", "[4]", "[5]", "[6]", "[7]", "[8]", "[9]", };
+		if (tag < (sizeof(context)/sizeof(context[0])))
+		{
+			return context[tag];
+		}
+		else
+		{
+			return("<Too big context>");
+		}
+	}
+	else
+	{
+		EAP_IF_RETURN_STRING(tag, asn1_tag_end_of_content)
+		else EAP_IF_RETURN_STRING(tag, asn1_tag_boolean)
+		else EAP_IF_RETURN_STRING(tag, asn1_tag_integer)
+		else EAP_IF_RETURN_STRING(tag, asn1_tag_bit_string)
+		else EAP_IF_RETURN_STRING(tag, asn1_tag_octet_string)
+		else EAP_IF_RETURN_STRING(tag, asn1_tag_null)
+		else EAP_IF_RETURN_STRING(tag, asn1_tag_object_identifier)
+		else EAP_IF_RETURN_STRING(tag, asn1_tag_object_descriptor)
+		else EAP_IF_RETURN_STRING(tag, asn1_tag_external)
+		else EAP_IF_RETURN_STRING(tag, asn1_tag_real)
+		else EAP_IF_RETURN_STRING(tag, asn1_tag_enumerated)
+		else EAP_IF_RETURN_STRING(tag, asn1_tag_empedded_pdv)
+		else EAP_IF_RETURN_STRING(tag, asn1_tag_utf8_string)
+		else EAP_IF_RETURN_STRING(tag, asn1_tag_relative_oid)
+		else EAP_IF_RETURN_STRING(tag, asn1_tag_unknown_14)
+		else EAP_IF_RETURN_STRING(tag, asn1_tag_unknown_15)
+		else EAP_IF_RETURN_STRING(tag, asn1_tag_sequence)
+		else EAP_IF_RETURN_STRING(tag, asn1_tag_set)
+		else EAP_IF_RETURN_STRING(tag, asn1_tag_numeric_string)
+		else EAP_IF_RETURN_STRING(tag, asn1_tag_printable_string)
+		else EAP_IF_RETURN_STRING(tag, asn1_tag_t61_string)
+		else EAP_IF_RETURN_STRING(tag, asn1_tag_videotex_string)
+		else EAP_IF_RETURN_STRING(tag, asn1_tag_ia5_string)
+		else EAP_IF_RETURN_STRING(tag, asn1_tag_utc_time)
+		else EAP_IF_RETURN_STRING(tag, asn1_tag_unknown_24)
+		else EAP_IF_RETURN_STRING(tag, asn1_tag_graphic_string)
+		else EAP_IF_RETURN_STRING(tag, asn1_tag_visible_string)
+		else EAP_IF_RETURN_STRING(tag, asn1_tag_general_string)
+		else EAP_IF_RETURN_STRING(tag, asn1_tag_universal_string)
+		else EAP_IF_RETURN_STRING(tag, asn1_tag_character_string)
+		else EAP_IF_RETURN_STRING(tag, asn1_tag_bmp_string)
+		else EAP_IF_RETURN_STRING(tag, asn1_tag_extented)
+		else EAP_IF_RETURN_STRING(tag, asn1_tag_none)
+		else
+		{
+			return("<Unknown Tag>");
+		}
+	}
+
+#else
+
+	return("<Unknown Tag>");
+
+#endif //#if defined(USE_EAP_DEBUG_TRACE)
+
+}
+
+//--------------------------------------------------
+
+#if defined(USE_EAP_DEBUG_TRACE)
+
+eap_status_e asn1_der_type_c::debug_create_prefix(const u32_t recursion, u8_t * const prefix, const u32_t max_prefix, u32_t * const prefix_length)
+{
+	if (prefix == 0)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+	}
+
+	u32_t offset(0ul);
+
+	for (u32_t ind = 0ul; ind < recursion; ++ind)
+	{
+		offset += m_am_tools->snprintf(
+			&prefix[offset],
+			max_prefix - offset,
+			"  |\0");
+
+		if (max_prefix <= offset)
+		{
+			break;
+		}
+	}
+
+	if (max_prefix > offset)
+	{
+		prefix[offset] = 0;
+	}
+
+	*prefix_length = offset;
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, eap_status_ok);
+}
+
+#endif //#if defined(USE_EAP_DEBUG_TRACE)
+
+//--------------------------------------------------
+
+#if defined(USE_EAP_DEBUG_TRACE)
+
+eap_status_e asn1_der_type_c::debug_header(eap_variable_data_c * const debug_buffer)
+{
+	if (debug_buffer == 0)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+	}
+
+	const u32_t max_data_length(m_recursion * SIZE_OF_ONE_OCTET_STRING + m_offset_of_contents_field * SIZE_OF_ONE_OCTET_STRING);
+
+	if (max_data_length > debug_buffer->get_buffer_length())
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	eap_status_e status = debug_buffer->set_data_length(max_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 ind(0ul);
+	u32_t offset(0ul);
+
+	u8_t * const prefix = reinterpret_cast<u8_t *>(debug_buffer->get_data(max_data_length));
+
+	if (prefix == 0)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	status = debug_create_prefix(m_recursion, prefix, max_data_length, &offset);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	for (ind = 0ul; ind < m_offset_of_length_field && ind < m_input_data_length; ++ind)
+	{
+		offset += m_am_tools->snprintf(
+			debug_buffer->get_data_offset(offset, debug_buffer->get_data_length() - offset),
+			debug_buffer->get_data_length() - offset,
+			"%02x \0",
+			m_input_data[ind]);
+	}
+
+	for (ind = m_offset_of_length_field; ind < m_offset_of_contents_field && ind < m_input_data_length; ++ind)
+	{
+		offset += m_am_tools->snprintf(
+			debug_buffer->get_data_offset(offset, debug_buffer->get_data_length() - offset),
+			debug_buffer->get_data_length() - offset,
+			"%02x \0",
+			m_input_data[ind]);
+	}
+
+	status = debug_buffer->add_end_null();
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	eap_const_string tag_string = 0;
+	eap_char buffer[MAX_STACK_BUFFER];
+
+	if (get_class() == asn1_class_application)
+	{
+		m_am_tools->snprintf(
+			reinterpret_cast<u8_t *>(buffer),
+			MAX_STACK_BUFFER,
+			"Application[%d]\0",
+			get_tag());
+
+		tag_string = buffer;
+	}
+	else if (get_class() == asn1_class_context_specific)
+	{
+		m_am_tools->snprintf(
+			reinterpret_cast<u8_t *>(buffer),
+			MAX_STACK_BUFFER,
+			"[%d]\0",
+			get_tag());
+
+		tag_string = buffer;
+	}
+	else
+	{
+		tag_string = get_tag_string();
+	}
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("%s # %s, %s, %s, length=0x%x=%d, this=0x%08x, index=%d\n"),
+			debug_buffer->get_data(max_data_length),
+			get_class_string(),
+			get_pc_string(),
+			tag_string,
+			get_content_length(),
+			get_content_length(),
+			this,
+			m_index));
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+#endif //#if defined(USE_EAP_DEBUG_TRACE)
+
+//--------------------------------------------------
+
+#if defined(USE_EAP_DEBUG_TRACE)
+
+eap_status_e asn1_der_type_c::debug_content(eap_variable_data_c * const debug_buffer)
+{
+	if (debug_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(eap_status_process_general_error);
+
+	if (get_tag() == asn1_tag_object_identifier)
+	{
+		status = debug_object_identifier(debug_buffer);
+	}
+	else
+	{
+		status = debug_data(debug_buffer);
+	}
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+#endif //#if defined(USE_EAP_DEBUG_TRACE)
+
+//--------------------------------------------------
+
+#if defined(USE_EAP_DEBUG_TRACE)
+
+eap_status_e asn1_der_type_c::debug_data(eap_variable_data_c * const debug_buffer)
+{
+	if (debug_buffer == 0)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+	}
+
+	const u32_t max_prefix_length((m_recursion + 1ul) * SIZE_OF_ONE_OCTET_STRING + COUNT_OF_OCTETS * SIZE_OF_ONE_OCTET_STRING + 1ul);
+	const u32_t ascii_length(COUNT_OF_OCTETS + 5ul);
+
+	if ((max_prefix_length + ascii_length) > debug_buffer->get_buffer_length())
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	eap_status_e status = debug_buffer->set_data_length(max_prefix_length + ascii_length);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	u8_t * const prefix = reinterpret_cast<u8_t *>(debug_buffer->get_data(max_prefix_length));
+	u8_t * const ascii = reinterpret_cast<u8_t *>(debug_buffer->get_data_offset(max_prefix_length, ascii_length));
+
+	if (prefix == 0 || ascii == 0)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	u32_t ind(0ul);
+	u32_t offset(0ul);
+
+	status = debug_create_prefix(m_recursion + 1u, prefix, max_prefix_length, &offset);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	u32_t init_indentation_offset = offset;
+	u32_t type_data_size = get_header_length() + get_content_length();
+	u32_t data_ind(0ul);
+	u32_t ascii_ind(0ul);
+	u8_t octet(0);
+
+	for (ind = m_offset_of_contents_field; ind < type_data_size && ind < m_input_data_length; ++ind)
+	{
+		offset += m_am_tools->snprintf(
+			&prefix[offset],
+			max_prefix_length - offset,
+			"%02x \0",
+			m_input_data[ind]);
+
+		octet = m_input_data[ind];
+
+		if (octet < 0x20 || 0x7e < octet)
+		{
+			octet = '.';
+		}
+
+		m_am_tools->snprintf(
+			&ascii[ascii_ind],
+			ascii_length - ascii_ind,
+			"%c\0",
+			octet);
+
+		++data_ind;
+		++ascii_ind;
+
+		if ((ascii_ind % COUNT_OF_OCTETS) == 0)
+		{
+			prefix[offset] = 0;
+			ascii[ascii_ind] = 0;
+
+			offset = init_indentation_offset;
+			ascii_ind = 0ul;
+
+			EAP_TRACE_DEBUG(
+				m_am_tools,
+				TRACE_FLAGS_DEFAULT,
+				(EAPL("%s |%s|\n"),
+					prefix,
+					ascii));
+		}
+	} // for()
+
+	u32_t remainder(ascii_ind % COUNT_OF_OCTETS);
+
+	if (remainder > 0ul)
+	{
+		for (; ascii_ind < COUNT_OF_OCTETS; ++ascii_ind)
+		{
+			offset += m_am_tools->snprintf(
+				&prefix[offset],
+				max_prefix_length - offset,
+				"   \0");
+
+			m_am_tools->snprintf(
+				&ascii[ascii_ind],
+				ascii_length - ascii_ind,
+				" \0");
+		} // for()
+
+		prefix[offset] = 0;
+		ascii[ascii_ind] = 0;
+
+		EAP_TRACE_DEBUG(
+			m_am_tools,
+			TRACE_FLAGS_DEFAULT,
+			(EAPL("%s |%s|\n"),
+				prefix,
+				ascii));
+	}
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+#endif //#if defined(USE_EAP_DEBUG_TRACE)
+
+//--------------------------------------------------
+
+#if defined(USE_EAP_DEBUG_TRACE)
+
+eap_status_e asn1_der_type_c::debug_object_identifier(eap_variable_data_c * const debug_buffer)
+{
+	if (debug_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(eap_status_process_general_error);
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	status = debug_buffer->set_data_length(debug_buffer->get_buffer_length());
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	const u32_t recursion(m_recursion + 1u);
+	const u32_t max_prefix_length(recursion * SIZE_OF_ONE_OCTET_STRING + 1ul);
+
+	if (debug_buffer->get_buffer_length() < (max_prefix_length + 2ul))
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	const u32_t max_data_output_length((debug_buffer->get_buffer_length() - (max_prefix_length + 2ul))/2ul);
+	const u32_t max_plain_output_length(max_data_output_length);
+
+	u32_t prefix_length(0ul);
+
+	u8_t * const prefix = debug_buffer->get_data();
+	if (prefix == 0)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+	}
+
+	status = debug_create_prefix(recursion, prefix, max_prefix_length, &prefix_length);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	u8_t * const data_output = debug_buffer->get_data_offset(max_prefix_length, max_data_output_length);
+	if (data_output == 0)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+	data_output[max_data_output_length-1ul] = 0;
+
+	u8_t * const plain_output = debug_buffer->get_data_offset(max_prefix_length + max_data_output_length, max_plain_output_length);
+	if (plain_output == 0)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+	plain_output[max_plain_output_length - 1ul] = 0;
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	const u32_t length = get_content_length();
+	
+	if (length == 0)
+	{
+		EAP_TRACE_DEBUG(
+			m_am_tools,
+			TRACE_FLAGS_DEFAULT,
+			(EAPL("# ERROR: invalid %s, length=%d\n"),
+				get_tag_string(),
+				length));
+
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_buffer_too_short);
+	}
+
+	const u8_t * const oid_data = get_content();
+
+	if (oid_data == 0)
+	{
+		EAP_TRACE_DEBUG(
+			m_am_tools,
+			TRACE_FLAGS_DEFAULT,
+			(EAPL("# ERROR: invalid %s, length=%d\n"),
+				get_tag_string(),
+				length));
+
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_buffer_too_short);
+	}
+
+	u32_t offset(0ul);
+	u8_t oid_octet = oid_data[offset];
+	u32_t oid1 = oid_octet / 40;
+	u32_t oid2 = (oid_octet - oid1*40);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("%s%02x # = %d = 40 * %d + %d => %d.%d\n"),
+		prefix,
+		oid_octet,
+		oid_octet,
+		oid1,
+		oid2,
+		oid1,
+		oid2));
+
+	++offset;
+
+	while(offset < length)
+	{
+		u32_t oid_length(0ul);
+		u32_t data_output_offset(0ul);
+		u32_t plain_output_offset(0ul);
+		u32_t ind(0ul);
+
+		for (ind = offset; ind < length; ++ind)
+		{
+			u8_t oid_octet = oid_data[ind];
+			++oid_length;
+			if ((oid_octet & OID_HIGH_BIT) == 0)
+			{
+				break;
+			}
+		} // for()
+
+		u32_t power = oid_length - 1ul;
+		u32_t oid_value(0ul);
+
+		for (ind = offset; ind < (offset+oid_length); ++ind)
+		{
+			u8_t oid_octet = oid_data[ind];
+
+			data_output_offset += m_am_tools->snprintf(
+				&data_output[data_output_offset],
+				max_data_output_length - data_output_offset,
+				"%02x \0",
+				oid_octet);
+
+			u8_t oid = oid_octet & (~OID_HIGH_BIT);
+
+			if (ind > offset)
+			{
+				plain_output_offset += m_am_tools->snprintf(
+					&plain_output[plain_output_offset],
+					max_data_output_length - plain_output_offset,
+					" + \0");
+			}
+
+			if (power > 1ul)
+			{
+				plain_output_offset += m_am_tools->snprintf(
+					&plain_output[plain_output_offset],
+					max_data_output_length - plain_output_offset,
+					"0x%02x * 128 ^ %d\0",
+					oid,
+					power);
+			}
+			else if (power > 0ul)
+			{
+				plain_output_offset += m_am_tools->snprintf(
+					&plain_output[plain_output_offset],
+					max_data_output_length - plain_output_offset,
+					"0x%02x * 128\0",
+					oid);
+			}
+			else
+			{
+				plain_output_offset += m_am_tools->snprintf(
+					&plain_output[plain_output_offset],
+					max_data_output_length - plain_output_offset,
+					"0x%02x\0",
+					oid);
+			}
+
+			oid_value = (oid_value << 7) + oid;
+
+			--power;
+		} // for()
+
+		EAP_TRACE_DEBUG(
+			m_am_tools,
+			TRACE_FLAGS_DEFAULT,
+			(EAPL("%s%s # %s = %d\n"),
+			prefix,
+			data_output,
+			plain_output,
+			oid_value));
+
+		offset += oid_length;
+	}
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+#endif //#if defined(USE_EAP_DEBUG_TRACE)
+
+//--------------------------------------------------
+
+eap_status_e asn1_der_type_c::initialize(
+	const u32_t length,
+	const u8_t * const data,
+	const u16_t recursion,
+	const u32_t index,
+	eap_variable_data_c * const debug_buffer)
+{
+	if (index > 0xffff)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+	}
+
+	m_index = static_cast<u16_t>(index);
+
+	m_recursion = recursion;
+
+	m_input_data_length = length;
+
+	if (m_input_data_length == 0)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_buffer_too_short);
+	}
+
+	m_input_data = data;
+
+	if (m_input_data == 0)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+	}
+	m_offset_of_length_field = get_offset_of_length_field();
+
+	if (m_offset_of_length_field > m_input_data_length
+		|| m_offset_of_length_field == 0)
+	{
+		ASN1_DEBUG_HEADER(this, debug_buffer);
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_buffer_too_short);
+	}
+
+	m_offset_of_contents_field = get_offset_of_contents_field();
+
+	if (m_offset_of_contents_field > m_input_data_length
+		|| m_offset_of_contents_field == 0)
+	{
+		ASN1_DEBUG_HEADER(this, debug_buffer);
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_buffer_too_short);
+	}
+
+	if ((m_offset_of_contents_field + get_content_length()) > m_input_data_length)
+	{
+		ASN1_DEBUG_HEADER(this, debug_buffer);
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_buffer_too_short);
+	}
+
+	u32_t data_length = get_header_length() + get_content_length();
+
+	if (m_input_data_length >= data_length)
+	{
+		m_input_data_length = data_length;
+	}
+	else
+	{
+		ASN1_DEBUG_HEADER(this, debug_buffer);
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_buffer_too_short);
+	}
+
+	add_used_octets(get_header_length());
+
+	ASN1_DEBUG_HEADER(this, debug_buffer);
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, eap_status_ok);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT asn1_der_type_c::asn1_class_e asn1_der_type_c::get_class() const
+{
+	if (m_input_data == 0)
+	{
+		return asn1_der_type_c::asn1_class_none;
+	}
+
+	return static_cast<asn1_der_type_c::asn1_class_e>(m_input_data[0] & asn1_identifier_mask_class);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT asn1_der_type_c::asn1_pc_e asn1_der_type_c::get_pc() const
+{
+	if (m_input_data == 0)
+	{
+		return asn1_der_type_c::asn1_pc_none;
+	}
+
+	return static_cast<asn1_der_type_c::asn1_pc_e>(m_input_data[0] & asn1_identifier_mask_pc);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT asn1_der_type_c::asn1_tag_e asn1_der_type_c::get_tag() const
+{
+	if (m_input_data == 0)
+	{
+		return asn1_der_type_c::asn1_tag_none;
+	}
+
+	asn1_der_type_c::asn1_tag_e tag = static_cast<asn1_der_type_c::asn1_tag_e>(m_input_data[0] & asn1_identifier_mask_tag);
+
+	if (m_offset_of_length_field == asn1_identifier_const_simple_tag_size
+		&& static_cast<asn1_identifier_mask_e>(tag) != asn1_identifier_mask_tag)
+	{
+		// Simple Tag.
+		return tag;
+	}
+	else if (m_offset_of_length_field > asn1_identifier_const_simple_tag_size)
+	{
+		return asn1_der_type_c::asn1_tag_extented;
+	}
+	else
+	{
+		return asn1_der_type_c::asn1_tag_none;
+	}
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e asn1_der_type_c::get_extented_tag(const u8_t ** const extented_tag, u32_t * const extented_tag_size) const
+{
+	if (extented_tag == 0
+		|| extented_tag_size == 0)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+	}
+
+	if (get_tag() == asn1_der_type_c::asn1_tag_extented)
+	{
+		*extented_tag = m_input_data + asn1_identifier_const_simple_tag_size;
+		*extented_tag_size = m_offset_of_length_field - asn1_identifier_const_simple_tag_size;
+	}
+	else
+	{
+		*extented_tag = m_input_data;
+		*extented_tag_size = m_offset_of_length_field;
+	}
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, eap_status_ok);
+}
+
+//--------------------------------------------------
+
+u16_t asn1_der_type_c::get_offset_of_length_field()
+{
+	if (m_input_data == 0)
+	{
+		return 0ul;
+	}
+
+	asn1_der_type_c::asn1_tag_e tag = static_cast<asn1_der_type_c::asn1_tag_e>(m_input_data[0] & asn1_identifier_mask_tag);
+
+	if (static_cast<asn1_identifier_mask_e>(tag) != asn1_identifier_mask_tag)
+	{
+		// Simple Tag.
+		return asn1_identifier_const_simple_tag_size;
+	}
+	else if (m_input_data_length > asn1_identifier_const_simple_tag_size)
+	{
+		const u8_t * extented_tag = &(m_input_data[1]);
+		if (extented_tag == 0)
+		{
+			return 0ul;
+		}
+
+		const u8_t * const end_byte = &(m_input_data[m_input_data_length]);
+		if (end_byte == 0)
+		{
+			return 0ul;
+		}
+
+		while (extented_tag < end_byte)
+		{
+			if ((extented_tag[0] & static_cast<u8_t>(asn1_high_bit_mask_tag)) == 0)
+			{
+				return (extented_tag - m_input_data);
+			}
+
+			++extented_tag;
+		}
+	}
+
+	// Illegal data.
+	return 0ul;
+}
+
+//--------------------------------------------------
+
+u16_t asn1_der_type_c::get_offset_of_contents_field()
+{
+	if (m_input_data == 0)
+	{
+		return 0ul;
+	}
+
+	if (m_offset_of_length_field < asn1_identifier_const_simple_tag_size
+		|| m_offset_of_length_field > m_input_data_length)
+	{
+		return 0ul;
+	}
+
+	const u8_t length_octet1 = m_input_data[m_offset_of_length_field];
+
+	if ((length_octet1 & static_cast<u8_t>(asn1_high_bit_mask_tag)) == 0)
+	{
+		// Short Length field.
+		return m_offset_of_length_field + asn1_identifier_const_short_length_size;
+	}
+	else if (m_input_data_length > static_cast<u32_t>(m_offset_of_length_field + asn1_identifier_const_short_length_size))
+	{
+		const u8_t * extented_length = &(m_input_data[m_offset_of_length_field]);
+		if (extented_length == 0)
+		{
+			return 0ul;
+		}
+
+		u16_t count_of_length_octets = static_cast<u16_t>((*extented_length) & static_cast<u8_t>(~asn1_high_bit_mask_tag));
+
+		u16_t offset(static_cast<u16_t>(m_offset_of_length_field + asn1_identifier_const_short_length_size + count_of_length_octets));
+
+		if (offset > m_input_data_length)
+		{
+			// Illegal data.
+			return 0ul;
+		}
+
+		return offset;
+	}
+
+	// Illegal data.
+	return 0ul;
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT u32_t asn1_der_type_c::get_header_length() const
+{
+	return m_offset_of_contents_field;
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT u32_t asn1_der_type_c::get_content_length() const
+{
+	if (m_input_data == 0
+		|| m_offset_of_length_field == 0ul
+		|| m_offset_of_contents_field == 0ul)
+	{
+		return 0ul;
+	}
+
+	const u8_t * extented_length = &(m_input_data[m_offset_of_length_field]);
+	if (extented_length == 0)
+	{
+		return 0ul;
+	}
+
+	if (((*extented_length) & static_cast<u8_t>(asn1_high_bit_mask_tag)) == 0)
+	{
+		// Short Length field.
+		return static_cast<u32_t>(*extented_length);
+	}
+
+	u32_t count_of_length_octets = static_cast<u32_t>((*extented_length) & static_cast<u8_t>(~asn1_high_bit_mask_tag));
+
+	if ((m_offset_of_length_field + asn1_identifier_const_short_length_size + count_of_length_octets) > m_input_data_length)
+	{
+		// Illegal data.
+		return 0ul;
+	}
+
+	const u8_t * end_byte = &(m_input_data[m_offset_of_contents_field]);
+	if (end_byte == 0)
+	{
+		return 0ul;
+	}
+
+	++extented_length;
+
+	if (static_cast<u32_t>(end_byte - extented_length) > sizeof(u32_t))
+	{
+		// Overflow of length.
+		return 0ul;
+	}
+
+	u32_t length(0ul);
+
+
+	while (extented_length < end_byte)
+	{
+		length = (length << 8) + static_cast<u32_t>(*extented_length);
+		++extented_length;
+	}
+
+	return length;
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT const u8_t * asn1_der_type_c::get_content() const
+{
+	if (m_input_data == 0)
+	{
+		return 0;
+	}
+
+	if (m_offset_of_contents_field > m_input_data_length
+		|| m_offset_of_contents_field == 0)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return 0;
+	}
+
+	return (&m_input_data[m_offset_of_contents_field]);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT u32_t asn1_der_type_c::get_full_data_length() const
+{
+	return m_input_data_length;
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT const u8_t * asn1_der_type_c::get_full_data() const
+{
+	return m_input_data;
+}
+
+//--------------------------------------------------
+
+void asn1_der_type_c::set_parent_type(asn1_der_type_c * const parent_type)
+{
+	m_parent_type = parent_type;
+}
+
+//--------------------------------------------------
+
+asn1_der_type_c * asn1_der_type_c::get_parent_type() const
+{
+	return m_parent_type;
+}
+
+//--------------------------------------------------
+
+eap_status_e asn1_der_type_c::add_sub_type(asn1_der_type_c * const sub_type)
+{
+	if (m_sub_types == 0)
+	{
+		m_sub_types = new eap_array_c<asn1_der_type_c>(m_am_tools);
+		if (m_sub_types == 0)
+		{
+			delete sub_type;
+
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+		}
+	}
+
+	sub_type->set_parent_type(this);
+
+	eap_status_e status = m_sub_types->add_object(sub_type, true);
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT const eap_array_c<asn1_der_type_c> * asn1_der_type_c::get_sub_types() const
+{
+	return m_sub_types;
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT const asn1_der_type_c * asn1_der_type_c::get_sub_type(
+	const asn1_type_const_c * const asn1_type) const
+{
+	const asn1_type_object_c * type = asn1_type[0].get_type();
+
+	if (type == 0
+		|| type->get_is_valid() == false)
+	{
+		return 0;
+	}
+
+	ASN1_TYPE_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("# asn1_type = 0x%08x, class=%d, tag=%d, index=%d\n"),
+		type,
+		type->get_asn1_class(),
+		type->get_asn1_tag(),
+		type->get_index()));
+
+	const asn1_der_type_c * current_type = this;
+
+	ASN1_TYPE_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("### current_type = 0x%08x, class=%d, tag=%d, index=%d\n"),
+		current_type,
+		current_type->get_class(),
+		current_type->get_tag(),
+		0));
+
+	if (current_type->get_class() != type->get_asn1_class()
+		|| current_type->get_tag() != type->get_asn1_tag())
+	{
+		ASN1_TYPE_TRACE_DEBUG(
+			m_am_tools,
+			TRACE_FLAGS_DEFAULT,
+			(EAPL("ERROR: ### current_type does not match.\n")));
+		return 0;
+	}
+
+	++type;
+
+	while (current_type != 0 && type->get_is_valid() == true)
+	{
+		ASN1_TYPE_TRACE_DEBUG(
+			m_am_tools,
+			TRACE_FLAGS_DEFAULT,
+			(EAPL("# asn1_type = 0x%08x, class=%d, tag=%d, index=%d\n"),
+			type,
+			type->get_asn1_class(),
+			type->get_asn1_tag(),
+			type->get_index()));
+
+		const eap_array_c<asn1_der_type_c> * sub_type_array = current_type->get_sub_types();
+		if (sub_type_array == 0)
+		{
+			ASN1_TYPE_TRACE_DEBUG(
+				m_am_tools,
+				TRACE_FLAGS_DEFAULT,
+				(EAPL("ERROR: ### No sub-type array.\n")));
+			return 0;
+		}
+
+		current_type = 0;
+
+		for (u32_t sub_ind = type->get_index(); sub_ind < sub_type_array->get_object_count(); ++sub_ind)
+		{
+			const asn1_der_type_c * const sub_type = sub_type_array->get_object(sub_ind);
+			if (sub_type == 0
+				|| sub_type->get_is_valid() == false)
+			{
+				ASN1_TYPE_TRACE_DEBUG(
+					m_am_tools,
+					TRACE_FLAGS_DEFAULT,
+					(EAPL("ERROR: ### No sub-type.\n")));
+				return 0;
+			}
+
+			ASN1_TYPE_TRACE_DEBUG(
+				m_am_tools,
+				TRACE_FLAGS_DEFAULT,
+				(EAPL("### sub_type = 0x%08x, class=%d, tag=%d, index=%d\n"),
+				sub_type,
+				sub_type->get_class(),
+				sub_type->get_tag(),
+				sub_ind));
+
+			if (sub_type->get_class() == type->get_asn1_class()
+				&& sub_type->get_tag() == type->get_asn1_tag())
+			{
+				current_type = sub_type;
+				break;
+			}
+		}
+
+		++type;
+	}
+
+	if (current_type == 0)
+	{
+		ASN1_TYPE_TRACE_DEBUG(
+			m_am_tools,
+			TRACE_FLAGS_DEFAULT,
+			(EAPL("ERROR: ### No match found.\n")));
+	}
+	else
+	{
+		ASN1_TYPE_TRACE_DEBUG(
+			m_am_tools,
+			TRACE_FLAGS_DEFAULT,
+			(EAPL("### Match found.\n")));
+
+		ASN1_TYPE_TRACE_DATA_DEBUG(
+			m_am_tools,
+			TRACE_FLAGS_DEFAULT,
+			(EAPL("ASN.1 type data"),
+			current_type->get_full_data(),
+			current_type->get_full_data_length()));
+	}
+
+	return current_type;
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT const asn1_der_type_c * asn1_der_type_c::get_previous_type() const
+{
+	const asn1_der_type_c * parent = get_parent_type();
+
+	if (parent == 0)
+	{
+		return 0;
+	}
+
+	if (parent->get_sub_types() == 0)
+	{
+		return 0;
+	}
+
+	const u32_t object_count = parent->get_sub_types()->get_object_count();
+
+	if (object_count <= 1ul)
+	{
+		return 0;
+	}
+
+	const u32_t previous_index = get_index() - 1ul;
+
+	if (object_count <= previous_index)
+	{
+		return 0;
+	}
+
+	return parent->get_sub_types()->get_object(previous_index);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT const asn1_der_type_c * asn1_der_type_c::get_next_type() const
+{
+	const asn1_der_type_c * parent = get_parent_type();
+
+	if (parent == 0)
+	{
+		return 0;
+	}
+
+	const u32_t next_index = get_index() + 1ul;
+
+	if (parent->get_sub_types() == 0
+		|| parent->get_sub_types()->get_object_count() <= next_index)
+	{
+		return 0;
+	}
+
+	return parent->get_sub_types()->get_object(next_index);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT u16_t asn1_der_type_c::get_count_of_sub_types() const
+{
+	return m_count_of_sub_types;
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT void asn1_der_type_c::increase_count_of_sub_types()
+{
+	++m_count_of_sub_types;
+}
+
+//--------------------------------------------------
+
+void asn1_der_type_c::add_used_octets(const u32_t used_octets)
+{
+	m_used_octets += used_octets;
+}
+
+//--------------------------------------------------
+
+u32_t asn1_der_type_c::get_used_octets() const
+{
+	return m_used_octets;
+}
+
+//--------------------------------------------------
+
+u32_t asn1_der_type_c::get_unused_data_length() const
+{
+	return get_input_data_length() - get_used_octets();
+}
+
+//--------------------------------------------------
+
+const u8_t * asn1_der_type_c::get_unused_data() const
+{
+	return get_input_data() + get_used_octets();
+}
+
+//--------------------------------------------------
+
+eap_status_e asn1_der_type_c::encode_oid_from_string(eap_const_string oid, const u32_t oid_length, eap_variable_data_c * const buffer) const
+{
+	if (oid == 0
+		|| buffer == 0
+		|| buffer->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_status_e status(eap_status_process_general_error);
+
+	const eap_char * oid_char = oid;
+	const eap_char * end_char = &oid[oid_length];
+	u32_t remaining_length(oid_length);
+	u32_t first_component(0ul);
+	u32_t component_index(0ul);
+
+	status = buffer->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);
+	}
+
+	while(oid_char < end_char)
+	{
+		// Search next dot (.).
+		const eap_char * dot  = reinterpret_cast<const eap_char *>(m_am_tools->memchr(oid_char, '.', remaining_length));
+		if (dot == 0)
+		{
+			// The last component.
+			dot  = reinterpret_cast<const eap_char *>(oid_char + remaining_length);
+			if (dot == 0
+				|| dot != end_char)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+			}
+		}
+
+		u32_t integer(0ul);
+
+		status = m_am_tools->number_string_to_u32(
+			reinterpret_cast<const u8_t *>(oid_char),
+			dot - oid_char,
+			&integer);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		if (component_index == 0ul)
+		{
+			// The first component is encoded with the second component.
+			first_component = integer;
+		}
+		else if (component_index == 1ul)
+		{
+			if (first_component < 2ul
+				&& integer > 39ul)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+			}
+			else if (first_component > 2ul)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+			}
+
+			const u32_t oid_value = first_component * 40ul + integer;
+			if (oid_value > 0xff)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+			}
+
+			const u8_t oid_octet(static_cast<u8_t>(oid_value));
+
+			status = buffer->add_data(&oid_octet, sizeof(oid_octet));
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+		}
+		else
+		{
+			eap_variable_data_c encoded_data(m_am_tools);
+			if (encoded_data.get_is_valid() == false)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+			}
+
+			const u32_t ENCODE_BASE = 128ul;
+
+			while(integer > 0ul)
+			{
+				const u8_t oid_octet = static_cast<u8_t>(integer % ENCODE_BASE);
+
+				// Encodes the octets to reverse order.
+				status = encoded_data.add_data(&oid_octet, sizeof(oid_octet));
+				if (status != eap_status_ok)
+				{
+					EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+					return EAP_STATUS_RETURN(m_am_tools, status);
+				}
+
+				integer = integer / ENCODE_BASE;
+			} // while()
+
+			for (u32_t ind = encoded_data.get_data_length(); ind > 0ul; --ind)
+			{
+				// reads the octets on reverse order.
+				u8_t * oid_octet = encoded_data.get_data_offset(ind-1ul, sizeof(u8_t));
+				if (oid_octet == 0)
+				{
+					EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+					return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+				}
+
+				if (ind > 1ul)
+				{
+					// All but the last octet have high bit set.
+					*oid_octet |= static_cast<u8_t>(asn1_high_bit_mask_tag);
+				}
+
+				status = buffer->add_data(oid_octet, sizeof(*oid_octet));
+				if (status != eap_status_ok)
+				{
+					EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+					return EAP_STATUS_RETURN(m_am_tools, status);
+				}
+			} // for()
+		}
+
+		remaining_length -= (dot - oid_char) + 1ul;
+
+		oid_char = dot+1ul;
+
+		++component_index;
+
+	} // while()
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e asn1_der_type_c::compare_object_identifier(const u8_t * const der_encoded_oid, const u32_t oid_length) const
+{
+	if (get_tag() != asn1_tag_object_identifier
+		|| der_encoded_oid == 0)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+	}
+
+	const u32_t content_length(get_content_length());
+
+	if (content_length != oid_length
+		|| m_am_tools->memcmp(
+			der_encoded_oid, 
+			get_content(), 
+			content_length) != 0)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return eap_status_no_match;
+	}
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, eap_status_ok);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e asn1_der_type_c::compare_object_identifier(eap_const_string oid, const u32_t oid_length) const
+{
+	if (get_tag() != asn1_tag_object_identifier
+		|| oid == 0)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+	}
+
+	eap_variable_data_c oid_buffer(m_am_tools);
+
+	if (oid_buffer.get_is_valid() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	eap_status_e status = encode_oid_from_string(oid, oid_length, &oid_buffer);
+	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 compare_object_identifier(oid_buffer.get_data(), oid_buffer.get_data_length());
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e asn1_der_type_c::decode(const eap_variable_data_c * const asn1_der_data)
+{
+	if (asn1_der_data == 0)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+	}
+
+	eap_variable_data_c * debug_buffer = 0;
+
+#if defined(USE_EAP_DEBUG_TRACE)
+	// Buffer is used for debug traces.
+	eap_variable_data_c tmp_debug_buffer(m_am_tools);
+
+	{
+		eap_status_e status = tmp_debug_buffer.set_buffer_length(MAX_DEBUG_BUFFER);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+		}
+
+		debug_buffer = &tmp_debug_buffer;
+	}
+#endif //	#if defined(USE_EAP_DEBUG_TRACE)
+
+
+	eap_status_e status = initialize(
+		asn1_der_data->get_data_length(),
+		asn1_der_data->get_data(),
+		0ul,
+		0ul,
+		debug_buffer);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	asn1_der_type_c * current_type = this;
+
+	if (current_type->get_pc() == asn1_pc_constructed)
+	{
+		ASN1_TYPE_TRACE_DEBUG(
+			m_am_tools,
+			TRACE_FLAGS_DEFAULT,
+			(EAPL("# this = 0x%08x, input_data_length=%d, used_octets=%d\n"),
+			 current_type,
+			 current_type->get_input_data_length(),
+			 current_type->get_used_octets()));
+
+		while (current_type->get_used_octets() != current_type->get_input_data_length())
+		{
+			asn1_der_type_c * sub_type = new asn1_der_type_c(m_am_tools);
+
+			eap_automatic_variable_c<asn1_der_type_c> automatic_sub_type(m_am_tools, sub_type);
+
+			if (sub_type == 0
+				|| sub_type->get_is_valid() == false)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+			}
+
+			ASN1_TYPE_TRACE_DEBUG(
+				m_am_tools,
+				TRACE_FLAGS_DEFAULT,
+				(EAPL("# this = 0x%08x, unused_data_length=%d, recursion=%d\n"),
+				 current_type,
+				 current_type->get_unused_data_length(),
+				 current_type->get_recursion()));
+
+			status = sub_type->initialize(
+				current_type->get_unused_data_length(),
+				current_type->get_unused_data(),
+				current_type->get_recursion() + 1u,
+				current_type->get_count_of_sub_types(),
+				debug_buffer);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			current_type->increase_count_of_sub_types();
+
+			ASN1_TYPE_TRACE_DEBUG(
+				m_am_tools,
+				TRACE_FLAGS_DEFAULT,
+				(EAPL("# this = 0x%08x, used_octets=%d\n"),
+				 sub_type,
+				 sub_type->get_used_octets()));
+
+			automatic_sub_type.do_not_free_variable();
+
+			status = current_type->add_sub_type(sub_type);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			if (sub_type->get_pc() == asn1_pc_primitive)
+			{
+				ASN1_DEBUG_DATA(sub_type, debug_buffer);
+
+				sub_type->add_used_octets(sub_type->get_content_length());
+
+				current_type = sub_type;
+
+				do
+				{
+					sub_type = current_type;
+					current_type = current_type->get_parent_type();
+					current_type->add_used_octets(sub_type->get_used_octets());
+
+					ASN1_TYPE_TRACE_DEBUG(
+						m_am_tools,
+						TRACE_FLAGS_DEFAULT,
+						(EAPL("# this = 0x%08x, input_data_length=%d, used_octets=%d\n"),
+						 current_type,
+						 current_type->get_input_data_length(),
+						 current_type->get_used_octets()));
+				}
+				while (current_type->get_used_octets() == current_type->get_input_data_length() && current_type->get_parent_type() != 0);
+			}
+			else
+			{
+				current_type = sub_type;
+			}
+
+		} // while()
+	}
+	else
+	{
+		ASN1_DEBUG_DATA(this, debug_buffer);
+	}
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, eap_status_ok);
+}
+
+//--------------------------------------------------
+
+// End.