/*
 * Copyright (C) 2002-2012 Free Software Foundation, Inc.
 *
 * This file is part of LIBTASN1.
 *
 * The LIBTASN1 library is free software; you can redistribute it
 * and/or modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301, USA
 */


/*****************************************************/
/* File: decoding.c                                  */
/* Description: Functions to manage DER decoding     */
/*****************************************************/

#include <int.h>
#include "parser_aux.h"
#include <gstr.h>
#include "structure.h"
#include "element.h"
#include <limits.h>

static int
_asn1_get_indefinite_length_string (const unsigned char *der, int *len);

static void
_asn1_error_description_tag_error (asn1_node node, char *ErrorDescription)
{

  Estrcpy (ErrorDescription, ":: tag error near element '");
  _asn1_hierarchical_name (node, ErrorDescription + strlen (ErrorDescription),
			   ASN1_MAX_ERROR_DESCRIPTION_SIZE - 40);
  Estrcat (ErrorDescription, "'");

}

/**
 * asn1_get_length_der:
 * @der: DER data to decode.
 * @der_len: Length of DER data to decode.
 * @len: Output variable containing the length of the DER length field.
 *
 * Extract a length field from DER data.
 *
 * Returns: Return the decoded length value, or -1 on indefinite
 *   length, or -2 when the value was too big to fit in a int, or -4
 *   when the decoded length value plus @len would exceed @der_len.
 **/
long
asn1_get_length_der (const unsigned char *der, int der_len, int *len)
{
  unsigned int ans, sum, last;
  int k, punt;

  *len = 0;
  if (der_len <= 0)
    return 0;

  if (!(der[0] & 128))
    {
      /* short form */
      *len = 1;
      ans = der[0];
    }
  else
    {
      /* Long form */
      k = der[0] & 0x7F;
      punt = 1;
      if (k)
	{			/* definite length method */
	  ans = 0;
	  while (punt <= k && punt < der_len)
	    {
	      last = ans;

	      ans = (ans*256) + der[punt++];
	      if (ans < last)
		/* we wrapped around, no bignum support... */
		return -2;
	    }
	}
      else
	{			/* indefinite length method */
	  *len = punt;
	  return -1;
	}

      *len = punt;
    }

  sum = ans + *len;

  /* check for overflow as well INT_MAX as a maximum upper
   * limit for length */
  if (sum >= INT_MAX || sum < ans)
    return -2;

  if (((int) sum) > der_len)
    return -4;

  return ans;
}

/**
 * asn1_get_tag_der:
 * @der: DER data to decode.
 * @der_len: Length of DER data to decode.
 * @cls: Output variable containing decoded class.
 * @len: Output variable containing the length of the DER TAG data.
 * @tag: Output variable containing the decoded tag.
 *
 * Decode the class and TAG from DER code.
 *
 * Returns: Returns %ASN1_SUCCESS on success, or an error.
 **/
int
asn1_get_tag_der (const unsigned char *der, int der_len,
		  unsigned char *cls, int *len, unsigned long *tag)
{
  unsigned int ris;
  int punt;
  unsigned int last;

  if (der == NULL || der_len < 2 || len == NULL)
    return ASN1_DER_ERROR;

  *cls = der[0] & 0xE0;
  if ((der[0] & 0x1F) != 0x1F)
    {
      /* short form */
      *len = 1;
      ris = der[0] & 0x1F;
    }
  else
    {
      /* Long form */
      punt = 1;
      ris = 0;
      while (punt <= der_len && der[punt] & 128)
	{
	  last = ris;

	  ris = (ris * 128) + (der[punt++] & 0x7F);
	  if (ris < last)
	    /* wrapped around, and no bignums... */
	    return ASN1_DER_ERROR;
	}

      if (punt >= der_len)
	return ASN1_DER_ERROR;

      last = ris;

      ris = (ris * 128) + (der[punt++] & 0x7F);
      if (ris < last)
	return ASN1_DER_ERROR;

      *len = punt;
    }
  if (tag)
    *tag = ris;
  return ASN1_SUCCESS;
}

/**
 * asn1_get_length_ber:
 * @ber: BER data to decode.
 * @ber_len: Length of BER data to decode.
 * @len: Output variable containing the length of the BER length field.
 *
 * Extract a length field from BER data.  The difference to
 * asn1_get_length_der() is that this function will return a length
 * even if the value has indefinite encoding.
 *
 * Returns: Return the decoded length value, or negative value when
 *   the value was too big.
 *
 * Since: 2.0
 **/
long
asn1_get_length_ber (const unsigned char *ber, int ber_len, int *len)
{
  int ret;
  long err;

  ret = asn1_get_length_der (ber, ber_len, len);
  if (ret == -1)
    {				/* indefinite length method */
      ret = ber_len;
      err = _asn1_get_indefinite_length_string (ber + 1, &ret);
      if (err != ASN1_SUCCESS)
	return -3;
    }

  return ret;
}

/**
 * asn1_get_octet_der:
 * @der: DER data to decode containing the OCTET SEQUENCE.
 * @der_len: Length of DER data to decode.
 * @ret_len: Output variable containing the length of the DER data.
 * @str: Pre-allocated output buffer to put decoded OCTET SEQUENCE in.
 * @str_size: Length of pre-allocated output buffer.
 * @str_len: Output variable containing the length of the OCTET SEQUENCE.
 *
 * Extract an OCTET SEQUENCE from DER data.
 *
 * Returns: Returns %ASN1_SUCCESS on success, or an error.
 **/
int
asn1_get_octet_der (const unsigned char *der, int der_len,
		    int *ret_len, unsigned char *str, int str_size,
		    int *str_len)
{
  int len_len;

  if (der_len <= 0)
    return ASN1_GENERIC_ERROR;

  /* if(str==NULL) return ASN1_SUCCESS; */
  *str_len = asn1_get_length_der (der, der_len, &len_len);

  if (*str_len < 0)
    return ASN1_DER_ERROR;

  *ret_len = *str_len + len_len;
  if (str_size >= *str_len)
    memcpy (str, der + len_len, *str_len);
  else
    {
      return ASN1_MEM_ERROR;
    }

  return ASN1_SUCCESS;
}

/* Returns ASN1_SUCCESS on success or an error code on error.
 */
static int
_asn1_get_time_der (const unsigned char *der, int der_len, int *ret_len,
		    char *str, int str_size)
{
  int len_len, str_len;

  if (der_len <= 0 || str == NULL)
    return ASN1_DER_ERROR;
  str_len = asn1_get_length_der (der, der_len, &len_len);
  if (str_len < 0 || str_size < str_len)
    return ASN1_DER_ERROR;
  memcpy (str, der + len_len, str_len);
  str[str_len] = 0;
  *ret_len = str_len + len_len;

  return ASN1_SUCCESS;
}

static int
_asn1_get_objectid_der (const unsigned char *der, int der_len, int *ret_len,
			char *str, int str_size)
{
  int len_len, len, k;
  int leading;
  char temp[20];
  unsigned long val, val1, prev_val;

  *ret_len = 0;
  if (str && str_size > 0)
    str[0] = 0;			/* no oid */

  if (str == NULL || der_len <= 0)
    return ASN1_GENERIC_ERROR;
  len = asn1_get_length_der (der, der_len, &len_len);

  if (len < 0 || len > der_len || len_len > der_len)
    return ASN1_DER_ERROR;

  val1 = der[len_len] / 40;
  val = der[len_len] - val1 * 40;

  _asn1_str_cpy (str, str_size, _asn1_ltostr (val1, temp));
  _asn1_str_cat (str, str_size, ".");
  _asn1_str_cat (str, str_size, _asn1_ltostr (val, temp));

  prev_val = 0;
  val = 0;
  leading = 1;
  for (k = 1; k < len; k++)
    {
      /* X.690 mandates that the leading byte must never be 0x80
       */
      if (leading != 0 && der[len_len + k] == 0x80)
	return ASN1_DER_ERROR;
      leading = 0;

      /* check for wrap around */
      val = val << 7;
      val |= der[len_len + k] & 0x7F;

      if (val < prev_val)
	return ASN1_DER_ERROR;

      prev_val = val;

      if (!(der[len_len + k] & 0x80))
	{
	  _asn1_str_cat (str, str_size, ".");
	  _asn1_str_cat (str, str_size, _asn1_ltostr (val, temp));
	  val = 0;
	  prev_val = 0;
	  leading = 1;
	}
    }
  *ret_len = len + len_len;

  return ASN1_SUCCESS;
}

/**
 * asn1_get_bit_der:
 * @der: DER data to decode containing the BIT SEQUENCE.
 * @der_len: Length of DER data to decode.
 * @ret_len: Output variable containing the length of the DER data.
 * @str: Pre-allocated output buffer to put decoded BIT SEQUENCE in.
 * @str_size: Length of pre-allocated output buffer.
 * @bit_len: Output variable containing the size of the BIT SEQUENCE.
 *
 * Extract a BIT SEQUENCE from DER data.
 *
 * Returns: Return %ASN1_SUCCESS on success, or an error.
 **/
int
asn1_get_bit_der (const unsigned char *der, int der_len,
		  int *ret_len, unsigned char *str, int str_size,
		  int *bit_len)
{
  int len_len, len_byte;

  if (der_len <= 0)
    return ASN1_GENERIC_ERROR;
  len_byte = asn1_get_length_der (der, der_len, &len_len) - 1;
  if (len_byte < 0)
    return ASN1_DER_ERROR;

  *ret_len = len_byte + len_len + 1;
  *bit_len = len_byte * 8 - der[len_len];

  if (str_size >= len_byte)
    memcpy (str, der + len_len + 1, len_byte);
  else
    {
      return ASN1_MEM_ERROR;
    }

  return ASN1_SUCCESS;
}

