Make CBS_get_any_asn1_element accept only DER.

This change makes |CBS_get_any_asn1_element| only handle DER elements.
Another function, |CBS_get_any_ber_asn1_element| is exposed internally
for the cases where we need to process BER data.

Change-Id: I544141a1a3d7913986352a8fd9a6d00b9f282652
Reviewed-on: https://boringssl-review.googlesource.com/4994
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/crypto/bytestring/ber.c b/crypto/bytestring/ber.c
index 2a7df63..e3b150c 100644
--- a/crypto/bytestring/ber.c
+++ b/crypto/bytestring/ber.c
@@ -43,7 +43,7 @@
     unsigned tag;
     size_t header_len;
 
-    if (!CBS_get_any_asn1_element(&in, &contents, &tag, &header_len)) {
+    if (!CBS_get_any_ber_asn1_element(&in, &contents, &tag, &header_len)) {
       return 0;
     }
     if (CBS_len(&contents) == header_len &&
@@ -74,7 +74,7 @@
 }
 
 /* is_eoc returns true if |header_len| and |contents|, as returned by
- * |CBS_get_any_asn1_element|, indicate an "end of contents" (EOC) value. */
+ * |CBS_get_any_ber_asn1_element|, indicate an "end of contents" (EOC) value. */
 static char is_eoc(size_t header_len, CBS *contents) {
   return header_len == 2 && CBS_len(contents) == 2 &&
          memcmp(CBS_data(contents), "\x00\x00", 2) == 0;
@@ -98,7 +98,7 @@
     size_t header_len;
     CBB *out_contents, out_contents_storage;
 
-    if (!CBS_get_any_asn1_element(in, &contents, &tag, &header_len)) {
+    if (!CBS_get_any_ber_asn1_element(in, &contents, &tag, &header_len)) {
       return 0;
     }
     out_contents = out;
@@ -129,8 +129,8 @@
           size_t inner_header_len;
 
           CBS_init(&in_copy, CBS_data(in), CBS_len(in));
-          if (!CBS_get_any_asn1_element(&in_copy, &inner_contents, &inner_tag,
-                                        &inner_header_len)) {
+          if (!CBS_get_any_ber_asn1_element(&in_copy, &inner_contents,
+                                            &inner_tag, &inner_header_len)) {
             return 0;
           }
           if (CBS_len(&inner_contents) > inner_header_len &&
diff --git a/crypto/bytestring/cbs.c b/crypto/bytestring/cbs.c
index 5979c51..b8caedd 100644
--- a/crypto/bytestring/cbs.c
+++ b/crypto/bytestring/cbs.c
@@ -157,8 +157,8 @@
   return cbs_get_length_prefixed(cbs, out, 3);
 }
 
-int CBS_get_any_asn1_element(CBS *cbs, CBS *out, unsigned *out_tag,
-                             size_t *out_header_len) {
+static int cbs_get_any_asn1_element(CBS *cbs, CBS *out, unsigned *out_tag,
+                                    size_t *out_header_len, int ber_ok) {
   uint8_t tag, length_byte;
   CBS header = *cbs;
   CBS throwaway;
@@ -193,7 +193,7 @@
     const size_t num_bytes = length_byte & 0x7f;
     uint32_t len32;
 
-    if ((tag & CBS_ASN1_CONSTRUCTED) != 0 && num_bytes == 0) {
+    if (ber_ok && (tag & CBS_ASN1_CONSTRUCTED) != 0 && num_bytes == 0) {
       /* indefinite length */
       if (out_header_len != NULL) {
         *out_header_len = 2;
@@ -229,6 +229,18 @@
   return CBS_get_bytes(cbs, out, len);
 }
 
+int CBS_get_any_asn1_element(CBS *cbs, CBS *out, unsigned *out_tag,
+                                    size_t *out_header_len) {
+  return cbs_get_any_asn1_element(cbs, out, out_tag, out_header_len,
+                                  0 /* DER only */);
+}
+
+int CBS_get_any_ber_asn1_element(CBS *cbs, CBS *out, unsigned *out_tag,
+                                 size_t *out_header_len) {
+  return cbs_get_any_asn1_element(cbs, out, out_tag, out_header_len,
+                                  1 /* BER allowed */);
+}
+
 static int cbs_get_asn1(CBS *cbs, CBS *out, unsigned tag_value,
                         int skip_header) {
   size_t header_len;
@@ -240,12 +252,7 @@
   }
 
   if (!CBS_get_any_asn1_element(cbs, out, &tag, &header_len) ||
-      tag != tag_value ||
-      (header_len > 0 &&
-       /* This ensures that the tag is either zero length or
-        * indefinite-length. */
-       CBS_len(out) == header_len &&
-       CBS_data(out)[header_len - 1] == 0x80)) {
+      tag != tag_value) {
     return 0;
   }
 
diff --git a/crypto/bytestring/internal.h b/crypto/bytestring/internal.h
index b4ea7e5..391ad19 100644
--- a/crypto/bytestring/internal.h
+++ b/crypto/bytestring/internal.h
@@ -38,6 +38,14 @@
  * It returns one on success and zero otherwise. */
 OPENSSL_EXPORT int CBS_asn1_ber_to_der(CBS *in, uint8_t **out, size_t *out_len);
 
+/* CBS_get_any_ber_asn1_element acts the same as |CBS_get_any_asn1_element| but
+ * also allows indefinite-length elements to be returned. In that case,
+ * |*out_header_len| and |CBS_len(out)| will both be two as only the header is
+ * returned. */
+OPENSSL_EXPORT int CBS_get_any_ber_asn1_element(CBS *cbs, CBS *out,
+                                                unsigned *out_tag,
+                                                size_t *out_header_len);
+
 
 #if defined(__cplusplus)
 }  /* extern C */
diff --git a/include/openssl/bytestring.h b/include/openssl/bytestring.h
index e10621a..9963426 100644
--- a/include/openssl/bytestring.h
+++ b/include/openssl/bytestring.h
@@ -150,10 +150,8 @@
 
 /* CBS_get_any_asn1_element sets |*out| to contain the next ASN.1 element from
  * |*cbs| (including header bytes) and advances |*cbs|. It sets |*out_tag| to
- * the tag number and |*out_header_len| to the length of the ASN.1 header. If
- * the element has indefinite length then |*out| will only contain the
- * header. Each of |out|, |out_tag|, and |out_header_len| may be NULL to ignore
- * the value.
+ * the tag number and |*out_header_len| to the length of the ASN.1 header. Each
+ * of |out|, |out_tag|, and |out_header_len| may be NULL to ignore the value.
  *
  * Tag numbers greater than 30 are not supported (i.e. short form only). */
 OPENSSL_EXPORT int CBS_get_any_asn1_element(CBS *cbs, CBS *out,