blob: e76114d8a461908b2562b6747e88bb9138c653bd [file] [log] [blame]
/*
* Copyright (C) 2002-2014 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 "minmax.h"
#include <structure.h>
#define MAX_TAG_LEN 16
/******************************************************/
/* 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");
}
/**
* 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;
}
/******************************************************/
/* 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;
}
/*
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;
}
/******************************************************/
/* 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;
}
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]);
/******************************************************/
/* 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)
return ASN1_VALUE_NOT_VALID;
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;
}