static int
_asn1_extract_tag_der (asn1_node node, const unsigned char *der, int der_len,
		       int *ret_len)
{
  asn1_node p;
  int counter, len2, len3, is_tag_implicit;
  unsigned long tag, tag_implicit = 0;
  unsigned char class, class2, class_implicit = 0;

  if (der_len <= 0)
    return ASN1_GENERIC_ERROR;

  counter = is_tag_implicit = 0;

  if (node->type & CONST_TAG)
    {
      p = node->down;
      while (p)
	{
	  if (type_field (p->type) == TYPE_TAG)
	    {
	      if (p->type & CONST_APPLICATION)
		class2 = ASN1_CLASS_APPLICATION;
	      else if (p->type & CONST_UNIVERSAL)
		class2 = ASN1_CLASS_UNIVERSAL;
	      else if (p->type & CONST_PRIVATE)
		class2 = ASN1_CLASS_PRIVATE;
	      else
		class2 = ASN1_CLASS_CONTEXT_SPECIFIC;

	      if (p->type & CONST_EXPLICIT)
		{
		  if (asn1_get_tag_der
		      (der + counter, der_len - counter, &class, &len2,
		       &tag) != ASN1_SUCCESS)
		    return ASN1_DER_ERROR;

		  if (counter + len2 > der_len)
		    return ASN1_DER_ERROR;
		  counter += len2;

		  len3 =
		    asn1_get_length_ber (der + counter, der_len - counter,
					 &len2);
		  if (len3 < 0)
		    return ASN1_DER_ERROR;

		  counter += len2;
		  if (counter > der_len)
		    return ASN1_DER_ERROR;

		  if (!is_tag_implicit)
		    {
		      if ((class != (class2 | ASN1_CLASS_STRUCTURED)) ||
			  (tag != strtoul ((char *) p->value, NULL, 10)))
			return ASN1_TAG_ERROR;
		    }
		  else
		    {		/* ASN1_TAG_IMPLICIT */
		      if ((class != class_implicit) || (tag != tag_implicit))
			return ASN1_TAG_ERROR;
		    }
		  is_tag_implicit = 0;
		}
	      else
		{		/* ASN1_TAG_IMPLICIT */
		  if (!is_tag_implicit)
		    {
		      if ((type_field (node->type) == TYPE_SEQUENCE) ||
			  (type_field (node->type) == TYPE_SEQUENCE_OF) ||
			  (type_field (node->type) == TYPE_SET) ||
			  (type_field (node->type) == TYPE_SET_OF))
			class2 |= ASN1_CLASS_STRUCTURED;
		      class_implicit = class2;
		      tag_implicit = strtoul ((char *) p->value, NULL, 10);
		      is_tag_implicit = 1;
		    }
		}
	    }
	  p = p->right;
	}
    }

  if (is_tag_implicit)
    {
      if (asn1_get_tag_der
	  (der + counter, der_len - counter, &class, &len2,
	   &tag) != ASN1_SUCCESS)
	return ASN1_DER_ERROR;
      if (counter + len2 > der_len)
	return ASN1_DER_ERROR;

      if ((class != class_implicit) || (tag != tag_implicit))
	{
	  if (type_field (node->type) == TYPE_OCTET_STRING)
	    {
	      class_implicit |= ASN1_CLASS_STRUCTURED;
	      if ((class != class_implicit) || (tag != tag_implicit))
		return ASN1_TAG_ERROR;
	    }
	  else
	    return ASN1_TAG_ERROR;
	}
    }
  else
    {
      if (type_field (node->type) == TYPE_TAG)
	{
	  counter = 0;
	  *ret_len = counter;
	  return ASN1_SUCCESS;
	}

      if (asn1_get_tag_der
	  (der + counter, der_len - counter, &class, &len2,
	   &tag) != ASN1_SUCCESS)
	return ASN1_DER_ERROR;

      if (counter + len2 > der_len)
	return ASN1_DER_ERROR;

      switch (type_field (node->type))
	{
	case TYPE_NULL:
	  if ((class != ASN1_CLASS_UNIVERSAL) || (tag != ASN1_TAG_NULL))
	    return ASN1_DER_ERROR;
	  break;
	case TYPE_BOOLEAN:
	  if ((class != ASN1_CLASS_UNIVERSAL) || (tag != ASN1_TAG_BOOLEAN))
	    return ASN1_DER_ERROR;
	  break;
	case TYPE_INTEGER:
	  if ((class != ASN1_CLASS_UNIVERSAL) || (tag != ASN1_TAG_INTEGER))
	    return ASN1_DER_ERROR;
	  break;
	case TYPE_ENUMERATED:
	  if ((class != ASN1_CLASS_UNIVERSAL) || (tag != ASN1_TAG_ENUMERATED))
	    return ASN1_DER_ERROR;
	  break;
	case TYPE_OBJECT_ID:
	  if ((class != ASN1_CLASS_UNIVERSAL) || (tag != ASN1_TAG_OBJECT_ID))
	    return ASN1_DER_ERROR;
	  break;
	case TYPE_TIME:
	  if (node->type & CONST_UTC)
	    {
	      if ((class != ASN1_CLASS_UNIVERSAL)
		  || (tag != ASN1_TAG_UTCTime))
		return ASN1_DER_ERROR;
	    }
	  else
	    {
	      if ((class != ASN1_CLASS_UNIVERSAL)
		  || (tag != ASN1_TAG_GENERALIZEDTime))
		return ASN1_DER_ERROR;
	    }
	  break;
	case TYPE_OCTET_STRING:
	  if (((class != ASN1_CLASS_UNIVERSAL)
	       && (class != (ASN1_CLASS_UNIVERSAL | ASN1_CLASS_STRUCTURED)))
	      || (tag != ASN1_TAG_OCTET_STRING))
	    return ASN1_DER_ERROR;
	  break;
	case TYPE_GENERALSTRING:
	  if ((class != ASN1_CLASS_UNIVERSAL)
	      || (tag != ASN1_TAG_GENERALSTRING))
	    return ASN1_DER_ERROR;
	  break;
	case TYPE_BIT_STRING:
	  if ((class != ASN1_CLASS_UNIVERSAL) || (tag != ASN1_TAG_BIT_STRING))
	    return ASN1_DER_ERROR;
	  break;
	case TYPE_SEQUENCE:
	case TYPE_SEQUENCE_OF:
	  if ((class != (ASN1_CLASS_UNIVERSAL | ASN1_CLASS_STRUCTURED))
	      || (tag != ASN1_TAG_SEQUENCE))
	    return ASN1_DER_ERROR;
	  break;
	case TYPE_SET:
	case TYPE_SET_OF:
	  if ((class != (ASN1_CLASS_UNIVERSAL | ASN1_CLASS_STRUCTURED))
	      || (tag != ASN1_TAG_SET))
	    return ASN1_DER_ERROR;
	  break;
	case TYPE_ANY:
	  counter -= len2;
	  break;
	default:
	  return ASN1_DER_ERROR;
	  break;
	}
    }

  counter += len2;
  *ret_len = counter;
  return ASN1_SUCCESS;
}

static int
_asn1_delete_not_used (asn1_node node)
{
  asn1_node p, p2;

  if (node == NULL)
    return ASN1_ELEMENT_NOT_FOUND;

  p = node;
  while (p)
    {
      if (p->type & CONST_NOT_USED)
	{
	  p2 = NULL;
	  if (p != node)
	    {
	      p2 = _asn1_find_left (p);
	      if (!p2)
		p2 = _asn1_find_up (p);
	    }
	  asn1_delete_structure (&p);
	  p = p2;
	}

      if (!p)
	break;			/* reach node */

      if (p->down)
	{
	  p = p->down;
	}
      else
	{
	  if (p == node)
	    p = NULL;
	  else if (p->right)
	    p = p->right;
	  else
	    {
	      while (1)
		{
		  p = _asn1_find_up (p);
		  if (p == node)
		    {
		      p = NULL;
		      break;
		    }
		  if (p->right)
		    {
		      p = p->right;
		      break;
		    }
		}
	    }
	}
    }
  return ASN1_SUCCESS;
}

static int
_asn1_extract_der_octet (asn1_node node, const unsigned char *der,
			 int der_len)
{
  int len2, len3;
  int counter2, counter_end;

  len2 = asn1_get_length_der (der, der_len, &len3);
  if (len2 < -1)
    return ASN1_DER_ERROR;

  counter2 = len3 + 1;

  if (len2 == -1)
    counter_end = der_len - 2;
  else
    counter_end = der_len;

  while (counter2 < counter_end)
    {
      len2 = asn1_get_length_der (der + counter2, der_len - counter2, &len3);

      if (len2 < -1)
	return ASN1_DER_ERROR;

      if (len2 > 0)
	{
	  _asn1_append_value (node, der + counter2 + len3, len2);
	}
      else
	{			/* indefinite */

	  len2 =
	    _asn1_extract_der_octet (node, der + counter2 + len3,
				     der_len - counter2 - len3);
	  if (len2 < 0)
	    return len2;
	}

      counter2 += len2 + len3 + 1;
    }

  return ASN1_SUCCESS;
}

