Add CBS_peek_asn1_tag.

Intended to make parsing ASN.1 structures with OPTIONAL elements easier. (Just
attempting to parse the next tag doesn't distinguish between a malformed CBS
which has now been partially advanced and an optional tag mismatch.)

Change-Id: Idceb3dfd6ec028e87e1bc5aaddcec177b0c32150
Reviewed-on: https://boringssl-review.googlesource.com/1995
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/crypto/bytestring/bytestring_test.c b/crypto/bytestring/bytestring_test.c
index e4afccd..f30179d 100644
--- a/crypto/bytestring/bytestring_test.c
+++ b/crypto/bytestring/bytestring_test.c
@@ -109,6 +109,10 @@
   CBS data, contents;
 
   CBS_init(&data, kData1, sizeof(kData1));
+  if (CBS_peek_asn1_tag(&data, 0x1) ||
+      !CBS_peek_asn1_tag(&data, 0x30)) {
+    return 0;
+  }
   if (!CBS_get_asn1(&data, &contents, 0x30) ||
       CBS_len(&contents) != 2 ||
       memcmp(CBS_data(&contents), "\x01\x02", 2) != 0) {
@@ -145,6 +149,11 @@
     return 0;
   }
 
+  CBS_init(&data, NULL, 0);
+  if (CBS_peek_asn1_tag(&data, 0x30)) {
+    return 0;
+  }
+
   return 1;
 }
 
diff --git a/crypto/bytestring/cbs.c b/crypto/bytestring/cbs.c
index 244daba..07cc126 100644
--- a/crypto/bytestring/cbs.c
+++ b/crypto/bytestring/cbs.c
@@ -264,6 +264,13 @@
   return cbs_get_asn1(cbs, out, tag_value, 0 /* include header */);
 }
 
+int CBS_peek_asn1_tag(const CBS *cbs, unsigned tag_value) {
+  if (CBS_len(cbs) < 1) {
+    return 0;
+  }
+  return CBS_data(cbs)[0] == tag_value;
+}
+
 int CBS_get_asn1_uint64(CBS *cbs, uint64_t *out) {
   CBS bytes;
   const uint8_t *data;
diff --git a/include/openssl/bytestring.h b/include/openssl/bytestring.h
index 1af20e8..acaba8e 100644
--- a/include/openssl/bytestring.h
+++ b/include/openssl/bytestring.h
@@ -141,6 +141,13 @@
  * ASN.1 header bytes too. */
 OPENSSL_EXPORT int CBS_get_asn1_element(CBS *cbs, CBS *out, unsigned tag_value);
 
+/* CBS_peek_asn1_tag looks ahead at the next ASN.1 tag and returns one
+ * if the next ASN.1 element on |cbs| would have tag |tag_value|. If
+ * |cbs| is empty or the tag does not match, it returns zero. Note: if
+ * it returns one, CBS_get_asn1 may still fail if the rest of the
+ * element is malformed. */
+OPENSSL_EXPORT int CBS_peek_asn1_tag(const CBS *cbs, unsigned tag_value);
+
 /* 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