/*
 * Copyright (C) 2002-2022 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: coding.c                                    */
/* Description: Functions to create a DER coding of  */
/*   an ASN1 type.                                   */
/*****************************************************/

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

#define MAX_TAG_LEN 16

#if 0 /* GRUB SKIPPED IMPORTING */
/******************************************************/
/* Function : _asn1_error_description_value_not_found */
/* Description: creates the ErrorDescription string   */
/* for the ASN1_VALUE_NOT_FOUND error.                */
/* Parameters:                                        */
/*   node: node of the tree where the value is NULL.  */
/*   ErrorDescription: string returned.               */
/* Return:                                            */
/******************************************************/
static void
_asn1_error_description_value_not_found (asn1_node node,
					 char *ErrorDescription)
{

  if (ErrorDescription == NULL)
    return;

  Estrcpy (ErrorDescription, ":: value of element '");
  _asn1_hierarchical_name (node, ErrorDescription + strlen (ErrorDescription),
			   ASN1_MAX_ERROR_DESCRIPTION_SIZE - 40);
  Estrcat (ErrorDescription, "' not found");

}
#endif

/**
 * asn1_length_der:
 * @len: value to convert.
 * @der: buffer to hold the returned encoding (may be %NULL).
 * @der_len: number of meaningful bytes of ANS (der[0]..der[der_len-1]).
 *
 * Creates the DER encoding of the provided length value.
 * The @der buffer must have enough room for the output. The maximum
 * length this function will encode is %ASN1_MAX_LENGTH_SIZE.
 *
 * To know the size of the DER encoding use a %NULL value for @der.
 **/
void
asn1_length_der (unsigned long int len, unsigned char *der, int *der_len)
{
  int k;
  unsigned char temp[ASN1_MAX_LENGTH_SIZE];
#if SIZEOF_UNSIGNED_LONG_INT > 8
  len &= 0xFFFFFFFFFFFFFFFF;
#endif

  if (len < 128)
    {
      /* short form */
      if (der != NULL)
	der[0] = (unsigned char) len;
      *der_len = 1;
    }
  else
    {
      /* Long form */
      k = 0;
      while (len)
	{
	  temp[k++] = len & 0xFF;
	  len = len >> 8;
	}
      *der_len = k + 1;
      if (der != NULL)
	{
	  der[0] = ((unsigned char) k & 0x7F) + 128;
	  while (k--)
	    der[*der_len - 1 - k] = temp[k];
	}
    }
}

/******************************************************/
/* Function : _asn1_tag_der                           */
/* Description: creates the DER coding for the CLASS  */
/* and TAG parameters.                                */
/* It is limited by the ASN1_MAX_TAG_SIZE variable    */
/* Parameters:                                        */
/*   class: value to convert.                         */
/*   tag_value: value to convert.                     */
/*   ans: string returned.                            */
/*   ans_len: number of meaningful bytes of ANS       */
/*            (ans[0]..ans[ans_len-1]).               */
/* Return:                                            */
/******************************************************/
static void
_asn1_tag_der (unsigned char class, unsigned int tag_value,
	       unsigned char ans[ASN1_MAX_TAG_SIZE], int *ans_len)
{
  int k;
  unsigned char temp[ASN1_MAX_TAG_SIZE];

  if (tag_value < 31)
    {
      /* short form */
      ans[0] = (class & 0xE0) + ((unsigned char) (tag_value & 0x1F));
      *ans_len = 1;
    }
  else
    {
      /* Long form */
      ans[0] = (class & 0xE0) + 31;
      k = 0;
      while (tag_value != 0)
	{
	  temp[k++] = tag_value & 0x7F;
	  tag_value >>= 7;

	  if (k >= ASN1_MAX_TAG_SIZE - 1)
	    break;		/* will not encode larger tags */
	}
      *ans_len = k + 1;
      while (k--)
	ans[*ans_len - 1 - k] = temp[k] + 128;
      ans[*ans_len - 1] -= 128;
    }
}

/**
 * asn1_octet_der:
 * @str: the input data.
 * @str_len: STR length (str[0]..str[*str_len-1]).
 * @der: encoded string returned.
 * @der_len: number of meaningful bytes of DER (der[0]..der[der_len-1]).
 *
 * Creates a length-value DER encoding for the input data.
 * The DER encoding of the input data will be placed in the @der variable.
 *
 * Note that the OCTET STRING tag is not included in the output.
 *
 * This function does not return any value because it is expected
 * that @der_len will contain enough bytes to store the string
 * plus the DER encoding. The DER encoding size can be obtained using
 * asn1_length_der().
 **/
void
asn1_octet_der (const unsigned char *str, int str_len,
		unsigned char *der, int *der_len)
{
  int len_len;

  if (der == NULL || str_len < 0)
    return;

  asn1_length_der (str_len, der, &len_len);
  memcpy (der + len_len, str, str_len);
  *der_len = str_len + len_len;
}


/**
 * asn1_encode_simple_der:
 * @etype: The type of the string to be encoded (ASN1_ETYPE_)
 * @str: the string data.
 * @str_len: the string length
 * @tl: the encoded tag and length
 * @tl_len: the bytes of the @tl field
 *
 * Creates the DER encoding for various simple ASN.1 types like strings etc.
 * It stores the tag and length in @tl, which should have space for at least
 * %ASN1_MAX_TL_SIZE bytes. Initially @tl_len should contain the size of @tl.
 *
 * The complete DER encoding should consist of the value in @tl appended
 * with the provided @str.
 *
 * Returns: %ASN1_SUCCESS if successful or an error value.
 **/
