diff options
author | Nikos Mavrogiannopoulos <nmav@crystal.(none)> | 2008-11-02 14:40:41 +0200 |
---|---|---|
committer | Nikos Mavrogiannopoulos <nmav@crystal.(none)> | 2008-11-03 21:49:50 +0200 |
commit | 47a0c91a38c458653be1645ff162c53253cbeadd (patch) | |
tree | 49b54e24bee2e3384041e527713c9f79ff3ce7fa | |
parent | df34fbdecce86c00f6287c5f3002078529dcc07e (diff) | |
download | gnutls-47a0c91a38c458653be1645ff162c53253cbeadd.tar.gz |
* added BER octet string decoder from libtasn1.
* added the tree generation optimizations.
-rw-r--r-- | lib/minitasn1/decoding.c | 193 | ||||
-rw-r--r-- | lib/minitasn1/element.c | 45 | ||||
-rw-r--r-- | lib/minitasn1/libtasn1.h | 5 | ||||
-rw-r--r-- | lib/minitasn1/parser_aux.c | 120 | ||||
-rw-r--r-- | lib/minitasn1/parser_aux.h | 10 |
5 files changed, 233 insertions, 140 deletions
diff --git a/lib/minitasn1/decoding.c b/lib/minitasn1/decoding.c index c1de9e6773..51ecc01cea 100644 --- a/lib/minitasn1/decoding.c +++ b/lib/minitasn1/decoding.c @@ -33,8 +33,10 @@ #include "structure.h" #include "element.h" +static asn1_retCode +_asn1_get_indefinite_length_string (const unsigned char *der, int *len); -void +static void _asn1_error_description_tag_error (node_asn * node, char *ErrorDescription) { @@ -101,8 +103,6 @@ asn1_get_length_der (const unsigned char *der, int der_len, int *len) } - - /** * asn1_get_tag_der: * @der: DER data to decode. @@ -121,7 +121,7 @@ asn1_get_tag_der (const unsigned char *der, int der_len, { int punt, ris; - if (der == NULL || der_len <= 0 || len == NULL) + if (der == NULL || der_len < 2 || len == NULL) return ASN1_DER_ERROR; *cls = der[0] & 0xE0; @@ -160,8 +160,36 @@ asn1_get_tag_der (const unsigned char *der, int der_len, 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. + * + * Return value: Return the decoded length value, or negative value + * when the value was too big. The difference with asn1_get_length_der() + * is that it will return length even if the value has indefinite encoding. + * + **/ +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: @@ -268,9 +296,6 @@ _asn1_get_objectid_der (const unsigned char *der, int der_len, int *ret_len, *ret_len = len + len_len; } - - - /** * asn1_get_bit_der: * @der: DER data to decode containing the BIT SEQUENCE. @@ -349,15 +374,21 @@ _asn1_extract_tag_der (node_asn * node, const unsigned char *der, int der_len, (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_der (der + counter, der_len - counter, + 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)) || @@ -369,7 +400,6 @@ _asn1_extract_tag_der (node_asn * node, const unsigned char *der, int der_len, if ((class != class_implicit) || (tag != tag_implicit)) return ASN1_TAG_ERROR; } - is_tag_implicit = 0; } else @@ -425,6 +455,7 @@ _asn1_extract_tag_der (node_asn * node, const unsigned char *der, int der_len, (der + counter, der_len - counter, &class, &len2, &tag) != ASN1_SUCCESS) return ASN1_DER_ERROR; + if (counter + len2 > der_len) return ASN1_DER_ERROR; @@ -565,12 +596,52 @@ _asn1_delete_not_used (node_asn * node) return ASN1_SUCCESS; } +asn1_retCode _asn1_extract_der_octet(node_asn * 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; +} + asn1_retCode _asn1_get_octet_string (const unsigned char *der, node_asn * node, int *len) { - int len2, len3, counter, counter2, counter_end, tot_len, indefinite; - unsigned char *temp, *temp2; + int len2, len3, counter, tot_len, indefinite; counter = 0; @@ -617,43 +688,20 @@ _asn1_get_octet_string (const unsigned char *der, node_asn * node, int *len) /* copy */ if (node) { - asn1_length_der (tot_len, NULL, &len2); - temp = _asn1_malloc (len2 + tot_len); - if (temp == NULL) - { - return ASN1_MEM_ALLOC_ERROR; - } + unsigned char temp[DER_LEN]; + int ret; + + len2 = sizeof(temp); asn1_length_der (tot_len, temp, &len2); - tot_len += len2; - temp2 = temp + len2; - len2 = asn1_get_length_der (der, *len, &len3); - if (len2 < -1) - return ASN1_DER_ERROR; - counter2 = len3 + 1; - - if (indefinite == -1) - counter_end = counter - 2; - else - counter_end = counter; + _asn1_set_value (node, temp, len2); - while (counter2 < counter_end) - { - len2 = - asn1_get_length_der (der + counter2, *len - counter, &len3); - if (len2 < -1) - return ASN1_DER_ERROR; + tot_len += len2; - /* FIXME: to be checked. Is this ok? Has the - * size been checked before? - */ - memcpy (temp2, der + counter2 + len3, len2); - temp2 += len2; - counter2 += len2 + len3 + 1; - } + ret = _asn1_extract_der_octet(node, der, *len); + if (ret!=ASN1_SUCCESS) + return ret; - _asn1_set_value (node, temp, tot_len); - _asn1_free (temp); } } else @@ -673,8 +721,7 @@ _asn1_get_octet_string (const unsigned char *der, node_asn * node, int *len) } - -asn1_retCode +static asn1_retCode _asn1_get_indefinite_length_string (const unsigned char *der, int *len) { int len2, len3, counter, indefinite; @@ -756,7 +803,7 @@ asn1_der_decoding (ASN1_TYPE * element, const void *ider, int len, node_asn *node, *p, *p2, *p3; char temp[128]; int counter, len2, len3, len4, move, ris, tlen; - unsigned char class, *temp2; + unsigned char class; unsigned long tag; int indefinite, result; const unsigned char *der = ider; @@ -1192,17 +1239,7 @@ asn1_der_decoding (ASN1_TYPE * element, const void *ider, int len, if (len4 != -1) { len2 += len4; - asn1_length_der (len2 + len3, NULL, &len4); - temp2 = (unsigned char *) _asn1_malloc (len2 + len3 + len4); - if (temp2 == NULL) - { - asn1_delete_structure (element); - return ASN1_MEM_ALLOC_ERROR; - } - - asn1_octet_der (der + counter, len2 + len3, temp2, &len4); - _asn1_set_value (p, temp2, len4); - _asn1_free (temp2); + _asn1_set_value_octet (p, der+counter, len2+len3); counter += len2 + len3; } else @@ -1221,17 +1258,8 @@ asn1_der_decoding (ASN1_TYPE * element, const void *ider, int len, asn1_delete_structure (element); return ris; } - asn1_length_der (len2, NULL, &len4); - temp2 = (unsigned char *) _asn1_malloc (len2 + len4); - if (temp2 == NULL) - { - asn1_delete_structure (element); - return ASN1_MEM_ALLOC_ERROR; - } - asn1_octet_der (der + counter, len2, temp2, &len4); - _asn1_set_value (p, temp2, len4); - _asn1_free (temp2); + _asn1_set_value_octet (p, der+counter, len2); counter += len2; /* Check if a couple of 0x00 are present due to an EXPLICIT TAG with @@ -1877,19 +1905,8 @@ asn1_der_decoding_element (ASN1_TYPE * structure, const char *elementName, len2 += len4; if (state == FOUND) { - asn1_length_der (len2 + len3, NULL, &len4); - temp2 = - (unsigned char *) _asn1_malloc (len2 + len3 + len4); - if (temp2 == NULL) - { - asn1_delete_structure (structure); - return ASN1_MEM_ALLOC_ERROR; - } - - asn1_octet_der (der + counter, len2 + len3, temp2, - &len4); - _asn1_set_value (p, temp2, len4); - _asn1_free (temp2); + _asn1_set_value_octet (p, der+counter, len2+len3); + temp2 = NULL; if (p == nodeFound) state = EXIT; @@ -1915,17 +1932,7 @@ asn1_der_decoding_element (ASN1_TYPE * structure, const char *elementName, if (state == FOUND) { - asn1_length_der (len2, NULL, &len4); - temp2 = (unsigned char *) _asn1_malloc (len2 + len4); - if (temp2 == NULL) - { - asn1_delete_structure (structure); - return ASN1_MEM_ALLOC_ERROR; - } - - asn1_octet_der (der + counter, len2, temp2, &len4); - _asn1_set_value (p, temp2, len4); - _asn1_free (temp2); + _asn1_set_value_octet (p, der+counter, len2); if (p == nodeFound) state = EXIT; diff --git a/lib/minitasn1/element.c b/lib/minitasn1/element.c index 703276c809..25ecd5af3f 100644 --- a/lib/minitasn1/element.c +++ b/lib/minitasn1/element.c @@ -417,19 +417,7 @@ asn1_write_value (ASN1_TYPE node_root, const char *name, (!negative && (value_temp[k] & 0x80))) k--; - asn1_length_der (len - k, NULL, &len2); - temp = (unsigned char *) _asn1_malloc (len - k + len2); - if (temp == NULL) - { - _asn1_free (value_temp); - return ASN1_MEM_ALLOC_ERROR; - } - - asn1_octet_der (value_temp + k, len - k, temp, &len2); - _asn1_set_value (node, temp, len2); - - _asn1_free (temp); - + _asn1_set_value_octet (node, value_temp+k, len-k); if (node->type & CONST_DEFAULT) { @@ -569,26 +557,12 @@ asn1_write_value (ASN1_TYPE node_root, const char *name, case TYPE_OCTET_STRING: if (len == 0) len = strlen (value); - asn1_length_der (len, NULL, &len2); - temp = (unsigned char *) _asn1_malloc (len + len2); - if (temp == NULL) - return ASN1_MEM_ALLOC_ERROR; - - asn1_octet_der (value, len, temp, &len2); - _asn1_set_value (node, temp, len2); - _asn1_free (temp); + _asn1_set_value_octet (node, value, len); break; case TYPE_GENERALSTRING: if (len == 0) len = strlen (value); - asn1_length_der (len, NULL, &len2); - temp = (unsigned char *) _asn1_malloc (len + len2); - if (temp == NULL) - return ASN1_MEM_ALLOC_ERROR; - - asn1_octet_der (value, len, temp, &len2); - _asn1_set_value (node, temp, len2); - _asn1_free (temp); + _asn1_set_value_octet (node, value, len); break; case TYPE_BIT_STRING: if (len == 0) @@ -599,8 +573,8 @@ asn1_write_value (ASN1_TYPE node_root, const char *name, return ASN1_MEM_ALLOC_ERROR; asn1_bit_der (value, len, temp, &len2); - _asn1_set_value (node, temp, len2); - _asn1_free (temp); + _asn1_set_value_m (node, temp, len2); + temp = NULL; break; case TYPE_CHOICE: p = node->down; @@ -627,14 +601,7 @@ asn1_write_value (ASN1_TYPE node_root, const char *name, return ASN1_ELEMENT_NOT_FOUND; break; case TYPE_ANY: - asn1_length_der (len, NULL, &len2); - temp = (unsigned char *) _asn1_malloc (len + len2); - if (temp == NULL) - return ASN1_MEM_ALLOC_ERROR; - - asn1_octet_der (value, len, temp, &len2); - _asn1_set_value (node, temp, len2); - _asn1_free (temp); + _asn1_set_value_octet (node, value, len); break; case TYPE_SEQUENCE_OF: case TYPE_SET_OF: diff --git a/lib/minitasn1/libtasn1.h b/lib/minitasn1/libtasn1.h index d7d4f70a7c..efcee0e1bc 100644 --- a/lib/minitasn1/libtasn1.h +++ b/lib/minitasn1/libtasn1.h @@ -105,11 +105,14 @@ extern "C" /* that represent an ASN.1 DEFINITION. */ /******************************************************/ +#define SMALL_VALUE_SIZE 16 + struct node_asn_struct { char *name; /* Node name */ unsigned int type; /* Node type */ unsigned char *value; /* Node value */ + unsigned char small_value[SMALL_VALUE_SIZE]; /* if value is less than that store it here */ int value_len; struct node_asn_struct *down; /* Pointer to the son node */ struct node_asn_struct *right; /* Pointer to the brother node */ @@ -228,6 +231,8 @@ extern "C" signed long asn1_get_length_der (const unsigned char *der, int der_len, int *len); + long asn1_get_length_ber (const unsigned char *ber, int ber_len, + int *len); void asn1_length_der (unsigned long int len, unsigned char *ans, int *ans_len); diff --git a/lib/minitasn1/parser_aux.c b/lib/minitasn1/parser_aux.c index 51169b14b4..43522dace6 100644 --- a/lib/minitasn1/parser_aux.c +++ b/lib/minitasn1/parser_aux.c @@ -199,23 +199,27 @@ asn1_find_node (ASN1_TYPE pointer, const char *name) /* Return: pointer to the NODE_ASN element. */ /******************************************************************/ node_asn * -_asn1_set_value (node_asn * node, const void *_value, unsigned int len) +_asn1_set_value (node_asn * node, const void *value, unsigned int len) { - const unsigned char *value = _value; - if (node == NULL) return node; if (node->value) { - _asn1_free (node->value); + if (node->value != node->small_value) _asn1_free (node->value); node->value = NULL; node->value_len = 0; } + if (!len) return node; - node->value = (unsigned char *) _asn1_malloc (len); - if (node->value == NULL) - return NULL; + + if (len < sizeof(node->small_value)) { + node->value = node->small_value; + } else { + node->value = _asn1_malloc (len); + if (node->value == NULL) + return NULL; + } node->value_len = len; memcpy (node->value, value, len); @@ -223,6 +227,106 @@ _asn1_set_value (node_asn * node, const void *_value, unsigned int len) } /******************************************************************/ +/* Function : _asn1_set_value_octet */ +/* Description: sets the field VALUE in a NODE_ASN element. The */ +/* previous value (if exist) will be lost. The value */ +/* given is stored as an octet string. */ +/* Parameters: */ +/* node: element pointer. */ +/* value: pointer to the value that you want to set. */ +/* len: character number of value. */ +/* Return: pointer to the NODE_ASN element. */ +/******************************************************************/ +node_asn * +_asn1_set_value_octet (node_asn * node, const void *value, unsigned int len) +{ +int len2; +void* temp; + + if (node == NULL) + return node; + + asn1_length_der (len, NULL, &len2); + temp = (unsigned char *) _asn1_malloc (len + len2); + if (temp == NULL) + return NULL; + + asn1_octet_der (value, len, temp, &len2); + return _asn1_set_value_m (node, temp, len2); +} + +/* the same as _asn1_set_value except that it sets an already malloc'ed + * value. + */ +node_asn * +_asn1_set_value_m (node_asn * node, void *value, unsigned int len) +{ + if (node == NULL) + return node; + + if (node->value) + { + if (node->value != node->small_value) _asn1_free (node->value); + node->value = NULL; + node->value_len = 0; + } + + if (!len) + return node; + + node->value = value; + node->value_len = len; + + return node; +} + +/******************************************************************/ +/* Function : _asn1_append_value */ +/* Description: appends to the field VALUE in a NODE_ASN element. */ +/* */ +/* Parameters: */ +/* node: element pointer. */ +/* value: pointer to the value that you want to be appended. */ +/* len: character number of value. */ +/* Return: pointer to the NODE_ASN element. */ +/******************************************************************/ +node_asn * +_asn1_append_value (node_asn * node, const void *value, unsigned int len) +{ + if (node == NULL) + return node; + if (node->value != NULL && node->value != node->small_value) /* value is allocated */ + { + int prev_len = node->value_len; + node->value_len+=len; + node->value = _asn1_realloc( node->value, node->value_len); + if (node->value == NULL) { + node->value_len = 0; + return NULL; + } + memcpy( &node->value[prev_len], value, len); + + return node; + } + else if (node->value == node->small_value) /* value is in node */ + { + int prev_len = node->value_len; + node->value_len+=len; + node->value = _asn1_malloc( node->value_len); + if (node->value == NULL) { + node->value_len = 0; + return NULL; + } + memcpy( node->value, node->small_value, prev_len); + memcpy( &node->value[prev_len], value, len); + + return node; + } + else /* node->value == NULL */ + return _asn1_set_value(node, value, len); +} + +/******************************************************************/ /* Function : _asn1_set_name */ /* Description: sets the field NAME in a NODE_ASN element. The */ /* previous value (if exist) will be lost */ @@ -401,7 +505,7 @@ _asn1_remove_node (node_asn * node) if (node->name != NULL) _asn1_free (node->name); - if (node->value != NULL) + if (node->value != NULL && node->value != node->small_value) _asn1_free (node->value); _asn1_free (node); } diff --git a/lib/minitasn1/parser_aux.h b/lib/minitasn1/parser_aux.h index 3055510cbe..6e18bb69c2 100644 --- a/lib/minitasn1/parser_aux.h +++ b/lib/minitasn1/parser_aux.h @@ -2,6 +2,7 @@ #ifndef _PARSER_AUX_H #define _PARSER_AUX_H +#define DER_LEN 16 /***************************************/ /* Functions used by ASN.1 parser */ @@ -13,6 +14,15 @@ node_asn * _asn1_set_value(node_asn *node,const void *value,unsigned int len); node_asn * +_asn1_set_value_m(node_asn *node,void *value,unsigned int len); + +node_asn * +_asn1_set_value_octet(node_asn *node,const void *value,unsigned int len); + +node_asn * +_asn1_append_value(node_asn *node,const void *value,unsigned int len); + +node_asn * _asn1_set_name(node_asn *node,const char *name); node_asn * |