Home | History | Annotate | Download | only in bytestring
      1 /* Copyright (c) 2014, Google Inc.
      2  *
      3  * Permission to use, copy, modify, and/or distribute this software for any
      4  * purpose with or without fee is hereby granted, provided that the above
      5  * copyright notice and this permission notice appear in all copies.
      6  *
      7  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
      8  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
      9  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
     10  * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     11  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
     12  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
     13  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
     14 
     15 #include <openssl/bytestring.h>
     16 
     17 #include <assert.h>
     18 #include <string.h>
     19 
     20 #include "internal.h"
     21 #include "../internal.h"
     22 
     23 
     24 /* kMaxDepth is a just a sanity limit. The code should be such that the length
     25  * of the input being processes always decreases. None the less, a very large
     26  * input could otherwise cause the stack to overflow. */
     27 static const unsigned kMaxDepth = 2048;
     28 
     29 /* is_string_type returns one if |tag| is a string type and zero otherwise. It
     30  * ignores the constructed bit. */
     31 static int is_string_type(unsigned tag) {
     32   if ((tag & 0xc0) != 0) {
     33     return 0;
     34   }
     35   switch (tag & 0x1f) {
     36     case CBS_ASN1_BITSTRING:
     37     case CBS_ASN1_OCTETSTRING:
     38     case CBS_ASN1_UTF8STRING:
     39     case CBS_ASN1_NUMERICSTRING:
     40     case CBS_ASN1_PRINTABLESTRING:
     41     case CBS_ASN1_T61STRING:
     42     case CBS_ASN1_VIDEOTEXSTRING:
     43     case CBS_ASN1_IA5STRING:
     44     case CBS_ASN1_GRAPHICSTRING:
     45     case CBS_ASN1_VISIBLESTRING:
     46     case CBS_ASN1_GENERALSTRING:
     47     case CBS_ASN1_UNIVERSALSTRING:
     48     case CBS_ASN1_BMPSTRING:
     49       return 1;
     50     default:
     51       return 0;
     52   }
     53 }
     54 
     55 /* cbs_find_ber walks an ASN.1 structure in |orig_in| and sets |*ber_found|
     56  * depending on whether an indefinite length element or constructed string was
     57  * found. The value of |orig_in| is not changed. It returns one on success (i.e.
     58  * |*ber_found| was set) and zero on error. */
     59 static int cbs_find_ber(const CBS *orig_in, char *ber_found, unsigned depth) {
     60   CBS in;
     61 
     62   if (depth > kMaxDepth) {
     63     return 0;
     64   }
     65 
     66   CBS_init(&in, CBS_data(orig_in), CBS_len(orig_in));
     67   *ber_found = 0;
     68 
     69   while (CBS_len(&in) > 0) {
     70     CBS contents;
     71     unsigned tag;
     72     size_t header_len;
     73 
     74     if (!CBS_get_any_ber_asn1_element(&in, &contents, &tag, &header_len)) {
     75       return 0;
     76     }
     77     if (CBS_len(&contents) == header_len &&
     78         header_len > 0 &&
     79         CBS_data(&contents)[header_len-1] == 0x80) {
     80       /* Found an indefinite-length element. */
     81       *ber_found = 1;
     82       return 1;
     83     }
     84     if (tag & CBS_ASN1_CONSTRUCTED) {
     85       if (is_string_type(tag)) {
     86         /* Constructed strings are only legal in BER and require conversion. */
     87         *ber_found = 1;
     88         return 1;
     89       }
     90       if (!CBS_skip(&contents, header_len) ||
     91           !cbs_find_ber(&contents, ber_found, depth + 1)) {
     92         return 0;
     93       }
     94     }
     95   }
     96 
     97   return 1;
     98 }
     99 
    100 /* is_eoc returns true if |header_len| and |contents|, as returned by
    101  * |CBS_get_any_ber_asn1_element|, indicate an "end of contents" (EOC) value. */
    102 static char is_eoc(size_t header_len, CBS *contents) {
    103   return header_len == 2 && CBS_len(contents) == 2 &&
    104          OPENSSL_memcmp(CBS_data(contents), "\x00\x00", 2) == 0;
    105 }
    106 
    107 /* cbs_convert_ber reads BER data from |in| and writes DER data to |out|. If
    108  * |string_tag| is non-zero, then all elements must match |string_tag| up to the
    109  * constructed bit and primitive element bodies are written to |out| without
    110  * element headers. This is used when concatenating the fragments of a
    111  * constructed string. If |looking_for_eoc| is set then any EOC elements found
    112  * will cause the function to return after consuming it. It returns one on
    113  * success and zero on error. */
    114 static int cbs_convert_ber(CBS *in, CBB *out, unsigned string_tag,
    115                            char looking_for_eoc, unsigned depth) {
    116   assert(!(string_tag & CBS_ASN1_CONSTRUCTED));
    117 
    118   if (depth > kMaxDepth) {
    119     return 0;
    120   }
    121 
    122   while (CBS_len(in) > 0) {
    123     CBS contents;
    124     unsigned tag, child_string_tag = string_tag;
    125     size_t header_len;
    126     CBB *out_contents, out_contents_storage;
    127 
    128     if (!CBS_get_any_ber_asn1_element(in, &contents, &tag, &header_len)) {
    129       return 0;
    130     }
    131 
    132     if (is_eoc(header_len, &contents)) {
    133       return looking_for_eoc;
    134     }
    135 
    136     if (string_tag != 0) {
    137       /* This is part of a constructed string. All elements must match
    138        * |string_tag| up to the constructed bit and get appended to |out|
    139        * without a child element. */
    140       if ((tag & ~CBS_ASN1_CONSTRUCTED) != string_tag) {
    141         return 0;
    142       }
    143       out_contents = out;
    144     } else {
    145       unsigned out_tag = tag;
    146       if ((tag & CBS_ASN1_CONSTRUCTED) && is_string_type(tag)) {
    147         /* If a constructed string, clear the constructed bit and inform
    148          * children to concatenate bodies. */
    149         out_tag &= ~CBS_ASN1_CONSTRUCTED;
    150         child_string_tag = out_tag;
    151       }
    152       if (!CBB_add_asn1(out, &out_contents_storage, out_tag)) {
    153         return 0;
    154       }
    155       out_contents = &out_contents_storage;
    156     }
    157 
    158     if (CBS_len(&contents) == header_len && header_len > 0 &&
    159         CBS_data(&contents)[header_len - 1] == 0x80) {
    160       /* This is an indefinite length element. */
    161       if (!cbs_convert_ber(in, out_contents, child_string_tag,
    162                            1 /* looking for eoc */, depth + 1) ||
    163           !CBB_flush(out)) {
    164         return 0;
    165       }
    166       continue;
    167     }
    168 
    169     if (!CBS_skip(&contents, header_len)) {
    170       return 0;
    171     }
    172 
    173     if (tag & CBS_ASN1_CONSTRUCTED) {
    174       /* Recurse into children. */
    175       if (!cbs_convert_ber(&contents, out_contents, child_string_tag,
    176                            0 /* not looking for eoc */, depth + 1)) {
    177         return 0;
    178       }
    179     } else {
    180       /* Copy primitive contents as-is. */
    181       if (!CBB_add_bytes(out_contents, CBS_data(&contents),
    182                          CBS_len(&contents))) {
    183         return 0;
    184       }
    185     }
    186 
    187     if (!CBB_flush(out)) {
    188       return 0;
    189     }
    190   }
    191 
    192   return looking_for_eoc == 0;
    193 }
    194 
    195 int CBS_asn1_ber_to_der(CBS *in, uint8_t **out, size_t *out_len) {
    196   CBB cbb;
    197 
    198   /* First, do a quick walk to find any indefinite-length elements. Most of the
    199    * time we hope that there aren't any and thus we can quickly return. */
    200   char conversion_needed;
    201   if (!cbs_find_ber(in, &conversion_needed, 0)) {
    202     return 0;
    203   }
    204 
    205   if (!conversion_needed) {
    206     *out = NULL;
    207     *out_len = 0;
    208     return 1;
    209   }
    210 
    211   if (!CBB_init(&cbb, CBS_len(in)) ||
    212       !cbs_convert_ber(in, &cbb, 0, 0, 0) ||
    213       !CBB_finish(&cbb, out, out_len)) {
    214     CBB_cleanup(&cbb);
    215     return 0;
    216   }
    217 
    218   return 1;
    219 }
    220 
    221 int CBS_get_asn1_implicit_string(CBS *in, CBS *out, uint8_t **out_storage,
    222                                  unsigned outer_tag, unsigned inner_tag) {
    223   assert(!(outer_tag & CBS_ASN1_CONSTRUCTED));
    224   assert(!(inner_tag & CBS_ASN1_CONSTRUCTED));
    225   assert(is_string_type(inner_tag));
    226 
    227   if (CBS_peek_asn1_tag(in, outer_tag)) {
    228     /* Normal implicitly-tagged string. */
    229     *out_storage = NULL;
    230     return CBS_get_asn1(in, out, outer_tag);
    231   }
    232 
    233   /* Otherwise, try to parse an implicitly-tagged constructed string.
    234    * |CBS_asn1_ber_to_der| is assumed to have run, so only allow one level deep
    235    * of nesting. */
    236   CBB result;
    237   CBS child;
    238   if (!CBB_init(&result, CBS_len(in)) ||
    239       !CBS_get_asn1(in, &child, outer_tag | CBS_ASN1_CONSTRUCTED)) {
    240     goto err;
    241   }
    242 
    243   while (CBS_len(&child) > 0) {
    244     CBS chunk;
    245     if (!CBS_get_asn1(&child, &chunk, inner_tag) ||
    246         !CBB_add_bytes(&result, CBS_data(&chunk), CBS_len(&chunk))) {
    247       goto err;
    248     }
    249   }
    250 
    251   uint8_t *data;
    252   size_t len;
    253   if (!CBB_finish(&result, &data, &len)) {
    254     goto err;
    255   }
    256 
    257   CBS_init(out, data, len);
    258   *out_storage = data;
    259   return 1;
    260 
    261 err:
    262   CBB_cleanup(&result);
    263   return 0;
    264 }
    265