diff options
Diffstat (limited to 'lib/decoding.c')
-rw-r--r-- | lib/decoding.c | 744 |
1 files changed, 511 insertions, 233 deletions
diff --git a/lib/decoding.c b/lib/decoding.c index b7e49b4..e5513a3 100644 --- a/lib/decoding.c +++ b/lib/decoding.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2014 Free Software Foundation, Inc. + * Copyright (C) 2002-2016 Free Software Foundation, Inc. * * This file is part of LIBTASN1. * @@ -39,8 +39,13 @@ # define warn() #endif +#define IS_ERR(len, flags) (len < -1 || ((flags & ASN1_DECODE_FLAG_STRICT_DER) && len < 0)) + #define HAVE_TWO(x) (x>=2?1:0) +#define DECODE_FLAG_HAVE_TAG 1 +#define DECODE_FLAG_INDEFINITE (1<<1) + #define DECR_LEN(l, s) do { \ l -= s; \ if (l < 0) { \ @@ -53,6 +58,17 @@ static int _asn1_get_indefinite_length_string (const unsigned char *der, int der_len, int *len); +static int +_asn1_decode_simple_ber (unsigned int etype, const unsigned char *der, + unsigned int _der_len, unsigned char **str, + unsigned int *str_len, unsigned int *ber_len, + unsigned dflags); + +static int +_asn1_decode_simple_der (unsigned int etype, const unsigned char *der, + unsigned int _der_len, const unsigned char **str, + unsigned int *str_len, unsigned dflags); + static void _asn1_error_description_tag_error (asn1_node node, char *ErrorDescription) { @@ -234,13 +250,15 @@ asn1_get_length_ber (const unsigned char *ber, int ber_len, int *len) /** * 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. + * @der_len: The length of the @der data to decode. + * @ret_len: Output variable containing the encoded length of the DER data. * @str: Pre-allocated output buffer to put decoded OCTET SEQUENCE in. * @str_size: Length of pre-allocated output buffer. - * @str_len: Output variable containing the length of the OCTET SEQUENCE. + * @str_len: Output variable containing the length of the contents of the OCTET SEQUENCE. * - * Extract an OCTET SEQUENCE from DER data. + * Extract an OCTET SEQUENCE from DER data. Note that this function + * expects the DER data past the tag field, i.e., the length and + * content octets. * * Returns: Returns %ASN1_SUCCESS on success, or an error. **/ @@ -273,13 +291,32 @@ asn1_get_octet_der (const unsigned char *der, int der_len, return ASN1_SUCCESS; } -/* Returns ASN1_SUCCESS on success or an error code on error. - */ + +/*- + * _asn1_get_time_der: + * @type: %ASN1_ETYPE_GENERALIZED_TIME or %ASN1_ETYPE_UTC_TIME + * @der: DER data to decode containing the time + * @der_len: Length of DER data to decode. + * @ret_len: Output variable containing the length of the DER data. + * @str: Pre-allocated output buffer to put the textual time in. + * @str_size: Length of pre-allocated output buffer. + * @flags: Zero or %ASN1_DECODE_FLAG_STRICT_DER + * + * Performs basic checks in the DER encoded time object and returns its textual form. + * The textual form will be in the YYYYMMDD000000Z format for GeneralizedTime + * and YYMMDD000000Z for UTCTime. + * + * Returns: %ASN1_SUCCESS on success, or an error. + -*/ static int -_asn1_get_time_der (const unsigned char *der, int der_len, int *ret_len, - char *str, int str_size) +_asn1_get_time_der (unsigned type, const unsigned char *der, int der_len, int *ret_len, + char *str, int str_size, unsigned flags) { int len_len, str_len; + unsigned i; + unsigned sign_count = 0; + unsigned dot_count = 0; + const unsigned char *p; if (der_len <= 0 || str == NULL) return ASN1_DER_ERROR; @@ -288,6 +325,50 @@ _asn1_get_time_der (const unsigned char *der, int der_len, int *ret_len, if (str_len <= 0 || str_size < str_len) return ASN1_DER_ERROR; + /* perform some sanity checks on the data */ + if (str_len < 8) + { + warn(); + return ASN1_DER_ERROR; + } + + if (flags & ASN1_DECODE_FLAG_STRICT_DER) + { + p = &der[len_len]; + for (i=0;i<(unsigned)(str_len-1);i++) + { + if (isdigit(p[i]) == 0) + { + if (type == ASN1_ETYPE_GENERALIZED_TIME) + { + /* tolerate lax encodings */ + if (p[i] == '.' && dot_count == 0) + { + dot_count++; + continue; + } + + /* This is not really valid DER, but there are + * structures using that */ + if (!(flags & ASN1_DECODE_FLAG_STRICT_DER) && + (p[i] == '+' || p[i] == '-') && sign_count == 0) + { + sign_count++; + continue; + } + } + + warn(); + return ASN1_DER_ERROR; + } + } + + if (sign_count == 0 && p[str_len-1] != 'Z') + { + warn(); + return ASN1_DER_ERROR; + } + } memcpy (str, der + len_len, str_len); str[str_len] = 0; *ret_len = str_len + len_len; @@ -295,8 +376,20 @@ _asn1_get_time_der (const unsigned char *der, int der_len, int *ret_len, return ASN1_SUCCESS; } -static int -_asn1_get_objectid_der (const unsigned char *der, int der_len, int *ret_len, +/** + * asn1_get_objectid_der: + * @der: DER data to decode containing the OBJECT IDENTIFIER + * @der_len: Length of DER data to decode. + * @ret_len: Output variable containing the length of the DER data. + * @str: Pre-allocated output buffer to put the textual object id in. + * @str_size: Length of pre-allocated output buffer. + * + * Converts a DER encoded object identifier to its textual form. + * + * Returns: %ASN1_SUCCESS on success, or an error. + **/ +int +asn1_get_object_id_der (const unsigned char *der, int der_len, int *ret_len, char *str, int str_size) { int len_len, len, k; @@ -368,7 +461,7 @@ _asn1_get_objectid_der (const unsigned char *der, int der_len, int *ret_len, * * Extract a BIT SEQUENCE from DER data. * - * Returns: Return %ASN1_SUCCESS on success, or an error. + * Returns: %ASN1_SUCCESS on success, or an error. **/ int asn1_get_bit_der (const unsigned char *der, int der_len, @@ -403,10 +496,12 @@ asn1_get_bit_der (const unsigned char *der, int der_len, return ASN1_SUCCESS; } - +/* tag_len: the total tag length (explicit+inner) + * inner_tag_len: the inner_tag length + */ static int _asn1_extract_tag_der (asn1_node node, const unsigned char *der, int der_len, - int *ret_len) + int *tag_len, int *inner_tag_len, unsigned flags) { asn1_node p; int counter, len2, len3, is_tag_implicit; @@ -445,8 +540,13 @@ _asn1_extract_tag_der (asn1_node node, const unsigned char *der, int der_len, DECR_LEN(der_len, len2); counter += len2; - len3 = - asn1_get_length_ber (der + counter, der_len, + if (flags & ASN1_DECODE_FLAG_STRICT_DER) + len3 = + asn1_get_length_der (der + counter, der_len, + &len2); + else + len3 = + asn1_get_length_ber (der + counter, der_len, &len2); if (len3 < 0) return ASN1_DER_ERROR; @@ -512,7 +612,9 @@ _asn1_extract_tag_der (asn1_node node, const unsigned char *der, int der_len, unsigned type = type_field (node->type); if (type == ASN1_ETYPE_TAG) { - *ret_len = 0; + *tag_len = 0; + if (inner_tag_len) + *inner_tag_len = 0; return ASN1_SUCCESS; } @@ -572,7 +674,9 @@ _asn1_extract_tag_der (asn1_node node, const unsigned char *der, int der_len, } counter += len2; - *ret_len = counter; + *tag_len = counter; + if (inner_tag_len) + *inner_tag_len = len2; return ASN1_SUCCESS; cleanup: @@ -581,7 +685,7 @@ cleanup: static int extract_tag_der_recursive(asn1_node node, const unsigned char *der, int der_len, - int *ret_len) + int *ret_len, int *inner_len, unsigned flags) { asn1_node p; int ris = ASN1_DER_ERROR; @@ -591,7 +695,7 @@ int ris = ASN1_DER_ERROR; p = node->down; while (p) { - ris = _asn1_extract_tag_der (p, der, der_len, ret_len); + ris = _asn1_extract_tag_der (p, der, der_len, ret_len, inner_len, flags); if (ris == ASN1_SUCCESS) break; p = p->right; @@ -601,7 +705,7 @@ int ris = ASN1_DER_ERROR; return ris; } else - return _asn1_extract_tag_der (node, der, der_len, ret_len); + return _asn1_extract_tag_der (node, der, der_len, ret_len, inner_len, flags); } static int @@ -622,7 +726,7 @@ _asn1_delete_not_used (asn1_node node) { p2 = _asn1_find_left (p); if (!p2) - p2 = _asn1_get_up (p); + p2 = _asn1_find_up (p); } asn1_delete_structure (&p); p = p2; @@ -645,7 +749,7 @@ _asn1_delete_not_used (asn1_node node) { while (1) { - p = _asn1_get_up (p); + p = _asn1_find_up (p); if (p == node) { p = NULL; @@ -664,145 +768,6 @@ _asn1_delete_not_used (asn1_node node) } 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; - len2 = 0; - } - - 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 (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) { @@ -889,6 +854,9 @@ static void delete_unneeded_choice_fields(asn1_node p) * padding after the decoded DER data. Upon a successful return the value of * *@max_ider_len will be set to the number of bytes decoded. * + * If %ASN1_DECODE_FLAG_STRICT_DER flag is set then the function will + * not decode any BER-encoded elements. + * * Returns: %ASN1_SUCCESS if DER encoding OK, %ASN1_ELEMENT_NOT_FOUND * if @ELEMENT is %NULL, and %ASN1_TAG_ERROR or * %ASN1_DER_ERROR if the der encoding doesn't match the structure @@ -901,10 +869,14 @@ asn1_der_decoding2 (asn1_node *element, const void *ider, int *max_ider_len, asn1_node node, p, p2, p3; char temp[128]; int counter, len2, len3, len4, move, ris, tlen; - asn1_node ptail = NULL; + struct node_tail_cache_st tcache = {NULL, NULL}; unsigned char class; unsigned long tag; + int tag_len; int indefinite, result, total_len = *max_ider_len, ider_len = *max_ider_len; + int inner_tag_len; + unsigned char *ptmp; + const unsigned char *ptag; const unsigned char *der = ider; node = *element; @@ -927,12 +899,14 @@ asn1_der_decoding2 (asn1_node *element, const void *ider, int *max_ider_len, p = node; while (1) { + tag_len = 0; + inner_tag_len = 0; ris = ASN1_SUCCESS; if (move != UP) { if (p->type & CONST_SET) { - p2 = _asn1_get_up (p); + p2 = _asn1_find_up (p); len2 = p2->tmp_ival; if (len2 == -1) { @@ -964,7 +938,7 @@ asn1_der_decoding2 (asn1_node *element, const void *ider, int *max_ider_len, { ris = extract_tag_der_recursive (p2, der + counter, - ider_len, &len2); + ider_len, &len2, NULL, flags); if (ris == ASN1_SUCCESS) { p2->type &= ~CONST_NOT_USED; @@ -988,7 +962,7 @@ asn1_der_decoding2 (asn1_node *element, const void *ider, int *max_ider_len, if ((p->type & CONST_OPTION) || (p->type & CONST_DEFAULT)) { - p2 = _asn1_get_up (p); + p2 = _asn1_find_up (p); len2 = p2->tmp_ival; if (counter == len2) { @@ -1014,7 +988,7 @@ asn1_der_decoding2 (asn1_node *element, const void *ider, int *max_ider_len, { ris = extract_tag_der_recursive (p->down, der + counter, - ider_len, &len2); + ider_len, &len2, NULL, flags); if (ris == ASN1_SUCCESS) { @@ -1051,7 +1025,7 @@ asn1_der_decoding2 (asn1_node *element, const void *ider, int *max_ider_len, if ((p->type & CONST_OPTION) || (p->type & CONST_DEFAULT)) { - p2 = _asn1_get_up (p); + p2 = _asn1_find_up (p); len2 = p2->tmp_ival; if ((len2 != -1) && (counter > len2)) @@ -1060,7 +1034,8 @@ asn1_der_decoding2 (asn1_node *element, const void *ider, int *max_ider_len, if (ris == ASN1_SUCCESS) ris = - extract_tag_der_recursive (p, der + counter, ider_len, &len2); + extract_tag_der_recursive (p, der + counter, ider_len, + &tag_len, &inner_tag_len, flags); if (ris != ASN1_SUCCESS) { @@ -1086,8 +1061,8 @@ asn1_der_decoding2 (asn1_node *element, const void *ider, int *max_ider_len, } else { - DECR_LEN(ider_len, len2); - counter += len2; + DECR_LEN(ider_len, tag_len); + counter += tag_len; } } @@ -1140,7 +1115,7 @@ asn1_der_decoding2 (asn1_node *element, const void *ider, int *max_ider_len, break; case ASN1_ETYPE_OBJECT_ID: result = - _asn1_get_objectid_der (der + counter, ider_len, &len2, + asn1_get_object_id_der (der + counter, ider_len, &len2, temp, sizeof (temp)); if (result != ASN1_SUCCESS) { @@ -1160,8 +1135,8 @@ asn1_der_decoding2 (asn1_node *element, const void *ider, int *max_ider_len, case ASN1_ETYPE_GENERALIZED_TIME: case ASN1_ETYPE_UTC_TIME: result = - _asn1_get_time_der (der + counter, ider_len, &len2, temp, - sizeof (temp) - 1); + _asn1_get_time_der (type_field (p->type), der + counter, ider_len, &len2, temp, + sizeof (temp) - 1, flags); if (result != ASN1_SUCCESS) { warn(); @@ -1178,15 +1153,51 @@ asn1_der_decoding2 (asn1_node *element, const void *ider, int *max_ider_len, move = RIGHT; break; case ASN1_ETYPE_OCTET_STRING: - result = _asn1_get_octet_string (p, der + counter, ider_len, &len3); - if (result != ASN1_SUCCESS) + if (counter < inner_tag_len) { + result = ASN1_DER_ERROR; warn(); goto cleanup; - } + } + + ptag = der + counter - inner_tag_len; + if (flags & ASN1_DECODE_FLAG_STRICT_DER || !(ptag[0] & ASN1_CLASS_STRUCTURED)) + { + len2 = + asn1_get_length_der (der + counter, ider_len, &len3); + if (len2 < 0) + { + result = ASN1_DER_ERROR; + warn(); + goto cleanup; + } + + DECR_LEN(ider_len, len3+len2); + + _asn1_set_value (p, der + counter, len3 + len2); + counter += len3 + len2; + } + else + { + unsigned dflags = 0, vlen, ber_len; - DECR_LEN(ider_len, len3); - counter += len3; + if (ptag[0] & ASN1_CLASS_STRUCTURED) + dflags |= DECODE_FLAG_INDEFINITE; + + result = _asn1_decode_simple_ber(type_field (p->type), der+counter, ider_len, &ptmp, &vlen, &ber_len, dflags); + if (result != ASN1_SUCCESS) + { + warn(); + goto cleanup; + } + + DECR_LEN(ider_len, ber_len); + + _asn1_set_value_lv (p, ptmp, vlen); + + counter += ber_len; + free(ptmp); + } move = RIGHT; break; case ASN1_ETYPE_GENERALSTRING: @@ -1246,7 +1257,7 @@ asn1_der_decoding2 (asn1_node *element, const void *ider, int *max_ider_len, { /* move==DOWN || move==RIGHT */ len3 = asn1_get_length_der (der + counter, ider_len, &len2); - if (len3 < -1) + if (IS_ERR(len3, flags)) { result = ASN1_DER_ERROR; warn(); @@ -1293,14 +1304,15 @@ asn1_der_decoding2 (asn1_node *element, const void *ider, int *max_ider_len, { /* indefinite length method */ if (!HAVE_TWO(ider_len) || ((der[counter]) || der[counter + 1])) { - _asn1_append_sequence_set (p, &ptail); - p = ptail; + _asn1_append_sequence_set (p, &tcache); + p = tcache.tail; move = RIGHT; continue; } p->tmp_ival = 0; - ptail = NULL; /* finished decoding this structure */ + tcache.tail = NULL; /* finished decoding this structure */ + tcache.head = NULL; DECR_LEN(ider_len, 2); counter += 2; } @@ -1308,14 +1320,15 @@ asn1_der_decoding2 (asn1_node *element, const void *ider, int *max_ider_len, { /* definite length method */ if (len2 > counter) { - _asn1_append_sequence_set (p, &ptail); - p = ptail; + _asn1_append_sequence_set (p, &tcache); + p = tcache.tail; move = RIGHT; continue; } p->tmp_ival = 0; - ptail = NULL; /* finished decoding this structure */ + tcache.tail = NULL; /* finished decoding this structure */ + tcache.head = NULL; if (len2 != counter) { @@ -1329,7 +1342,7 @@ asn1_der_decoding2 (asn1_node *element, const void *ider, int *max_ider_len, { /* move==DOWN || move==RIGHT */ len3 = asn1_get_length_der (der + counter, ider_len, &len2); - if (len3 < -1) + if (IS_ERR(len3, flags)) { result = ASN1_DER_ERROR; warn(); @@ -1353,13 +1366,21 @@ asn1_der_decoding2 (asn1_node *element, const void *ider, int *max_ider_len, || (type_field (p2->type) == ASN1_ETYPE_SIZE)) p2 = p2->right; if (p2->right == NULL) - _asn1_append_sequence_set (p, &ptail); + _asn1_append_sequence_set (p, &tcache); p = p2; } } move = RIGHT; break; case ASN1_ETYPE_ANY: + /* Check indefinite lenth method in an EXPLICIT TAG */ + + if (!(flags & ASN1_DECODE_FLAG_STRICT_DER) && (p->type & CONST_TAG) && + tag_len == 2 && (der[counter - 1] == 0x80)) + indefinite = 1; + else + indefinite = 0; + if (asn1_get_tag_der (der + counter, ider_len, &class, &len2, &tag) != ASN1_SUCCESS) @@ -1374,7 +1395,7 @@ asn1_der_decoding2 (asn1_node *element, const void *ider, int *max_ider_len, len4 = asn1_get_length_der (der + counter + len2, ider_len, &len3); - if (len4 < -1) + if (IS_ERR(len4, flags)) { result = ASN1_DER_ERROR; warn(); @@ -1399,12 +1420,6 @@ asn1_der_decoding2 (asn1_node *element, const void *ider, int *max_ider_len, 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) @@ -1417,23 +1432,25 @@ asn1_der_decoding2 (asn1_node *element, const void *ider, int *max_ider_len, _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; - } - } } + + /* 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: @@ -1465,7 +1482,7 @@ asn1_der_decoding2 (asn1_node *element, const void *ider, int *max_ider_len, move = UP; } if (move == UP) - p = _asn1_get_up (p); + p = _asn1_find_up (p); } _asn1_delete_not_used (*element); @@ -1678,7 +1695,7 @@ asn1_expand_any_defined_by (asn1_node definitions, asn1_node * element) break; } - p3 = _asn1_get_up (p); + p3 = _asn1_find_up (p); if (!p3) { @@ -1698,8 +1715,8 @@ asn1_expand_any_defined_by (asn1_node definitions, asn1_node * element) (p3->value == NULL)) { - p3 = _asn1_get_up (p); - p3 = _asn1_get_up (p3); + p3 = _asn1_find_up (p); + p3 = _asn1_find_up (p3); if (!p3) { @@ -1834,7 +1851,7 @@ asn1_expand_any_defined_by (asn1_node definitions, asn1_node * element) { while (1) { - p = _asn1_get_up (p); + p = _asn1_find_up (p); if (p == *element) { p = NULL; @@ -1994,26 +2011,28 @@ asn1_expand_octet_string (asn1_node definitions, asn1_node * element, return retCode; } -/** - * asn1_decode_simple_der: +/*- + * _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 + * @_der_len: the bytes of the encoded string * @str: a pointer to the data * @str_len: the length of the data + * @dflags: DECODE_FLAG_* * * Decodes a simple DER encoded type (e.g. a string, which is not constructed). * The output is a pointer inside the @der. * * Returns: %ASN1_SUCCESS if successful or an error value. - **/ -int -asn1_decode_simple_der (unsigned int etype, const unsigned char *der, - unsigned int der_len, const unsigned char **str, - unsigned int *str_len) + -*/ +static int +_asn1_decode_simple_der (unsigned int etype, const unsigned char *der, + unsigned int _der_len, const unsigned char **str, + unsigned int *str_len, unsigned dflags) { int tag_len, len_len; const unsigned char *p; + int der_len = _der_len; unsigned char class; unsigned long tag; long ret; @@ -2021,23 +2040,33 @@ asn1_decode_simple_der (unsigned int etype, const unsigned char *der, if (der == NULL || der_len == 0) return ASN1_VALUE_NOT_VALID; - if (ETYPE_OK (etype) == 0) + if (ETYPE_OK (etype) == 0 || ETYPE_IS_STRING(etype) == 0) return ASN1_VALUE_NOT_VALID; /* doesn't handle constructed classes */ - if (ETYPE_CLASS (etype) != ASN1_CLASS_UNIVERSAL) + class = ETYPE_CLASS(etype); + if (class != 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; + if (dflags & DECODE_FLAG_HAVE_TAG) + { + ret = asn1_get_tag_der (p, der_len, &class, &tag_len, &tag); + if (ret != ASN1_SUCCESS) + return ret; + + if (class != ETYPE_CLASS (etype) || tag != ETYPE_TAG (etype)) + { + warn(); + return ASN1_DER_ERROR; + } - p += tag_len; - der_len -= tag_len; + p += tag_len; + der_len -= tag_len; + if (der_len <= 0) + return ASN1_DER_ERROR; + } ret = asn1_get_length_der (p, der_len, &len_len); if (ret < 0) @@ -2045,9 +2074,258 @@ asn1_decode_simple_der (unsigned int etype, const unsigned char *der, p += len_len; der_len -= len_len; + if (der_len <= 0) + return ASN1_DER_ERROR; *str_len = ret; *str = p; return ASN1_SUCCESS; } + +/** + * asn1_decode_simple_der: + * @etype: The type of the string to be encoded (ASN1_ETYPE_) + * @der: the encoded string + * @_der_len: the bytes of the encoded string + * @str: a pointer to the data + * @str_len: the length of the data + * + * Decodes a simple DER encoded type (e.g. a string, which is not constructed). + * The output is a pointer inside the @der. + * + * Returns: %ASN1_SUCCESS if successful or an error value. + **/ +int +asn1_decode_simple_der (unsigned int etype, const unsigned char *der, + unsigned int _der_len, const unsigned char **str, + unsigned int *str_len) +{ + return _asn1_decode_simple_der(etype, der, _der_len, str, str_len, DECODE_FLAG_HAVE_TAG); +} + +static int append(uint8_t **dst, unsigned *dst_size, const unsigned char *src, unsigned src_size) +{ + *dst = _asn1_realloc(*dst, *dst_size+src_size); + if (*dst == NULL) + return ASN1_MEM_ERROR; + memcpy(*dst + *dst_size, src, src_size); + *dst_size += src_size; + return ASN1_SUCCESS; +} + +/*- + * _asn1_decode_simple_ber: + * @etype: The type of the string to be encoded (ASN1_ETYPE_) + * @der: the encoded string + * @_der_len: the bytes of the encoded string + * @str: a pointer to the data + * @str_len: the length of the data + * @ber_len: the total length occupied by BER (may be %NULL) + * @have_tag: whether a DER tag is included + * + * Decodes a BER encoded type. The output is an allocated value + * of the data. This decodes BER STRINGS only. Other types are + * decoded as DER. + * + * Returns: %ASN1_SUCCESS if successful or an error value. + -*/ +static int +_asn1_decode_simple_ber (unsigned int etype, const unsigned char *der, + unsigned int _der_len, unsigned char **str, + unsigned int *str_len, unsigned int *ber_len, + unsigned dflags) +{ + int tag_len, len_len; + const unsigned char *p; + int der_len = _der_len; + uint8_t *total = NULL; + unsigned total_size = 0; + unsigned char class; + unsigned long tag; + unsigned char *out = NULL; + const unsigned char *cout = NULL; + unsigned out_len; + long result; + + if (ber_len) *ber_len = 0; + + if (der == NULL || der_len == 0) + { + warn(); + return ASN1_VALUE_NOT_VALID; + } + + if (ETYPE_OK (etype) == 0) + { + warn(); + return ASN1_VALUE_NOT_VALID; + } + + /* doesn't handle constructed + definite classes */ + class = ETYPE_CLASS (etype); + if (class != ASN1_CLASS_UNIVERSAL) + { + warn(); + return ASN1_VALUE_NOT_VALID; + } + + p = der; + + if (dflags & DECODE_FLAG_HAVE_TAG) + { + result = asn1_get_tag_der (p, der_len, &class, &tag_len, &tag); + if (result != ASN1_SUCCESS) + { + warn(); + return result; + } + + if (tag != ETYPE_TAG (etype)) + { + warn(); + return ASN1_DER_ERROR; + } + + p += tag_len; + + DECR_LEN(der_len, tag_len); + + if (ber_len) *ber_len += tag_len; + } + + /* indefinite constructed */ + if (((dflags & DECODE_FLAG_INDEFINITE) || class == ASN1_CLASS_STRUCTURED) && ETYPE_IS_STRING(etype)) + { + len_len = 1; + + DECR_LEN(der_len, len_len); + if (p[0] != 0x80) + { + warn(); + result = ASN1_DER_ERROR; + goto cleanup; + } + + p += len_len; + + if (ber_len) *ber_len += len_len; + + /* decode the available octet strings */ + do + { + unsigned tmp_len; + + result = asn1_decode_simple_ber(etype, p, der_len, &out, &out_len, &tmp_len); + if (result != ASN1_SUCCESS) + { + warn(); + goto cleanup; + } + + p += tmp_len; + DECR_LEN(der_len, tmp_len); + + if (ber_len) *ber_len += tmp_len; + + DECR_LEN(der_len, 2); /* we need the EOC */ + + if (out_len > 0) + { + result = append(&total, &total_size, out, out_len); + if (result != ASN1_SUCCESS) + { + warn(); + goto cleanup; + } + } + + free(out); + out = NULL; + + if (p[0] == 0 && p[1] == 0) /* EOC */ + { + if (ber_len) *ber_len += 2; + break; + } + + /* no EOC */ + der_len += 2; + + if (der_len == 2) + { + warn(); + result = ASN1_DER_ERROR; + goto cleanup; + } + } + while(1); + } + else if (class == ETYPE_CLASS(etype)) + { + if (ber_len) + { + result = asn1_get_length_der (p, der_len, &len_len); + if (result < 0) + { + warn(); + result = ASN1_DER_ERROR; + goto cleanup; + } + *ber_len += result + len_len; + } + + /* non-string values are decoded as DER */ + result = _asn1_decode_simple_der(etype, der, _der_len, &cout, &out_len, dflags); + if (result != ASN1_SUCCESS) + { + warn(); + goto cleanup; + } + + result = append(&total, &total_size, cout, out_len); + if (result != ASN1_SUCCESS) + { + warn(); + goto cleanup; + } + } + else + { + warn(); + result = ASN1_DER_ERROR; + goto cleanup; + } + + *str = total; + *str_len = total_size; + + return ASN1_SUCCESS; +cleanup: + free(out); + free(total); + return result; +} + +/** + * asn1_decode_simple_ber: + * @etype: The type of the string to be encoded (ASN1_ETYPE_) + * @der: the encoded string + * @_der_len: the bytes of the encoded string + * @str: a pointer to the data + * @str_len: the length of the data + * @ber_len: the total length occupied by BER (may be %NULL) + * + * Decodes a BER encoded type. The output is an allocated value + * of the data. This decodes BER STRINGS only. Other types are + * decoded as DER. + * + * Returns: %ASN1_SUCCESS if successful or an error value. + **/ +int +asn1_decode_simple_ber (unsigned int etype, const unsigned char *der, + unsigned int _der_len, unsigned char **str, + unsigned int *str_len, unsigned int *ber_len) +{ + return _asn1_decode_simple_ber(etype, der, _der_len, str, str_len, ber_len, DECODE_FLAG_HAVE_TAG); +} |