static int
_asn1_get_octet_string (const unsigned char *der, asn1_node node, int *len)
{
  int len2, len3, counter, tot_len, indefinite;

  counter = 0;

  if (*(der - 1) & ASN1_CLASS_STRUCTURED)
    {
      tot_len = 0;
      indefinite = asn1_get_length_der (der, *len, &len3);
      if (indefinite < -1)
	return ASN1_DER_ERROR;

      counter += len3;
      if (indefinite >= 0)
	indefinite += len3;

      while (1)
	{
	  if (counter > (*len))
	    return ASN1_DER_ERROR;

	  if (indefinite == -1)
	    {
	      if ((der[counter] == 0) && (der[counter + 1] == 0))
		{
		  counter += 2;
		  break;
		}
	    }
	  else if (counter >= indefinite)
	    break;

	  if (der[counter] != ASN1_TAG_OCTET_STRING)
	    return ASN1_DER_ERROR;

	  counter++;

	  len2 = asn1_get_length_der (der + counter, *len - counter, &len3);
	  if (len2 <= 0)
	    return ASN1_DER_ERROR;

	  counter += len3 + len2;
	  tot_len += len2;
	}

      /* copy */
      if (node)
	{
	  unsigned char temp[DER_LEN];
	  int ret;

	  len2 = sizeof (temp);

	  asn1_length_der (tot_len, temp, &len2);
	  _asn1_set_value (node, temp, len2);

	  ret = _asn1_extract_der_octet (node, der, *len);
	  if (ret != ASN1_SUCCESS)
	    return ret;

	}
    }
  else
    {				/* NOT STRUCTURED */
      len2 = asn1_get_length_der (der, *len, &len3);
      if (len2 < 0)
	return ASN1_DER_ERROR;

      counter = len3 + len2;
      if (node)
	_asn1_set_value (node, der, counter);
    }

  *len = counter;
  return ASN1_SUCCESS;

}

static int
_asn1_get_indefinite_length_string (const unsigned char *der, int *len)
{
  int len2, len3, counter, indefinite;
  unsigned long tag;
  unsigned char class;

  counter = indefinite = 0;

  while (1)
    {
      if ((*len) < counter)
	return ASN1_DER_ERROR;

      if ((der[counter] == 0) && (der[counter + 1] == 0))
	{
	  counter += 2;
	  indefinite--;
	  if (indefinite <= 0)
	    break;
	  else
	    continue;
	}

      if (asn1_get_tag_der
	  (der + counter, *len - counter, &class, &len2,
	   &tag) != ASN1_SUCCESS)
	return ASN1_DER_ERROR;
      if (counter + len2 > *len)
	return ASN1_DER_ERROR;
      counter += len2;
      len2 = asn1_get_length_der (der + counter, *len - counter, &len3);
      if (len2 < -1)
	return ASN1_DER_ERROR;
      if (len2 == -1)
	{
	  indefinite++;
	  counter += 1;
	}
      else
	{
	  counter += len2 + len3;
	}
    }

  *len = counter;
  return ASN1_SUCCESS;

}

/**
 * asn1_der_decoding:
 * @element: pointer to an ASN1 structure.
 * @ider: vector that contains the DER encoding.
 * @len: number of bytes of *@ider: @ider[0]..@ider[len-1].
 * @errorDescription: null-terminated string contains details when an
 *   error occurred.
 *
 * Fill the structure *@ELEMENT with values of a DER encoding
 * string. The structure must just be created with function
 * asn1_create_element().  If an error occurs during the decoding
 * procedure, the *@ELEMENT is deleted and set equal to
 * %NULL.
 *
 * Returns: %ASN1_SUCCESS if DER encoding OK, %ASN1_ELEMENT_NOT_FOUND
 *   if @ELEMENT is %NULL, and %ASN1_TAG_ERROR or
 *   %ASN1_DER_ERROR if the der encoding doesn't match the structure
 *   name (*@ELEMENT deleted).
 **/
