summaryrefslogtreecommitdiffstats
path: root/lib/minitasn1/decoding.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--lib/minitasn1/decoding.c2478
1 files changed, 2478 insertions, 0 deletions
diff --git a/lib/minitasn1/decoding.c b/lib/minitasn1/decoding.c
new file mode 100644
index 0000000..378219c
--- /dev/null
+++ b/lib/minitasn1/decoding.c
@@ -0,0 +1,2478 @@
+/*
+ * Copyright (C) 2002-2021 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);
+}