int
asn1_encode_simple_der (unsigned int etype, const unsigned char *str,
			unsigned int str_len, unsigned char *tl,
			unsigned int *tl_len)
{
  int tag_len, len_len;
  unsigned tlen;
  unsigned char der_tag[ASN1_MAX_TAG_SIZE];
  unsigned char der_length[ASN1_MAX_LENGTH_SIZE];
  unsigned char *p;

  if (str == NULL)
    return ASN1_VALUE_NOT_VALID;

  if (ETYPE_OK (etype) == 0)
    return ASN1_VALUE_NOT_VALID;

  /* doesn't handle constructed classes */
  if (ETYPE_CLASS (etype) != ASN1_CLASS_UNIVERSAL)
    return ASN1_VALUE_NOT_VALID;

  _asn1_tag_der (ETYPE_CLASS (etype), ETYPE_TAG (etype), der_tag, &tag_len);

  asn1_length_der (str_len, der_length, &len_len);

  if (tag_len <= 0 || len_len <= 0)
    return ASN1_VALUE_NOT_VALID;

  tlen = tag_len + len_len;

  if (*tl_len < tlen)
    return ASN1_MEM_ERROR;

  p = tl;
  memcpy (p, der_tag, tag_len);
  p += tag_len;
  memcpy (p, der_length, len_len);

  *tl_len = tlen;

  return ASN1_SUCCESS;
}

#if 0 /* GRUB SKIPPED IMPORTING */
/******************************************************/
/* Function : _asn1_time_der                          */
/* Description: creates the DER coding for a TIME     */
/* type (length included).                            */
/* Parameters:                                        */
/*   str: TIME null-terminated string.                */
/*   der: string returned.                            */
/*   der_len: number of meaningful bytes of DER       */
/*            (der[0]..der[ans_len-1]). Initially it  */
/*            if must store the lenght of DER.        */
/* Return:                                            */
/*   ASN1_MEM_ERROR when DER isn't big enough         */
/*   ASN1_SUCCESS otherwise                           */
/******************************************************/
static int
_asn1_time_der (unsigned char *str, int str_len, unsigned char *der,
		int *der_len)
{
  int len_len;
  int max_len;

  max_len = *der_len;

  asn1_length_der (str_len, (max_len > 0) ? der : NULL, &len_len);

  if ((len_len + str_len) <= max_len)
    memcpy (der + len_len, str, str_len);
  *der_len = len_len + str_len;

  if ((*der_len) > max_len)
    return ASN1_MEM_ERROR;

  return ASN1_SUCCESS;
}
#endif

/*
void
_asn1_get_utctime_der(unsigned char *der,int *der_len,unsigned char *str)
{
  int len_len,str_len;
  char temp[20];

  if(str==NULL) return;
  str_len=asn1_get_length_der(der,*der_len,&len_len);
  if (str_len<0) return;
  memcpy(temp,der+len_len,str_len);
  *der_len=str_len+len_len;
  switch(str_len)
  {
  case 11:
    temp[10]=0;
    strcat(temp,"00+0000");
    break;
  case 13:
    temp[12]=0;
    strcat(temp,"+0000");
    break;
  case 15:
    temp[15]=0;
    memmove(temp+12,temp+10,6);
    temp[10]=temp[11]='0';
    break;
  case 17:
    temp[17]=0;
    break;
  default:
    return;
  }
  strcpy(str,temp);
}
*/

static void
encode_val (uint64_t val, unsigned char *der, int max_len, int *der_len)
{
  int first, k;
  unsigned char bit7;

  first = 0;
  for (k = sizeof (val); k >= 0; k--)
    {
      bit7 = (val >> (k * 7)) & 0x7F;
      if (bit7 || first || !k)
	{
	  if (k)
	    bit7 |= 0x80;
	  if (max_len > (*der_len))
	    der[*der_len] = bit7;
	  (*der_len)++;
	  first = 1;
	}
    }
}

/******************************************************/
/* Function : _asn1_object_id_der                     */
/* Description: creates the DER coding for an         */
/* OBJECT IDENTIFIER  type (length included).         */
/* Parameters:                                        */
/*   str: OBJECT IDENTIFIER null-terminated string.   */
/*   der: string returned.                            */
/*   der_len: number of meaningful bytes of DER       */
/*            (der[0]..der[ans_len-1]). Initially it  */
/*            must store the length of DER.           */
/* Return:                                            */
/*   ASN1_MEM_ERROR when DER isn't big enough         */
/*   ASN1_SUCCESS if succesful                        */
/*   or an error value.                               */
/******************************************************/
static int
_asn1_object_id_der (const char *str, unsigned char *der, int *der_len)
{
  int len_len, counter, max_len;
  char *temp, *n_end, *n_start;
  uint64_t val, val1 = 0;
  int str_len = _asn1_strlen (str);

  max_len = *der_len;
  *der_len = 0;

  if (der == NULL && max_len > 0)
    return ASN1_VALUE_NOT_VALID;

  temp = malloc (str_len + 2);
  if (temp == NULL)
    return ASN1_MEM_ALLOC_ERROR;

  memcpy (temp, str, str_len);
  temp[str_len] = '.';
  temp[str_len + 1] = 0;

  counter = 0;
  n_start = temp;
  while ((n_end = strchr (n_start, '.')))
    {
      *n_end = 0;
      val = _asn1_strtou64 (n_start, NULL, 10);
      counter++;

      if (counter == 1)
	{
	  val1 = val;
	}
      else if (counter == 2)
	{
	  uint64_t val0;

	  if (val1 > 2)
	    {
	      free (temp);
	      return ASN1_VALUE_NOT_VALID;
	    }
	  else if ((val1 == 0 || val1 == 1) && val > 39)
	    {
	      free (temp);
	      return ASN1_VALUE_NOT_VALID;
	    }

	  val0 = 40 * val1 + val;
	  encode_val (val0, der, max_len, der_len);
	}
      else
	{
	  encode_val (val, der, max_len, der_len);
	}
      n_start = n_end + 1;
    }

  asn1_length_der (*der_len, NULL, &len_len);
  if (max_len >= (*der_len + len_len))
    {
      memmove (der + len_len, der, *der_len);
      asn1_length_der (*der_len, der, &len_len);
    }
  *der_len += len_len;

  free (temp);

  if (max_len < (*der_len))
    return ASN1_MEM_ERROR;

  return ASN1_SUCCESS;
}