int
asn1_der_decoding (asn1_node * element, const void *ider, int len,
		   char *errorDescription)
{
  asn1_node node, p, p2, p3;
  char temp[128];
  int counter, len2, len3, len4, move, ris, tlen;
  unsigned char class;
  unsigned long tag;
  int indefinite, result;
  const unsigned char *der = ider;

  node = *element;

  if (errorDescription != NULL)
    errorDescription[0] = 0;

  if (node == NULL)
    return ASN1_ELEMENT_NOT_FOUND;

  if (node->type & CONST_OPTION)
    {
      result = ASN1_GENERIC_ERROR;
      goto cleanup;
    }

  counter = 0;
  move = DOWN;
  p = node;
  while (1)
    {
      ris = ASN1_SUCCESS;
      if (move != UP)
	{
	  if (p->type & CONST_SET)
	    {
	      p2 = _asn1_find_up (p);
	      len2 = _asn1_strtol (p2->value, NULL, 10);
	      if (len2 == -1)
		{
		  if (!der[counter] && !der[counter + 1])
		    {
		      p = p2;
		      move = UP;
		      counter += 2;
		      continue;
		    }
		}
	      else if (counter == len2)
		{
		  p = p2;
		  move = UP;
		  continue;
		}
	      else if (counter > len2)
		{
		  result = ASN1_DER_ERROR;
		  goto cleanup;
		}
	      p2 = p2->down;
	      while (p2)
		{
		  if ((p2->type & CONST_SET) && (p2->type & CONST_NOT_USED))
		    {
		      if (type_field (p2->type) != TYPE_CHOICE)
			ris =
			  _asn1_extract_tag_der (p2, der + counter,
						 len - counter, &len2);
		      else
			{
			  p3 = p2->down;
			  while (p3)
			    {
			      ris =
				_asn1_extract_tag_der (p3, der + counter,
						       len - counter, &len2);
			      if (ris == ASN1_SUCCESS)
				break;
			      p3 = p3->right;
			    }
			}
		      if (ris == ASN1_SUCCESS)
			{
			  p2->type &= ~CONST_NOT_USED;
			  p = p2;
			  break;
			}
		    }
		  p2 = p2->right;
		}
	      if (p2 == NULL)
		{
		  result = ASN1_DER_ERROR;
		  goto cleanup;
		}
	    }

	  if ((p->type & CONST_OPTION) || (p->type & CONST_DEFAULT))
	    {
	      p2 = _asn1_find_up (p);
	      len2 = _asn1_strtol (p2->value, NULL, 10);
	      if (counter == len2)
		{
		  if (p->right)
		    {
		      p2 = p->right;
		      move = RIGHT;
		    }
		  else
		    move = UP;

		  if (p->type & CONST_OPTION)
		    asn1_delete_structure (&p);

		  p = p2;
		  continue;
		}
	    }

	  if (type_field (p->type) == TYPE_CHOICE)
	    {
	      while (p->down)
		{
		  if (counter < len)
		    ris =
		      _asn1_extract_tag_der (p->down, der + counter,
					     len - counter, &len2);
		  else
		    ris = ASN1_DER_ERROR;
		  if (ris == ASN1_SUCCESS)
		    {
		      while (p->down->right)
			{
			  p2 = p->down->right;
			  asn1_delete_structure (&p2);
			}
		      break;
		    }
		  else if (ris == ASN1_ERROR_TYPE_ANY)
		    {
		      result = ASN1_ERROR_TYPE_ANY;
		      goto cleanup;
		    }
		  else
		    {
		      p2 = p->down;
		      asn1_delete_structure (&p2);
		    }
		}

	      if (p->down == NULL)
		{
		  if (!(p->type & CONST_OPTION))
		    {
		      result = ASN1_DER_ERROR;
		      goto cleanup;
		    }
		}
	      else
		p = p->down;
	    }

	  if ((p->type & CONST_OPTION) || (p->type & CONST_DEFAULT))
	    {
	      p2 = _asn1_find_up (p);
	      len2 = _asn1_strtol (p2->value, NULL, 10);
	      if ((len2 != -1) && (counter > len2))
		ris = ASN1_TAG_ERROR;
	    }

	  if (ris == ASN1_SUCCESS)
	    ris =
	      _asn1_extract_tag_der (p, der + counter, len - counter, &len2);
	  if (ris != ASN1_SUCCESS)
	    {
	      if (p->type & CONST_OPTION)
		{
		  p->type |= CONST_NOT_USED;
		  move = RIGHT;
		}
	      else if (p->type & CONST_DEFAULT)
		{
		  _asn1_set_value (p, NULL, 0);
		  move = RIGHT;
		}
	      else
		{
		  if (errorDescription != NULL)
		    _asn1_error_description_tag_error (p, errorDescription);

		  result = ASN1_TAG_ERROR;
		  goto cleanup;
		}
	    }
	  else
	    counter += len2;
	}

      if (ris == ASN1_SUCCESS)
	{
	  switch (type_field (p->type))
	    {
	    case TYPE_NULL:
	      if (der[counter])
		{
		  result = ASN1_DER_ERROR;
		  goto cleanup;
		}
	      counter++;
	      move = RIGHT;
	      break;
	    case TYPE_BOOLEAN:
	      if (der[counter++] != 1)
		{
		  result = ASN1_DER_ERROR;
		  goto cleanup;
		}
	      if (der[counter++] == 0)
		_asn1_set_value (p, "F", 1);
	      else
		_asn1_set_value (p, "T", 1);
	      move = RIGHT;
	      break;
	    case TYPE_INTEGER:
	    case TYPE_ENUMERATED:
	      len2 =
		asn1_get_length_der (der + counter, len - counter, &len3);
	      if (len2 < 0)
		{
		  result = ASN1_DER_ERROR;
		  goto cleanup;
		}

	      _asn1_set_value (p, der + counter, len3 + len2);
	      counter += len3 + len2;
	      move = RIGHT;
	      break;
	    case TYPE_OBJECT_ID:
	      result =
		_asn1_get_objectid_der (der + counter, len - counter, &len2,
					temp, sizeof (temp));
	      if (result != ASN1_SUCCESS)
		goto cleanup;

	      tlen = strlen (temp);
	      if (tlen > 0)
		_asn1_set_value (p, temp, tlen + 1);
	      counter += len2;
	      move = RIGHT;
	      break;
	    case TYPE_TIME:
	      result =
		_asn1_get_time_der (der + counter, len - counter, &len2, temp,
				    sizeof (temp) - 1);
	      if (result != ASN1_SUCCESS)
		goto cleanup;

	      tlen = strlen (temp);
	      if (tlen > 0)
		_asn1_set_value (p, temp, tlen + 1);
	      counter += len2;
	      move = RIGHT;
	      break;
	    case TYPE_OCTET_STRING:
	      len3 = len - counter;
	      result = _asn1_get_octet_string (der + counter, p, &len3);
	      if (result != ASN1_SUCCESS)
		goto cleanup;

	      counter += len3;
	      move = RIGHT;
	      break;
	    case TYPE_GENERALSTRING:
	      len2 =
		asn1_get_length_der (der + counter, len - counter, &len3);
	      if (len2 < 0)
		{
		  result = ASN1_DER_ERROR;
		  goto cleanup;
		}

	      _asn1_set_value (p, der + counter, len3 + len2);
	      counter += len3 + len2;
	      move = RIGHT;
	      break;
	    case TYPE_BIT_STRING:
	      len2 =
		asn1_get_length_der (der + counter, len - counter, &len3);
	      if (len2 < 0)
		{
		  result = ASN1_DER_ERROR;
		  goto cleanup;
		}

	      _asn1_set_value (p, der + counter, len3 + len2);
	      counter += len3 + len2;
	      move = RIGHT;
	      break;
	    case TYPE_SEQUENCE:
	    case TYPE_SET:
	      if (move == UP)
		{
		  len2 = _asn1_strtol (p->value, NULL, 10);
		  _asn1_set_value (p, NULL, 0);
		  if (len2 == -1)
		    {		/* indefinite length method */
		      if (len - counter + 1 > 0)
			{
			  if ((der[counter]) || der[counter + 1])
			    {
			      result = ASN1_DER_ERROR;
			      goto cleanup;
			    }
			}
		      else
			{
			  result = ASN1_DER_ERROR;
			  goto cleanup;
			}
		      counter += 2;
		    }
		  else
		    {		/* definite length method */
		      if (len2 != counter)
			{
			  result = ASN1_DER_ERROR;
			  goto cleanup;
			}
		    }
		  move = RIGHT;
		}
	      else
		{		/* move==DOWN || move==RIGHT */
		  len3 =
		    asn1_get_length_der (der + counter, len - counter, &len2);
		  if (len3 < -1)
		    {
		      result = ASN1_DER_ERROR;
		      goto cleanup;
		    }
		  counter += len2;
		  if (len3 > 0)
		    {
		      _asn1_ltostr (counter + len3, temp);
		      tlen = strlen (temp);
		      if (tlen > 0)
			_asn1_set_value (p, temp, tlen + 1);
		      move = DOWN;
		    }
		  else if (len3 == 0)
		    {
		      p2 = p->down;
		      while (p2)
			{
			  if (type_field (p2->type) != TYPE_TAG)
			    {
			      p3 = p2->right;
			      asn1_delete_structure (&p2);
			      p2 = p3;
			    }
			  else
			    p2 = p2->right;
			}
		      move = RIGHT;
		    }
		  else
		    {		/* indefinite length method */
		      _asn1_set_value (p, "-1", 3);
		      move = DOWN;
		    }
		}
	      break;
	    case TYPE_SEQUENCE_OF:
	    case TYPE_SET_OF:
	      if (move == UP)
		{
		  len2 = _asn1_strtol (p->value, NULL, 10);
		  if (len2 == -1)
		    {		/* indefinite length method */
		      if ((counter + 2) > len)
			{
			  result = ASN1_DER_ERROR;
			  goto cleanup;
			}

		      if ((der[counter]) || der[counter + 1])
			{
			  _asn1_append_sequence_set (p);
			  p = p->down;
			  while (p->right)
			    p = p->right;
			  move = RIGHT;
			  continue;
			}
		      _asn1_set_value (p, NULL, 0);
		      counter += 2;
		    }
		  else
		    {		/* definite length method */
		      if (len2 > counter)
			{
			  _asn1_append_sequence_set (p);
			  p = p->down;
			  while (p->right)
			    p = p->right;
			  move = RIGHT;
			  continue;
			}
		      _asn1_set_value (p, NULL, 0);
		      if (len2 != counter)
			{
			  result = ASN1_DER_ERROR;
			  goto cleanup;
			}
		    }
		}
	      else
		{		/* move==DOWN || move==RIGHT */
		  len3 =
		    asn1_get_length_der (der + counter, len - counter, &len2);
		  if (len3 < -1)
		    {
		      result = ASN1_DER_ERROR;
		      goto cleanup;
		    }
		  counter += len2;
		  if (len3)
		    {
		      if (len3 > 0)
			{	/* definite length method */
			  _asn1_ltostr (counter + len3, temp);
			  tlen = strlen (temp);

			  if (tlen > 0)
			    _asn1_set_value (p, temp, tlen + 1);
			}
		      else
			{	/* indefinite length method */
			  _asn1_set_value (p, "-1", 3);
			}
		      p2 = p->down;
		      while ((type_field (p2->type) == TYPE_TAG)
			     || (type_field (p2->type) == TYPE_SIZE))
			p2 = p2->right;
		      if (p2->right == NULL)
			_asn1_append_sequence_set (p);
		      p = p2;
		    }
		}
	      move = RIGHT;
	      break;
	    case TYPE_ANY:
	      if (asn1_get_tag_der
		  (der + counter, len - counter, &class, &len2,
		   &tag) != ASN1_SUCCESS)
		{
		  result = ASN1_DER_ERROR;
		  goto cleanup;
		}

	      if (counter + len2 > len)
		{
		  result = ASN1_DER_ERROR;
		  goto cleanup;
		}
	      len4 =
		asn1_get_length_der (der + counter + len2,
				     len - counter - len2, &len3);
	      if (len4 < -1)
		{
		  result = ASN1_DER_ERROR;
		  goto cleanup;
		}
	      if (len4 != -1)
		{
		  len2 += len4;
		  _asn1_set_value_octet (p, der + counter, len2 + len3);
		  counter += len2 + len3;
		}
	      else
		{		/* indefinite length */
		  /* Check indefinite lenth method in an EXPLICIT TAG */
		  if ((p->type & CONST_TAG) && (der[counter - 1] == 0x80))
		    indefinite = 1;
		  else
		    indefinite = 0;

		  len2 = len - counter;
		  result =
		    _asn1_get_indefinite_length_string (der + counter, &len2);
		  if (result != ASN1_SUCCESS)
		    goto cleanup;

		  _asn1_set_value_octet (p, der + counter, len2);
		  counter += len2;

		  /* Check if a couple of 0x00 are present due to an EXPLICIT TAG with
		     an indefinite length method. */
		  if (indefinite)
		    {
		      if (!der[counter] && !der[counter + 1])
			{
			  counter += 2;
			}
		      else
			{
			  result = ASN1_DER_ERROR;
			  goto cleanup;
			}
		    }
		}
	      move = RIGHT;
	      break;
	    default:
	      move = (move == UP) ? RIGHT : DOWN;
	      break;
	    }
	}

      if (p == node && move != DOWN)
	break;

      if (move == DOWN)
	{
	  if (p->down)
	    p = p->down;
	  else
	    move = RIGHT;
	}
      if ((move == RIGHT) && !(p->type & CONST_SET))
	{
	  if (p->right)
	    p = p->right;
	  else
	    move = UP;
	}
      if (move == UP)
	p = _asn1_find_up (p);
    }

  _asn1_delete_not_used (*element);

  if (counter != len)
    {
      result = ASN1_DER_ERROR;
      goto cleanup;
    }

  return ASN1_SUCCESS;

cleanup:
  asn1_delete_structure (element);
  return result;
}

#define FOUND        1
#define SAME_BRANCH  2
#define OTHER_BRANCH 3
#define EXIT         4

/**
 * asn1_der_decoding_element:
 * @structure: pointer to an ASN1 structure
 * @elementName: name of the element to fill
 * @ider: vector that contains the DER encoding of the whole structure.
 * @len: number of bytes of *der: der[0]..der[len-1]
 * @errorDescription: null-terminated string contains details when an
 *   error occurred.
 *
 * Fill the element named @ELEMENTNAME with values of a DER encoding
 * string.  The structure must just be created with function
 * asn1_create_element().  The DER vector must contain the encoding
 * string of the whole @STRUCTURE.  If an error occurs during the
 * decoding procedure, the *@STRUCTURE is deleted and set equal to
 * %NULL.
 *
 * Returns: %ASN1_SUCCESS if DER encoding OK, %ASN1_ELEMENT_NOT_FOUND
 *   if ELEMENT is %NULL or @elementName == NULL, and
 *   %ASN1_TAG_ERROR or %ASN1_DER_ERROR if the der encoding doesn't
 *   match the structure @structure (*ELEMENT deleted).
 **/
