Import a very trimmed-down set of libtasn1 files: curl -L -O https://ftp.gnu.org/gnu/libtasn1/libtasn1-4.19.0.tar.gz tar xvzf libtasn1-4.19.0.tar.gz rm -rf grub-core/lib/libtasn1 mkdir -p grub-core/lib/libtasn1/lib mkdir -p grub-core/lib/libtasn1/tests cp libtasn1-4.19.0/{README.md,COPYING} grub-core/lib/libtasn1 cp libtasn1-4.19.0/lib/{coding.c,decoding.c,element.c,element.h,errors.c,gstr.c,gstr.h,int.h,parser_aux.c,parser_aux.h,structure.c,structure.h} grub-core/libtasn1/lib cp libtasn1-4.19.0/lib/includes/libtasn1.h grub-core/lib/libtasn1 cp libtasn1-4.19.0/tests/{CVE-2018-1000654-1_asn1_tab.h,CVE-2018-1000654-2_asn1_tab.h,CVE-2018-1000654.c,object-id-decoding.c,object-id-encoding.c,octet-string.c,reproducers.c,Test_overflow.c,Test_simple.c,Test_strings.c} grub-core/lib/libtasn1/tests rm -rf libtasn1-4.19.0* Signed-off-by: Daniel Axtens <dja@axtens.net> Signed-off-by: Gary Lin <glin@suse.com> Reviewed-by: Vladimir Serbinenko <phcoder@gmail.com> Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com> Tested-by: Stefan Berger <stefanb@linux.ibm.com>
1426 lines
36 KiB
C
1426 lines
36 KiB
C
/*
|
|
* 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 "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)
|
|
{
|
|
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;
|
|
}
|