/**
 * asn1_object_id_der:
 * @str: An object identifier in numeric, dot format.
 * @der: buffer to hold the returned encoding (may be %NULL).
 * @der_len: initially the size of @der; will hold the final size.
 * @flags: must be zero
 *
 * Creates the DER encoding of the provided object identifier.
 *
 * Returns: %ASN1_SUCCESS if DER encoding was OK, %ASN1_VALUE_NOT_VALID
 *   if @str is not a valid OID, %ASN1_MEM_ERROR if the @der
 *   vector isn't big enough and in this case @der_len will contain the
 *   length needed.
 **/
int
asn1_object_id_der (const char *str, unsigned char *der, int *der_len,
		    unsigned flags)
{
  unsigned char tag_der[MAX_TAG_LEN];
  int tag_len = 0, r;
  int max_len = *der_len;

  *der_len = 0;

  _asn1_tag_der (ETYPE_CLASS (ASN1_ETYPE_OBJECT_ID),
		 ETYPE_TAG (ASN1_ETYPE_OBJECT_ID), tag_der, &tag_len);

  if (max_len > tag_len)
    {
      memcpy (der, tag_der, tag_len);
    }
  max_len -= tag_len;
  der += tag_len;

  r = _asn1_object_id_der (str, der, &max_len);
  if (r == ASN1_MEM_ERROR || r == ASN1_SUCCESS)
    {
      *der_len = max_len + tag_len;
    }

  return r;
}

static const unsigned char bit_mask[] =
  { 0xFF, 0xFE, 0xFC, 0xF8, 0xF0, 0xE0, 0xC0, 0x80 };

/**
 * asn1_bit_der:
 * @str: BIT string.
 * @bit_len: number of meaningful bits in STR.
 * @der: string returned.
 * @der_len: number of meaningful bytes of DER
 *   (der[0]..der[ans_len-1]).
 *
 * Creates a length-value DER encoding for the input data
 * as it would have been for a BIT STRING.
 * The DER encoded data will be copied in @der.
 *
 * Note that the BIT STRING tag is not included in the output.
 *
 * This function does not return any value because it is expected
 * that @der_len will contain enough bytes to store the string
 * plus the DER encoding. The DER encoding size can be obtained using
 * asn1_length_der().
 **/
void
asn1_bit_der (const unsigned char *str, int bit_len,
	      unsigned char *der, int *der_len)
{
  int len_len, len_byte, len_pad;

  if (der == NULL)
    return;

  len_byte = bit_len >> 3;
  len_pad = 8 - (bit_len & 7);
  if (len_pad == 8)
    len_pad = 0;
  else
    len_byte++;
  asn1_length_der (len_byte + 1, der, &len_len);
  der[len_len] = len_pad;

  if (str)
    memcpy (der + len_len + 1, str, len_byte);
  der[len_len + len_byte] &= bit_mask[len_pad];
  *der_len = len_byte + len_len + 1;
}


#if 0 /* GRUB SKIPPED IMPORTING */
/******************************************************/
/* Function : _asn1_complete_explicit_tag             */
/* Description: add the length coding to the EXPLICIT */
/* tags.                                              */
/* Parameters:                                        */
/*   node: pointer to the tree element.               */
/*   der: string with the DER coding of the whole tree*/
/*   counter: number of meaningful bytes of DER       */
/*            (der[0]..der[*counter-1]).              */
/*   max_len: size of der vector                      */
/* Return:                                            */
/*   ASN1_MEM_ERROR if der vector isn't big enough,   */
/*   otherwise ASN1_SUCCESS.                          */
/******************************************************/
static int
_asn1_complete_explicit_tag (asn1_node node, unsigned char *der,
			     int *counter, int *max_len)
{
  asn1_node p;
  int is_tag_implicit, len2, len3;
  unsigned char temp[SIZEOF_UNSIGNED_INT];

  if (der == NULL && *max_len > 0)
    return ASN1_VALUE_NOT_VALID;

  is_tag_implicit = 0;

  if (node->type & CONST_TAG)
    {
      p = node->down;
      if (p == NULL)
	return ASN1_DER_ERROR;
      /* When there are nested tags we must complete them reverse to
         the order they were created. This is because completing a tag
         modifies all data within it, including the incomplete tags
         which store buffer positions -- simon@josefsson.org 2002-09-06
       */
      while (p->right)
	p = p->right;
      while (p && p != node->down->left)
	{
	  if (type_field (p->type) == ASN1_ETYPE_TAG)
	    {
	      if (p->type & CONST_EXPLICIT)
		{
		  len2 = strtol (p->name, NULL, 10);
		  _asn1_set_name (p, NULL);

		  asn1_length_der (*counter - len2, temp, &len3);
		  if (len3 <= (*max_len))
		    {
		      memmove (der + len2 + len3, der + len2,
			       *counter - len2);
		      memcpy (der + len2, temp, len3);
		    }
		  *max_len -= len3;
		  *counter += len3;
		  is_tag_implicit = 0;
		}
	      else
		{		/* CONST_IMPLICIT */
		  if (!is_tag_implicit)
		    {
		      is_tag_implicit = 1;
		    }
		}
	    }
	  p = p->left;
	}
    }

  if (*max_len < 0)
    return ASN1_MEM_ERROR;

  return ASN1_SUCCESS;
}
#endif