int
asn1_der_decoding_element (asn1_node * structure, const char *elementName,
			   const void *ider, int len, char *errorDescription)
{
  asn1_node node, p, p2, p3, nodeFound = NULL;
  char temp[128], currentName[ASN1_MAX_NAME_SIZE * 10], *dot_p, *char_p;
  int nameLen = ASN1_MAX_NAME_SIZE * 10 - 1, state;
  int counter, len2, len3, len4, move, ris, tlen;
  unsigned char class;
  unsigned long tag;
  int indefinite, result;
  const unsigned char *der = ider;

  node = *structure;

  if (node == NULL)
    return ASN1_ELEMENT_NOT_FOUND;

  if (elementName == NULL)
    {
      result = ASN1_ELEMENT_NOT_FOUND;
      goto cleanup;
    }

  if (node->type & CONST_OPTION)
    {
      result = ASN1_GENERIC_ERROR;
      goto cleanup;
    }

  if ((*structure)->name[0] != 0)
    {				/* Has *structure got a name? */
      nameLen -= strlen ((*structure)->name);
      if (nameLen > 0)
	strcpy (currentName, (*structure)->name);
      else
	{
	  result = ASN1_MEM_ERROR;
	  goto cleanup;
	}
      if (!(strcmp (currentName, elementName)))
	{
	  state = FOUND;
	  nodeFound = *structure;
	}
      else if (!memcmp (currentName, elementName, strlen (currentName)))
	state = SAME_BRANCH;
      else
	state = OTHER_BRANCH;
    }
  else
    {				/* *structure doesn't have a name? */
      currentName[0] = 0;
      if (elementName[0] == 0)
	{
	  state = FOUND;
	  nodeFound = *structure;
	}
      else
	{
	  state = SAME_BRANCH;
	}
    }

  counter = 0;
  move = DOWN;
  p = node;
  while (1)
    {

      ris = ASN1_SUCCESS;

      if (move != UP)
	{
	  if (p->type & CONST_SET)
	    {
	      p2 = _asn1_find_up (p);
	      len2 = _asn1_strtol (p2->value, NULL, 10);
	      if (counter == len2)
		{
		  p = p2;
		  move = UP;
		  continue;
		}
	      else if (counter > len2)
		{
		  result = ASN1_DER_ERROR;
		  goto cleanup;
		}
	      p2 = p2->down;
	      while (p2)
		{
		  if ((p2->type & CONST_SET) && (p2->type & CONST_NOT_USED))
		    {
		      if (type_field (p2->type) != TYPE_CHOICE)
			ris =
			  _asn1_extract_tag_der (p2, der + counter,
						 len - counter, &len2);
		      else
			{
			  p3 = p2->down;
			  while (p3)
			    {
			      ris =
				_asn1_extract_tag_der (p3, der + counter,
						       len - counter, &len2);
			      if (ris == ASN1_SUCCESS)
				break;
			      p3 = p3->right;
			    }
			}
		      if (ris == ASN1_SUCCESS)
			{
			  p2->type &= ~CONST_NOT_USED;
			  p = p2;
			  break;
			}
		    }
		  p2 = p2->right;
		}
	      if (p2 == NULL)
		{
		  result = ASN1_DER_ERROR;
		  goto cleanup;
		}
	    }

	  if ((p->type & CONST_OPTION) || (p->type & CONST_DEFAULT))
	    {
	      p2 = _asn1_find_up (p);
	      len2 = _asn1_strtol (p2->value, NULL, 10);
	      if (counter == len2)
		{
		  if (p->right)
		    {
		      p2 = p->right;
		      move = RIGHT;
		    }
		  else
		    move = UP;

		  if (p->type & CONST_OPTION)
		    asn1_delete_structure (&p);

		  p = p2;
		  continue;
		}
	    }

	  if (type_field (p->type) == TYPE_CHOICE)
	    {
	      while (p->down)
		{
		  if (counter < len)
		    ris =
		      _asn1_extract_tag_der (p->down, der + counter,
					     len - counter, &len2);
		  else
		    ris = ASN1_DER_ERROR;
		  if (ris == ASN1_SUCCESS)
		    {
		      while (p->down->right)
			{
			  p2 = p->down->right;
			  asn1_delete_structure (&p2);
			}
		      break;
		    }
		  else if (ris == ASN1_ERROR_TYPE_ANY)
		    {
		      result = ASN1_ERROR_TYPE_ANY;
		      goto cleanup;
		    }
		  else
		    {
		      p2 = p->down;
		      asn1_delete_structure (&p2);
		    }
		}

	      if (p->down == NULL)
		{
		  if (!(p->type & CONST_OPTION))
		    {
		      result = ASN1_DER_ERROR;
		      goto cleanup;
		    }
		}
	      else
		p = p->down;
	    }

	  if ((p->type & CONST_OPTION) || (p->type & CONST_DEFAULT))
	    {
	      p2 = _asn1_find_up (p);
	      len2 = _asn1_strtol (p2->value, NULL, 10);
	      if (counter > len2)
		ris = ASN1_TAG_ERROR;
	    }

	  if (ris == ASN1_SUCCESS)
	    ris =
	      _asn1_extract_tag_der (p, der + counter, len - counter, &len2);
	  if (ris != ASN1_SUCCESS)
	    {
	      if (p->type & CONST_OPTION)
		{
		  p->type |= CONST_NOT_USED;
		  move = RIGHT;
		}
	      else if (p->type & CONST_DEFAULT)
		{
		  _asn1_set_value (p, NULL, 0);
		  move = RIGHT;
		}
	      else
		{
		  if (errorDescription != NULL)
		    _asn1_error_description_tag_error (p, errorDescription);

		  result = ASN1_TAG_ERROR;
		  goto cleanup;
		}
	    }
	  else
	    counter += len2;
	}

      if (ris == ASN1_SUCCESS)
	{
	  switch (type_field (p->type))
	    {
	    case TYPE_NULL:
	      if (der[counter])
		{
		  result = ASN1_DER_ERROR;
		  goto cleanup;
		}

	      if (p == nodeFound)
		state = EXIT;

	      counter++;
	      move = RIGHT;
	      break;
	    case TYPE_BOOLEAN:
	      if (der[counter++] != 1)
		{
		  result = ASN1_DER_ERROR;
		  goto cleanup;
		}

	      if (state == FOUND)
		{
		  if (der[counter++] == 0)
		    _asn1_set_value (p, "F", 1);
		  else
		    _asn1_set_value (p, "T", 1);

		  if (p == nodeFound)
		    state = EXIT;

		}
	      else
		counter++;

	      move = RIGHT;
	      break;
	    case TYPE_INTEGER:
	    case TYPE_ENUMERATED:
	      len2 =
		asn1_get_length_der (der + counter, len - counter, &len3);
	      if (len2 < 0)
		{
		  result = ASN1_DER_ERROR;
		  goto cleanup;
		}

	      if (state == FOUND)
		{
		  if (len3 + len2 > len - counter)
		    {
		      result = ASN1_DER_ERROR;
		      goto cleanup;
		    }
		  _asn1_set_value (p, der + counter, len3 + len2);

		  if (p == nodeFound)
		    state = EXIT;
		}
	      counter += len3 + len2;
	      move = RIGHT;
	      break;
	    case TYPE_OBJECT_ID:
	      if (state == FOUND)
		{
		  result =
		    _asn1_get_objectid_der (der + counter, len - counter,
					    &len2, temp, sizeof (temp));
		  if (result != ASN1_SUCCESS)
		    goto cleanup;

		  tlen = strlen (temp);

		  if (tlen > 0)
		    _asn1_set_value (p, temp, tlen + 1);

		  if (p == nodeFound)
		    state = EXIT;
		}
	      else
		{
		  len2 =
		    asn1_get_length_der (der + counter, len - counter, &len3);
		  if (len2 < 0)
		    {
		      result = ASN1_DER_ERROR;
		      goto cleanup;
		    }
		  len2 += len3;
		}

	      counter += len2;
	      move = RIGHT;
	      break;
	    case TYPE_TIME:
	      if (state == FOUND)
		{
		  result =
		    _asn1_get_time_der (der + counter, len - counter, &len2,
					temp, sizeof (temp) - 1);
		  if (result != ASN1_SUCCESS)
		    goto cleanup;

		  tlen = strlen (temp);
		  if (tlen > 0)
		    _asn1_set_value (p, temp, tlen + 1);

		  if (p == nodeFound)
		    state = EXIT;
		}
	      else
		{
		  len2 =
		    asn1_get_length_der (der + counter, len - counter, &len3);
		  if (len2 < 0)
		    {
		      result = ASN1_DER_ERROR;
		      goto cleanup;
		    }
		  len2 += len3;
		}

	      counter += len2;
	      move = RIGHT;
	      break;
	    case TYPE_OCTET_STRING:
	      len3 = len - counter;
	      if (state == FOUND)
		{
		  result = _asn1_get_octet_string (der + counter, p, &len3);
		  if (p == nodeFound)
		    state = EXIT;
		}
	      else
		result = _asn1_get_octet_string (der + counter, NULL, &len3);

	      if (result != ASN1_SUCCESS)
		goto cleanup;

	      counter += len3;
	      move = RIGHT;
	      break;
	    case TYPE_GENERALSTRING:
	      len2 =
		asn1_get_length_der (der + counter, len - counter, &len3);
	      if (len2 < 0)
		{
		  result = ASN1_DER_ERROR;
		  goto cleanup;
		}

	      if (state == FOUND)
		{
		  if (len3 + len2 > len - counter)
		    {
		      result = ASN1_DER_ERROR;
		      goto cleanup;
		    }
		  _asn1_set_value (p, der + counter, len3 + len2);

		  if (p == nodeFound)
		    state = EXIT;
		}
	      counter += len3 + len2;
	      move = RIGHT;
	      break;
	    case TYPE_BIT_STRING:
	      len2 =
		asn1_get_length_der (der + counter, len - counter, &len3);
	      if (len2 < 0)
		{
		  result = ASN1_DER_ERROR;
		  goto cleanup;
		}
	      if (state == FOUND)
		{
		  if (len3 + len2 > len - counter)
		    {
		      result = ASN1_DER_ERROR;
		      goto cleanup;
		    }
		  _asn1_set_value (p, der + counter, len3 + len2);

		  if (p == nodeFound)
		    state = EXIT;
		}
	      counter += len3 + len2;
	      move = RIGHT;
	      break;
	    case TYPE_SEQUENCE:
	    case TYPE_SET:
	      if (move == UP)
		{
		  len2 = _asn1_strtol (p->value, NULL, 10);
		  _asn1_set_value (p, NULL, 0);
		  if (len2 == -1)
		    {		/* indefinite length method */
		      if ((der[counter]) || der[counter + 1])
			{
			  result = ASN1_DER_ERROR;
			  goto cleanup;
			}
		      counter += 2;
		    }
		  else
		    {		/* definite length method */
		      if (len2 != counter)
			{
			  result = ASN1_DER_ERROR;
			  goto cleanup;
			}
		    }
		  if (p == nodeFound)
		    state = EXIT;
		  move = RIGHT;
		}
	      else
		{		/* move==DOWN || move==RIGHT */
		  if (state == OTHER_BRANCH)
		    {
		      len3 =
			asn1_get_length_der (der + counter, len - counter,
					     &len2);
		      if (len3 < 0)
			{
			  result = ASN1_DER_ERROR;
			  goto cleanup;
			}
		      counter += len2 + len3;
		      move = RIGHT;
		    }
		  else
		    {		/*  state==SAME_BRANCH or state==FOUND */
		      len3 =
			asn1_get_length_der (der + counter, len - counter,
					     &len2);
		      if (len3 < 0)
			{
			  result = ASN1_DER_ERROR;
			  goto cleanup;
			}
		      counter += len2;
		      if (len3 > 0)
			{
			  _asn1_ltostr (counter + len3, temp);
			  tlen = strlen (temp);

			  if (tlen > 0)
			    _asn1_set_value (p, temp, tlen + 1);
			  move = DOWN;
			}
		      else if (len3 == 0)
			{
			  p2 = p->down;
			  while (p2)
			    {
			      if (type_field (p2->type) != TYPE_TAG)
				{
				  p3 = p2->right;
				  asn1_delete_structure (&p2);
				  p2 = p3;
				}
			      else
				p2 = p2->right;
			    }
			  move = RIGHT;
			}
		      else
			{	/* indefinite length method */
			  _asn1_set_value (p, "-1", 3);
			  move = DOWN;
			}
		    }
		}
	      break;
	    case TYPE_SEQUENCE_OF:
	    case TYPE_SET_OF:
	      if (move == UP)
		{
		  len2 = _asn1_strtol (p->value, NULL, 10);
		  if (len2 > counter)
		    {
		      _asn1_append_sequence_set (p);
		      p = p->down;
		      while (p->right)
			p = p->right;
		      move = RIGHT;
		      continue;
		    }
		  _asn1_set_value (p, NULL, 0);
		  if (len2 != counter)
		    {
		      result = ASN1_DER_ERROR;
		      goto cleanup;
		    }

		  if (p == nodeFound)
		    state = EXIT;
		}
	      else
		{		/* move==DOWN || move==RIGHT */
		  if (state == OTHER_BRANCH)
		    {
		      len3 =
			asn1_get_length_der (der + counter, len - counter,
					     &len2);
		      if (len3 < 0)
			{
			  result = ASN1_DER_ERROR;
			  goto cleanup;
			}
		      counter += len2 + len3;
		      move = RIGHT;
		    }
		  else
		    {		/* state==FOUND or state==SAME_BRANCH */
		      len3 =
			asn1_get_length_der (der + counter, len - counter,
					     &len2);
		      if (len3 < 0)
			{
			  result = ASN1_DER_ERROR;
			  goto cleanup;
			}
		      counter += len2;
		      if (len3)
			{
			  _asn1_ltostr (counter + len3, temp);
			  tlen = strlen (temp);

			  if (tlen > 0)
			    _asn1_set_value (p, temp, tlen + 1);
			  p2 = p->down;
			  while ((type_field (p2->type) == TYPE_TAG)
				 || (type_field (p2->type) == TYPE_SIZE))
			    p2 = p2->right;
			  if (p2->right == NULL)
			    _asn1_append_sequence_set (p);
			  p = p2;
			  state = FOUND;
			}
		    }
		}

	      break;
	    case TYPE_ANY:
	      if (asn1_get_tag_der
		  (der + counter, len - counter, &class, &len2,
		   &tag) != ASN1_SUCCESS)
		{
		  result = ASN1_DER_ERROR;
		  goto cleanup;
		}

	      if (counter + len2 > len)
		{
		  result = ASN1_DER_ERROR;
		  goto cleanup;
		}

	      len4 =
		asn1_get_length_der (der + counter + len2,
				     len - counter - len2, &len3);
	      if (len4 < -1)
		{
		  result = ASN1_DER_ERROR;
		  goto cleanup;
		}

	      if (len4 != -1)
		{
		  len2 += len4;
		  if (state == FOUND)
		    {
		      _asn1_set_value_octet (p, der + counter, len2 + len3);

		      if (p == nodeFound)
			state = EXIT;
		    }
		  counter += len2 + len3;
		}
	      else
		{		/* indefinite length */
		  /* Check indefinite lenth method in an EXPLICIT TAG */
		  if ((p->type & CONST_TAG) && (der[counter - 1] == 0x80))
		    indefinite = 1;
		  else
		    indefinite = 0;

		  len2 = len - counter;
		  result =
		    _asn1_get_indefinite_length_string (der + counter, &len2);
		  if (result != ASN1_SUCCESS)
		    goto cleanup;

		  if (state == FOUND)
		    {
		      _asn1_set_value_octet (p, der + counter, len2);

		      if (p == nodeFound)
			state = EXIT;
		    }

		  counter += len2;

		  /* Check if a couple of 0x00 are present due to an EXPLICIT TAG with
		     an indefinite length method. */
		  if (indefinite)
		    {
		      if (!der[counter] && !der[counter + 1])
			{
			  counter += 2;
			}
		      else
			{
			  result = ASN1_DER_ERROR;
			  goto cleanup;
			}
		    }
		}
	      move = RIGHT;
	      break;

	    default:
	      move = (move == UP) ? RIGHT : DOWN;
	      break;
	    }
	}

      if ((p == node && move != DOWN) || (state == EXIT))
	break;

      if (move == DOWN)
	{
	  if (p->down)
	    {
	      p = p->down;

	      if (state != FOUND)
		{
		  nameLen -= strlen (p->name) + 1;
		  if (nameLen > 0)
		    {
		      if (currentName[0])
			strcat (currentName, ".");
		      strcat (currentName, p->name);
		    }
		  else
		    {
		      result = ASN1_MEM_ERROR;
		      goto cleanup;
		    }
		  if (!(strcmp (currentName, elementName)))
		    {
		      state = FOUND;
		      nodeFound = p;
		    }
		  else
		    if (!memcmp
			(currentName, elementName, strlen (currentName)))
		    state = SAME_BRANCH;
		  else
		    state = OTHER_BRANCH;
		}
	    }
	  else
	    move = RIGHT;
	}

      if ((move == RIGHT) && !(p->type & CONST_SET))
	{
	  if (p->right)
	    {
	      p = p->right;

	      if (state != FOUND)
		{
		  dot_p = char_p = currentName;
		  while ((char_p = strchr (char_p, '.')))
		    {
		      dot_p = char_p++;
		      dot_p++;
		    }

		  nameLen += strlen (currentName) - (dot_p - currentName);
		  *dot_p = 0;

		  nameLen -= strlen (p->name);
		  if (nameLen > 0)
		    strcat (currentName, p->name);
		  else
		    {
		      result = ASN1_MEM_ERROR;
		      goto cleanup;
		    }

		  if (!(strcmp (currentName, elementName)))
		    {
		      state = FOUND;
		      nodeFound = p;
		    }
		  else
		    if (!memcmp
			(currentName, elementName, strlen (currentName)))
		    state = SAME_BRANCH;
		  else
		    state = OTHER_BRANCH;
		}
	    }
	  else
	    move = UP;
	}

      if (move == UP)
	{
	  p = _asn1_find_up (p);

	  if (state != FOUND)
	    {
	      dot_p = char_p = currentName;
	      while ((char_p = strchr (char_p, '.')))
		{
		  dot_p = char_p++;
		  dot_p++;
		}

	      nameLen += strlen (currentName) - (dot_p - currentName);
	      *dot_p = 0;

	      if (!(strcmp (currentName, elementName)))
		{
		  state = FOUND;
		  nodeFound = p;
		}
	      else
		if (!memcmp (currentName, elementName, strlen (currentName)))
		state = SAME_BRANCH;
	      else
		state = OTHER_BRANCH;
	    }
	}
    }

  _asn1_delete_not_used (*structure);

  if (counter > len)
    {
      result = ASN1_DER_ERROR;
      goto cleanup;
    }

  return ASN1_SUCCESS;

cleanup:
  asn1_delete_structure (structure);
  return result;
}

