/* * 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: coding.c */ /* Description: Functions to create a DER coding of */ /* an ASN1 type. */ /*****************************************************/ #include #include "parser_aux.h" #include #include "element.h" #include "minmax.h" #include #define MAX_TAG_LEN 16 /******************************************************/ /* Function : _asn1_error_description_value_not_found */ /* Description: creates the ErrorDescription string */ /* for the ASN1_VALUE_NOT_FOUND error. */ /* Parameters: */ /* node: node of the tree where the value is NULL. */ /* ErrorDescription: string returned. */ /* Return: */ /******************************************************/ static void _asn1_error_description_value_not_found (asn1_node node, char *ErrorDescription) { if (ErrorDescription == NULL) return; Estrcpy (ErrorDescription, ":: value of element '"); _asn1_hierarchical_name (node, ErrorDescription + strlen (ErrorDescription), ASN1_MAX_ERROR_DESCRIPTION_SIZE - 40); Estrcat (ErrorDescription, "' not found"); } /** * asn1_length_der: * @len: value to convert. * @der: buffer to hold the returned encoding (may be %NULL). * @der_len: number of meaningful bytes of ANS (der[0]..der[der_len-1]). * * Creates the DER encoding of the provided length value. * The @der buffer must have enough room for the output. The maximum * length this function will encode is %ASN1_MAX_LENGTH_SIZE. * * To know the size of the DER encoding use a %NULL value for @der. **/ void asn1_length_der (unsigned long int len, unsigned char *der, int *der_len) { int k; unsigned char temp[ASN1_MAX_LENGTH_SIZE]; #if SIZEOF_UNSIGNED_LONG_INT > 8 len &= 0xFFFFFFFFFFFFFFFF; #endif if (len < 128) { /* short form */ if (der != NULL) der[0] = (unsigned char) len; *der_len = 1; } else { /* Long form */ k = 0; while (len) { temp[k++] = len & 0xFF; len = len >> 8; } *der_len = k + 1; if (der != NULL) { der[0] = ((unsigned char) k & 0x7F) + 128; while (k--) der[*der_len - 1 - k] = temp[k]; } } } /******************************************************/ /* Function : _asn1_tag_der */ /* Description: creates the DER coding for the CLASS */ /* and TAG parameters. */ /* It is limited by the ASN1_MAX_TAG_SIZE variable */ /* Parameters: */ /* class: value to convert. */ /* tag_value: value to convert. */ /* ans: string returned. */ /* ans_len: number of meaningful bytes of ANS */ /* (ans[0]..ans[ans_len-1]). */ /* Return: */ /******************************************************/ static void _asn1_tag_der (unsigned char class, unsigned int tag_value, unsigned char *ans, int *ans_len) { int k; unsigned char temp[ASN1_MAX_TAG_SIZE]; if (tag_value < 31) { /* short form */ ans[0] = (class & 0xE0) + ((unsigned char) (tag_value & 0x1F)); *ans_len = 1; } else { /* Long form */ ans[0] = (class & 0xE0) + 31; k = 0; while (tag_value != 0) { temp[k++] = tag_value & 0x7F; tag_value >>= 7; if (k > ASN1_MAX_TAG_SIZE - 1) break; /* will not encode larger tags */ } *ans_len = k + 1; while (k--) ans[*ans_len - 1 - k] = temp[k] + 128; ans[*ans_len - 1] -= 128; } } /** * asn1_octet_der: * @str: the input data. * @str_len: STR length (str[0]..str[*str_len-1]). * @der: encoded string returned. * @der_len: number of meaningful bytes of DER (der[0]..der[der_len-1]). * * Creates a length-value DER encoding for the input data. * The DER encoding of the input data will be placed in the @der variable. * * Note that the OCTET STRING tag is not included in the output. * * This function does not return any value because it is expected * that @der_len will contain enough bytes to store the string * plus the DER encoding. The DER encoding size can be obtained using * asn1_length_der(). **/ void asn1_octet_der (const unsigned char *str, int str_len, unsigned char *der, int *der_len) { int len_len; if (der == NULL || str_len < 0) return; asn1_length_der (str_len, der, &len_len); memcpy (der + len_len, str, str_len); *der_len = str_len + len_len; } /** * asn1_encode_simple_der: * @etype: The type of the string to be encoded (ASN1_ETYPE_) * @str: the string data. * @str_len: the string length * @tl: the encoded tag and length * @tl_len: the bytes of the @tl field * * Creates the DER encoding for various simple ASN.1 types like strings etc. * It stores the tag and length in @tl, which should have space for at least * %ASN1_MAX_TL_SIZE bytes. Initially @tl_len should contain the size of @tl. * * The complete DER encoding should consist of the value in @tl appended * with the provided @str. * * Returns: %ASN1_SUCCESS if successful or an error value. **/ int asn1_encode_simple_der (unsigned int etype, const unsigned char *str, unsigned int str_len, unsigned char *tl, unsigned int *tl_len) { int tag_len, len_len; unsigned tlen; unsigned char der_tag[ASN1_MAX_TAG_SIZE]; unsigned char der_length[ASN1_MAX_LENGTH_SIZE]; unsigned char *p; if (str == NULL) return ASN1_VALUE_NOT_VALID; if (ETYPE_OK (etype) == 0) return ASN1_VALUE_NOT_VALID; /* doesn't handle constructed classes */ if (ETYPE_CLASS (etype) != ASN1_CLASS_UNIVERSAL) return ASN1_VALUE_NOT_VALID; _asn1_tag_der (ETYPE_CLASS (etype), ETYPE_TAG (etype), der_tag, &tag_len); asn1_length_der (str_len, der_length, &len_len); if (tag_len <= 0 || len_len <= 0) return ASN1_VALUE_NOT_VALID; tlen = tag_len + len_len; if (*tl_len < tlen) return ASN1_MEM_ERROR; p = tl; memcpy (p, der_tag, tag_len); p += tag_len; memcpy (p, der_length, len_len); *tl_len = tlen; return ASN1_SUCCESS; } /******************************************************/ /* Function : _asn1_time_der */ /* Description: creates the DER coding for a TIME */ /* type (length included). */ /* Parameters: */ /* str: TIME null-terminated string. */ /* der: string returned. */ /* der_len: number of meaningful bytes of DER */ /* (der[0]..der[ans_len-1]). Initially it */ /* if must store the lenght of DER. */ /* Return: */ /* ASN1_MEM_ERROR when DER isn't big enough */ /* ASN1_SUCCESS otherwise */ /******************************************************/ static int _asn1_time_der (unsigned char *str, int str_len, unsigned char *der, int *der_len) { int len_len; int max_len; if (der == NULL) return ASN1_VALUE_NOT_VALID; max_len = *der_len; asn1_length_der (str_len, (max_len > 0) ? der : NULL, &len_len); if ((len_len + str_len) <= max_len) memcpy (der + len_len, str, str_len); *der_len = len_len + str_len; if ((*der_len) > max_len) return ASN1_MEM_ERROR; return ASN1_SUCCESS; } /* void _asn1_get_utctime_der(unsigned char *der,int *der_len,unsigned char *str) { int len_len,str_len; char temp[20]; if(str==NULL) return; str_len=asn1_get_length_der(der,*der_len,&len_len); if (str_len<0) return; memcpy(temp,der+len_len,str_len); *der_len=str_len+len_len; switch(str_len){ case 11: temp[10]=0; strcat(temp,"00+0000"); break; case 13: temp[12]=0; strcat(temp,"+0000"); break; case 15: temp[15]=0; memmove(temp+12,temp+10,6); temp[10]=temp[11]='0'; break; case 17: temp[17]=0; break; default: return; } strcpy(str,temp); } */ /******************************************************/ /* Function : _asn1_objectid_der */ /* Description: creates the DER coding for an */ /* OBJECT IDENTIFIER type (length included). */ /* Parameters: */ /* str: OBJECT IDENTIFIER null-terminated string. */ /* der: string returned. */ /* der_len: number of meaningful bytes of DER */ /* (der[0]..der[ans_len-1]). Initially it */ /* must store the length of DER. */ /* Return: */ /* ASN1_MEM_ERROR when DER isn't big enough */ /* ASN1_SUCCESS if succesful */ /* or an error value. */ /******************************************************/ static int _asn1_objectid_der (unsigned char *str, unsigned char *der, int *der_len) { int len_len, counter, k, first, max_len; char *temp, *n_end, *n_start; unsigned char bit7; uint64_t val, val1 = 0; int str_len = _asn1_strlen (str); max_len = *der_len; if (der == NULL && max_len > 0) return ASN1_VALUE_NOT_VALID; temp = malloc (str_len + 2); if (temp == NULL) return ASN1_MEM_ALLOC_ERROR; memcpy (temp, str, str_len); temp[str_len] = '.'; temp[str_len + 1] = 0; counter = 0; n_start = temp; while ((n_end = strchr (n_start, '.'))) { *n_end = 0; val = _asn1_strtou64 (n_start, NULL, 10); counter++; if (counter == 1) val1 = val; else if (counter == 2) { if (max_len > 0) der[0] = 40 * val1 + val; *der_len = 1; } else { first = 0; for (k = sizeof(val); k >= 0; k--) { bit7 = (val >> (k * 7)) & 0x7F; if (bit7 || first || !k) { if (k) bit7 |= 0x80; if (max_len > (*der_len)) der[*der_len] = bit7; (*der_len)++; first = 1; } } } n_start = n_end + 1; } asn1_length_der (*der_len, NULL, &len_len); if (max_len >= (*der_len + len_len)) { memmove (der + len_len, der, *der_len); asn1_length_der (*der_len, der, &len_len); } *der_len += len_len; free (temp); if (max_len < (*der_len)) return ASN1_MEM_ERROR; return ASN1_SUCCESS; } static const unsigned char bit_mask[] = { 0xFF, 0xFE, 0xFC, 0xF8, 0xF0, 0xE0, 0xC0, 0x80 }; /** * asn1_bit_der: * @str: BIT string. * @bit_len: number of meaningful bits in STR. * @der: string returned. * @der_len: number of meaningful bytes of DER * (der[0]..der[ans_len-1]). * * Creates a length-value DER encoding for the input data * as it would have been for a BIT STRING. * The DER encoded data will be copied in @der. * * Note that the BIT STRING tag is not included in the output. * * This function does not return any value because it is expected * that @der_len will contain enough bytes to store the string * plus the DER encoding. The DER encoding size can be obtained using * asn1_length_der(). **/ void asn1_bit_der (const unsigned char *str, int bit_len, unsigned char *der, int *der_len) { int len_len, len_byte, len_pad; if (der == NULL) return; len_byte = bit_len >> 3; len_pad = 8 - (bit_len & 7); if (len_pad == 8) len_pad = 0; else len_byte++; asn1_length_der (len_byte + 1, der, &len_len); der[len_len] = len_pad; if (str) memcpy (der + len_len + 1, str, len_byte); der[len_len + len_byte] &= bit_mask[len_pad]; *der_len = len_byte + len_len + 1; } /******************************************************/ /* Function : _asn1_complete_explicit_tag */ /* Description: add the length coding to the EXPLICIT */ /* tags. */ /* Parameters: */ /* node: pointer to the tree element. */ /* der: string with the DER coding of the whole tree*/ /* counter: number of meaningful bytes of DER */ /* (der[0]..der[*counter-1]). */ /* max_len: size of der vector */ /* Return: */ /* ASN1_MEM_ERROR if der vector isn't big enough, */ /* otherwise ASN1_SUCCESS. */ /******************************************************/ static int _asn1_complete_explicit_tag (asn1_node node, unsigned char *der, int *counter, int *max_len) { asn1_node p; int is_tag_implicit, len2, len3; unsigned char temp[SIZEOF_UNSIGNED_INT]; if (der == NULL && *max_len > 0) { return ASN1_VALUE_NOT_VALID; } is_tag_implicit = 0; if (node->type & CONST_TAG) { p = node->down; if (p == NULL) return ASN1_DER_ERROR; /* When there are nested tags we must complete them reverse to the order they were created. This is because completing a tag modifies all data within it, including the incomplete tags which store buffer positions -- simon@josefsson.org 2002-09-06 */ while (p->right) p = p->right; while (p && p != node->down->left) { if (type_field (p->type) == ASN1_ETYPE_TAG) { if (p->type & CONST_EXPLICIT) { len2 = strtol (p->name, NULL, 10); _asn1_set_name (p, NULL); asn1_length_der (*counter - len2, temp, &len3); if (len3 <= (*max_len)) { memmove (der + len2 + len3, der + len2, *counter - len2); memcpy (der + len2, temp, len3); } *max_len -= len3; *counter += len3; is_tag_implicit = 0; } else { /* CONST_IMPLICIT */ if (!is_tag_implicit) { is_tag_implicit = 1; } } } p = p->left; } } if (*max_len < 0) return ASN1_MEM_ERROR; return ASN1_SUCCESS; } const tag_and_class_st _asn1_tags[] = { [ASN1_ETYPE_GENERALSTRING] = {ASN1_TAG_GENERALSTRING, ASN1_CLASS_UNIVERSAL, "type:GENERALSTRING"}, [ASN1_ETYPE_NUMERIC_STRING] = {ASN1_TAG_NUMERIC_STRING, ASN1_CLASS_UNIVERSAL, "type:NUMERIC_STR"}, [ASN1_ETYPE_IA5_STRING] = {ASN1_TAG_IA5_STRING, ASN1_CLASS_UNIVERSAL, "type:IA5_STR"}, [ASN1_ETYPE_TELETEX_STRING] = {ASN1_TAG_TELETEX_STRING, ASN1_CLASS_UNIVERSAL, "type:TELETEX_STR"}, [ASN1_ETYPE_PRINTABLE_STRING] = {ASN1_TAG_PRINTABLE_STRING, ASN1_CLASS_UNIVERSAL, "type:PRINTABLE_STR"}, [ASN1_ETYPE_UNIVERSAL_STRING] = {ASN1_TAG_UNIVERSAL_STRING, ASN1_CLASS_UNIVERSAL, "type:UNIVERSAL_STR"}, [ASN1_ETYPE_BMP_STRING] = {ASN1_TAG_BMP_STRING, ASN1_CLASS_UNIVERSAL, "type:BMP_STR"}, [ASN1_ETYPE_UTF8_STRING] = {ASN1_TAG_UTF8_STRING, ASN1_CLASS_UNIVERSAL, "type:UTF8_STR"}, [ASN1_ETYPE_VISIBLE_STRING] = {ASN1_TAG_VISIBLE_STRING, ASN1_CLASS_UNIVERSAL, "type:VISIBLE_STR"}, [ASN1_ETYPE_OCTET_STRING] = {ASN1_TAG_OCTET_STRING, ASN1_CLASS_UNIVERSAL, "type:OCT_STR"}, [ASN1_ETYPE_BIT_STRING] = {ASN1_TAG_BIT_STRING, ASN1_CLASS_UNIVERSAL, "type:BIT_STR"}, [ASN1_ETYPE_OBJECT_ID] = {ASN1_TAG_OBJECT_ID, ASN1_CLASS_UNIVERSAL, "type:OBJ_ID"}, [ASN1_ETYPE_NULL] = {ASN1_TAG_NULL, ASN1_CLASS_UNIVERSAL, "type:NULL"}, [ASN1_ETYPE_BOOLEAN] = {ASN1_TAG_BOOLEAN, ASN1_CLASS_UNIVERSAL, "type:BOOLEAN"}, [ASN1_ETYPE_INTEGER] = {ASN1_TAG_INTEGER, ASN1_CLASS_UNIVERSAL, "type:INTEGER"}, [ASN1_ETYPE_ENUMERATED] = {ASN1_TAG_ENUMERATED, ASN1_CLASS_UNIVERSAL, "type:ENUMERATED"}, [ASN1_ETYPE_SEQUENCE] = {ASN1_TAG_SEQUENCE, ASN1_CLASS_UNIVERSAL | ASN1_CLASS_STRUCTURED, "type:SEQUENCE"}, [ASN1_ETYPE_SEQUENCE_OF] = {ASN1_TAG_SEQUENCE, ASN1_CLASS_UNIVERSAL | ASN1_CLASS_STRUCTURED, "type:SEQ_OF"}, [ASN1_ETYPE_SET] = {ASN1_TAG_SET, ASN1_CLASS_UNIVERSAL | ASN1_CLASS_STRUCTURED, "type:SET"}, [ASN1_ETYPE_SET_OF] = {ASN1_TAG_SET, ASN1_CLASS_UNIVERSAL | ASN1_CLASS_STRUCTURED, "type:SET_OF"}, [ASN1_ETYPE_GENERALIZED_TIME] = {ASN1_TAG_GENERALIZEDTime, ASN1_CLASS_UNIVERSAL, "type:GENERALIZED_TIME"}, [ASN1_ETYPE_UTC_TIME] = {ASN1_TAG_UTCTime, ASN1_CLASS_UNIVERSAL, "type:UTC_TIME"}, }; unsigned int _asn1_tags_size = sizeof (_asn1_tags) / sizeof (_asn1_tags[0]); /******************************************************/ /* Function : _asn1_insert_tag_der */ /* Description: creates the DER coding of tags of one */ /* NODE. */ /* Parameters: */ /* node: pointer to the tree element. */ /* der: string returned */ /* counter: number of meaningful bytes of DER */ /* (counter[0]..der[*counter-1]). */ /* max_len: size of der vector */ /* Return: */ /* ASN1_GENERIC_ERROR if the type is unknown, */ /* ASN1_MEM_ERROR if der vector isn't big enough, */ /* otherwise ASN1_SUCCESS. */ /******************************************************/ static int _asn1_insert_tag_der (asn1_node node, unsigned char *der, int *counter, int *max_len) { asn1_node p; int tag_len, is_tag_implicit; unsigned char class, class_implicit = 0, temp[MAX(SIZEOF_UNSIGNED_INT * 3 + 1, LTOSTR_MAX_SIZE)]; unsigned long tag_implicit = 0; unsigned char tag_der[MAX_TAG_LEN]; is_tag_implicit = 0; if (node->type & CONST_TAG) { p = node->down; while (p) { if (type_field (p->type) == ASN1_ETYPE_TAG) { if (p->type & CONST_APPLICATION) class = ASN1_CLASS_APPLICATION; else if (p->type & CONST_UNIVERSAL) class = ASN1_CLASS_UNIVERSAL; else if (p->type & CONST_PRIVATE) class = ASN1_CLASS_PRIVATE; else class = ASN1_CLASS_CONTEXT_SPECIFIC; if (p->type & CONST_EXPLICIT) { if (is_tag_implicit) _asn1_tag_der (class_implicit, tag_implicit, tag_der, &tag_len); else _asn1_tag_der (class | ASN1_CLASS_STRUCTURED, _asn1_strtoul (p->value, NULL, 10), tag_der, &tag_len); *max_len -= tag_len; if (der && *max_len >= 0) memcpy (der + *counter, tag_der, tag_len); *counter += tag_len; _asn1_ltostr (*counter, (char *) temp); _asn1_set_name (p, (const char *) temp); is_tag_implicit = 0; } else { /* CONST_IMPLICIT */ if (!is_tag_implicit) { if ((type_field (node->type) == ASN1_ETYPE_SEQUENCE) || (type_field (node->type) == ASN1_ETYPE_SEQUENCE_OF) || (type_field (node->type) == ASN1_ETYPE_SET) || (type_field (node->type) == ASN1_ETYPE_SET_OF)) class |= ASN1_CLASS_STRUCTURED; class_implicit = class; tag_implicit = _asn1_strtoul (p->value, NULL, 10); is_tag_implicit = 1; } } } p = p->right; } } if (is_tag_implicit) { _asn1_tag_der (class_implicit, tag_implicit, tag_der, &tag_len); } else { unsigned type = type_field (node->type); switch (type) { CASE_HANDLED_ETYPES: _asn1_tag_der (_asn1_tags[type].class, _asn1_tags[type].tag, tag_der, &tag_len); break; case ASN1_ETYPE_TAG: case ASN1_ETYPE_CHOICE: case ASN1_ETYPE_ANY: tag_len = 0; break; default: return ASN1_GENERIC_ERROR; } } *max_len -= tag_len; if (der && *max_len >= 0) memcpy (der + *counter, tag_der, tag_len); *counter += tag_len; if (*max_len < 0) return ASN1_MEM_ERROR; return ASN1_SUCCESS; } /******************************************************/ /* Function : _asn1_ordering_set */ /* Description: puts the elements of a SET type in */ /* the correct order according to DER rules. */ /* Parameters: */ /* der: string with the DER coding. */ /* node: pointer to the SET element. */ /* Return: */ /* ASN1_SUCCESS if successful */ /* or an error value. */ /******************************************************/ static int _asn1_ordering_set (unsigned char *der, int der_len, asn1_node node) { struct vet { int end; unsigned long value; struct vet *next, *prev; }; int counter, len, len2; struct vet *first, *last, *p_vet, *p2_vet; asn1_node p; unsigned char class, *temp; unsigned long tag, t; int err; counter = 0; if (type_field (node->type) != ASN1_ETYPE_SET) return ASN1_VALUE_NOT_VALID; p = node->down; while (p && ((type_field (p->type) == ASN1_ETYPE_TAG) || (type_field (p->type) == ASN1_ETYPE_SIZE))) p = p->right; if ((p == NULL) || (p->right == NULL)) return ASN1_SUCCESS; first = last = NULL; while (p) { p_vet = malloc (sizeof (struct vet)); if (p_vet == NULL) { err = ASN1_MEM_ALLOC_ERROR; goto error; } p_vet->next = NULL; p_vet->prev = last; if (first == NULL) first = p_vet; else last->next = p_vet; last = p_vet; /* tag value calculation */ err = asn1_get_tag_der (der + counter, der_len - counter, &class, &len2, &tag); if (err != ASN1_SUCCESS) goto error; t = ((unsigned int)class) << 24; p_vet->value = t | tag; counter += len2; /* extraction and length */ len2 = asn1_get_length_der (der + counter, der_len - counter, &len); if (len2 < 0) { err = ASN1_DER_ERROR; goto error; } counter += len + len2; p_vet->end = counter; p = p->right; } p_vet = first; while (p_vet) { p2_vet = p_vet->next; counter = 0; while (p2_vet) { if (p_vet->value > p2_vet->value) { /* change position */ temp = malloc (p_vet->end - counter); if (temp == NULL) { err = ASN1_MEM_ALLOC_ERROR; goto error; } memcpy (temp, der + counter, p_vet->end - counter); memcpy (der + counter, der + p_vet->end, p2_vet->end - p_vet->end); memcpy (der + counter + p2_vet->end - p_vet->end, temp, p_vet->end - counter); free (temp); tag = p_vet->value; p_vet->value = p2_vet->value; p2_vet->value = tag; p_vet->end = counter + (p2_vet->end - p_vet->end); } counter = p_vet->end; p2_vet = p2_vet->next; p_vet = p_vet->next; } if (p_vet != first) p_vet->prev->next = NULL; else first = NULL; free (p_vet); p_vet = first; } return ASN1_SUCCESS; error: while (first != NULL) { p_vet = first; first = first->next; free(p_vet); } return err; } struct vet { unsigned char *ptr; int size; }; static int setof_compar(const void *_e1, const void *_e2) { unsigned length; const struct vet *e1 = _e1, *e2 = _e2; int rval; /* The encodings of the component values of a set-of value shall * appear in ascending order, the encodings being compared * as octet strings with the shorter components being * padded at their trailing end with 0-octets. * The padding octets are for comparison purposes and * do not appear in the encodings. */ length = MIN(e1->size, e2->size); rval = memcmp(e1->ptr, e2->ptr, length); if (rval == 0 && e1->size != e2->size) { if (e1->size > e2->size) rval = 1; else if (e2->size > e1->size) rval = -1; } return rval; } /******************************************************/ /* Function : _asn1_ordering_set_of */ /* Description: puts the elements of a SET OF type in */ /* the correct order according to DER rules. */ /* Parameters: */ /* der: string with the DER coding. */ /* node: pointer to the SET OF element. */ /* Return: */ /* ASN1_SUCCESS if successful */ /* or an error value. */ /******************************************************/ static int _asn1_ordering_set_of (unsigned char *der, int der_len, asn1_node node) { int counter, len, len2; struct vet *list = NULL, *tlist; unsigned list_size = 0; struct vet *p_vet; asn1_node p; unsigned char class; unsigned i; unsigned char *out = NULL; int err; if (der == NULL) return ASN1_VALUE_NOT_VALID; counter = 0; if (type_field (node->type) != ASN1_ETYPE_SET_OF) return ASN1_VALUE_NOT_VALID; p = node->down; while (p && ((type_field (p->type) == ASN1_ETYPE_TAG) || (type_field (p->type) == ASN1_ETYPE_SIZE))) p = p->right; if (p == NULL) return ASN1_VALUE_NOT_VALID; p = p->right; if ((p == NULL) || (p->right == NULL)) return ASN1_SUCCESS; while (p) { list_size++; tlist = realloc (list, list_size*sizeof(struct vet)); if (tlist == NULL) { err = ASN1_MEM_ALLOC_ERROR; goto error; } list = tlist; p_vet = &list[list_size-1]; p_vet->ptr = der+counter; p_vet->size = 0; /* extraction of tag and length */ if (der_len - counter > 0) { err = asn1_get_tag_der (der + counter, der_len - counter, &class, &len, NULL); if (err != ASN1_SUCCESS) goto error; counter += len; p_vet->size += len; len2 = asn1_get_length_der (der + counter, der_len - counter, &len); if (len2 < 0) { err = ASN1_DER_ERROR; goto error; } counter += len + len2; p_vet->size += len + len2; } else { err = ASN1_DER_ERROR; goto error; } p = p->right; } if (counter > der_len) { err = ASN1_DER_ERROR; goto error; } qsort(list, list_size, sizeof(struct vet), setof_compar); out = malloc(der_len); if (out == NULL) { err = ASN1_MEM_ERROR; goto error; } /* the sum of p_vet->size == der_len */ counter = 0; for (i=0;iptr, p_vet->size); counter += p_vet->size; } memcpy(der, out, der_len); free(out); err = ASN1_SUCCESS; error: free(list); return err; } /** * asn1_der_coding: * @element: pointer to an ASN1 element * @name: the name of the structure you want to encode (it must be * inside *POINTER). * @ider: vector that will contain the DER encoding. DER must be a * pointer to memory cells already allocated. * @len: number of bytes of *@ider: @ider[0]..@ider[len-1], Initialy * holds the sizeof of der vector. * @ErrorDescription: return the error description or an empty * string if success. * * Creates the DER encoding for the NAME structure (inside *POINTER * structure). * * Returns: %ASN1_SUCCESS if DER encoding OK, %ASN1_ELEMENT_NOT_FOUND * if @name is not a valid element, %ASN1_VALUE_NOT_FOUND if there * is an element without a value, %ASN1_MEM_ERROR if the @ider * vector isn't big enough and in this case @len will contain the * length needed. **/ int asn1_der_coding (asn1_node_const element, const char *name, void *ider, int *len, char *ErrorDescription) { asn1_node node, p, p2; unsigned char temp[MAX(LTOSTR_MAX_SIZE, SIZEOF_UNSIGNED_LONG_INT * 3 + 1)]; int counter, counter_old, len2, len3, move, max_len, max_len_old; int err; unsigned char *der = ider; if (ErrorDescription) ErrorDescription[0] = 0; node = asn1_find_node (element, name); if (node == NULL) return ASN1_ELEMENT_NOT_FOUND; /* Node is now a locally allocated variable. * That is because in some point we modify the * structure, and I don't know why! --nmav */ node = _asn1_copy_structure3 (node); if (node == NULL) return ASN1_ELEMENT_NOT_FOUND; max_len = *len; if (der == NULL && max_len > 0) return ASN1_VALUE_NOT_VALID; counter = 0; move = DOWN; p = node; while (1) { counter_old = counter; max_len_old = max_len; if (move != UP) { p->start = counter; err = _asn1_insert_tag_der (p, der, &counter, &max_len); if (err != ASN1_SUCCESS && err != ASN1_MEM_ERROR) goto error; } switch (type_field (p->type)) { case ASN1_ETYPE_NULL: max_len--; if (der != NULL && max_len >= 0) der[counter] = 0; counter++; move = RIGHT; break; case ASN1_ETYPE_BOOLEAN: if ((p->type & CONST_DEFAULT) && (p->value == NULL)) { counter = counter_old; max_len = max_len_old; } else { if (p->value == NULL) { _asn1_error_description_value_not_found (p, ErrorDescription); err = ASN1_VALUE_NOT_FOUND; goto error; } max_len -= 2; if (der != NULL && max_len >= 0) { der[counter++] = 1; if (p->value[0] == 'F') der[counter++] = 0; else der[counter++] = 0xFF; } else counter += 2; } move = RIGHT; break; case ASN1_ETYPE_INTEGER: case ASN1_ETYPE_ENUMERATED: if ((p->type & CONST_DEFAULT) && (p->value == NULL)) { counter = counter_old; max_len = max_len_old; } else { if (p->value == NULL) { _asn1_error_description_value_not_found (p, ErrorDescription); err = ASN1_VALUE_NOT_FOUND; goto error; } len2 = asn1_get_length_der (p->value, p->value_len, &len3); if (len2 < 0) { err = ASN1_DER_ERROR; goto error; } max_len -= len2 + len3; if (der != NULL && max_len >= 0) memcpy (der + counter, p->value, len3 + len2); counter += len3 + len2; } move = RIGHT; break; case ASN1_ETYPE_OBJECT_ID: if ((p->type & CONST_DEFAULT) && (p->value == NULL)) { counter = counter_old; max_len = max_len_old; } else { if (p->value == NULL) { _asn1_error_description_value_not_found (p, ErrorDescription); err = ASN1_VALUE_NOT_FOUND; goto error; } len2 = max_len; err = _asn1_objectid_der (p->value, der + counter, &len2); if (err != ASN1_SUCCESS && err != ASN1_MEM_ERROR) goto error; max_len -= len2; counter += len2; } move = RIGHT; break; case ASN1_ETYPE_GENERALIZED_TIME: case ASN1_ETYPE_UTC_TIME: if (p->value == NULL) { _asn1_error_description_value_not_found (p, ErrorDescription); err = ASN1_VALUE_NOT_FOUND; goto error; } len2 = max_len; err = _asn1_time_der (p->value, p->value_len, der + counter, &len2); if (err != ASN1_SUCCESS && err != ASN1_MEM_ERROR) goto error; max_len -= len2; counter += len2; move = RIGHT; break; case ASN1_ETYPE_OCTET_STRING: case ASN1_ETYPE_GENERALSTRING: case ASN1_ETYPE_NUMERIC_STRING: case ASN1_ETYPE_IA5_STRING: case ASN1_ETYPE_TELETEX_STRING: case ASN1_ETYPE_PRINTABLE_STRING: case ASN1_ETYPE_UNIVERSAL_STRING: case ASN1_ETYPE_BMP_STRING: case ASN1_ETYPE_UTF8_STRING: case ASN1_ETYPE_VISIBLE_STRING: case ASN1_ETYPE_BIT_STRING: if (p->value == NULL) { _asn1_error_description_value_not_found (p, ErrorDescription); err = ASN1_VALUE_NOT_FOUND; goto error; } len2 = asn1_get_length_der (p->value, p->value_len, &len3); if (len2 < 0) { err = ASN1_DER_ERROR; goto error; } max_len -= len2 + len3; if (der != NULL && max_len >= 0) memcpy (der + counter, p->value, len3 + len2); counter += len3 + len2; move = RIGHT; break; case ASN1_ETYPE_SEQUENCE: case ASN1_ETYPE_SET: if (move != UP) { p->tmp_ival = counter; if (p->down == NULL) { move = UP; continue; } else { p2 = p->down; while (p2 && (type_field (p2->type) == ASN1_ETYPE_TAG)) p2 = p2->right; if (p2) { p = p2; move = RIGHT; continue; } move = UP; continue; } } else { /* move==UP */ len2 = p->tmp_ival; p->tmp_ival = 0; if ((type_field (p->type) == ASN1_ETYPE_SET) && (max_len >= 0)) { err = _asn1_ordering_set (der + len2, counter - len2, p); if (err != ASN1_SUCCESS) goto error; } asn1_length_der (counter - len2, temp, &len3); max_len -= len3; if (der != NULL && max_len >= 0) { memmove (der + len2 + len3, der + len2, counter - len2); memcpy (der + len2, temp, len3); } counter += len3; move = RIGHT; } break; case ASN1_ETYPE_SEQUENCE_OF: case ASN1_ETYPE_SET_OF: if (move != UP) { p->tmp_ival = counter; p = p->down; while ((type_field (p->type) == ASN1_ETYPE_TAG) || (type_field (p->type) == ASN1_ETYPE_SIZE)) p = p->right; if (p->right) { p = p->right; move = RIGHT; continue; } else p = _asn1_find_up (p); move = UP; } if (move == UP) { len2 = p->tmp_ival; p->tmp_ival = 0; if ((type_field (p->type) == ASN1_ETYPE_SET_OF) && (counter - len2 > 0) && (max_len >= 0)) { err = _asn1_ordering_set_of (der + len2, counter - len2, p); if (err != ASN1_SUCCESS) goto error; } asn1_length_der (counter - len2, temp, &len3); max_len -= len3; if (der != NULL && max_len >= 0) { memmove (der + len2 + len3, der + len2, counter - len2); memcpy (der + len2, temp, len3); } counter += len3; move = RIGHT; } break; case ASN1_ETYPE_ANY: if (p->value == NULL) { _asn1_error_description_value_not_found (p, ErrorDescription); err = ASN1_VALUE_NOT_FOUND; goto error; } len2 = asn1_get_length_der (p->value, p->value_len, &len3); if (len2 < 0) { err = ASN1_DER_ERROR; goto error; } max_len -= len2; if (der != NULL && max_len >= 0) memcpy (der + counter, p->value + len3, len2); counter += len2; move = RIGHT; break; default: move = (move == UP) ? RIGHT : DOWN; break; } if ((move != DOWN) && (counter != counter_old)) { p->end = counter - 1; err = _asn1_complete_explicit_tag (p, der, &counter, &max_len); if (err != ASN1_SUCCESS && err != ASN1_MEM_ERROR) goto error; } if (p == node && move != DOWN) break; if (move == DOWN) { if (p->down) p = p->down; else move = RIGHT; } if (move == RIGHT) { if (p->right) p = p->right; else move = UP; } if (move == UP) p = _asn1_find_up (p); } *len = counter; if (max_len < 0) { err = ASN1_MEM_ERROR; goto error; } err = ASN1_SUCCESS; error: asn1_delete_structure (&node); return err; }