const tag_and_class_st _asn1_tags[] = {
  [ASN1_ETYPE_GENERALSTRING] =
    {ASN1_TAG_GENERALSTRING, ASN1_CLASS_UNIVERSAL, "type:GENERALSTRING"},
  [ASN1_ETYPE_NUMERIC_STRING] =
    {ASN1_TAG_NUMERIC_STRING, ASN1_CLASS_UNIVERSAL, "type:NUMERIC_STR"},
  [ASN1_ETYPE_IA5_STRING] =
    {ASN1_TAG_IA5_STRING, ASN1_CLASS_UNIVERSAL, "type:IA5_STR"},
  [ASN1_ETYPE_TELETEX_STRING] =
    {ASN1_TAG_TELETEX_STRING, ASN1_CLASS_UNIVERSAL, "type:TELETEX_STR"},
  [ASN1_ETYPE_PRINTABLE_STRING] =
    {ASN1_TAG_PRINTABLE_STRING, ASN1_CLASS_UNIVERSAL, "type:PRINTABLE_STR"},
  [ASN1_ETYPE_UNIVERSAL_STRING] =
    {ASN1_TAG_UNIVERSAL_STRING, ASN1_CLASS_UNIVERSAL, "type:UNIVERSAL_STR"},
  [ASN1_ETYPE_BMP_STRING] =
    {ASN1_TAG_BMP_STRING, ASN1_CLASS_UNIVERSAL, "type:BMP_STR"},
  [ASN1_ETYPE_UTF8_STRING] =
    {ASN1_TAG_UTF8_STRING, ASN1_CLASS_UNIVERSAL, "type:UTF8_STR"},
  [ASN1_ETYPE_VISIBLE_STRING] =
    {ASN1_TAG_VISIBLE_STRING, ASN1_CLASS_UNIVERSAL, "type:VISIBLE_STR"},
  [ASN1_ETYPE_OCTET_STRING] =
    {ASN1_TAG_OCTET_STRING, ASN1_CLASS_UNIVERSAL, "type:OCT_STR"},
  [ASN1_ETYPE_BIT_STRING] =
    {ASN1_TAG_BIT_STRING, ASN1_CLASS_UNIVERSAL, "type:BIT_STR"},
  [ASN1_ETYPE_OBJECT_ID] =
    {ASN1_TAG_OBJECT_ID, ASN1_CLASS_UNIVERSAL, "type:OBJ_ID"},
  [ASN1_ETYPE_NULL] = {ASN1_TAG_NULL, ASN1_CLASS_UNIVERSAL, "type:NULL"},
  [ASN1_ETYPE_BOOLEAN] =
    {ASN1_TAG_BOOLEAN, ASN1_CLASS_UNIVERSAL, "type:BOOLEAN"},
  [ASN1_ETYPE_INTEGER] =
    {ASN1_TAG_INTEGER, ASN1_CLASS_UNIVERSAL, "type:INTEGER"},
  [ASN1_ETYPE_ENUMERATED] =
    {ASN1_TAG_ENUMERATED, ASN1_CLASS_UNIVERSAL, "type:ENUMERATED"},
  [ASN1_ETYPE_SEQUENCE] =
    {ASN1_TAG_SEQUENCE, ASN1_CLASS_UNIVERSAL | ASN1_CLASS_STRUCTURED,
     "type:SEQUENCE"},
  [ASN1_ETYPE_SEQUENCE_OF] =
    {ASN1_TAG_SEQUENCE, ASN1_CLASS_UNIVERSAL | ASN1_CLASS_STRUCTURED,
     "type:SEQ_OF"},
  [ASN1_ETYPE_SET] =
    {ASN1_TAG_SET, ASN1_CLASS_UNIVERSAL | ASN1_CLASS_STRUCTURED, "type:SET"},
  [ASN1_ETYPE_SET_OF] =
    {ASN1_TAG_SET, ASN1_CLASS_UNIVERSAL | ASN1_CLASS_STRUCTURED,
     "type:SET_OF"},
  [ASN1_ETYPE_GENERALIZED_TIME] =
    {ASN1_TAG_GENERALIZEDTime, ASN1_CLASS_UNIVERSAL, "type:GENERALIZED_TIME"},
  [ASN1_ETYPE_UTC_TIME] =
    {ASN1_TAG_UTCTime, ASN1_CLASS_UNIVERSAL, "type:UTC_TIME"},
};

unsigned int _asn1_tags_size = sizeof (_asn1_tags) / sizeof (_asn1_tags[0]);