/**
 * asn1_der_decoding_startEnd:
 * @element: pointer to an ASN1 element
 * @ider: vector that contains the DER encoding.
 * @len: number of bytes of *@ider: @ider[0]..@ider[len-1]
 * @name_element: an element of NAME structure.
 * @start: the position of the first byte of NAME_ELEMENT decoding
 *   (@ider[*start])
 * @end: the position of the last byte of NAME_ELEMENT decoding
 *  (@ider[*end])
 *
 * Find the start and end point of an element in a DER encoding
 * string. I mean that if you have a der encoding and you have already
 * used the function asn1_der_decoding() to fill a structure, it may
 * happen that you want to find the piece of string concerning an
 * element of the structure.
 *
 * One example is the sequence "tbsCertificate" inside an X509
 * certificate.
 *
 * Returns: %ASN1_SUCCESS if DER encoding OK, %ASN1_ELEMENT_NOT_FOUND
 *   if ELEMENT is %asn1_node EMPTY or @name_element is not a valid
 *   element, %ASN1_TAG_ERROR or %ASN1_DER_ERROR if the der encoding
 *   doesn't match the structure ELEMENT.
 **/
int
asn1_der_decoding_startEnd (asn1_node element, const void *ider, int len,
			    const char *name_element, int *start, int *end)
{
  asn1_node node, node_to_find, p, p2, p3;
  int counter, len2, len3, len4, move, ris;
  unsigned char class;
  unsigned long tag;
  int indefinite;
  const unsigned char *der = ider;

  node = element;

  if (node == NULL)
    return ASN1_ELEMENT_NOT_FOUND;

  node_to_find = asn1_find_node (node, name_element);

  if (node_to_find == NULL)
    return ASN1_ELEMENT_NOT_FOUND;

  if (node_to_find == node)
    {
      *start = 0;
      *end = len - 1;
      return ASN1_SUCCESS;
    }

  if (node->type & CONST_OPTION)
    return ASN1_GENERIC_ERROR;

  counter = 0;
  move = DOWN;
  p = node;
  while (1)
    {
      if (p == NULL)
        return ASN1_DER_ERROR;

      ris = ASN1_SUCCESS;

      if (move != UP)
	{
	  if (p->type & CONST_SET)
	    {
	      p2 = _asn1_find_up (p);
              if (p2 == NULL)
                return ASN1_DER_ERROR;

	      len2 = _asn1_strtol (p2->value, NULL, 10);
	      if (len2 == -1)
		{
		  if (!der[counter] && !der[counter + 1])
		    {
		      p = p2;
		      move = UP;
		      counter += 2;
		      continue;
		    }
		}
	      else if (counter == len2)
		{
		  p = p2;
		  move = UP;
		  continue;
		}
	      else if (counter > len2)
		return ASN1_DER_ERROR;

	      p2 = p2->down;

	      while (p2)
		{
		  if ((p2->type & CONST_SET) && (p2->type & CONST_NOT_USED))
		    {		/* CONTROLLARE */
		      if (type_field (p2->type) != TYPE_CHOICE)
			ris =
			  _asn1_extract_tag_der (p2, der + counter,
						 len - counter, &len2);
		      else
			{
			  p3 = p2->down;
                          if (p3 == NULL)
                            return ASN1_DER_ERROR;

			  ris =
			    _asn1_extract_tag_der (p3, der + counter,
						   len - counter, &len2);
			}
		      if (ris == ASN1_SUCCESS)
			{
			  p2->type &= ~CONST_NOT_USED;
			  p = p2;
			  break;
			}
		    }
		  p2 = p2->right;
		}
	      if (p2 == NULL)
		return ASN1_DER_ERROR;
	    }

	  if (p == node_to_find)
	    *start = counter;

	  if (type_field (p->type) == TYPE_CHOICE)
	    {
	      p = p->down;
              if (p == NULL)
                return ASN1_DER_ERROR;

	      ris =
		_asn1_extract_tag_der (p, der + counter, len - counter,
				       &len2);
	      if (p == node_to_find)
		*start = counter;
	    }

	  if (ris == ASN1_SUCCESS)
	    ris =
	      _asn1_extract_tag_der (p, der + counter, len - counter, &len2);
	  if (ris != ASN1_SUCCESS)
	    {
	      if (p->type & CONST_OPTION)
		{
		  p->type |= CONST_NOT_USED;
		  move = RIGHT;
		}
	      else if (p->type & CONST_DEFAULT)
		{
		  move = RIGHT;
		}
	      else
		{
		  return ASN1_TAG_ERROR;
		}
	    }
	  else
	    counter += len2;
	}

      if (ris == ASN1_SUCCESS)
	{
	  switch (type_field (p->type))
	    {
	    case TYPE_NULL:
	      if (der[counter])
		return ASN1_DER_ERROR;
	      counter++;
	      move = RIGHT;
	      break;
	    case TYPE_BOOLEAN:
	      if (der[counter++] != 1)
		return ASN1_DER_ERROR;
	      counter++;
	      move = RIGHT;
	      break;
	    case TYPE_INTEGER:
	    case TYPE_ENUMERATED:
	      len2 =
		asn1_get_length_der (der + counter, len - counter, &len3);
	      if (len2 < 0)
		return ASN1_DER_ERROR;
	      counter += len3 + len2;
	      move = RIGHT;
	      break;
	    case TYPE_OBJECT_ID:
	      len2 =
		asn1_get_length_der (der + counter, len - counter, &len3);
	      if (len2 < 0)
		return ASN1_DER_ERROR;
	      counter += len2 + len3;
	      move = RIGHT;
	      break;
	    case TYPE_TIME:
	      len2 =
		asn1_get_length_der (der + counter, len - counter, &len3);
	      if (len2 < 0)
		return ASN1_DER_ERROR;
	      counter += len2 + len3;
	      move = RIGHT;
	      break;
	    case TYPE_OCTET_STRING:
	      len3 = len - counter;
	      ris = _asn1_get_octet_string (der + counter, NULL, &len3);
	      if (ris != ASN1_SUCCESS)
		return ris;
	      counter += len3;
	      move = RIGHT;
	      break;
	    case TYPE_GENERALSTRING:
	      len2 =
		asn1_get_length_der (der + counter, len - counter, &len3);
	      if (len2 < 0)
		return ASN1_DER_ERROR;
	      counter += len3 + len2;
	      move = RIGHT;
	      break;
	    case TYPE_BIT_STRING:
	      len2 =
		asn1_get_length_der (der + counter, len - counter, &len3);
	      if (len2 < 0)
		return ASN1_DER_ERROR;
	      counter += len3 + len2;
	      move = RIGHT;
	      break;
	    case TYPE_SEQUENCE:
	    case TYPE_SET:
	      if (move != UP)
		{
		  len3 =
		    asn1_get_length_der (der + counter, len - counter, &len2);
		  if (len3 < -1)
		    return ASN1_DER_ERROR;
		  counter += len2;
		  if (len3 == 0)
		    move = RIGHT;
		  else
		    move = DOWN;
		}
	      else
		{
		  if (!der[counter] && !der[counter + 1])	/* indefinite length method */
		    counter += 2;
		  move = RIGHT;
		}
	      break;
	    case TYPE_SEQUENCE_OF:
	    case TYPE_SET_OF:
	      if (move != UP)
		{
		  len3 =
		    asn1_get_length_der (der + counter, len - counter, &len2);
		  if (len3 < -1)
		    return ASN1_DER_ERROR;
		  counter += len2;
		  if ((len3 == -1) && !der[counter] && !der[counter + 1])
		    counter += 2;
		  else if (len3)
		    {
		      p2 = p->down;
		      while ((type_field (p2->type) == TYPE_TAG) ||
			     (type_field (p2->type) == TYPE_SIZE))
			p2 = p2->right;
		      p = p2;
		    }
		}
	      else
		{
		  if (!der[counter] && !der[counter + 1])	/* indefinite length method */
		    counter += 2;
		}
	      move = RIGHT;
	      break;
	    case TYPE_ANY:
	      if (asn1_get_tag_der
		  (der + counter, len - counter, &class, &len2,
		   &tag) != ASN1_SUCCESS)
		return ASN1_DER_ERROR;
	      if (counter + len2 > len)
		return ASN1_DER_ERROR;

	      len4 =
		asn1_get_length_der (der + counter + len2,
				     len - counter - len2, &len3);
	      if (len4 < -1)
		return ASN1_DER_ERROR;

	      if (len4 != -1)
		{
		  counter += len2 + len4 + len3;
		}
	      else
		{		/* indefinite length */
		  /* Check indefinite lenth method in an EXPLICIT TAG */
		  if ((p->type & CONST_TAG) && (der[counter - 1] == 0x80))
		    indefinite = 1;
		  else
		    indefinite = 0;

		  len2 = len - counter;
		  ris =
		    _asn1_get_indefinite_length_string (der + counter, &len2);
		  if (ris != ASN1_SUCCESS)
		    return ris;
		  counter += len2;

		  /* Check if a couple of 0x00 are present due to an EXPLICIT TAG with
		     an indefinite length method. */
		  if (indefinite)
		    {
		      if (!der[counter] && !der[counter + 1])
			counter += 2;
		      else
			return ASN1_DER_ERROR;
		    }
		}
	      move = RIGHT;
	      break;
	    default:
	      move = (move == UP) ? RIGHT : DOWN;
	      break;
	    }
	}

      if ((p == node_to_find) && (move == RIGHT))
	{
	  *end = counter - 1;
	  return ASN1_SUCCESS;
	}

      if (p == node && move != DOWN)
	break;

      if (move == DOWN)
	{
	  if (p->down)
	    p = p->down;
	  else
	    move = RIGHT;
	}
      if ((move == RIGHT) && !(p->type & CONST_SET))
	{
	  if (p->right)
	    p = p->right;
	  else
	    move = UP;
	}
      if (move == UP)
	p = _asn1_find_up (p);
    }

  return ASN1_ELEMENT_NOT_FOUND;
}

