/* * 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 #include #include #include #include #include #include #define DEBUG #ifdef DEBUG # define warn() fprintf(stderr, "%s: %d\n", __func__, __LINE__) #else # define warn() #endif #define HAVE_TWO(x) (x>=2?1:0) #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 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 */ err = _asn1_get_indefinite_length_string (ber + 1, ber_len, &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; 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; 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) { *ret_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; *ret_len = counter; 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) { 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 counter, counter_end; int result; len2 = asn1_get_length_der (der, der_len, &len3); if (len2 < -1) return ASN1_DER_ERROR; counter = len3 + 1; if (len2 == -1) counter_end = der_len - 2; else counter_end = der_len; while (counter < counter_end) { len2 = asn1_get_length_der (der + counter, der_len, &len3); if (len2 < -1) return ASN1_DER_ERROR; if (len2 > 0) { DECR_LEN(der_len, len2+len3); _asn1_append_value (node, der + counter + len3, len2); } else { /* indefinite */ DECR_LEN(der_len, len3); result = _asn1_extract_der_octet (node, der + counter + len3, der_len); if (result != ASN1_SUCCESS) return result; DECR_LEN(der_len, len2); } DECR_LEN(der_len, 1); counter += len2 + len3 + 1; } return ASN1_SUCCESS; cleanup: return result; } static int _asn1_get_octet_string (asn1_node node, const unsigned char *der, int der_len, int *len) { int len2, len3, counter, tot_len, indefinite; int result; counter = 0; if (*(der - 1) & ASN1_CLASS_STRUCTURED) { tot_len = 0; indefinite = asn1_get_length_der (der, der_len, &len3); if (indefinite < -1) return ASN1_DER_ERROR; counter += len3; DECR_LEN(der_len, len3); if (indefinite >= 0) indefinite += len3; while (1) { if (counter > der_len) return ASN1_DER_ERROR; if (indefinite == -1) { if (HAVE_TWO(der_len) && (der[counter] == 0) && (der[counter + 1] == 0)) { counter += 2; DECR_LEN(der_len, 2); break; } } else if (counter >= indefinite) break; DECR_LEN(der_len, 1); if (der[counter] != ASN1_TAG_OCTET_STRING) return ASN1_DER_ERROR; counter++; len2 = asn1_get_length_der (der + counter, der_len, &len3); if (len2 <= 0) return ASN1_DER_ERROR; DECR_LEN(der_len, len3 + len2); counter += len3 + len2; tot_len += len2; } /* copy */ if (node) { unsigned char temp[ASN1_MAX_LENGTH_SIZE]; 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, der_len); if (ret != ASN1_SUCCESS) return ret; } } else { /* NOT STRUCTURED */ len2 = asn1_get_length_der (der, der_len, &len3); if (len2 < 0) return ASN1_DER_ERROR; DECR_LEN(der_len, len3+len2); counter = len3 + len2; if (node) _asn1_set_value (node, der, counter); } *len = counter; return ASN1_SUCCESS; cleanup: return result; } 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_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 ider_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; warn(); 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 (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); 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; } } 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) { ris = extract_tag_der_recursive (p->down, der + counter, ider_len, &len2); 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; } 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, ider_len, &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; warn(); goto cleanup; } } else { DECR_LEN(ider_len, len2); counter += len2; } } 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_objectid_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 (der + counter, ider_len, &len2, temp, sizeof (temp) - 1); 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: result = _asn1_get_octet_string (p, der + counter, ider_len, &len3); if (result != ASN1_SUCCESS) { warn(); goto cleanup; } DECR_LEN(ider_len, len3); 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, 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 = _asn1_strtol (p->value, NULL, 10); _asn1_set_value (p, NULL, 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 (len3 < -1) { result = ASN1_DER_ERROR; warn(); goto cleanup; } DECR_LEN(ider_len, len2); 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 (!HAVE_TWO(ider_len) || ((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); DECR_LEN(ider_len, 2); 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; warn(); goto cleanup; } } } else { /* move==DOWN || move==RIGHT */ len3 = asn1_get_length_der (der + counter, ider_len, &len2); if (len3 < -1) { result = ASN1_DER_ERROR; warn(); goto cleanup; } DECR_LEN(ider_len, len2); 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, 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 (len4 < -1) { 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; } /* Check indefinite lenth method in an EXPLICIT TAG */ if ((p->type & CONST_TAG) && (der[counter - 1] == 0x80)) indefinite = 1; else indefinite = 0; 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 == 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) { warn(); result = ASN1_DER_ERROR; goto cleanup; } return ASN1_SUCCESS; cleanup: asn1_delete_structure (element); return result; } /** * 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. * * As of libtasn1 3.6 this function is an alias of asn1_der_decoding(). * * 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. * @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 ider_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, result = ASN1_DER_ERROR; 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 = ider_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) { warn(); return ASN1_DER_ERROR; } len2 = _asn1_strtol (p2->value, NULL, 10); 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) { warn(); 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, ider_len, &len2); if (ris == ASN1_SUCCESS) { p2->type &= ~CONST_NOT_USED; p = p2; break; } } p2 = p2->right; } if (p2 == NULL) { warn(); 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) { warn(); return ASN1_DER_ERROR; } ris = _asn1_extract_tag_der (p, der + counter, ider_len, &len2); if (p == node_to_find) *start = counter; } if (ris == ASN1_SUCCESS) ris = _asn1_extract_tag_der (p, der + counter, ider_len, &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 { warn(); return ASN1_TAG_ERROR; } } else { DECR_LEN(ider_len, len2); counter += len2; } } if (ris == ASN1_SUCCESS) { switch (type_field (p->type)) { case ASN1_ETYPE_NULL: DECR_LEN(ider_len, 1); if (der[counter]) { warn(); return ASN1_DER_ERROR; } counter++; move = RIGHT; break; case ASN1_ETYPE_BOOLEAN: DECR_LEN(ider_len, 2); if (der[counter] != 1) { warn(); return ASN1_DER_ERROR; } counter += 2; move = RIGHT; break; case ASN1_ETYPE_OCTET_STRING: ris = _asn1_get_octet_string (NULL, der + counter, ider_len, &len3); if (ris != ASN1_SUCCESS) { warn(); return ris; } DECR_LEN(ider_len, len3); 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, ider_len, &len3); if (len2 < 0) { warn(); return ASN1_DER_ERROR; } DECR_LEN(ider_len, len3 + len2); counter += len3 + len2; move = RIGHT; break; case ASN1_ETYPE_SEQUENCE: case ASN1_ETYPE_SET: if (move != UP) { len3 = asn1_get_length_der (der + counter, ider_len, &len2); if (len3 < -1) { warn(); return ASN1_DER_ERROR; } DECR_LEN(ider_len, len2); counter += len2; if (len3 == 0) move = RIGHT; else move = DOWN; } else { if (HAVE_TWO(ider_len) && !der[counter] && !der[counter + 1]) /* indefinite length method */ { counter += 2; DECR_LEN(ider_len, 2); } move = RIGHT; } break; case ASN1_ETYPE_SEQUENCE_OF: case ASN1_ETYPE_SET_OF: if (move != UP) { len3 = asn1_get_length_der (der + counter, ider_len, &len2); if (len3 < -1) { warn(); return ASN1_DER_ERROR; } DECR_LEN(ider_len, len2); counter += len2; if (len3 == -1) { if (HAVE_TWO(ider_len) && !der[counter] && !der[counter + 1]) { DECR_LEN(ider_len, 2); counter += 2; } } 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 (HAVE_TWO(ider_len) && !der[counter] && !der[counter + 1]) /* indefinite length method */ { DECR_LEN(ider_len, 2); counter += 2; } } move = RIGHT; break; case ASN1_ETYPE_ANY: if (asn1_get_tag_der (der + counter, ider_len, &class, &len2, &tag) != ASN1_SUCCESS) { warn(); return ASN1_DER_ERROR; } DECR_LEN(ider_len, len2); len4 = asn1_get_length_der (der + counter + len2, ider_len, &len3); if (len4 < -1) { warn(); return ASN1_DER_ERROR; } if (len4 != -1) { DECR_LEN(ider_len, len3 + len4); counter += len2 + len3 + len4; } else { /* indefinite length */ /* Check indefinite lenth method in an EXPLICIT TAG */ ider_len += len2; /* undo DECR_LEN */ if (counter == 0) { result = ASN1_DER_ERROR; warn(); goto cleanup; } if ((p->type & CONST_TAG) && (der[counter - 1] == 0x80)) indefinite = 1; else indefinite = 0; ris = _asn1_get_indefinite_length_string (der + counter, ider_len, &len2); if (ris != ASN1_SUCCESS) { warn(); return ris; } counter += len2; DECR_LEN(ider_len, 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 { warn(); 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); } warn(); return ASN1_ELEMENT_NOT_FOUND; cleanup: return result; } /** * 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; }