#if 0 /* GRUB SKIPPED IMPORTING */
/******************************************************/
/* Function : _asn1_insert_tag_der                    */
/* Description: creates the DER coding of tags of one */
/* NODE.                                              */
/* Parameters:                                        */
/*   node: pointer to the tree element.               */
/*   der: string returned                             */
/*   counter: number of meaningful bytes of DER       */
/*            (counter[0]..der[*counter-1]).          */
/*   max_len: size of der vector                      */
/* Return:                                            */
/*   ASN1_GENERIC_ERROR if the type is unknown,       */
/*   ASN1_MEM_ERROR if der vector isn't big enough,   */
/*   otherwise ASN1_SUCCESS.                          */
/******************************************************/
static int
_asn1_insert_tag_der (asn1_node node, unsigned char *der, int *counter,
		      int *max_len)
{
  asn1_node p;
  int tag_len, is_tag_implicit;
  unsigned char class, class_implicit =
    0, temp[MAX (SIZEOF_UNSIGNED_INT * 3 + 1, LTOSTR_MAX_SIZE)];
  unsigned long tag_implicit = 0;
  unsigned char tag_der[MAX_TAG_LEN];

  is_tag_implicit = 0;

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

	      if (p->type & CONST_EXPLICIT)
		{
		  if (is_tag_implicit)
		    _asn1_tag_der (class_implicit, tag_implicit, tag_der,
				   &tag_len);
		  else
		    _asn1_tag_der (class | ASN1_CLASS_STRUCTURED,
				   _asn1_strtoul (p->value, NULL, 10),
				   tag_der, &tag_len);

		  *max_len -= tag_len;
		  if (der && *max_len >= 0)
		    memcpy (der + *counter, tag_der, tag_len);
		  *counter += tag_len;

		  _asn1_ltostr (*counter, (char *) temp);
		  _asn1_set_name (p, (const char *) temp);

		  is_tag_implicit = 0;
		}
	      else
		{		/* CONST_IMPLICIT */
		  if (!is_tag_implicit)
		    {
		      if ((type_field (node->type) == ASN1_ETYPE_SEQUENCE) ||
			  (type_field (node->type) == ASN1_ETYPE_SEQUENCE_OF)
			  || (type_field (node->type) == ASN1_ETYPE_SET)
			  || (type_field (node->type) == ASN1_ETYPE_SET_OF))
			class |= ASN1_CLASS_STRUCTURED;
		      class_implicit = class;
		      tag_implicit = _asn1_strtoul (p->value, NULL, 10);
		      is_tag_implicit = 1;
		    }
		}
	    }
	  p = p->right;
	}
    }

  if (is_tag_implicit)
    {
      _asn1_tag_der (class_implicit, tag_implicit, tag_der, &tag_len);
    }
  else
    {
      unsigned type = type_field (node->type);
      switch (type)
	{
	CASE_HANDLED_ETYPES:
	  _asn1_tag_der (_asn1_tags[type].class, _asn1_tags[type].tag,
			 tag_der, &tag_len);
	  break;
	case ASN1_ETYPE_TAG:
	case ASN1_ETYPE_CHOICE:
	case ASN1_ETYPE_ANY:
	  tag_len = 0;
	  break;
	default:
	  return ASN1_GENERIC_ERROR;
	}
    }

  *max_len -= tag_len;
  if (der && *max_len >= 0)
    memcpy (der + *counter, tag_der, tag_len);
  *counter += tag_len;

  if (*max_len < 0)
    return ASN1_MEM_ERROR;

  return ASN1_SUCCESS;
}

/******************************************************/
/* Function : _asn1_ordering_set                      */
/* Description: puts the elements of a SET type in    */
/* the correct order according to DER rules.          */
/* Parameters:                                        */
/*   der: string with the DER coding.                 */
/*   node: pointer to the SET element.                */
/* Return:                                            */
/*    ASN1_SUCCESS if successful                      */
/*    or an error value.                              */
/******************************************************/
static int
_asn1_ordering_set (unsigned char *der, int der_len, asn1_node node)
{
  struct vet
  {
    int end;
    unsigned long value;
    struct vet *next, *prev;
  };

  int counter, len, len2;
  struct vet *first, *last, *p_vet, *p2_vet;
  asn1_node p;
  unsigned char class, *temp;
  unsigned long tag, t;
  int err;

  counter = 0;

  if (type_field (node->type) != ASN1_ETYPE_SET)
    return ASN1_VALUE_NOT_VALID;

  p = node->down;
  while (p && ((type_field (p->type) == ASN1_ETYPE_TAG) ||
	       (type_field (p->type) == ASN1_ETYPE_SIZE)))
    p = p->right;

  if ((p == NULL) || (p->right == NULL))
    return ASN1_SUCCESS;

  first = last = NULL;
  while (p)
    {
      p_vet = malloc (sizeof (struct vet));
      if (p_vet == NULL)
	{
	  err = ASN1_MEM_ALLOC_ERROR;
	  goto error;
	}

      p_vet->next = NULL;
      p_vet->prev = last;
      if (first == NULL)
	first = p_vet;
      else
	last->next = p_vet;
      last = p_vet;

      /* tag value calculation */
      err = asn1_get_tag_der (der + counter, der_len - counter, &class, &len2,
			      &tag);
      if (err != ASN1_SUCCESS)
	goto error;

      t = ((unsigned int) class) << 24;
      p_vet->value = t | tag;
      counter += len2;

      /* extraction and length */
      len2 = asn1_get_length_der (der + counter, der_len - counter, &len);
      if (len2 < 0)
	{
	  err = ASN1_DER_ERROR;
	  goto error;
	}
      counter += len + len2;

      p_vet->end = counter;
      p = p->right;
    }

  p_vet = first;

  while (p_vet)
    {
      p2_vet = p_vet->next;
      counter = 0;
      while (p2_vet)
	{
	  if (p_vet->value > p2_vet->value)
	    {
	      /* change position */
	      temp = malloc (p_vet->end - counter);
	      if (temp == NULL)
		{
		  err = ASN1_MEM_ALLOC_ERROR;
		  goto error;
		}

	      memcpy (temp, der + counter, p_vet->end - counter);
	      memcpy (der + counter, der + p_vet->end,
		      p2_vet->end - p_vet->end);
	      memcpy (der + counter + p2_vet->end - p_vet->end, temp,
		      p_vet->end - counter);
	      free (temp);

	      tag = p_vet->value;
	      p_vet->value = p2_vet->value;
	      p2_vet->value = tag;

	      p_vet->end = counter + (p2_vet->end - p_vet->end);
	    }
	  counter = p_vet->end;

	  p2_vet = p2_vet->next;
	  p_vet = p_vet->next;
	}

      if (p_vet != first)
	p_vet->prev->next = NULL;
      else
	first = NULL;
      free (p_vet);
      p_vet = first;
    }
  return ASN1_SUCCESS;

error:
  while (first != NULL)
    {
      p_vet = first;
      first = first->next;
      free (p_vet);
    }
  return err;
}