/**
 * asn1_expand_any_defined_by:
 * @definitions: ASN1 definitions
 * @element: pointer to an ASN1 structure
 *
 * Expands every "ANY DEFINED BY" element of a structure created from
 * a DER decoding process (asn1_der_decoding function). The element
 * ANY must be defined by an OBJECT IDENTIFIER. The type used to
 * expand the element ANY is the first one following the definition of
 * the actual value of the OBJECT IDENTIFIER.
 *
 * Returns: %ASN1_SUCCESS if Substitution OK, %ASN1_ERROR_TYPE_ANY if
 *   some "ANY DEFINED BY" element couldn't be expanded due to a
 *   problem in OBJECT_ID -> TYPE association, or other error codes
 *   depending on DER decoding.
 **/
int
asn1_expand_any_defined_by (asn1_node definitions, asn1_node * element)
{
  char definitionsName[ASN1_MAX_NAME_SIZE], name[2 * ASN1_MAX_NAME_SIZE + 1],
    value[ASN1_MAX_NAME_SIZE];
  int retCode = ASN1_SUCCESS, result;
  int len, len2, len3;
  asn1_node p, p2, p3, aux = NULL;
  char errorDescription[ASN1_MAX_ERROR_DESCRIPTION_SIZE];

  if ((definitions == NULL) || (*element == NULL))
    return ASN1_ELEMENT_NOT_FOUND;

  strcpy (definitionsName, definitions->name);
  strcat (definitionsName, ".");

  p = *element;
  while (p)
    {

      switch (type_field (p->type))
	{
	case TYPE_ANY:
	  if ((p->type & CONST_DEFINED_BY) && (p->value))
	    {
	      /* search the "DEF_BY" element */
	      p2 = p->down;
	      while ((p2) && (type_field (p2->type) != TYPE_CONSTANT))
		p2 = p2->right;

	      if (!p2)
		{
		  retCode = ASN1_ERROR_TYPE_ANY;
		  break;
		}

	      p3 = _asn1_find_up (p);

	      if (!p3)
		{
		  retCode = ASN1_ERROR_TYPE_ANY;
		  break;
		}

	      p3 = p3->down;
	      while (p3)
		{
		  if (!(strcmp (p3->name, p2->name)))
		    break;
		  p3 = p3->right;
		}

	      if ((!p3) || (type_field (p3->type) != TYPE_OBJECT_ID) ||
		  (p3->value == NULL))
		{

		  p3 = _asn1_find_up (p);
		  p3 = _asn1_find_up (p3);

		  if (!p3)
		    {
		      retCode = ASN1_ERROR_TYPE_ANY;
		      break;
		    }

		  p3 = p3->down;

		  while (p3)
		    {
		      if (!(strcmp (p3->name, p2->name)))
			break;
		      p3 = p3->right;
		    }

		  if ((!p3) || (type_field (p3->type) != TYPE_OBJECT_ID) ||
		      (p3->value == NULL))
		    {
		      retCode = ASN1_ERROR_TYPE_ANY;
		      break;
		    }
		}

	      /* search the OBJECT_ID into definitions */
	      p2 = definitions->down;
	      while (p2)
		{
		  if ((type_field (p2->type) == TYPE_OBJECT_ID) &&
		      (p2->type & CONST_ASSIGN))
		    {
		      strcpy (name, definitionsName);
		      strcat (name, p2->name);

		      len = ASN1_MAX_NAME_SIZE;
		      result =
			asn1_read_value (definitions, name, value, &len);

		      if ((result == ASN1_SUCCESS)
			  && (!_asn1_strcmp (p3->value, value)))
			{
			  p2 = p2->right;	/* pointer to the structure to
						   use for expansion */
			  while ((p2) && (p2->type & CONST_ASSIGN))
			    p2 = p2->right;

			  if (p2)
			    {
			      strcpy (name, definitionsName);
			      strcat (name, p2->name);

			      result =
				asn1_create_element (definitions, name, &aux);
			      if (result == ASN1_SUCCESS)
				{
				  _asn1_cpy_name (aux, p);
				  len2 =
				    asn1_get_length_der (p->value,
							 p->value_len, &len3);
				  if (len2 < 0)
				    return ASN1_DER_ERROR;

				  result =
				    asn1_der_decoding (&aux, p->value + len3,
						       len2,
						       errorDescription);
				  if (result == ASN1_SUCCESS)
				    {

				      _asn1_set_right (aux, p->right);
				      _asn1_set_right (p, aux);

				      result = asn1_delete_structure (&p);
				      if (result == ASN1_SUCCESS)
					{
					  p = aux;
					  aux = NULL;
					  break;
					}
				      else
					{	/* error with asn1_delete_structure */
					  asn1_delete_structure (&aux);
					  retCode = result;
					  break;
					}
				    }
				  else
				    {	/* error with asn1_der_decoding */
				      retCode = result;
				      break;
				    }
				}
			      else
				{	/* error with asn1_create_element */
				  retCode = result;
				  break;
				}
			    }
			  else
			    {	/* error with the pointer to the structure to exapand */
			      retCode = ASN1_ERROR_TYPE_ANY;
			      break;
			    }
			}
		    }
		  p2 = p2->right;
		}		/* end while */

	      if (!p2)
		{
		  retCode = ASN1_ERROR_TYPE_ANY;
		  break;
		}

	    }
	  break;
	default:
	  break;
	}


      if (p->down)
	{
	  p = p->down;
	}
      else if (p == *element)
	{
	  p = NULL;
	  break;
	}
      else if (p->right)
	p = p->right;
      else
	{
	  while (1)
	    {
	      p = _asn1_find_up (p);
	      if (p == *element)
		{
		  p = NULL;
		  break;
		}
	      if (p->right)
		{
		  p = p->right;
		  break;
		}
	    }
	}
    }

  return retCode;
}

