| /* |
| * Copyright (C) 2002-2014 Free Software Foundation, Inc. |
| * |
| * This file is part of LIBTASN1. |
| * |
| * The LIBTASN1 library is free software; you can redistribute it |
| * and/or modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, but |
| * WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
| * 02110-1301, USA |
| */ |
| |
| |
| /*****************************************************/ |
| /* File: 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> |
| |
| static int |
| _asn1_get_indefinite_length_string (const unsigned char *der, int *len); |
| |
| 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. |
| * |
| * 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) |
| { /* indefinite length method */ |
| ret = ber_len; |
| err = _asn1_get_indefinite_length_string (ber + 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: 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 OCTET SEQUENCE in. |
| * @str_size: Length of pre-allocated output buffer. |
| * @str_len: Output variable containing the length of the OCTET SEQUENCE. |
| * |
| * Extract an OCTET SEQUENCE from DER data. |
| * |
| * 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; |
| |
| if (der_len <= 0) |
| return ASN1_GENERIC_ERROR; |
| |
| /* if(str==NULL) return ASN1_SUCCESS; */ |
| *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) |
| memcpy (str, der + len_len, *str_len); |
| else |
| { |
| return ASN1_MEM_ERROR; |
| } |
| |
| return ASN1_SUCCESS; |
| } |
| |
| /* Returns ASN1_SUCCESS on success or an error code on error. |
| */ |
| static int |
| _asn1_get_time_der (const unsigned char *der, int der_len, int *ret_len, |
| char *str, int str_size) |
| { |
| int len_len, str_len; |
| |
| 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; |
| memcpy (str, der + len_len, str_len); |
| str[str_len] = 0; |
| *ret_len = str_len + len_len; |
| |
| return ASN1_SUCCESS; |
| } |
| |
| static int |
| _asn1_get_objectid_der (const unsigned char *der, int der_len, int *ret_len, |
| char *str, int str_size) |
| { |
| int len_len, len, k; |
| int leading; |
| char temp[20]; |
| unsigned long val, val1; |
| |
| *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 > der_len || len_len > der_len) |
| return ASN1_DER_ERROR; |
| |
| val1 = der[len_len] / 40; |
| val = der[len_len] - val1 * 40; |
| |
| _asn1_str_cpy (str, str_size, _asn1_ltostr (val1, temp)); |
| _asn1_str_cat (str, str_size, "."); |
| _asn1_str_cat (str, str_size, _asn1_ltostr (val, temp)); |
| |
| val = 0; |
| leading = 1; |
| for (k = 1; 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: Return %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, 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 (str_size >= len_byte) |
| memcpy (str, der + len_len + 1, len_byte); |
| else |
| { |
| return ASN1_MEM_ERROR; |
| } |
| |
| return ASN1_SUCCESS; |
| } |
| |
| |
| static int |
| _asn1_extract_tag_der (asn1_node node, const unsigned char *der, int der_len, |
| int *ret_len) |
| { |
| asn1_node p; |
| int counter, len2, len3, is_tag_implicit; |
| 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 - counter, &class, &len2, |
| &tag) != ASN1_SUCCESS) |
| return ASN1_DER_ERROR; |
| |
| if (counter + len2 > der_len) |
| return ASN1_DER_ERROR; |
| counter += len2; |
| |
| len3 = |
| asn1_get_length_ber (der + counter, der_len - counter, |
| &len2); |
| if (len3 < 0) |
| return ASN1_DER_ERROR; |
| |
| counter += len2; |
| if (counter > der_len) |
| return ASN1_DER_ERROR; |
| |
| 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 - counter, &class, &len2, |
| &tag) != ASN1_SUCCESS) |
| return ASN1_DER_ERROR; |
| if (counter + len2 > der_len) |
| return ASN1_DER_ERROR; |
| |
| 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) |
| { |
| counter = 0; |
| *ret_len = counter; |
| return ASN1_SUCCESS; |
| } |
| |
| if (asn1_get_tag_der |
| (der + counter, der_len - counter, &class, &len2, |
| &tag) != ASN1_SUCCESS) |
| return ASN1_DER_ERROR; |
| |
| if (counter + len2 > der_len) |
| return ASN1_DER_ERROR; |
| |
| 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; |
| *ret_len = counter; |
| return ASN1_SUCCESS; |
| } |
| |
| static int |
| extract_tag_der_recursive(asn1_node node, const unsigned char *der, int der_len, |
| int *ret_len) |
| { |
| 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); |
| 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); |
| } |
| |
| 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_extract_der_octet (asn1_node node, const unsigned char *der, |
| int der_len) |
| { |
| int len2, len3; |
| int counter2, counter_end; |
| |
| len2 = asn1_get_length_der (der, der_len, &len3); |
| if (len2 < -1) |
| return ASN1_DER_ERROR; |
| |
| counter2 = len3 + 1; |
| |
| if (len2 == -1) |
| counter_end = der_len - 2; |
| else |
| counter_end = der_len; |
| |
| while (counter2 < counter_end) |
| { |
| len2 = asn1_get_length_der (der + counter2, der_len - counter2, &len3); |
| |
| if (len2 < -1) |
| return ASN1_DER_ERROR; |
| |
| if (len2 > 0) |
| { |
| _asn1_append_value (node, der + counter2 + len3, len2); |
| } |
| else |
| { /* indefinite */ |
| |
| len2 = |
| _asn1_extract_der_octet (node, der + counter2 + len3, |
| der_len - counter2 - len3); |
| if (len2 < 0) |
| return len2; |
| } |
| |
| counter2 += len2 + len3 + 1; |
| } |
| |
| return ASN1_SUCCESS; |
| } |
| |
| static int |
| _asn1_get_octet_string (const unsigned char *der, asn1_node node, int *len) |
| { |
| int len2, len3, counter, tot_len, indefinite; |
| |
| counter = 0; |
| |
| if (*(der - 1) & ASN1_CLASS_STRUCTURED) |
| { |
| tot_len = 0; |
| indefinite = asn1_get_length_der (der, *len, &len3); |
| if (indefinite < -1) |
| return ASN1_DER_ERROR; |
| |
| counter += len3; |
| if (indefinite >= 0) |
| indefinite += len3; |
| |
| while (1) |
| { |
| if (counter > (*len)) |
| return ASN1_DER_ERROR; |
| |
| if (indefinite == -1) |
| { |
| if ((der[counter] == 0) && (der[counter + 1] == 0)) |
| { |
| counter += 2; |
| break; |
| } |
| } |
| else if (counter >= indefinite) |
| break; |
| |
| if (der[counter] != ASN1_TAG_OCTET_STRING) |
| return ASN1_DER_ERROR; |
| |
| counter++; |
| |
| len2 = asn1_get_length_der (der + counter, *len - counter, &len3); |
| if (len2 <= 0) |
| return ASN1_DER_ERROR; |
| |
| counter += len3 + len2; |
| tot_len += len2; |
| } |
| |
| /* copy */ |
| if (node) |
| { |
| unsigned char temp[DER_LEN]; |
| int ret; |
| |
| len2 = sizeof (temp); |
| |
| asn1_length_der (tot_len, temp, &len2); |
| _asn1_set_value (node, temp, len2); |
| |
| ret = _asn1_extract_der_octet (node, der, *len); |
| if (ret != ASN1_SUCCESS) |
| return ret; |
| |
| } |
| } |
| else |
| { /* NOT STRUCTURED */ |
| len2 = asn1_get_length_der (der, *len, &len3); |
| if (len2 < 0) |
| return ASN1_DER_ERROR; |
| |
| counter = len3 + len2; |
| if (node) |
| _asn1_set_value (node, der, counter); |
| } |
| |
| *len = counter; |
| return ASN1_SUCCESS; |
| |
| } |
| |
| static int |
| _asn1_get_indefinite_length_string (const unsigned char *der, int *len) |
| { |
| int len2, len3, counter, indefinite; |
| unsigned long tag; |
| unsigned char class; |
| |
| counter = indefinite = 0; |
| |
| while (1) |
| { |
| if ((*len) < counter) |
| return ASN1_DER_ERROR; |
| |
| if ((der[counter] == 0) && (der[counter + 1] == 0)) |
| { |
| counter += 2; |
| indefinite--; |
| if (indefinite <= 0) |
| break; |
| else |
| continue; |
| } |
| |
| if (asn1_get_tag_der |
| (der + counter, *len - counter, &class, &len2, |
| &tag) != ASN1_SUCCESS) |
| return ASN1_DER_ERROR; |
| if (counter + len2 > *len) |
| return ASN1_DER_ERROR; |
| counter += len2; |
| len2 = asn1_get_length_der (der + counter, *len - counter, &len3); |
| if (len2 < -1) |
| return ASN1_DER_ERROR; |
| if (len2 == -1) |
| { |
| indefinite++; |
| counter += 1; |
| } |
| else |
| { |
| counter += len2 + len3; |
| } |
| } |
| |
| *len = counter; |
| return ASN1_SUCCESS; |
| |
| } |
| |
| static void delete_unneeded_choice_fields(asn1_node p) |
| { |
| asn1_node p2; |
| |
| while (p->right) |
| { |
| p2 = p->right; |
| asn1_delete_structure (&p2); |
| } |
| } |
| |
| /** |
| * asn1_der_decoding: |
| * @element: pointer to an ASN1 structure. |
| * @ider: vector that contains the DER encoding. |
| * @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(). If an error occurs during the decoding |
| * procedure, the *@ELEMENT is deleted and set equal to |
| * %NULL. |
| * |
| * 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 len, |
| char *errorDescription) |
| { |
| asn1_node node, p, p2, p3; |
| char temp[128]; |
| int counter, len2, len3, len4, move, ris, tlen; |
| unsigned char class; |
| unsigned long tag; |
| int indefinite, result; |
| 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; |
| goto cleanup; |
| } |
| |
| counter = 0; |
| move = DOWN; |
| p = node; |
| while (1) |
| { |
| ris = ASN1_SUCCESS; |
| if (move != UP) |
| { |
| if (p->type & CONST_SET) |
| { |
| p2 = _asn1_find_up (p); |
| len2 = _asn1_strtol (p2->value, NULL, 10); |
| if (len2 == -1) |
| { |
| if (!der[counter] && !der[counter + 1]) |
| { |
| p = p2; |
| move = UP; |
| counter += 2; |
| continue; |
| } |
| } |
| else if (counter == len2) |
| { |
| p = p2; |
| move = UP; |
| continue; |
| } |
| else if (counter > len2) |
| { |
| result = ASN1_DER_ERROR; |
| 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, |
| len - counter, &len2); |
| if (ris == ASN1_SUCCESS) |
| { |
| p2->type &= ~CONST_NOT_USED; |
| p = p2; |
| break; |
| } |
| } |
| p2 = p2->right; |
| } |
| if (p2 == NULL) |
| { |
| result = ASN1_DER_ERROR; |
| goto cleanup; |
| } |
| } |
| |
| if ((p->type & CONST_OPTION) || (p->type & CONST_DEFAULT)) |
| { |
| p2 = _asn1_find_up (p); |
| len2 = _asn1_strtol (p2->value, NULL, 10); |
| 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) |
| { |
| if (counter < len) |
| ris = |
| extract_tag_der_recursive (p->down, der + counter, |
| len - counter, &len2); |
| else |
| ris = ASN1_DER_ERROR; |
| if (ris == ASN1_SUCCESS) |
| { |
| delete_unneeded_choice_fields(p->down); |
| break; |
| } |
| else if (ris == ASN1_ERROR_TYPE_ANY) |
| { |
| result = ASN1_ERROR_TYPE_ANY; |
| goto cleanup; |
| } |
| else |
| { |
| p2 = p->down; |
| asn1_delete_structure (&p2); |
| } |
| } |
| |
| if (p->down == NULL) |
| { |
| if (!(p->type & CONST_OPTION)) |
| { |
| result = ASN1_DER_ERROR; |
| goto cleanup; |
| } |
| } |
| else if (type_field (p->type) != ASN1_ETYPE_CHOICE) |
| p = p->down; |
| } |
| |
| if ((p->type & CONST_OPTION) || (p->type & CONST_DEFAULT)) |
| { |
| p2 = _asn1_find_up (p); |
| len2 = _asn1_strtol (p2->value, NULL, 10); |
| if ((len2 != -1) && (counter > len2)) |
| ris = ASN1_TAG_ERROR; |
| } |
| |
| if (ris == ASN1_SUCCESS) |
| ris = |
| extract_tag_der_recursive (p, der + counter, len - counter, &len2); |
| 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; |
| goto cleanup; |
| } |
| } |
| else |
| counter += len2; |
| } |
| |
| if (ris == ASN1_SUCCESS) |
| { |
| switch (type_field (p->type)) |
| { |
| case ASN1_ETYPE_NULL: |
| if (der[counter]) |
| { |
| result = ASN1_DER_ERROR; |
| goto cleanup; |
| } |
| counter++; |
| move = RIGHT; |
| break; |
| case ASN1_ETYPE_BOOLEAN: |
| if (der[counter++] != 1) |
| { |
| result = ASN1_DER_ERROR; |
| 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, len - counter, &len3); |
| if (len2 < 0) |
| { |
| result = ASN1_DER_ERROR; |
| goto cleanup; |
| } |
| |
| _asn1_set_value (p, der + counter, len3 + len2); |
| counter += len3 + len2; |
| move = RIGHT; |
| break; |
| case ASN1_ETYPE_OBJECT_ID: |
| result = |
| _asn1_get_objectid_der (der + counter, len - counter, &len2, |
| temp, sizeof (temp)); |
| if (result != ASN1_SUCCESS) |
| goto cleanup; |
| |
| 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 (der + counter, len - counter, &len2, temp, |
| sizeof (temp) - 1); |
| if (result != ASN1_SUCCESS) |
| goto cleanup; |
| |
| tlen = strlen (temp); |
| if (tlen > 0) |
| _asn1_set_value (p, temp, tlen); |
| counter += len2; |
| move = RIGHT; |
| break; |
| case ASN1_ETYPE_OCTET_STRING: |
| len3 = len - counter; |
| result = _asn1_get_octet_string (der + counter, p, &len3); |
| if (result != ASN1_SUCCESS) |
| goto cleanup; |
| |
| counter += len3; |
| 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, len - counter, &len3); |
| if (len2 < 0) |
| { |
| result = ASN1_DER_ERROR; |
| goto cleanup; |
| } |
| |
| _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 = _asn1_strtol (p->value, NULL, 10); |
| _asn1_set_value (p, NULL, 0); |
| if (len2 == -1) |
| { /* indefinite length method */ |
| if (len - counter + 1 > 0) |
| { |
| if ((der[counter]) || der[counter + 1]) |
| { |
| result = ASN1_DER_ERROR; |
| goto cleanup; |
| } |
| } |
| else |
| { |
| result = ASN1_DER_ERROR; |
| goto cleanup; |
| } |
| counter += 2; |
| } |
| else |
| { /* definite length method */ |
| if (len2 != counter) |
| { |
| result = ASN1_DER_ERROR; |
| goto cleanup; |
| } |
| } |
| move = RIGHT; |
| } |
| else |
| { /* move==DOWN || move==RIGHT */ |
| len3 = |
| asn1_get_length_der (der + counter, len - counter, &len2); |
| if (len3 < -1) |
| { |
| result = ASN1_DER_ERROR; |
| goto cleanup; |
| } |
| counter += len2; |
| if (len3 > 0) |
| { |
| _asn1_ltostr (counter + len3, temp); |
| tlen = strlen (temp); |
| if (tlen > 0) |
| _asn1_set_value (p, temp, tlen + 1); |
| 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 */ |
| _asn1_set_value (p, "-1", 3); |
| move = DOWN; |
| } |
| } |
| break; |
| case ASN1_ETYPE_SEQUENCE_OF: |
| case ASN1_ETYPE_SET_OF: |
| if (move == UP) |
| { |
| len2 = _asn1_strtol (p->value, NULL, 10); |
| if (len2 == -1) |
| { /* indefinite length method */ |
| if ((counter + 2) > len) |
| { |
| result = ASN1_DER_ERROR; |
| goto cleanup; |
| } |
| |
| if ((der[counter]) || der[counter + 1]) |
| { |
| _asn1_append_sequence_set (p); |
| p = p->down; |
| while (p->right) |
| p = p->right; |
| move = RIGHT; |
| continue; |
| } |
| _asn1_set_value (p, NULL, 0); |
| counter += 2; |
| } |
| else |
| { /* definite length method */ |
| if (len2 > counter) |
| { |
| _asn1_append_sequence_set (p); |
| p = p->down; |
| while (p->right) |
| p = p->right; |
| move = RIGHT; |
| continue; |
| } |
| _asn1_set_value (p, NULL, 0); |
| if (len2 != counter) |
| { |
| result = ASN1_DER_ERROR; |
| goto cleanup; |
| } |
| } |
| } |
| else |
| { /* move==DOWN || move==RIGHT */ |
| len3 = |
| asn1_get_length_der (der + counter, len - counter, &len2); |
| if (len3 < -1) |
| { |
| result = ASN1_DER_ERROR; |
| goto cleanup; |
| } |
| counter += len2; |
| if (len3) |
| { |
| if (len3 > 0) |
| { /* definite length method */ |
| _asn1_ltostr (counter + len3, temp); |
| tlen = strlen (temp); |
| |
| if (tlen > 0) |
| _asn1_set_value (p, temp, tlen + 1); |
| } |
| else |
| { /* indefinite length method */ |
| _asn1_set_value (p, "-1", 3); |
| } |
| p2 = p->down; |
| while ((type_field (p2->type) == ASN1_ETYPE_TAG) |
| || (type_field (p2->type) == ASN1_ETYPE_SIZE)) |
| p2 = p2->right; |
| if (p2->right == NULL) |
| _asn1_append_sequence_set (p); |
| p = p2; |
| } |
| } |
| move = RIGHT; |
| break; |
| case ASN1_ETYPE_ANY: |
| if (asn1_get_tag_der |
| (der + counter, len - counter, &class, &len2, |
| &tag) != ASN1_SUCCESS) |
| { |
| result = ASN1_DER_ERROR; |
| goto cleanup; |
| } |
| |
| if (counter + len2 > len) |
| { |
| result = ASN1_DER_ERROR; |
| goto cleanup; |
| } |
| len4 = |
| asn1_get_length_der (der + counter + len2, |
| len - counter - len2, &len3); |
| if (len4 < -1) |
| { |
| result = ASN1_DER_ERROR; |
| goto cleanup; |
| } |
| if (len4 != -1) |
| { |
| len2 += len4; |
| _asn1_set_value_lv (p, der + counter, len2 + len3); |
| counter += len2 + len3; |
| } |
| else |
| { /* indefinite length */ |
| /* Check indefinite lenth method in an EXPLICIT TAG */ |
| if ((p->type & CONST_TAG) && (der[counter - 1] == 0x80)) |
| indefinite = 1; |
| else |
| indefinite = 0; |
| |
| len2 = len - counter; |
| result = |
| _asn1_get_indefinite_length_string (der + counter, &len2); |
| if (result != ASN1_SUCCESS) |
| goto cleanup; |
| |
| _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) |
| { |
| if (!der[counter] && !der[counter + 1]) |
| { |
| counter += 2; |
| } |
| else |
| { |
| result = ASN1_DER_ERROR; |
| goto cleanup; |
| } |
| } |
| } |
| move = RIGHT; |
| break; |
| default: |
| move = (move == UP) ? RIGHT : DOWN; |
| break; |
| } |
| } |
| |
| 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 (counter != len) |
| { |
| result = ASN1_DER_ERROR; |
| goto cleanup; |
| } |
| |
| return ASN1_SUCCESS; |
| |
| cleanup: |
| asn1_delete_structure (element); |
| return result; |
| } |
| |
| #define FOUND 1 |
| #define SAME_BRANCH 2 |
| #define OTHER_BRANCH 3 |
| #define EXIT 4 |
| |
| /** |
| * 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. |
| * |
| * 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) |
| { |
| asn1_node node, p, p2, p3, nodeFound = NULL; |
| char temp[128], currentName[ASN1_MAX_NAME_SIZE * 10], *dot_p, *char_p; |
| int nameLen = ASN1_MAX_NAME_SIZE * 10 - 1, state; |
| int counter, len2, len3, len4, move, ris, tlen; |
| unsigned char class; |
| unsigned long tag; |
| int indefinite, result; |
| const unsigned char *der = ider; |
| |
| node = *structure; |
| |
| if (node == NULL) |
| return ASN1_ELEMENT_NOT_FOUND; |
| |
| if (elementName == NULL) |
| { |
| result = ASN1_ELEMENT_NOT_FOUND; |
| goto cleanup; |
| } |
| |
| if (node->type & CONST_OPTION) |
| { |
| result = ASN1_GENERIC_ERROR; |
| goto cleanup; |
| } |
| |
| if ((*structure)->name[0] != 0) |
| { /* Has *structure got a name? */ |
| nameLen -= strlen ((*structure)->name); |
| if (nameLen > 0) |
| strcpy (currentName, (*structure)->name); |
| else |
| { |
| result = ASN1_MEM_ERROR; |
| goto cleanup; |
| } |
| if (!(strcmp (currentName, elementName))) |
| { |
| state = FOUND; |
| nodeFound = *structure; |
| } |
| else if (!memcmp (currentName, elementName, strlen (currentName))) |
| state = SAME_BRANCH; |
| else |
| state = OTHER_BRANCH; |
| } |
| else |
| { /* *structure doesn't have a name? */ |
| currentName[0] = 0; |
| if (elementName[0] == 0) |
| { |
| state = FOUND; |
| nodeFound = *structure; |
| } |
| else |
| { |
| state = SAME_BRANCH; |
| } |
| } |
| |
| counter = 0; |
| move = DOWN; |
| p = node; |
| while (1) |
| { |
| |
| ris = ASN1_SUCCESS; |
| |
| if (move != UP) |
| { |
| if (p->type & CONST_SET) |
| { |
| p2 = _asn1_find_up (p); |
| len2 = _asn1_strtol (p2->value, NULL, 10); |
| if (counter == len2) |
| { |
| p = p2; |
| move = UP; |
| continue; |
| } |
| else if (counter > len2) |
| { |
| result = ASN1_DER_ERROR; |
| 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, |
| len - counter, &len2); |
| if (ris == ASN1_SUCCESS) |
| { |
| p2->type &= ~CONST_NOT_USED; |
| p = p2; |
| break; |
| } |
| } |
| p2 = p2->right; |
| } |
| if (p2 == NULL) |
| { |
| result = ASN1_DER_ERROR; |
| goto cleanup; |
| } |
| } |
| |
| if ((p->type & CONST_OPTION) || (p->type & CONST_DEFAULT)) |
| { |
| p2 = _asn1_find_up (p); |
| len2 = _asn1_strtol (p2->value, NULL, 10); |
| 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) |
| { |
| if (counter < len) |
| ris = |
| _asn1_extract_tag_der (p->down, der + counter, |
| len - counter, &len2); |
| else |
| ris = ASN1_DER_ERROR; |
| if (ris == ASN1_SUCCESS) |
| { |
| delete_unneeded_choice_fields(p->down); |
| break; |
| } |
| else if (ris == ASN1_ERROR_TYPE_ANY) |
| { |
| result = ASN1_ERROR_TYPE_ANY; |
| goto cleanup; |
| } |
| else |
| { |
| p2 = p->down; |
| asn1_delete_structure (&p2); |
| } |
| } |
| |
| if (p->down == NULL) |
| { |
| if (!(p->type & CONST_OPTION)) |
| { |
| result = ASN1_DER_ERROR; |
| goto cleanup; |
| } |
| } |
| else if (type_field (p->type) != ASN1_ETYPE_CHOICE) |
| p = p->down; |
| } |
| |
| if ((p->type & CONST_OPTION) || (p->type & CONST_DEFAULT)) |
| { |
| p2 = _asn1_find_up (p); |
| len2 = _asn1_strtol (p2->value, NULL, 10); |
| if (counter > len2) |
| ris = ASN1_TAG_ERROR; |
| } |
| |
| if (ris == ASN1_SUCCESS) |
| ris = |
| _asn1_extract_tag_der (p, der + counter, len - counter, &len2); |
| 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; |
| goto cleanup; |
| } |
| } |
| else |
| counter += len2; |
| } |
| |
| if (ris == ASN1_SUCCESS) |
| { |
| switch (type_field (p->type)) |
| { |
| case ASN1_ETYPE_NULL: |
| if (der[counter]) |
| { |
| result = ASN1_DER_ERROR; |
| goto cleanup; |
| } |
| |
| if (p == nodeFound) |
| state = EXIT; |
| |
| counter++; |
| move = RIGHT; |
| break; |
| case ASN1_ETYPE_BOOLEAN: |
| if (der[counter++] != 1) |
| { |
| result = ASN1_DER_ERROR; |
| goto cleanup; |
| } |
| |
| if (state == FOUND) |
| { |
| if (der[counter++] == 0) |
| _asn1_set_value (p, "F", 1); |
| else |
| _asn1_set_value (p, "T", 1); |
| |
| if (p == nodeFound) |
| state = EXIT; |
| |
| } |
| else |
| counter++; |
| |
| move = RIGHT; |
| break; |
| case ASN1_ETYPE_INTEGER: |
| case ASN1_ETYPE_ENUMERATED: |
| len2 = |
| asn1_get_length_der (der + counter, len - counter, &len3); |
| if (len2 < 0) |
| { |
| result = ASN1_DER_ERROR; |
| goto cleanup; |
| } |
| |
| if (state == FOUND) |
| { |
| if (len3 + len2 > len - counter) |
| { |
| result = ASN1_DER_ERROR; |
| goto cleanup; |
| } |
| _asn1_set_value (p, der + counter, len3 + len2); |
| |
| if (p == nodeFound) |
| state = EXIT; |
| } |
| counter += len3 + len2; |
| move = RIGHT; |
| break; |
| case ASN1_ETYPE_OBJECT_ID: |
| if (state == FOUND) |
| { |
| result = |
| _asn1_get_objectid_der (der + counter, len - counter, |
| &len2, temp, sizeof (temp)); |
| if (result != ASN1_SUCCESS) |
| goto cleanup; |
| |
| tlen = strlen (temp); |
| |
| if (tlen > 0) |
| _asn1_set_value (p, temp, tlen + 1); |
| |
| if (p == nodeFound) |
| state = EXIT; |
| } |
| else |
| { |
| len2 = |
| asn1_get_length_der (der + counter, len - counter, &len3); |
| if (len2 < 0) |
| { |
| result = ASN1_DER_ERROR; |
| goto cleanup; |
| } |
| len2 += len3; |
| } |
| |
| counter += len2; |
| move = RIGHT; |
| break; |
| case ASN1_ETYPE_GENERALIZED_TIME: |
| case ASN1_ETYPE_UTC_TIME: |
| if (state == FOUND) |
| { |
| result = |
| _asn1_get_time_der (der + counter, len - counter, &len2, |
| temp, sizeof (temp) - 1); |
| if (result != ASN1_SUCCESS) |
| goto cleanup; |
| |
| tlen = strlen (temp); |
| if (tlen > 0) |
| _asn1_set_value (p, temp, tlen + 1); |
| |
| if (p == nodeFound) |
| state = EXIT; |
| } |
| else |
| { |
| len2 = |
| asn1_get_length_der (der + counter, len - counter, &len3); |
| if (len2 < 0) |
| { |
| result = ASN1_DER_ERROR; |
| goto cleanup; |
| } |
| len2 += len3; |
| } |
| |
| counter += len2; |
| move = RIGHT; |
| break; |
| case ASN1_ETYPE_OCTET_STRING: |
| len3 = len - counter; |
| if (state == FOUND) |
| { |
| result = _asn1_get_octet_string (der + counter, p, &len3); |
| if (p == nodeFound) |
| state = EXIT; |
| } |
| else |
| result = _asn1_get_octet_string (der + counter, NULL, &len3); |
| |
| if (result != ASN1_SUCCESS) |
| goto cleanup; |
| |
| counter += len3; |
| 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, len - counter, &len3); |
| if (len2 < 0) |
| { |
| result = ASN1_DER_ERROR; |
| goto cleanup; |
| } |
| |
| if (state == FOUND) |
| { |
| if (len3 + len2 > len - counter) |
| { |
| result = ASN1_DER_ERROR; |
| goto cleanup; |
| } |
| _asn1_set_value (p, der + counter, len3 + len2); |
| |
| if (p == nodeFound) |
| state = EXIT; |
| } |
| counter += len3 + len2; |
| move = RIGHT; |
| break; |
| case ASN1_ETYPE_SEQUENCE: |
| case ASN1_ETYPE_SET: |
| if (move == UP) |
| { |
| len2 = _asn1_strtol (p->value, NULL, 10); |
| _asn1_set_value (p, NULL, 0); |
| if (len2 == -1) |
| { /* indefinite length method */ |
| if ((der[counter]) || der[counter + 1]) |
| { |
| result = ASN1_DER_ERROR; |
| goto cleanup; |
| } |
| counter += 2; |
| } |
| else |
| { /* definite length method */ |
| if (len2 != counter) |
| { |
| result = ASN1_DER_ERROR; |
| goto cleanup; |
| } |
| } |
| if (p == nodeFound) |
| state = EXIT; |
| move = RIGHT; |
| } |
| else |
| { /* move==DOWN || move==RIGHT */ |
| if (state == OTHER_BRANCH) |
| { |
| len3 = |
| asn1_get_length_der (der + counter, len - counter, |
| &len2); |
| if (len3 < 0) |
| { |
| result = ASN1_DER_ERROR; |
| goto cleanup; |
| } |
| counter += len2 + len3; |
| move = RIGHT; |
| } |
| else |
| { /* state==SAME_BRANCH or state==FOUND */ |
| len3 = |
| asn1_get_length_der (der + counter, len - counter, |
| &len2); |
| if (len3 < 0) |
| { |
| result = ASN1_DER_ERROR; |
| goto cleanup; |
| } |
| counter += len2; |
| if (len3 > 0) |
| { |
| _asn1_ltostr (counter + len3, temp); |
| tlen = strlen (temp); |
| |
| if (tlen > 0) |
| _asn1_set_value (p, temp, tlen + 1); |
| 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 */ |
| _asn1_set_value (p, "-1", 3); |
| move = DOWN; |
| } |
| } |
| } |
| break; |
| case ASN1_ETYPE_SEQUENCE_OF: |
| case ASN1_ETYPE_SET_OF: |
| if (move == UP) |
| { |
| len2 = _asn1_strtol (p->value, NULL, 10); |
| if (len2 > counter) |
| { |
| _asn1_append_sequence_set (p); |
| p = p->down; |
| while (p->right) |
| p = p->right; |
| move = RIGHT; |
| continue; |
| } |
| _asn1_set_value (p, NULL, 0); |
| if (len2 != counter) |
| { |
| result = ASN1_DER_ERROR; |
| goto cleanup; |
| } |
| |
| if (p == nodeFound) |
| state = EXIT; |
| } |
| else |
| { /* move==DOWN || move==RIGHT */ |
| if (state == OTHER_BRANCH) |
| { |
| len3 = |
| asn1_get_length_der (der + counter, len - counter, |
| &len2); |
| if (len3 < 0) |
| { |
| result = ASN1_DER_ERROR; |
| goto cleanup; |
| } |
| counter += len2 + len3; |
| move = RIGHT; |
| } |
| else |
| { /* state==FOUND or state==SAME_BRANCH */ |
| len3 = |
| asn1_get_length_der (der + counter, len - counter, |
| &len2); |
| if (len3 < 0) |
| { |
| result = ASN1_DER_ERROR; |
| goto cleanup; |
| } |
| counter += len2; |
| if (len3) |
| { |
| _asn1_ltostr (counter + len3, temp); |
| tlen = strlen (temp); |
| |
| if (tlen > 0) |
| _asn1_set_value (p, temp, tlen + 1); |
| p2 = p->down; |
| while ((type_field (p2->type) == ASN1_ETYPE_TAG) |
| || (type_field (p2->type) == |
| ASN1_ETYPE_SIZE)) |
| p2 = p2->right; |
| if (p2->right == NULL) |
| _asn1_append_sequence_set (p); |
| p = p2; |
| state = FOUND; |
| } |
| } |
| } |
| |
| break; |
| case ASN1_ETYPE_ANY: |
| if (asn1_get_tag_der |
| (der + counter, len - counter, &class, &len2, |
| &tag) != ASN1_SUCCESS) |
| { |
| result = ASN1_DER_ERROR; |
| goto cleanup; |
| } |
| |
| if (counter + len2 > len) |
| { |
| result = ASN1_DER_ERROR; |
| goto cleanup; |
| } |
| |
| len4 = |
| asn1_get_length_der (der + counter + len2, |
| len - counter - len2, &len3); |
| if (len4 < -1) |
| { |
| result = ASN1_DER_ERROR; |
| goto cleanup; |
| } |
| |
| if (len4 != -1) |
| { |
| len2 += len4; |
| if (state == FOUND) |
| { |
| _asn1_set_value_lv (p, der + counter, len2 + len3); |
| |
| if (p == nodeFound) |
| state = EXIT; |
| } |
| counter += len2 + len3; |
| } |
| else |
| { /* indefinite length */ |
| /* Check indefinite lenth method in an EXPLICIT TAG */ |
| if ((p->type & CONST_TAG) && (der[counter - 1] == 0x80)) |
| indefinite = 1; |
| else |
| indefinite = 0; |
| |
| len2 = len - counter; |
| result = |
| _asn1_get_indefinite_length_string (der + counter, &len2); |
| if (result != ASN1_SUCCESS) |
| goto cleanup; |
| |
| if (state == FOUND) |
| { |
| _asn1_set_value_lv (p, der + counter, len2); |
| |
| if (p == nodeFound) |
| state = EXIT; |
| } |
| |
| counter += len2; |
| |
| /* Check if a couple of 0x00 are present due to an EXPLICIT TAG with |
| an indefinite length method. */ |
| if (indefinite) |
| { |
| if (!der[counter] && !der[counter + 1]) |
| { |
| counter += 2; |
| } |
| else |
| { |
| result = ASN1_DER_ERROR; |
| goto cleanup; |
| } |
| } |
| } |
| move = RIGHT; |
| break; |
| |
| default: |
| move = (move == UP) ? RIGHT : DOWN; |
| break; |
| } |
| } |
| |
| if ((p == node && move != DOWN) || (state == EXIT)) |
| break; |
| |
| if (move == DOWN) |
| { |
| if (p->down) |
| { |
| p = p->down; |
| |
| if (state != FOUND) |
| { |
| nameLen -= strlen (p->name) + 1; |
| if (nameLen > 0) |
| { |
| if (currentName[0]) |
| strcat (currentName, "."); |
| strcat (currentName, p->name); |
| } |
| else |
| { |
| result = ASN1_MEM_ERROR; |
| goto cleanup; |
| } |
| if (!(strcmp (currentName, elementName))) |
| { |
| state = FOUND; |
| nodeFound = p; |
| } |
| else |
| if (!memcmp |
| (currentName, elementName, strlen (currentName))) |
| state = SAME_BRANCH; |
| else |
| state = OTHER_BRANCH; |
| } |
| } |
| else |
| move = RIGHT; |
| } |
| |
| if ((move == RIGHT) && !(p->type & CONST_SET)) |
| { |
| if (p->right) |
| { |
| p = p->right; |
| |
| if (state != FOUND) |
| { |
| dot_p = char_p = currentName; |
| while ((char_p = strchr (char_p, '.'))) |
| { |
| dot_p = char_p++; |
| dot_p++; |
| } |
| |
| nameLen += strlen (currentName) - (dot_p - currentName); |
| *dot_p = 0; |
| |
| nameLen -= strlen (p->name); |
| if (nameLen > 0) |
| strcat (currentName, p->name); |
| else |
| { |
| result = ASN1_MEM_ERROR; |
| goto cleanup; |
| } |
| |
| if (!(strcmp (currentName, elementName))) |
| { |
| state = FOUND; |
| nodeFound = p; |
| } |
| else |
| if (!memcmp |
| (currentName, elementName, strlen (currentName))) |
| state = SAME_BRANCH; |
| else |
| state = OTHER_BRANCH; |
| } |
| } |
| else |
| move = UP; |
| } |
| |
| if (move == UP) |
| { |
| p = _asn1_find_up (p); |
| |
| if (state != FOUND) |
| { |
| dot_p = char_p = currentName; |
| while ((char_p = strchr (char_p, '.'))) |
| { |
| dot_p = char_p++; |
| dot_p++; |
| } |
| |
| nameLen += strlen (currentName) - (dot_p - currentName); |
| *dot_p = 0; |
| |
| if (!(strcmp (currentName, elementName))) |
| { |
| state = FOUND; |
| nodeFound = p; |
| } |
| else |
| if (!memcmp (currentName, elementName, strlen (currentName))) |
| state = SAME_BRANCH; |
| else |
| state = OTHER_BRANCH; |
| } |
| } |
| } |
| |
| _asn1_delete_not_used (*structure); |
| |
| if (counter > len) |
| { |
| result = ASN1_DER_ERROR; |
| goto cleanup; |
| } |
| |
| return ASN1_SUCCESS; |
| |
| cleanup: |
| asn1_delete_structure (structure); |
| return result; |
| } |
| |
| /** |
| * asn1_der_decoding_startEnd: |
| * @element: pointer to an ASN1 element |
| * @ider: vector that contains the DER encoding. |
| * @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. |
| * |
| * 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 len, |
| const char *name_element, int *start, int *end) |
| { |
| asn1_node node, node_to_find, p, p2; |
| int counter, len2, len3, len4, move, ris; |
| unsigned char class; |
| unsigned long tag; |
| int indefinite; |
| const unsigned char *der = ider; |
| |
| 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; |
| |
| if (node_to_find == node) |
| { |
| *start = 0; |
| *end = len - 1; |
| return ASN1_SUCCESS; |
| } |
| |
| if (node->type & CONST_OPTION) |
| return ASN1_GENERIC_ERROR; |
| |
| counter = 0; |
| move = DOWN; |
| p = node; |
| while (1) |
| { |
| if (p == NULL) |
| return ASN1_DER_ERROR; |
| |
| ris = ASN1_SUCCESS; |
| |
| if (move != UP) |
| { |
| if (p->type & CONST_SET) |
| { |
| p2 = _asn1_find_up (p); |
| if (p2 == NULL) |
| return ASN1_DER_ERROR; |
| |
| len2 = _asn1_strtol (p2->value, NULL, 10); |
| if (len2 == -1) |
| { |
| if (!der[counter] && !der[counter + 1]) |
| { |
| p = p2; |
| move = UP; |
| counter += 2; |
| continue; |
| } |
| } |
| else if (counter == len2) |
| { |
| p = p2; |
| move = UP; |
| continue; |
| } |
| else if (counter > len2) |
| return ASN1_DER_ERROR; |
| |
| p2 = p2->down; |
| |
| while (p2) |
| { |
| if ((p2->type & CONST_SET) && (p2->type & CONST_NOT_USED)) |
| { /* CONTROLLARE */ |
| ris = |
| extract_tag_der_recursive (p2, der + counter, |
| len - counter, &len2); |
| if (ris == ASN1_SUCCESS) |
| { |
| p2->type &= ~CONST_NOT_USED; |
| p = p2; |
| break; |
| } |
| } |
| p2 = p2->right; |
| } |
| if (p2 == NULL) |
| return ASN1_DER_ERROR; |
| } |
| |
| if (p == node_to_find) |
| *start = counter; |
| |
| if (type_field (p->type) == ASN1_ETYPE_CHOICE) |
| { |
| p = p->down; |
| if (p == NULL) |
| return ASN1_DER_ERROR; |
| |
| ris = |
| _asn1_extract_tag_der (p, der + counter, len - counter, |
| &len2); |
| if (p == node_to_find) |
| *start = counter; |
| } |
| |
| if (ris == ASN1_SUCCESS) |
| ris = |
| _asn1_extract_tag_der (p, der + counter, len - counter, &len2); |
| if (ris != ASN1_SUCCESS) |
| { |
| if (p->type & CONST_OPTION) |
| { |
| p->type |= CONST_NOT_USED; |
| move = RIGHT; |
| } |
| else if (p->type & CONST_DEFAULT) |
| { |
| move = RIGHT; |
| } |
| else |
| { |
| return ASN1_TAG_ERROR; |
| } |
| } |
| else |
| counter += len2; |
| } |
| |
| if (ris == ASN1_SUCCESS) |
| { |
| switch (type_field (p->type)) |
| { |
| case ASN1_ETYPE_NULL: |
| if (der[counter]) |
| return ASN1_DER_ERROR; |
| counter++; |
| move = RIGHT; |
| break; |
| case ASN1_ETYPE_BOOLEAN: |
| if (der[counter++] != 1) |
| return ASN1_DER_ERROR; |
| counter++; |
| move = RIGHT; |
| break; |
| case ASN1_ETYPE_OCTET_STRING: |
| len3 = len - counter; |
| ris = _asn1_get_octet_string (der + counter, NULL, &len3); |
| if (ris != ASN1_SUCCESS) |
| return ris; |
| counter += len3; |
| move = RIGHT; |
| break; |
| case ASN1_ETYPE_UTC_TIME: |
| case ASN1_ETYPE_GENERALIZED_TIME: |
| case ASN1_ETYPE_OBJECT_ID: |
| case ASN1_ETYPE_INTEGER: |
| case ASN1_ETYPE_ENUMERATED: |
| 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, len - counter, &len3); |
| if (len2 < 0) |
| return ASN1_DER_ERROR; |
| counter += len3 + len2; |
| move = RIGHT; |
| break; |
| case ASN1_ETYPE_SEQUENCE: |
| case ASN1_ETYPE_SET: |
| if (move != UP) |
| { |
| len3 = |
| asn1_get_length_der (der + counter, len - counter, &len2); |
| if (len3 < -1) |
| return ASN1_DER_ERROR; |
| counter += len2; |
| if (len3 == 0) |
| move = RIGHT; |
| else |
| move = DOWN; |
| } |
| else |
| { |
| if (!der[counter] && !der[counter + 1]) /* indefinite length method */ |
| counter += 2; |
| move = RIGHT; |
| } |
| break; |
| case ASN1_ETYPE_SEQUENCE_OF: |
| case ASN1_ETYPE_SET_OF: |
| if (move != UP) |
| { |
| len3 = |
| asn1_get_length_der (der + counter, len - counter, &len2); |
| if (len3 < -1) |
| return ASN1_DER_ERROR; |
| counter += len2; |
| if ((len3 == -1) && !der[counter] && !der[counter + 1]) |
| counter += 2; |
| else if (len3) |
| { |
| p2 = p->down; |
| while ((type_field (p2->type) == ASN1_ETYPE_TAG) || |
| (type_field (p2->type) == ASN1_ETYPE_SIZE)) |
| p2 = p2->right; |
| p = p2; |
| } |
| } |
| else |
| { |
| if (!der[counter] && !der[counter + 1]) /* indefinite length method */ |
| counter += 2; |
| } |
| move = RIGHT; |
| break; |
| case ASN1_ETYPE_ANY: |
| if (asn1_get_tag_der |
| (der + counter, len - counter, &class, &len2, |
| &tag) != ASN1_SUCCESS) |
| return ASN1_DER_ERROR; |
| if (counter + len2 > len) |
| return ASN1_DER_ERROR; |
| |
| len4 = |
| asn1_get_length_der (der + counter + len2, |
| len - counter - len2, &len3); |
| if (len4 < -1) |
| return ASN1_DER_ERROR; |
| |
| if (len4 != -1) |
| { |
| counter += len2 + len4 + len3; |
| } |
| else |
| { /* indefinite length */ |
| /* Check indefinite lenth method in an EXPLICIT TAG */ |
| if ((p->type & CONST_TAG) && (der[counter - 1] == 0x80)) |
| indefinite = 1; |
| else |
| indefinite = 0; |
| |
| len2 = len - counter; |
| ris = |
| _asn1_get_indefinite_length_string (der + counter, &len2); |
| if (ris != ASN1_SUCCESS) |
| return ris; |
| counter += len2; |
| |
| /* Check if a couple of 0x00 are present due to an EXPLICIT TAG with |
| an indefinite length method. */ |
| if (indefinite) |
| { |
| if (!der[counter] && !der[counter + 1]) |
| counter += 2; |
| else |
| return ASN1_DER_ERROR; |
| } |
| } |
| move = RIGHT; |
| break; |
| default: |
| move = (move == UP) ? RIGHT : DOWN; |
| break; |
| } |
| } |
| |
| if ((p == node_to_find) && (move == RIGHT)) |
| { |
| *end = counter - 1; |
| return ASN1_SUCCESS; |
| } |
| |
| 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); |
| } |
| |
| return ASN1_ELEMENT_NOT_FOUND; |
| } |
| |
| /** |
| * 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 definitions, asn1_node * element) |
| { |
| char name[2 * ASN1_MAX_NAME_SIZE + 1], |
| value[ASN1_MAX_NAME_SIZE]; |
| int retCode = ASN1_SUCCESS, result; |
| int len, len2, len3; |
| asn1_node p, p2, 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 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 p2, 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 |
| * |
| * 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) |
| { |
| int tag_len, len_len; |
| const unsigned char *p; |
| unsigned char class; |
| unsigned long tag; |
| long ret; |
| |
| if (der == NULL || der_len == 0) |
| return ASN1_VALUE_NOT_VALID; |
| |
| if (ETYPE_OK (etype) == 0) |
| return ASN1_VALUE_NOT_VALID; |
| |
| /* doesn't handle constructed classes */ |
| if (ETYPE_CLASS (etype) != ASN1_CLASS_UNIVERSAL) |
| return ASN1_VALUE_NOT_VALID; |
| |
| p = der; |
| 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)) |
| return ASN1_DER_ERROR; |
| |
| p += tag_len; |
| der_len -= tag_len; |
| |
| ret = asn1_get_length_der (p, der_len, &len_len); |
| if (ret < 0) |
| return ASN1_DER_ERROR; |
| |
| p += len_len; |
| der_len -= len_len; |
| |
| *str_len = ret; |
| *str = p; |
| |
| return ASN1_SUCCESS; |
| } |