struct vet
{
  unsigned char *ptr;
  int size;
};

static int
setof_compar (const void *_e1, const void *_e2)
{
  unsigned length;
  const struct vet *e1 = _e1, *e2 = _e2;
  int rval;

  /* The encodings of the component values of a set-of value shall
   * appear in ascending order, the encodings being compared
   * as octet strings with the shorter components being
   * padded at their trailing end with 0-octets.
   * The padding octets are for comparison purposes and
   * do not appear in the encodings.
   */
  length = MIN (e1->size, e2->size);

  rval = memcmp (e1->ptr, e2->ptr, length);
  if (rval == 0 && e1->size != e2->size)
    {
      if (e1->size > e2->size)
	rval = 1;
      else if (e2->size > e1->size)
	rval = -1;
    }

  return rval;
}

/******************************************************/
/* Function : _asn1_ordering_set_of                   */
/* Description: puts the elements of a SET OF type in */
/* the correct order according to DER rules.          */
/* Parameters:                                        */
/*   der: string with the DER coding.                 */
/*   node: pointer to the SET OF element.             */
/* Return:                                            */
/*    ASN1_SUCCESS if successful                      */
/*    or an error value.                              */
/******************************************************/
static int
_asn1_ordering_set_of (unsigned char *der, int der_len, asn1_node node)
{
  int counter, len, len2;
  struct vet *list = NULL, *tlist;
  unsigned list_size = 0;
  struct vet *p_vet;
  asn1_node p;
  unsigned char class;
  unsigned i;
  unsigned char *out = NULL;
  int err;

  counter = 0;

  if (type_field (node->type) != ASN1_ETYPE_SET_OF)
    return ASN1_VALUE_NOT_VALID;

  p = node->down;
  while (p && ((type_field (p->type) == ASN1_ETYPE_TAG) ||
	       (type_field (p->type) == ASN1_ETYPE_SIZE)))
    p = p->right;
  if (p == NULL)
    return ASN1_VALUE_NOT_VALID;
  p = p->right;

  if ((p == NULL) || (p->right == NULL))
    return ASN1_SUCCESS;

  while (p)
    {
      list_size++;
      tlist = realloc (list, list_size * sizeof (struct vet));
      if (tlist == NULL)
	{
	  err = ASN1_MEM_ALLOC_ERROR;
	  goto error;
	}
      list = tlist;
      p_vet = &list[list_size - 1];

      p_vet->ptr = der + counter;
      p_vet->size = 0;

      /* extraction of tag and length */
      if (der_len - counter > 0)
	{
	  err = asn1_get_tag_der (der + counter, der_len - counter, &class,
				  &len, NULL);
	  if (err != ASN1_SUCCESS)
	    goto error;
	  counter += len;
	  p_vet->size += len;

	  len2 = asn1_get_length_der (der + counter, der_len - counter, &len);
	  if (len2 < 0)
	    {
	      err = ASN1_DER_ERROR;
	      goto error;
	    }
	  counter += len + len2;
	  p_vet->size += len + len2;

	}
      else
	{
	  err = ASN1_DER_ERROR;
	  goto error;
	}
      p = p->right;
    }

  if (counter > der_len)
    {
      err = ASN1_DER_ERROR;
      goto error;
    }

  qsort (list, list_size, sizeof (struct vet), setof_compar);

  out = malloc (der_len);
  if (out == NULL)
    {
      err = ASN1_MEM_ERROR;
      goto error;
    }

  /* the sum of p_vet->size == der_len */
  counter = 0;
  for (i = 0; i < list_size; i++)
    {
      p_vet = &list[i];
      memcpy (out + counter, p_vet->ptr, p_vet->size);
      counter += p_vet->size;
    }
  memcpy (der, out, der_len);
  free (out);

  err = ASN1_SUCCESS;

error:
  free (list);
  return err;
}