/**
 * asn1_expand_octet_string:
 * @definitions: ASN1 definitions
 * @element: pointer to an ASN1 structure
 * @octetName: name of the OCTECT STRING field to expand.
 * @objectName: name of the OBJECT IDENTIFIER field to use to define
 *    the type for expansion.
 *
 * Expands an "OCTET STRING" element of a structure created from a DER
 * decoding process (the asn1_der_decoding() function).  The type used
 * for expansion is the first one following the definition of the
 * actual value of the OBJECT IDENTIFIER indicated by OBJECTNAME.
 *
 * Returns: %ASN1_SUCCESS if substitution OK, %ASN1_ELEMENT_NOT_FOUND
 *   if @objectName or @octetName are not correct,
 *   %ASN1_VALUE_NOT_VALID if it wasn't possible to find the type to
 *   use for expansion, or other errors depending on DER decoding.
 **/
int
asn1_expand_octet_string (asn1_node definitions, asn1_node * element,
			  const char *octetName, const char *objectName)
{
  char name[2 * ASN1_MAX_NAME_SIZE + 1], value[ASN1_MAX_NAME_SIZE];
  int retCode = ASN1_SUCCESS, result;
  int len, len2, len3;
  asn1_node p2, aux = NULL;
  asn1_node octetNode = NULL, objectNode = NULL;
  char errorDescription[ASN1_MAX_ERROR_DESCRIPTION_SIZE];

  if ((definitions == NULL) || (*element == NULL))
    return ASN1_ELEMENT_NOT_FOUND;

  octetNode = asn1_find_node (*element, octetName);
  if (octetNode == NULL)
    return ASN1_ELEMENT_NOT_FOUND;
  if (type_field (octetNode->type) != TYPE_OCTET_STRING)
    return ASN1_ELEMENT_NOT_FOUND;
  if (octetNode->value == NULL)
    return ASN1_VALUE_NOT_FOUND;

  objectNode = asn1_find_node (*element, objectName);
  if (objectNode == NULL)
    return ASN1_ELEMENT_NOT_FOUND;

  if (type_field (objectNode->type) != TYPE_OBJECT_ID)
    return ASN1_ELEMENT_NOT_FOUND;

  if (objectNode->value == NULL)
    return ASN1_VALUE_NOT_FOUND;


  /* search the OBJECT_ID into definitions */
  p2 = definitions->down;
  while (p2)
    {
      if ((type_field (p2->type) == TYPE_OBJECT_ID) &&
	  (p2->type & CONST_ASSIGN))
	{
	  strcpy (name, definitions->name);
	  strcat (name, ".");
	  strcat (name, p2->name);

	  len = sizeof (value);
	  result = asn1_read_value (definitions, name, value, &len);

	  if ((result == ASN1_SUCCESS)
	      && (!_asn1_strcmp (objectNode->value, value)))
	    {

	      p2 = p2->right;	/* pointer to the structure to
				   use for expansion */
	      while ((p2) && (p2->type & CONST_ASSIGN))
		p2 = p2->right;

	      if (p2)
		{
		  strcpy (name, definitions->name);
		  strcat (name, ".");
		  strcat (name, p2->name);

		  result = asn1_create_element (definitions, name, &aux);
		  if (result == ASN1_SUCCESS)
		    {
		      _asn1_cpy_name (aux, octetNode);
		      len2 =
			asn1_get_length_der (octetNode->value,
					     octetNode->value_len, &len3);
		      if (len2 < 0)
			return ASN1_DER_ERROR;

		      result =
			asn1_der_decoding (&aux, octetNode->value + len3,
					   len2, errorDescription);
		      if (result == ASN1_SUCCESS)
			{

			  _asn1_set_right (aux, octetNode->right);
			  _asn1_set_right (octetNode, aux);

			  result = asn1_delete_structure (&octetNode);
			  if (result == ASN1_SUCCESS)
			    {
			      aux = NULL;
			      break;
			    }
			  else
			    {	/* error with asn1_delete_structure */
			      asn1_delete_structure (&aux);
			      retCode = result;
			      break;
			    }
			}
		      else
			{	/* error with asn1_der_decoding */
			  retCode = result;
			  break;
			}
		    }
		  else
		    {		/* error with asn1_create_element */
		      retCode = result;
		      break;
		    }
		}
	      else
		{		/* error with the pointer to the structure to exapand */
		  retCode = ASN1_VALUE_NOT_VALID;
		  break;
		}
	    }
	}

      p2 = p2->right;

    }

  if (!p2)
    retCode = ASN1_VALUE_NOT_VALID;

  return retCode;
}
