blob: ff04eb778cba5a5388772cadbd7b0c1c16ffe847 [file] [log] [blame]
/*
* Copyright (C) 2002-2016 Free Software Foundation, Inc.
*
* This file is part of LIBTASN1.
*
* The LIBTASN1 library is free software; you can redistribute it
* and/or modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA
*/
/*****************************************************/
/* File: decoding.c */
/* Description: Functions to manage DER decoding */
/*****************************************************/
#include <int.h>
#include <parser_aux.h>
#include <gstr.h>
#include <structure.h>
#include <element.h>
#include <limits.h>
#include <intprops.h>
#include <c-ctype.h>
#ifdef DEBUG
# define warn() fprintf(stderr, "%s: %d\n", __func__, __LINE__)
#else
# define warn()
#endif
#define IS_ERR(len, flags) (len < -1 || ((flags & ASN1_DECODE_FLAG_STRICT_DER) && len < 0))
#define HAVE_TWO(x) (x>=2?1:0)
/* Decoding flags (dflags) used in several decoding functions.
* DECODE_FLAG_HAVE_TAG: The provided buffer includes a tag
* DECODE_FLAG_CONSTRUCTED: The provided buffer is of indefinite encoding (useful
* when no tags are present).
* DECODE_FLAG_LEVEL1: Internal flag to indicate a level of recursion for BER strings.
* DECODE_FLAG_LEVEL2: Internal flag to indicate two levels of recursion for BER strings.
* DECODE_FLAG_LEVEL3: Internal flag to indicate three levels of recursion for BER strings.
* This is the maximum levels of recursion possible to prevent stack
* exhaustion.
*/
#define DECODE_FLAG_HAVE_TAG 1
#define DECODE_FLAG_CONSTRUCTED (1<<1)
#define DECODE_FLAG_LEVEL1 (1<<2)
#define DECODE_FLAG_LEVEL2 (1<<3)
#define DECODE_FLAG_LEVEL3 (1<<4)
#define DECR_LEN(l, s) do { \
l -= s; \
if (l < 0) { \
warn(); \
result = ASN1_DER_ERROR; \
goto cleanup; \
} \
} while (0)
static int
_asn1_get_indefinite_length_string (const unsigned char *der, int der_len, int *len);
static int
_asn1_decode_simple_ber (unsigned int etype, const unsigned char *der,
unsigned int _der_len, unsigned char **str,
unsigned int *str_len, unsigned int *ber_len,
unsigned dflags);
static int
_asn1_decode_simple_der (unsigned int etype, const unsigned char *der,
unsigned int _der_len, const unsigned char **str,
unsigned int *str_len, unsigned dflags);
static void
_asn1_error_description_tag_error (asn1_node node, char *ErrorDescription)
{
Estrcpy (ErrorDescription, ":: tag error near element '");
_asn1_hierarchical_name (node, ErrorDescription + strlen (ErrorDescription),
ASN1_MAX_ERROR_DESCRIPTION_SIZE - 40);
Estrcat (ErrorDescription, "'");
}
/**
* asn1_get_length_der:
* @der: DER data to decode.
* @der_len: Length of DER data to decode.
* @len: Output variable containing the length of the DER length field.
*
* Extract a length field from DER data.
*
* Returns: Return the decoded length value, or -1 on indefinite
* length, or -2 when the value was too big to fit in a int, or -4
* when the decoded length value plus @len would exceed @der_len.
**/
long
asn1_get_length_der (const unsigned char *der, int der_len, int *len)
{
unsigned int ans;
int k, punt, sum;
*len = 0;
if (der_len <= 0)
return 0;
if (!(der[0] & 128))
{
/* short form */
*len = 1;
ans = der[0];
}
else
{
/* Long form */
k = der[0] & 0x7F;
punt = 1;
if (k)
{ /* definite length method */
ans = 0;
while (punt <= k && punt < der_len)
{
if (INT_MULTIPLY_OVERFLOW (ans, 256))
return -2;
ans *= 256;
if (INT_ADD_OVERFLOW (ans, ((unsigned) der[punt])))
return -2;
ans += der[punt];
punt++;
}
}
else
{ /* indefinite length method */
*len = punt;
return -1;
}
*len = punt;
}
sum = ans;
if (ans >= INT_MAX || INT_ADD_OVERFLOW (sum, (*len)))
return -2;
sum += *len;
if (sum > der_len)
return -4;
return ans;
}
/**
* asn1_get_tag_der:
* @der: DER data to decode.
* @der_len: Length of DER data to decode.
* @cls: Output variable containing decoded class.
* @len: Output variable containing the length of the DER TAG data.
* @tag: Output variable containing the decoded tag (may be %NULL).
*
* Decode the class and TAG from DER code.
*
* Returns: Returns %ASN1_SUCCESS on success, or an error.
**/
int
asn1_get_tag_der (const unsigned char *der, int der_len,
unsigned char *cls, int *len, unsigned long *tag)
{
unsigned int ris;
int punt;
if (der == NULL || der_len < 2 || len == NULL)
return ASN1_DER_ERROR;
*cls = der[0] & 0xE0;
if ((der[0] & 0x1F) != 0x1F)
{
/* short form */
*len = 1;
ris = der[0] & 0x1F;
}
else
{
/* Long form */
punt = 1;
ris = 0;
while (punt < der_len && der[punt] & 128)
{
if (INT_MULTIPLY_OVERFLOW (ris, 128))
return ASN1_DER_ERROR;
ris *= 128;
if (INT_ADD_OVERFLOW (ris, ((unsigned) (der[punt] & 0x7F))))
return ASN1_DER_ERROR;
ris += (der[punt] & 0x7F);
punt++;
}
if (punt >= der_len)
return ASN1_DER_ERROR;
if (INT_MULTIPLY_OVERFLOW (ris, 128))
return ASN1_DER_ERROR;
ris *= 128;
if (INT_ADD_OVERFLOW (ris, ((unsigned) (der[punt] & 0x7F))))
return ASN1_DER_ERROR;
ris += (der[punt] & 0x7F);
punt++;
*len = punt;
}
if (tag)
*tag = ris;
return ASN1_SUCCESS;
}
/**
* asn1_get_length_ber:
* @ber: BER data to decode.
* @ber_len: Length of BER data to decode.
* @len: Output variable containing the length of the BER length field.
*
* Extract a length field from BER data. The difference to
* asn1_get_length_der() is that this function will return a length
* even if the value has indefinite encoding.
*
* Returns: Return the decoded length value, or negative value when
* the value was too big.
*
* Since: 2.0
**/
long
asn1_get_length_ber (const unsigned char *ber, int ber_len, int *len)
{
int ret;
long err;
ret = asn1_get_length_der (ber, ber_len, len);
if (ret == -1 && ber_len > 1)
{ /* indefinite length method */
err = _asn1_get_indefinite_length_string (ber + 1, ber_len-1, &ret);
if (err != ASN1_SUCCESS)
return -3;
}
return ret;
}
/**
* asn1_get_octet_der:
* @der: DER data to decode containing the OCTET SEQUENCE.
* @der_len: The length of the @der data to decode.
* @ret_len: Output variable containing the encoded length of the DER data.
* @str: Pre-allocated output buffer to put decoded OCTET SEQUENCE in.
* @str_size: Length of pre-allocated output buffer.
* @str_len: Output variable containing the length of the contents of the OCTET SEQUENCE.
*
* Extract an OCTET SEQUENCE from DER data. Note that this function
* expects the DER data past the tag field, i.e., the length and
* content octets.
*
* Returns: Returns %ASN1_SUCCESS on success, or an error.
**/
int
asn1_get_octet_der (const unsigned char *der, int der_len,
int *ret_len, unsigned char *str, int str_size,
int *str_len)
{
int len_len = 0;
if (der_len <= 0)
return ASN1_GENERIC_ERROR;
*str_len = asn1_get_length_der (der, der_len, &len_len);
if (*str_len < 0)
return ASN1_DER_ERROR;
*ret_len = *str_len + len_len;
if (str_size >= *str_len)
{
if (*str_len > 0 && str != NULL)
memcpy (str, der + len_len, *str_len);
}
else
{
return ASN1_MEM_ERROR;
}
return ASN1_SUCCESS;
}
/*-
* _asn1_get_time_der:
* @type: %ASN1_ETYPE_GENERALIZED_TIME or %ASN1_ETYPE_UTC_TIME
* @der: DER data to decode containing the time
* @der_len: Length of DER data to decode.
* @ret_len: Output variable containing the length of the DER data.
* @str: Pre-allocated output buffer to put the textual time in.
* @str_size: Length of pre-allocated output buffer.
* @flags: Zero or %ASN1_DECODE_FLAG_STRICT_DER
*
* Performs basic checks in the DER encoded time object and returns its textual form.
* The textual form will be in the YYYYMMDD000000Z format for GeneralizedTime
* and YYMMDD000000Z for UTCTime.
*
* Returns: %ASN1_SUCCESS on success, or an error.
-*/
static int
_asn1_get_time_der (unsigned type, const unsigned char *der, int der_len, int *ret_len,
char *str, int str_size, unsigned flags)
{
int len_len, str_len;
unsigned i;
unsigned sign_count = 0;
unsigned dot_count = 0;
const unsigned char *p;
if (der_len <= 0 || str == NULL)
return ASN1_DER_ERROR;
str_len = asn1_get_length_der (der, der_len, &len_len);
if (str_len <= 0 || str_size < str_len)
return ASN1_DER_ERROR;
/* perform some sanity checks on the data */
if (str_len < 8)
{
warn();
return ASN1_TIME_ENCODING_ERROR;
}
if ((flags & ASN1_DECODE_FLAG_STRICT_DER) && !(flags & ASN1_DECODE_FLAG_ALLOW_INCORRECT_TIME))
{
p = &der[len_len];
for (i=0;i<(unsigned)(str_len-1);i++)
{
if (c_isdigit(p[i]) == 0)
{
if (type == ASN1_ETYPE_GENERALIZED_TIME)
{
/* tolerate lax encodings */
if (p[i] == '.' && dot_count == 0)
{
dot_count++;
continue;
}
/* This is not really valid DER, but there are
* structures using that */
if (!(flags & ASN1_DECODE_FLAG_STRICT_DER) &&
(p[i] == '+' || p[i] == '-') && sign_count == 0)
{
sign_count++;
continue;
}
}
warn();
return ASN1_TIME_ENCODING_ERROR;
}
}
if (sign_count == 0 && p[str_len-1] != 'Z')
{
warn();
return ASN1_TIME_ENCODING_ERROR;
}
}
memcpy (str, der + len_len, str_len);
str[str_len] = 0;
*ret_len = str_len + len_len;
return ASN1_SUCCESS;
}
/**
* asn1_get_object_id_der:
* @der: DER data to decode containing the OBJECT IDENTIFIER
* @der_len: Length of DER data to decode.
* @ret_len: Output variable containing the length of the DER data.
* @str: Pre-allocated output buffer to put the textual object id in.
* @str_size: Length of pre-allocated output buffer.
*
* Converts a DER encoded object identifier to its textual form. This
* function expects the DER object identifier without the tag.
*
* Returns: %ASN1_SUCCESS on success, or an error.
**/
int
asn1_get_object_id_der (const unsigned char *der, int der_len, int *ret_len,
char *str, int str_size)
{
int len_len, len, k;
int leading, parsed;
char temp[LTOSTR_MAX_SIZE];
uint64_t val, val1, val0;
*ret_len = 0;
if (str && str_size > 0)
str[0] = 0; /* no oid */
if (str == NULL || der_len <= 0)
return ASN1_GENERIC_ERROR;
len = asn1_get_length_der (der, der_len, &len_len);
if (len <= 0 || len + len_len > der_len)
return ASN1_DER_ERROR;
/* leading octet can never be 0x80 */
if (der[len_len] == 0x80)
return ASN1_DER_ERROR;
val0 = 0;
for (k = 0; k < len; k++)
{
if (INT_LEFT_SHIFT_OVERFLOW (val0, 7))
return ASN1_DER_ERROR;
val0 <<= 7;
val0 |= der[len_len + k] & 0x7F;
if (!(der[len_len + k] & 0x80))
break;
}
parsed = ++k;
/* val0 = (X*40) + Y, X={0,1,2}, Y<=39 when X={0,1} */
/* X = val, Y = val1 */
/* check if X == 0 */
val = 0;
val1 = val0;
if (val1 > 39)
{
val = 1;
val1 = val0 - 40;
if (val1 > 39)
{
val = 2;
val1 = val0 - 80;
}
}
_asn1_str_cpy (str, str_size, _asn1_ltostr (val, temp));
_asn1_str_cat (str, str_size, ".");
_asn1_str_cat (str, str_size, _asn1_ltostr (val1, temp));
val = 0;
leading = 1;
for (k = parsed; k < len; k++)
{
/* X.690 mandates that the leading byte must never be 0x80
*/
if (leading != 0 && der[len_len + k] == 0x80)
return ASN1_DER_ERROR;
leading = 0;
/* check for wrap around */
if (INT_LEFT_SHIFT_OVERFLOW (val, 7))
return ASN1_DER_ERROR;
val = val << 7;
val |= der[len_len + k] & 0x7F;
if (!(der[len_len + k] & 0x80))
{
_asn1_str_cat (str, str_size, ".");
_asn1_str_cat (str, str_size, _asn1_ltostr (val, temp));
val = 0;
leading = 1;
}
}
if (INT_ADD_OVERFLOW (len, len_len))
return ASN1_DER_ERROR;
*ret_len = len + len_len;
return ASN1_SUCCESS;
}
/**
* asn1_get_bit_der:
* @der: DER data to decode containing the BIT SEQUENCE.
* @der_len: Length of DER data to decode.
* @ret_len: Output variable containing the length of the DER data.
* @str: Pre-allocated output buffer to put decoded BIT SEQUENCE in.
* @str_size: Length of pre-allocated output buffer.
* @bit_len: Output variable containing the size of the BIT SEQUENCE.
*
* Extract a BIT SEQUENCE from DER data.
*
* Returns: %ASN1_SUCCESS on success, or an error.
**/
int
asn1_get_bit_der (const unsigned char *der, int der_len,
int *ret_len, unsigned char *str, int str_size,
int *bit_len)
{
int len_len = 0, len_byte;
if (der_len <= 0)
return ASN1_GENERIC_ERROR;
len_byte = asn1_get_length_der (der, der_len, &len_len) - 1;
if (len_byte < 0)
return ASN1_DER_ERROR;
*ret_len = len_byte + len_len + 1;
*bit_len = len_byte * 8 - der[len_len];
if (*bit_len < 0)
return ASN1_DER_ERROR;
if (str_size >= len_byte)
{
if (len_byte > 0 && str)
memcpy (str, der + len_len + 1, len_byte);
}
else
{
return ASN1_MEM_ERROR;
}
return ASN1_SUCCESS;
}
/* tag_len: the total tag length (explicit+inner)
* inner_tag_len: the inner_tag length
*/
static int
_asn1_extract_tag_der (asn1_node node, const unsigned char *der, int der_len,
int *tag_len, int *inner_tag_len, unsigned flags)
{
asn1_node p;
int counter, len2, len3, is_tag_implicit;
int result;
unsigned long tag, tag_implicit = 0;
unsigned char class, class2, class_implicit = 0;
if (der_len <= 0)
return ASN1_GENERIC_ERROR;
counter = is_tag_implicit = 0;
if (node->type & CONST_TAG)
{
p = node->down;
while (p)
{
if (type_field (p->type) == ASN1_ETYPE_TAG)
{
if (p->type & CONST_APPLICATION)
class2 = ASN1_CLASS_APPLICATION;
else if (p->type & CONST_UNIVERSAL)
class2 = ASN1_CLASS_UNIVERSAL;
else if (p->type & CONST_PRIVATE)
class2 = ASN1_CLASS_PRIVATE;
else
class2 = ASN1_CLASS_CONTEXT_SPECIFIC;
if (p->type & CONST_EXPLICIT)
{
if (asn1_get_tag_der
(der + counter, der_len, &class, &len2,
&tag) != ASN1_SUCCESS)
return ASN1_DER_ERROR;
DECR_LEN(der_len, len2);
counter += len2;
if (flags & ASN1_DECODE_FLAG_STRICT_DER)
len3 =
asn1_get_length_der (der + counter, der_len,
&len2);
else
len3 =
asn1_get_length_ber (der + counter, der_len,
&len2);
if (len3 < 0)
return ASN1_DER_ERROR;
DECR_LEN(der_len, len2);
counter += len2;
if (!is_tag_implicit)
{
if ((class != (class2 | ASN1_CLASS_STRUCTURED)) ||
(tag != strtoul ((char *) p->value, NULL, 10)))
return ASN1_TAG_ERROR;
}
else
{ /* ASN1_TAG_IMPLICIT */
if ((class != class_implicit) || (tag != tag_implicit))
return ASN1_TAG_ERROR;
}
is_tag_implicit = 0;
}
else
{ /* ASN1_TAG_IMPLICIT */
if (!is_tag_implicit)
{
if ((type_field (node->type) == 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))
class2 |= ASN1_CLASS_STRUCTURED;
class_implicit = class2;
tag_implicit = strtoul ((char *) p->value, NULL, 10);
is_tag_implicit = 1;
}
}
}
p = p->right;
}
}
if (is_tag_implicit)
{
if (asn1_get_tag_der
(der + counter, der_len, &class, &len2,
&tag) != ASN1_SUCCESS)
return ASN1_DER_ERROR;
DECR_LEN(der_len, len2);
if ((class != class_implicit) || (tag != tag_implicit))
{
if (type_field (node->type) == ASN1_ETYPE_OCTET_STRING)
{
class_implicit |= ASN1_CLASS_STRUCTURED;
if ((class != class_implicit) || (tag != tag_implicit))
return ASN1_TAG_ERROR;
}
else
return ASN1_TAG_ERROR;
}
}
else
{
unsigned type = type_field (node->type);
if (type == ASN1_ETYPE_TAG)
{
*tag_len = 0;
if (inner_tag_len)
*inner_tag_len = 0;
return ASN1_SUCCESS;
}
if (asn1_get_tag_der
(der + counter, der_len, &class, &len2,
&tag) != ASN1_SUCCESS)
return ASN1_DER_ERROR;
DECR_LEN(der_len, len2);
switch (type)
{
case ASN1_ETYPE_NULL:
case ASN1_ETYPE_BOOLEAN:
case ASN1_ETYPE_INTEGER:
case ASN1_ETYPE_ENUMERATED:
case ASN1_ETYPE_OBJECT_ID:
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:
case ASN1_ETYPE_SEQUENCE:
case ASN1_ETYPE_SEQUENCE_OF:
case ASN1_ETYPE_SET:
case ASN1_ETYPE_SET_OF:
case ASN1_ETYPE_GENERALIZED_TIME:
case ASN1_ETYPE_UTC_TIME:
if ((class != _asn1_tags[type].class)
|| (tag != _asn1_tags[type].tag))
return ASN1_DER_ERROR;
break;
case ASN1_ETYPE_OCTET_STRING:
/* OCTET STRING is handled differently to allow
* BER encodings (structured class). */
if (((class != ASN1_CLASS_UNIVERSAL)
&& (class != (ASN1_CLASS_UNIVERSAL | ASN1_CLASS_STRUCTURED)))
|| (tag != ASN1_TAG_OCTET_STRING))
return ASN1_DER_ERROR;
break;
case ASN1_ETYPE_ANY:
counter -= len2;
break;
case ASN1_ETYPE_CHOICE:
counter -= len2;
break;
default:
return ASN1_DER_ERROR;
break;
}
}
counter += len2;
*tag_len = counter;
if (inner_tag_len)
*inner_tag_len = len2;
return ASN1_SUCCESS;
cleanup:
return result;
}
static int
extract_tag_der_recursive(asn1_node node, const unsigned char *der, int der_len,
int *ret_len, int *inner_len, unsigned flags)
{
asn1_node p;
int ris = ASN1_DER_ERROR;
if (type_field (node->type) == ASN1_ETYPE_CHOICE)
{
p = node->down;
while (p)
{
ris = _asn1_extract_tag_der (p, der, der_len, ret_len, inner_len, flags);
if (ris == ASN1_SUCCESS)
break;
p = p->right;
}
*ret_len = 0;
return ris;
}
else
return _asn1_extract_tag_der (node, der, der_len, ret_len, inner_len, flags);
}
static int
_asn1_delete_not_used (asn1_node node)
{
asn1_node p, p2;
if (node == NULL)
return ASN1_ELEMENT_NOT_FOUND;
p = node;
while (p)
{
if (p->type & CONST_NOT_USED)
{
p2 = NULL;
if (p != node)
{
p2 = _asn1_find_left (p);
if (!p2)
p2 = _asn1_find_up (p);
}
asn1_delete_structure (&p);
p = p2;
}
if (!p)
break; /* reach node */
if (p->down)
{
p = p->down;
}
else
{
if (p == node)
p = NULL;
else if (p->right)
p = p->right;
else
{
while (1)
{
p = _asn1_find_up (p);
if (p == node)
{
p = NULL;
break;
}
if (p->right)
{
p = p->right;
break;
}
}
}
}
}
return ASN1_SUCCESS;
}
static int
_asn1_get_indefinite_length_string (const unsigned char *der,
int der_len, int *len)
{
int len2, len3, counter, indefinite;
int result;
unsigned long tag;
unsigned char class;
counter = indefinite = 0;
while (1)
{
if (HAVE_TWO(der_len) && (der[counter] == 0) && (der[counter + 1] == 0))
{
counter += 2;
DECR_LEN(der_len, 2);
indefinite--;
if (indefinite <= 0)
break;
else
continue;
}
if (asn1_get_tag_der
(der + counter, der_len, &class, &len2,
&tag) != ASN1_SUCCESS)
return ASN1_DER_ERROR;
DECR_LEN(der_len, len2);
counter += len2;
len2 = asn1_get_length_der (der + counter, der_len, &len3);
if (len2 < -1)
return ASN1_DER_ERROR;
if (len2 == -1)
{
indefinite++;
counter += 1;
DECR_LEN(der_len, 1);
}
else
{
counter += len2 + len3;
DECR_LEN(der_len, len2+len3);
}
}
*len = counter;
return ASN1_SUCCESS;
cleanup:
return result;
}
static void delete_unneeded_choice_fields(asn1_node p)
{
asn1_node p2;
while (p->right)
{
p2 = p->right;
asn1_delete_structure (&p2);
}
}
/**
* asn1_der_decoding2
* @element: pointer to an ASN1 structure.
* @ider: vector that contains the DER encoding.
* @max_ider_len: pointer to an integer giving the information about the
* maximal number of bytes occupied by *@ider. The real size of the DER
* encoding is returned through this pointer.
* @flags: flags controlling the behaviour of the function.
* @errorDescription: null-terminated string contains details when an
* error occurred.
*
* Fill the structure *@element with values of a DER encoding string. The
* structure must just be created with function asn1_create_element().
*
* If %ASN1_DECODE_FLAG_ALLOW_PADDING flag is set then the function will ignore
* padding after the decoded DER data. Upon a successful return the value of
* *@max_ider_len will be set to the number of bytes decoded.
*
* If %ASN1_DECODE_FLAG_STRICT_DER flag is set then the function will
* not decode any BER-encoded elements.
*
* Returns: %ASN1_SUCCESS if DER encoding OK, %ASN1_ELEMENT_NOT_FOUND
* if @ELEMENT is %NULL, and %ASN1_TAG_ERROR or
* %ASN1_DER_ERROR if the der encoding doesn't match the structure
* name (*@ELEMENT deleted).
**/
int
asn1_der_decoding2 (asn1_node *element, const void *ider, int *max_ider_len,
unsigned int flags, char *errorDescription)
{
asn1_node node, p, p2, p3;
char temp[128];
int counter, len2, len3, len4, move, ris, tlen;
struct node_tail_cache_st tcache = {NULL, NULL};
unsigned char class;
unsigned long tag;
int tag_len;
int indefinite, result, total_len = *max_ider_len, ider_len = *max_ider_len;
int inner_tag_len;
unsigned char *ptmp;
const unsigned char *ptag;
const unsigned char *der = ider;
node = *element;
if (errorDescription != NULL)
errorDescription[0] = 0;
if (node == NULL)
return ASN1_ELEMENT_NOT_FOUND;
if (node->type & CONST_OPTION)
{
result = ASN1_GENERIC_ERROR;
warn();
goto cleanup;
}
counter = 0;
move = DOWN;
p = node;
while (1)
{
tag_len = 0;
inner_tag_len = 0;
ris = ASN1_SUCCESS;
if (move != UP)
{
if (p->type & CONST_SET)
{
p2 = _asn1_find_up (p);
len2 = p2->tmp_ival;
if (len2 == -1)
{
if (HAVE_TWO(ider_len) && !der[counter] && !der[counter + 1])
{
p = p2;
move = UP;
counter += 2;
DECR_LEN(ider_len, 2);
continue;
}
}
else if (counter == len2)
{
p = p2;
move = UP;
continue;
}
else if (counter > len2)
{
result = ASN1_DER_ERROR;
warn();
goto cleanup;
}
p2 = p2->down;
while (p2)
{
if ((p2->type & CONST_SET) && (p2->type & CONST_NOT_USED))
{
ris =
extract_tag_der_recursive (p2, der + counter,
ider_len, &len2, NULL, flags);
if (ris == ASN1_SUCCESS)
{
p2->type &= ~CONST_NOT_USED;
p = p2;
break;
}
}
p2 = p2->right;
}
if (p2 == NULL)
{
result = ASN1_DER_ERROR;
warn();
goto cleanup;
}
}
/* the position in the DER structure this starts */
p->start = counter;
p->end = total_len - 1;
if ((p->type & CONST_OPTION) || (p->type & CONST_DEFAULT))
{
p2 = _asn1_find_up (p);
len2 = p2->tmp_ival;
if (counter == len2)
{
if (p->right)
{
p2 = p->right;
move = RIGHT;
}
else
move = UP;
if (p->type & CONST_OPTION)
asn1_delete_structure (&p);
p = p2;
continue;
}
}
if (type_field (p->type) == ASN1_ETYPE_CHOICE)
{
while (p->down)
{
ris =
extract_tag_der_recursive (p->down, der + counter,
ider_len, &len2, NULL, flags);
if (ris == ASN1_SUCCESS)
{
delete_unneeded_choice_fields(p->down);
break;
}
else if (ris == ASN1_ERROR_TYPE_ANY)
{
result = ASN1_ERROR_TYPE_ANY;
warn();
goto cleanup;
}
else
{
p2 = p->down;
asn1_delete_structure (&p2);
}
}
if (p->down == NULL)
{
if (!(p->type & CONST_OPTION))
{
result = ASN1_DER_ERROR;
warn();
goto cleanup;
}
}
else if (type_field (p->type) != ASN1_ETYPE_CHOICE)
p = p->down;
p->start = counter;
}
if ((p->type & CONST_OPTION) || (p->type & CONST_DEFAULT))
{
p2 = _asn1_find_up (p);
len2 = p2->tmp_ival;
if ((len2 != -1) && (counter > len2))
ris = ASN1_TAG_ERROR;
}
if (ris == ASN1_SUCCESS)
ris =
extract_tag_der_recursive (p, der + counter, ider_len,
&tag_len, &inner_tag_len, flags);
if (ris != ASN1_SUCCESS)
{
if (p->type & CONST_OPTION)
{
p->type |= CONST_NOT_USED;
move = RIGHT;
}
else if (p->type & CONST_DEFAULT)
{
_asn1_set_value (p, NULL, 0);
move = RIGHT;
}
else
{
if (errorDescription != NULL)
_asn1_error_description_tag_error (p, errorDescription);
result = ASN1_TAG_ERROR;
warn();
goto cleanup;
}
}
else
{
DECR_LEN(ider_len, tag_len);
counter += tag_len;
}
}
if (ris == ASN1_SUCCESS)
{
switch (type_field (p->type))
{
case ASN1_ETYPE_NULL:
DECR_LEN(ider_len, 1);
if (der[counter])
{
result = ASN1_DER_ERROR;
warn();
goto cleanup;
}
counter++;
move = RIGHT;
break;
case ASN1_ETYPE_BOOLEAN:
DECR_LEN(ider_len, 2);
if (der[counter++] != 1)
{
result = ASN1_DER_ERROR;
warn();
goto cleanup;
}
if (der[counter++] == 0)
_asn1_set_value (p, "F", 1);
else
_asn1_set_value (p, "T", 1);
move = RIGHT;
break;
case ASN1_ETYPE_INTEGER:
case ASN1_ETYPE_ENUMERATED:
len2 =
asn1_get_length_der (der + counter, ider_len, &len3);
if (len2 < 0)
{
result = ASN1_DER_ERROR;
warn();
goto cleanup;
}
DECR_LEN(ider_len, len3+len2);
_asn1_set_value (p, der + counter, len3 + len2);
counter += len3 + len2;
move = RIGHT;
break;
case ASN1_ETYPE_OBJECT_ID:
result =
asn1_get_object_id_der (der + counter, ider_len, &len2,
temp, sizeof (temp));
if (result != ASN1_SUCCESS)
{
warn();
goto cleanup;
}
DECR_LEN(ider_len, len2);
tlen = strlen (temp);
if (tlen > 0)
_asn1_set_value (p, temp, tlen + 1);
counter += len2;
move = RIGHT;
break;
case ASN1_ETYPE_GENERALIZED_TIME:
case ASN1_ETYPE_UTC_TIME:
result =
_asn1_get_time_der (type_field (p->type), der + counter, ider_len, &len2, temp,
sizeof (temp) - 1, flags);
if (result != ASN1_SUCCESS)
{
warn();
goto cleanup;
}
DECR_LEN(ider_len, len2);
tlen = strlen (temp);
if (tlen > 0)
_asn1_set_value (p, temp, tlen);
counter += len2;
move = RIGHT;
break;
case ASN1_ETYPE_OCTET_STRING:
if (counter < inner_tag_len)
{
result = ASN1_DER_ERROR;
warn();
goto cleanup;
}
ptag = der + counter - inner_tag_len;
if ((flags & ASN1_DECODE_FLAG_STRICT_DER) || !(ptag[0] & ASN1_CLASS_STRUCTURED))
{
if (ptag[0] & ASN1_CLASS_STRUCTURED)
{
result = ASN1_DER_ERROR;
warn();
goto cleanup;
}
len2 =
asn1_get_length_der (der + counter, ider_len, &len3);
if (len2 < 0)
{
result = ASN1_DER_ERROR;
warn();
goto cleanup;
}
DECR_LEN(ider_len, len3+len2);
_asn1_set_value (p, der + counter, len3 + len2);
counter += len3 + len2;
}
else
{
unsigned dflags = 0, vlen, ber_len;
if (ptag[0] & ASN1_CLASS_STRUCTURED)
dflags |= DECODE_FLAG_CONSTRUCTED;
result = _asn1_decode_simple_ber(type_field (p->type), der+counter, ider_len, &ptmp, &vlen, &ber_len, dflags);
if (result != ASN1_SUCCESS)
{
warn();
goto cleanup;
}
DECR_LEN(ider_len, ber_len);
_asn1_set_value_lv (p, ptmp, vlen);
counter += ber_len;
free(ptmp);
}
move = RIGHT;
break;
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:
len2 =
asn1_get_length_der (der + counter, ider_len, &len3);
if (len2 < 0)
{
result = ASN1_DER_ERROR;
warn();
goto cleanup;
}
DECR_LEN(ider_len, len3+len2);
_asn1_set_value (p, der + counter, len3 + len2);
counter += len3 + len2;
move = RIGHT;
break;
case ASN1_ETYPE_SEQUENCE:
case ASN1_ETYPE_SET:
if (move == UP)
{
len2 = p->tmp_ival;
p->tmp_ival = 0;
if (len2 == -1)
{ /* indefinite length method */
DECR_LEN(ider_len, 2);
if ((der[counter]) || der[counter + 1])
{
result = ASN1_DER_ERROR;
warn();
goto cleanup;
}
counter += 2;
}
else
{ /* definite length method */
if (len2 != counter)
{
result = ASN1_DER_ERROR;
warn();
goto cleanup;
}
}
move = RIGHT;
}
else
{ /* move==DOWN || move==RIGHT */
len3 =
asn1_get_length_der (der + counter, ider_len, &len2);
if (IS_ERR(len3, flags))
{
result = ASN1_DER_ERROR;
warn();
goto cleanup;
}
DECR_LEN(ider_len, len2);
counter += len2;
if (len3 > 0)
{
p->tmp_ival = counter + len3;
move = DOWN;
}
else if (len3 == 0)
{
p2 = p->down;
while (p2)
{
if (type_field (p2->type) != ASN1_ETYPE_TAG)
{
p3 = p2->right;
asn1_delete_structure (&p2);
p2 = p3;
}
else
p2 = p2->right;
}
move = RIGHT;
}
else
{ /* indefinite length method */
p->tmp_ival = -1;
move = DOWN;
}
}
break;
case ASN1_ETYPE_SEQUENCE_OF:
case ASN1_ETYPE_SET_OF:
if (move == UP)
{
len2 = p->tmp_ival;
if (len2 == -1)
{ /* indefinite length method */
if (!HAVE_TWO(ider_len) || ((der[counter]) || der[counter + 1]))
{
result = _asn1_append_sequence_set (p, &tcache);
if (result != 0)
{
warn();
goto cleanup;
}
p = tcache.tail;
move = RIGHT;
continue;
}
p->tmp_ival = 0;
tcache.tail = NULL; /* finished decoding this structure */
tcache.head = NULL;
DECR_LEN(ider_len, 2);
counter += 2;
}
else
{ /* definite length method */
if (len2 > counter)
{
result = _asn1_append_sequence_set (p, &tcache);
if (result != 0)
{
warn();
goto cleanup;
}
p = tcache.tail;
move = RIGHT;
continue;
}
p->tmp_ival = 0;
tcache.tail = NULL; /* finished decoding this structure */
tcache.head = NULL;
if (len2 != counter)
{
result = ASN1_DER_ERROR;
warn();
goto cleanup;
}
}
}
else
{ /* move==DOWN || move==RIGHT */
len3 =
asn1_get_length_der (der + counter, ider_len, &len2);
if (IS_ERR(len3, flags))
{
result = ASN1_DER_ERROR;
warn();
goto cleanup;
}
DECR_LEN(ider_len, len2);
counter += len2;
if (len3)
{
if (len3 > 0)
{ /* definite length method */
p->tmp_ival = counter + len3;
}
else
{ /* indefinite length method */
p->tmp_ival = -1;
}
p2 = p->down;
if (p2 == NULL)
{
result = ASN1_DER_ERROR;
warn();
goto cleanup;
}
while ((type_field (p2->type) == ASN1_ETYPE_TAG)
|| (type_field (p2->type) == ASN1_ETYPE_SIZE))
p2 = p2->right;
if (p2->right == NULL)
{
result = _asn1_append_sequence_set (p, &tcache);
if (result != 0)
{
warn();
goto cleanup;
}
}
p = p2;
}
}
move = RIGHT;
break;
case ASN1_ETYPE_ANY:
/* Check indefinite lenth method in an EXPLICIT TAG */
if (!(flags & ASN1_DECODE_FLAG_STRICT_DER) && (p->type & CONST_TAG) &&
tag_len == 2 && (der[counter - 1] == 0x80))
indefinite = 1;
else
indefinite = 0;
if (asn1_get_tag_der
(der + counter, ider_len, &class, &len2,
&tag) != ASN1_SUCCESS)
{
result = ASN1_DER_ERROR;
warn();
goto cleanup;
}
DECR_LEN(ider_len, len2);
len4 =
asn1_get_length_der (der + counter + len2,
ider_len, &len3);
if (IS_ERR(len4, flags))
{
result = ASN1_DER_ERROR;
warn();
goto cleanup;
}
if (len4 != -1) /* definite */
{
len2 += len4;
DECR_LEN(ider_len, len4+len3);
_asn1_set_value_lv (p, der + counter, len2 + len3);
counter += len2 + len3;
}
else /* == -1 */
{ /* indefinite length */
ider_len += len2; /* undo DECR_LEN */
if (counter == 0)
{
result = ASN1_DER_ERROR;
warn();
goto cleanup;
}
result =
_asn1_get_indefinite_length_string (der + counter, ider_len, &len2);
if (result != ASN1_SUCCESS)
{
warn();
goto cleanup;
}
DECR_LEN(ider_len, len2);
_asn1_set_value_lv (p, der + counter, len2);
counter += len2;
}
/* Check if a couple of 0x00 are present due to an EXPLICIT TAG with
an indefinite length method. */
if (indefinite)
{
DECR_LEN(ider_len, 2);
if (!der[counter] && !der[counter + 1])
{
counter += 2;
}
else
{
result = ASN1_DER_ERROR;
warn();
goto cleanup;
}
}
move = RIGHT;
break;
default:
move = (move == UP) ? RIGHT : DOWN;
break;
}
}
if (p)
{
p->end = counter - 1;
}
if (p == node && move != DOWN)
break;
if (move == DOWN)
{
if (p->down)
p = p->down;
else
move = RIGHT;
}
if ((move == RIGHT) && !(p->type & CONST_SET))
{
if (p->right)
p = p->right;
else
move = UP;
}
if (move == UP)
p = _asn1_find_up (p);
}
_asn1_delete_not_used (*element);
if ((ider_len < 0) ||
(!(flags & ASN1_DECODE_FLAG_ALLOW_PADDING) && (ider_len != 0)))
{
warn();
result = ASN1_DER_ERROR;
goto cleanup;
}
*max_ider_len = total_len - ider_len;
return ASN1_SUCCESS;
cleanup:
asn1_delete_structure (element);
return result;
}
/**
* asn1_der_decoding:
* @element: pointer to an ASN1 structure.
* @ider: vector that contains the DER encoding.
* @ider_len: number of bytes of *@ider: @ider[0]..@ider[len-1].
* @errorDescription: null-terminated string contains details when an
* error occurred.
*
* Fill the structure *@element with values of a DER encoding
* string. The structure must just be created with function
* asn1_create_element().
*
* Note that the *@element variable is provided as a pointer for
* historical reasons.
*
* Returns: %ASN1_SUCCESS if DER encoding OK, %ASN1_ELEMENT_NOT_FOUND
* if @ELEMENT is %NULL, and %ASN1_TAG_ERROR or
* %ASN1_DER_ERROR if the der encoding doesn't match the structure
* name (*@ELEMENT deleted).
**/
int
asn1_der_decoding (asn1_node * element, const void *ider, int ider_len,
char *errorDescription)
{
return asn1_der_decoding2 (element, ider, &ider_len, 0, errorDescription);
}
/**
* asn1_der_decoding_element:
* @structure: pointer to an ASN1 structure
* @elementName: name of the element to fill
* @ider: vector that contains the DER encoding of the whole structure.
* @len: number of bytes of *der: der[0]..der[len-1]
* @errorDescription: null-terminated string contains details when an
* error occurred.
*
* Fill the element named @ELEMENTNAME with values of a DER encoding
* string. The structure must just be created with function
* asn1_create_element(). The DER vector must contain the encoding
* string of the whole @STRUCTURE. If an error occurs during the
* decoding procedure, the *@STRUCTURE is deleted and set equal to
* %NULL.
*
* This function is deprecated and may just be an alias to asn1_der_decoding
* in future versions. Use asn1_der_decoding() instead.
*
* Returns: %ASN1_SUCCESS if DER encoding OK, %ASN1_ELEMENT_NOT_FOUND
* if ELEMENT is %NULL or @elementName == NULL, and
* %ASN1_TAG_ERROR or %ASN1_DER_ERROR if the der encoding doesn't
* match the structure @structure (*ELEMENT deleted).
**/
int
asn1_der_decoding_element (asn1_node * structure, const char *elementName,
const void *ider, int len, char *errorDescription)
{
return asn1_der_decoding(structure, ider, len, errorDescription);
}
/**
* asn1_der_decoding_startEnd:
* @element: pointer to an ASN1 element
* @ider: vector that contains the DER encoding.
* @ider_len: number of bytes of *@ider: @ider[0]..@ider[len-1]
* @name_element: an element of NAME structure.
* @start: the position of the first byte of NAME_ELEMENT decoding
* (@ider[*start])
* @end: the position of the last byte of NAME_ELEMENT decoding
* (@ider[*end])
*
* Find the start and end point of an element in a DER encoding
* string. I mean that if you have a der encoding and you have already
* used the function asn1_der_decoding() to fill a structure, it may
* happen that you want to find the piece of string concerning an
* element of the structure.
*
* One example is the sequence "tbsCertificate" inside an X509
* certificate.
*
* Note that since libtasn1 3.7 the @ider and @ider_len parameters
* can be omitted, if the element is already decoded using asn1_der_decoding().
*
* Returns: %ASN1_SUCCESS if DER encoding OK, %ASN1_ELEMENT_NOT_FOUND
* if ELEMENT is %asn1_node EMPTY or @name_element is not a valid
* element, %ASN1_TAG_ERROR or %ASN1_DER_ERROR if the der encoding
* doesn't match the structure ELEMENT.
**/
int
asn1_der_decoding_startEnd (asn1_node element, const void *ider, int ider_len,
const char *name_element, int *start, int *end)
{
asn1_node node, node_to_find;
int result = ASN1_DER_ERROR;
node = element;
if (node == NULL)
return ASN1_ELEMENT_NOT_FOUND;
node_to_find = asn1_find_node (node, name_element);
if (node_to_find == NULL)
return ASN1_ELEMENT_NOT_FOUND;
*start = node_to_find->start;
*end = node_to_find->end;
if (*start == 0 && *end == 0)
{
if (ider == NULL || ider_len == 0)
return ASN1_GENERIC_ERROR;
/* it seems asn1_der_decoding() wasn't called before. Do it now */
result = asn1_der_decoding (&node, ider, ider_len, NULL);
if (result != ASN1_SUCCESS)
{
warn();
return result;
}
node_to_find = asn1_find_node (node, name_element);
if (node_to_find == NULL)
return ASN1_ELEMENT_NOT_FOUND;
*start = node_to_find->start;
*end = node_to_find->end;
}
if (*end < *start)
return ASN1_GENERIC_ERROR;
return ASN1_SUCCESS;
}
/**
* asn1_expand_any_defined_by:
* @definitions: ASN1 definitions
* @element: pointer to an ASN1 structure
*
* Expands every "ANY DEFINED BY" element of a structure created from
* a DER decoding process (asn1_der_decoding function). The element
* ANY must be defined by an OBJECT IDENTIFIER. The type used to
* expand the element ANY is the first one following the definition of
* the actual value of the OBJECT IDENTIFIER.
*
* Returns: %ASN1_SUCCESS if Substitution OK, %ASN1_ERROR_TYPE_ANY if
* some "ANY DEFINED BY" element couldn't be expanded due to a
* problem in OBJECT_ID -> TYPE association, or other error codes
* depending on DER decoding.
**/
int
asn1_expand_any_defined_by (asn1_node_const definitions, asn1_node * element)
{
char name[2 * ASN1_MAX_NAME_SIZE + 2],
value[ASN1_MAX_NAME_SIZE];
int retCode = ASN1_SUCCESS, result;
int len, len2, len3;
asn1_node_const p2;
asn1_node p, p3, aux = NULL;
char errorDescription[ASN1_MAX_ERROR_DESCRIPTION_SIZE];
const char *definitionsName;
if ((definitions == NULL) || (*element == NULL))
return ASN1_ELEMENT_NOT_FOUND;
definitionsName = definitions->name;
p = *element;
while (p)
{
switch (type_field (p->type))
{
case ASN1_ETYPE_ANY:
if ((p->type & CONST_DEFINED_BY) && (p->value))
{
/* search the "DEF_BY" element */
p2 = p->down;
while ((p2) && (type_field (p2->type) != ASN1_ETYPE_CONSTANT))
p2 = p2->right;
if (!p2)
{
retCode = ASN1_ERROR_TYPE_ANY;
break;
}
p3 = _asn1_find_up (p);
if (!p3)
{
retCode = ASN1_ERROR_TYPE_ANY;
break;
}
p3 = p3->down;
while (p3)
{
if (!(strcmp (p3->name, p2->name)))
break;
p3 = p3->right;
}
if ((!p3) || (type_field (p3->type) != ASN1_ETYPE_OBJECT_ID) ||
(p3->value == NULL))
{
p3 = _asn1_find_up (p);
p3 = _asn1_find_up (p3);
if (!p3)
{
retCode = ASN1_ERROR_TYPE_ANY;
break;
}
p3 = p3->down;
while (p3)
{
if (!(strcmp (p3->name, p2->name)))
break;
p3 = p3->right;
}
if ((!p3) || (type_field (p3->type) != ASN1_ETYPE_OBJECT_ID)
|| (p3->value == NULL))
{
retCode = ASN1_ERROR_TYPE_ANY;
break;
}
}
/* search the OBJECT_ID into definitions */
p2 = definitions->down;
while (p2)
{
if ((type_field (p2->type) == ASN1_ETYPE_OBJECT_ID) &&
(p2->type & CONST_ASSIGN))
{
snprintf(name, sizeof(name), "%s.%s", definitionsName, p2->name);
len = ASN1_MAX_NAME_SIZE;
result =
asn1_read_value (definitions, name, value, &len);
if ((result == ASN1_SUCCESS)
&& (!_asn1_strcmp (p3->value, value)))
{
p2 = p2->right; /* pointer to the structure to
use for expansion */
while ((p2) && (p2->type & CONST_ASSIGN))
p2 = p2->right;
if (p2)
{
snprintf(name, sizeof(name), "%s.%s", definitionsName, p2->name);
result =
asn1_create_element (definitions, name, &aux);
if (result == ASN1_SUCCESS)
{
_asn1_cpy_name (aux, p);
len2 =
asn1_get_length_der (p->value,
p->value_len, &len3);
if (len2 < 0)
return ASN1_DER_ERROR;
result =
asn1_der_decoding (&aux, p->value + len3,
len2,
errorDescription);
if (result == ASN1_SUCCESS)
{
_asn1_set_right (aux, p->right);
_asn1_set_right (p, aux);
result = asn1_delete_structure (&p);
if (result == ASN1_SUCCESS)
{
p = aux;
aux = NULL;
break;
}
else
{ /* error with asn1_delete_structure */
asn1_delete_structure (&aux);
retCode = result;
break;
}
}
else
{ /* error with asn1_der_decoding */
retCode = result;
break;
}
}
else
{ /* error with asn1_create_element */
retCode = result;
break;
}
}
else
{ /* error with the pointer to the structure to exapand */
retCode = ASN1_ERROR_TYPE_ANY;
break;
}
}
}
p2 = p2->right;
} /* end while */
if (!p2)
{
retCode = ASN1_ERROR_TYPE_ANY;
break;
}
}
break;
default:
break;
}
if (p->down)
{
p = p->down;
}
else if (p == *element)
{
p = NULL;
break;
}
else if (p->right)
p = p->right;
else
{
while (1)
{
p = _asn1_find_up (p);
if (p == *element)
{
p = NULL;
break;
}
if (p->right)
{
p = p->right;
break;
}
}
}
}
return retCode;
}
/**
* asn1_expand_octet_string:
* @definitions: ASN1 definitions
* @element: pointer to an ASN1 structure
* @octetName: name of the OCTECT STRING field to expand.
* @objectName: name of the OBJECT IDENTIFIER field to use to define
* the type for expansion.
*
* Expands an "OCTET STRING" element of a structure created from a DER
* decoding process (the asn1_der_decoding() function). The type used
* for expansion is the first one following the definition of the
* actual value of the OBJECT IDENTIFIER indicated by OBJECTNAME.
*
* Returns: %ASN1_SUCCESS if substitution OK, %ASN1_ELEMENT_NOT_FOUND
* if @objectName or @octetName are not correct,
* %ASN1_VALUE_NOT_VALID if it wasn't possible to find the type to
* use for expansion, or other errors depending on DER decoding.
**/
int
asn1_expand_octet_string (asn1_node_const definitions, asn1_node * element,
const char *octetName, const char *objectName)
{
char name[2 * ASN1_MAX_NAME_SIZE + 1], value[ASN1_MAX_NAME_SIZE];
int retCode = ASN1_SUCCESS, result;
int len, len2, len3;
asn1_node_const p2;
asn1_node aux = NULL;
asn1_node octetNode = NULL, objectNode = NULL;
char errorDescription[ASN1_MAX_ERROR_DESCRIPTION_SIZE];
if ((definitions == NULL) || (*element == NULL))
return ASN1_ELEMENT_NOT_FOUND;
octetNode = asn1_find_node (*element, octetName);
if (octetNode == NULL)
return ASN1_ELEMENT_NOT_FOUND;
if (type_field (octetNode->type) != ASN1_ETYPE_OCTET_STRING)
return ASN1_ELEMENT_NOT_FOUND;
if (octetNode->value == NULL)
return ASN1_VALUE_NOT_FOUND;
objectNode = asn1_find_node (*element, objectName);
if (objectNode == NULL)
return ASN1_ELEMENT_NOT_FOUND;
if (type_field (objectNode->type) != ASN1_ETYPE_OBJECT_ID)
return ASN1_ELEMENT_NOT_FOUND;
if (objectNode->value == NULL)
return ASN1_VALUE_NOT_FOUND;
/* search the OBJECT_ID into definitions */
p2 = definitions->down;
while (p2)
{
if ((type_field (p2->type) == ASN1_ETYPE_OBJECT_ID) &&
(p2->type & CONST_ASSIGN))
{
strcpy (name, definitions->name);
strcat (name, ".");
strcat (name, p2->name);
len = sizeof (value);
result = asn1_read_value (definitions, name, value, &len);
if ((result == ASN1_SUCCESS)
&& (!_asn1_strcmp (objectNode->value, value)))
{
p2 = p2->right; /* pointer to the structure to
use for expansion */
while ((p2) && (p2->type & CONST_ASSIGN))
p2 = p2->right;
if (p2)
{
strcpy (name, definitions->name);
strcat (name, ".");
strcat (name, p2->name);
result = asn1_create_element (definitions, name, &aux);
if (result == ASN1_SUCCESS)
{
_asn1_cpy_name (aux, octetNode);
len2 =
asn1_get_length_der (octetNode->value,
octetNode->value_len, &len3);
if (len2 < 0)
return ASN1_DER_ERROR;
result =
asn1_der_decoding (&aux, octetNode->value + len3,
len2, errorDescription);
if (result == ASN1_SUCCESS)
{
_asn1_set_right (aux, octetNode->right);
_asn1_set_right (octetNode, aux);
result = asn1_delete_structure (&octetNode);
if (result == ASN1_SUCCESS)
{
aux = NULL;
break;
}
else
{ /* error with asn1_delete_structure */
asn1_delete_structure (&aux);
retCode = result;
break;
}
}
else
{ /* error with asn1_der_decoding */
retCode = result;
break;
}
}
else
{ /* error with asn1_create_element */
retCode = result;
break;
}
}
else
{ /* error with the pointer to the structure to exapand */
retCode = ASN1_VALUE_NOT_VALID;
break;
}
}
}
p2 = p2->right;
}
if (!p2)
retCode = ASN1_VALUE_NOT_VALID;
return retCode;
}
/*-
* _asn1_decode_simple_der:
* @etype: The type of the string to be encoded (ASN1_ETYPE_)
* @der: the encoded string
* @_der_len: the bytes of the encoded string
* @str: a pointer to the data
* @str_len: the length of the data
* @dflags: DECODE_FLAG_*
*
* Decodes a simple DER encoded type (e.g. a string, which is not constructed).
* The output is a pointer inside the @der.
*
* Returns: %ASN1_SUCCESS if successful or an error value.
-*/
static int
_asn1_decode_simple_der (unsigned int etype, const unsigned char *der,
unsigned int _der_len, const unsigned char **str,
unsigned int *str_len, unsigned dflags)
{
int tag_len, len_len;
const unsigned char *p;
int der_len = _der_len;
unsigned char class;
unsigned long tag;
long ret;
if (der == NULL || der_len == 0)
return ASN1_VALUE_NOT_VALID;
if (ETYPE_OK (etype) == 0 || ETYPE_IS_STRING(etype) == 0)
return ASN1_VALUE_NOT_VALID;
/* doesn't handle constructed classes */
class = ETYPE_CLASS(etype);
if (class != ASN1_CLASS_UNIVERSAL)
return ASN1_VALUE_NOT_VALID;
p = der;
if (dflags & DECODE_FLAG_HAVE_TAG)
{
ret = asn1_get_tag_der (p, der_len, &class, &tag_len, &tag);
if (ret != ASN1_SUCCESS)
return ret;
if (class != ETYPE_CLASS (etype) || tag != ETYPE_TAG (etype))
{
warn();
return ASN1_DER_ERROR;
}
p += tag_len;
der_len -= tag_len;
if (der_len <= 0)
return ASN1_DER_ERROR;
}
ret = asn1_get_length_der (p, der_len, &len_len);
if (ret < 0)
return ASN1_DER_ERROR;
p += len_len;
der_len -= len_len;
if (der_len <= 0)
return ASN1_DER_ERROR;
*str_len = ret;
*str = p;
return ASN1_SUCCESS;
}
/**
* asn1_decode_simple_der:
* @etype: The type of the string to be encoded (ASN1_ETYPE_)
* @der: the encoded string
* @_der_len: the bytes of the encoded string
* @str: a pointer to the data
* @str_len: the length of the data
*
* Decodes a simple DER encoded type (e.g. a string, which is not constructed).
* The output is a pointer inside the @der.
*
* Returns: %ASN1_SUCCESS if successful or an error value.
**/
int
asn1_decode_simple_der (unsigned int etype, const unsigned char *der,
unsigned int _der_len, const unsigned char **str,
unsigned int *str_len)
{
return _asn1_decode_simple_der(etype, der, _der_len, str, str_len, DECODE_FLAG_HAVE_TAG);
}
static int append(uint8_t **dst, unsigned *dst_size, const unsigned char *src, unsigned src_size)
{
if (src_size == 0)
return ASN1_SUCCESS;
*dst = _asn1_realloc(*dst, *dst_size+src_size);
if (*dst == NULL)
return ASN1_MEM_ALLOC_ERROR;
memcpy(*dst + *dst_size, src, src_size);
*dst_size += src_size;
return ASN1_SUCCESS;
}
/*-
* _asn1_decode_simple_ber:
* @etype: The type of the string to be encoded (ASN1_ETYPE_)
* @der: the encoded string
* @_der_len: the bytes of the encoded string
* @str: a pointer to the data
* @str_len: the length of the data
* @ber_len: the total length occupied by BER (may be %NULL)
* @have_tag: whether a DER tag is included
*
* Decodes a BER encoded type. The output is an allocated value
* of the data. This decodes BER STRINGS only. Other types are
* decoded as DER.
*
* Returns: %ASN1_SUCCESS if successful or an error value.
-*/
static int
_asn1_decode_simple_ber (unsigned int etype, const unsigned char *der,
unsigned int _der_len, unsigned char **str,
unsigned int *str_len, unsigned int *ber_len,
unsigned dflags)
{
int tag_len, len_len;
const unsigned char *p;
int der_len = _der_len;
uint8_t *total = NULL;
unsigned total_size = 0;
unsigned char class;
unsigned long tag;
unsigned char *out = NULL;
const unsigned char *cout = NULL;
unsigned out_len;
long result;
if (ber_len) *ber_len = 0;
if (der == NULL || der_len == 0)
{
warn();
return ASN1_VALUE_NOT_VALID;
}
if (ETYPE_OK (etype) == 0)
{
warn();
return ASN1_VALUE_NOT_VALID;
}
/* doesn't handle constructed + definite classes */
class = ETYPE_CLASS (etype);
if (class != ASN1_CLASS_UNIVERSAL)
{
warn();
return ASN1_VALUE_NOT_VALID;
}
p = der;
if (dflags & DECODE_FLAG_HAVE_TAG)
{
result = asn1_get_tag_der (p, der_len, &class, &tag_len, &tag);
if (result != ASN1_SUCCESS)
{
warn();
return result;
}
if (tag != ETYPE_TAG (etype))
{
warn();
return ASN1_DER_ERROR;
}
p += tag_len;
DECR_LEN(der_len, tag_len);
if (ber_len) *ber_len += tag_len;
}
/* indefinite constructed */
if ((((dflags & DECODE_FLAG_CONSTRUCTED) || class == ASN1_CLASS_STRUCTURED) && ETYPE_IS_STRING(etype)) &&
!(dflags & DECODE_FLAG_LEVEL3))
{
if (der_len == 0)
{
warn();
result = ASN1_DER_ERROR;
goto cleanup;
}
if (der_len > 0 && p[0] == 0x80) /* indefinite */
{
len_len = 1;
DECR_LEN(der_len, len_len);
p += len_len;
if (ber_len) *ber_len += len_len;
/* decode the available octet strings */
do
{
unsigned tmp_len;
unsigned flags = DECODE_FLAG_HAVE_TAG;
if (dflags & DECODE_FLAG_LEVEL1)
flags |= DECODE_FLAG_LEVEL2;
else if (dflags & DECODE_FLAG_LEVEL2)
flags |= DECODE_FLAG_LEVEL3;
else
flags |= DECODE_FLAG_LEVEL1;
result = _asn1_decode_simple_ber(etype, p, der_len, &out, &out_len, &tmp_len,
flags);
if (result != ASN1_SUCCESS)
{
warn();
goto cleanup;
}
p += tmp_len;
DECR_LEN(der_len, tmp_len);
if (ber_len) *ber_len += tmp_len;
DECR_LEN(der_len, 2); /* we need the EOC */
result = append(&total, &total_size, out, out_len);
if (result != ASN1_SUCCESS)
{
warn();
goto cleanup;
}
free(out);
out = NULL;
if (p[0] == 0 && p[1] == 0) /* EOC */
{
if (ber_len) *ber_len += 2;
break;
}
/* no EOC */
der_len += 2;
if (der_len == 2)
{
warn();
result = ASN1_DER_ERROR;
goto cleanup;
}
}
while(1);
}
else /* constructed */
{
long const_len;
result = asn1_get_length_ber(p, der_len, &len_len);
if (result < 0)
{
warn();
result = ASN1_DER_ERROR;
goto cleanup;
}
DECR_LEN(der_len, len_len);
p += len_len;
const_len = result;
if (ber_len) *ber_len += len_len;
/* decode the available octet strings */
while(const_len > 0)
{
unsigned tmp_len;
unsigned flags = DECODE_FLAG_HAVE_TAG;
if (dflags & DECODE_FLAG_LEVEL1)
flags |= DECODE_FLAG_LEVEL2;
else if (dflags & DECODE_FLAG_LEVEL2)
flags |= DECODE_FLAG_LEVEL3;
else
flags |= DECODE_FLAG_LEVEL1;
result = _asn1_decode_simple_ber(etype, p, der_len, &out, &out_len, &tmp_len,
flags);
if (result != ASN1_SUCCESS)
{
warn();
goto cleanup;
}
p += tmp_len;
DECR_LEN(der_len, tmp_len);
DECR_LEN(const_len, tmp_len);
if (ber_len) *ber_len += tmp_len;
result = append(&total, &total_size, out, out_len);
if (result != ASN1_SUCCESS)
{
warn();
goto cleanup;
}
free(out);
out = NULL;
}
}
}
else if (class == ETYPE_CLASS(etype))
{
if (ber_len)
{
result = asn1_get_length_der (p, der_len, &len_len);
if (result < 0)
{
warn();
result = ASN1_DER_ERROR;
goto cleanup;
}
*ber_len += result + len_len;
}
/* non-string values are decoded as DER */
result = _asn1_decode_simple_der(etype, der, _der_len, &cout, &out_len, dflags);
if (result != ASN1_SUCCESS)
{
warn();
goto cleanup;
}
result = append(&total, &total_size, cout, out_len);
if (result != ASN1_SUCCESS)
{
warn();
goto cleanup;
}
}
else
{
warn();
result = ASN1_DER_ERROR;
goto cleanup;
}
*str = total;
*str_len = total_size;
return ASN1_SUCCESS;
cleanup:
free(out);
free(total);
return result;
}
/**
* asn1_decode_simple_ber:
* @etype: The type of the string to be encoded (ASN1_ETYPE_)
* @der: the encoded string
* @_der_len: the bytes of the encoded string
* @str: a pointer to the data
* @str_len: the length of the data
* @ber_len: the total length occupied by BER (may be %NULL)
*
* Decodes a BER encoded type. The output is an allocated value
* of the data. This decodes BER STRINGS only. Other types are
* decoded as DER.
*
* Returns: %ASN1_SUCCESS if successful or an error value.
**/
int
asn1_decode_simple_ber (unsigned int etype, const unsigned char *der,
unsigned int _der_len, unsigned char **str,
unsigned int *str_len, unsigned int *ber_len)
{
return _asn1_decode_simple_ber(etype, der, _der_len, str, str_len, ber_len, DECODE_FLAG_HAVE_TAG);
}