/**
 * asn1_der_coding:
 * @element: pointer to an ASN1 element
 * @name: the name of the structure you want to encode (it must be
 *   inside *POINTER).
 * @ider: vector that will contain the DER encoding. DER must be a
 *   pointer to memory cells already allocated.
 * @len: number of bytes of *@ider: @ider[0]..@ider[len-1], Initialy
 *   holds the sizeof of der vector.
 * @ErrorDescription: return the error description or an empty
 *   string if success.
 *
 * Creates the DER encoding for the NAME structure (inside *POINTER
 * structure).
 *
 * Returns: %ASN1_SUCCESS if DER encoding OK, %ASN1_ELEMENT_NOT_FOUND
 *   if @name is not a valid element, %ASN1_VALUE_NOT_FOUND if there
 *   is an element without a value, %ASN1_MEM_ERROR if the @ider
 *   vector isn't big enough and in this case @len will contain the
 *   length needed.
 **/
int
asn1_der_coding (asn1_node_const element, const char *name, void *ider,
		 int *len, char *ErrorDescription)
{
  asn1_node node, p, p2;
  unsigned char temp[MAX (LTOSTR_MAX_SIZE, SIZEOF_UNSIGNED_LONG_INT * 3 + 1)];
  int counter, counter_old, len2, len3, move, max_len, max_len_old;
  int err;
  unsigned char *der = ider;
  unsigned char dummy;

  if (ErrorDescription)
    ErrorDescription[0] = 0;

  node = asn1_find_node (element, name);
  if (node == NULL)
    return ASN1_ELEMENT_NOT_FOUND;

  /* Node is now a locally allocated variable.
   * That is because in some point we modify the
   * structure, and I don't know why! --nmav
   */
  node = _asn1_copy_structure3 (node);
  if (node == NULL)
    return ASN1_ELEMENT_NOT_FOUND;

  max_len = *len;

  if (der == NULL && max_len > 0)
    {
      err = ASN1_VALUE_NOT_VALID;
      goto error;
    }

  counter = 0;
  move = DOWN;
  p = node;

  while (1)
    {

      counter_old = counter;
      max_len_old = max_len;
      if (move != UP)
	{
	  p->start = counter;
	  err = _asn1_insert_tag_der (p, der, &counter, &max_len);
	  if (err != ASN1_SUCCESS && err != ASN1_MEM_ERROR)
	    goto error;
	}
      switch (type_field (p->type))
	{
	case ASN1_ETYPE_NULL:
	  max_len--;
	  if (der != NULL && max_len >= 0)
	    der[counter] = 0;
	  counter++;
	  move = RIGHT;
	  break;
	case ASN1_ETYPE_BOOLEAN:
	  if ((p->type & CONST_DEFAULT) && (p->value == NULL))
	    {
	      counter = counter_old;
	      max_len = max_len_old;
	    }
	  else
	    {
	      if (p->value == NULL)
		{
		  _asn1_error_description_value_not_found (p,
							   ErrorDescription);
		  err = ASN1_VALUE_NOT_FOUND;
		  goto error;
		}
	      max_len -= 2;
	      if (der != NULL && max_len >= 0)
		{
		  der[counter++] = 1;
		  if (p->value[0] == 'F')
		    der[counter++] = 0;
		  else
		    der[counter++] = 0xFF;
		}
	      else
		counter += 2;
	    }
	  move = RIGHT;
	  break;
	case ASN1_ETYPE_INTEGER:
	case ASN1_ETYPE_ENUMERATED:
	  if ((p->type & CONST_DEFAULT) && (p->value == NULL))
	    {
	      counter = counter_old;
	      max_len = max_len_old;
	    }
	  else
	    {
	      if (p->value == NULL)
		{
		  _asn1_error_description_value_not_found (p,
							   ErrorDescription);
		  err = ASN1_VALUE_NOT_FOUND;
		  goto error;
		}
	      len2 = asn1_get_length_der (p->value, p->value_len, &len3);
	      if (len2 < 0)
		{
		  err = ASN1_DER_ERROR;
		  goto error;
		}
	      max_len -= len2 + len3;
	      if (der != NULL && max_len >= 0)
		memcpy (der + counter, p->value, len3 + len2);
	      counter += len3 + len2;
	    }
	  move = RIGHT;
	  break;
	case ASN1_ETYPE_OBJECT_ID:
	  if ((p->type & CONST_DEFAULT) && (p->value == NULL))
	    {
	      counter = counter_old;
	      max_len = max_len_old;
	    }
	  else
	    {
	      if (p->value == NULL)
		{
		  _asn1_error_description_value_not_found (p,
							   ErrorDescription);
		  err = ASN1_VALUE_NOT_FOUND;
		  goto error;
		}
	      len2 = max_len;
	      err =
		_asn1_object_id_der ((char *) p->value,
				     der ? der + counter : &dummy, &len2);
	      if (err != ASN1_SUCCESS && err != ASN1_MEM_ERROR)
		goto error;

	      max_len -= len2;
	      counter += len2;
	    }
	  move = RIGHT;
	  break;
	case ASN1_ETYPE_GENERALIZED_TIME:
	case ASN1_ETYPE_UTC_TIME:
	  if (p->value == NULL)
	    {
	      _asn1_error_description_value_not_found (p, ErrorDescription);
	      err = ASN1_VALUE_NOT_FOUND;
	      goto error;
	    }
	  len2 = max_len;
	  err =
	    _asn1_time_der (p->value, p->value_len,
			    der ? der + counter : &dummy, &len2);
	  if (err != ASN1_SUCCESS && err != ASN1_MEM_ERROR)
	    goto error;

	  max_len -= len2;
	  counter += len2;
	  move = RIGHT;
	  break;
	case ASN1_ETYPE_OCTET_STRING:
	case ASN1_ETYPE_GENERALSTRING:
	case ASN1_ETYPE_NUMERIC_STRING:
	case ASN1_ETYPE_IA5_STRING:
	case ASN1_ETYPE_TELETEX_STRING:
	case ASN1_ETYPE_PRINTABLE_STRING:
	case ASN1_ETYPE_UNIVERSAL_STRING:
	case ASN1_ETYPE_BMP_STRING:
	case ASN1_ETYPE_UTF8_STRING:
	case ASN1_ETYPE_VISIBLE_STRING:
	case ASN1_ETYPE_BIT_STRING:
	  if (p->value == NULL)
	    {
	      _asn1_error_description_value_not_found (p, ErrorDescription);
	      err = ASN1_VALUE_NOT_FOUND;
	      goto error;
	    }
	  len2 = asn1_get_length_der (p->value, p->value_len, &len3);
	  if (len2 < 0)
	    {
	      err = ASN1_DER_ERROR;
	      goto error;
	    }
	  max_len -= len2 + len3;
	  if (der != NULL && max_len >= 0)
	    memcpy (der + counter, p->value, len3 + len2);
	  counter += len3 + len2;
	  move = RIGHT;
	  break;
	case ASN1_ETYPE_SEQUENCE:
	case ASN1_ETYPE_SET:
	  if (move != UP)
	    {
	      p->tmp_ival = counter;
	      if (p->down == NULL)
		{
		  move = UP;
		  continue;
		}
	      else
		{
		  p2 = p->down;
		  while (p2 && (type_field (p2->type) == ASN1_ETYPE_TAG))
		    p2 = p2->right;
		  if (p2)
		    {
		      p = p2;
		      move = RIGHT;
		      continue;
		    }
		  move = UP;
		  continue;
		}
	    }
	  else
	    {			/* move==UP */
	      len2 = p->tmp_ival;
	      p->tmp_ival = 0;
	      if ((type_field (p->type) == ASN1_ETYPE_SET) && (max_len >= 0))
		{
		  err =
		    _asn1_ordering_set (der ? der + len2 : &dummy,
					counter - len2, p);
		  if (err != ASN1_SUCCESS)
		    goto error;
		}
	      asn1_length_der (counter - len2, temp, &len3);
	      max_len -= len3;
	      if (der != NULL && max_len >= 0)
		{
		  memmove (der + len2 + len3, der + len2, counter - len2);
		  memcpy (der + len2, temp, len3);
		}
	      counter += len3;
	      move = RIGHT;
	    }
	  break;
	case ASN1_ETYPE_SEQUENCE_OF:
	case ASN1_ETYPE_SET_OF:
	  if (move != UP)
	    {
	      p->tmp_ival = counter;
	      p = p->down;
	      while ((type_field (p->type) == ASN1_ETYPE_TAG)
		     || (type_field (p->type) == ASN1_ETYPE_SIZE))
		p = p->right;
	      if (p->right)
		{
		  p = p->right;
		  move = RIGHT;
		  continue;
		}
	      else
		p = _asn1_find_up (p);
	      move = UP;
	    }
	  if (move == UP)
	    {
	      len2 = p->tmp_ival;
	      p->tmp_ival = 0;
	      if ((type_field (p->type) == ASN1_ETYPE_SET_OF)
		  && (counter - len2 > 0) && (max_len >= 0))
		{
		  err =
		    _asn1_ordering_set_of (der ? der + len2 : &dummy,
					   counter - len2, p);
		  if (err != ASN1_SUCCESS)
		    goto error;
		}
	      asn1_length_der (counter - len2, temp, &len3);
	      max_len -= len3;
	      if (der != NULL && max_len >= 0)
		{
		  memmove (der + len2 + len3, der + len2, counter - len2);
		  memcpy (der + len2, temp, len3);
		}
	      counter += len3;
	      move = RIGHT;
	    }
	  break;
	case ASN1_ETYPE_ANY:
	  if (p->value == NULL)
	    {
	      _asn1_error_description_value_not_found (p, ErrorDescription);
	      err = ASN1_VALUE_NOT_FOUND;
	      goto error;
	    }
	  len2 = asn1_get_length_der (p->value, p->value_len, &len3);
	  if (len2 < 0)
	    {
	      err = ASN1_DER_ERROR;
	      goto error;
	    }
	  max_len -= len2;
	  if (der != NULL && max_len >= 0)
	    memcpy (der + counter, p->value + len3, len2);
	  counter += len2;
	  move = RIGHT;
	  break;
	default:
	  move = (move == UP) ? RIGHT : DOWN;
	  break;
	}

      if ((move != DOWN) && (counter != counter_old))
	{
	  p->end = counter - 1;
	  err = _asn1_complete_explicit_tag (p, der, &counter, &max_len);
	  if (err != ASN1_SUCCESS && err != ASN1_MEM_ERROR)
	    goto error;
	}

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

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

  *len = counter;

  if (max_len < 0)
    {
      err = ASN1_MEM_ERROR;
      goto error;
    }

  err = ASN1_SUCCESS;

error:
  asn1_delete_structure (&node);
  return err;
}

#endif
