Check tag class and constructed bit in d2i_ASN1_OBJECT.

d2i_ASN1_OBJECT had a similar set of bugs in as in
https://boringssl-review.googlesource.com/c/boringssl/+/49866.

This does not affect any other d2i functions. Those already go through
the ASN1_ITEM machinery.

Update-Note: d2i_ASN1_OBJECT will now notice more incorrect tags. It was
already checking for tag number 6, so it is unlikely anyone was relying
on this as a non-tag-checking parser.

Change-Id: I30f9ad28e3859aeb7a38c0ea299cd2e30002abce
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/50290
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/crypto/asn1/a_object.c b/crypto/asn1/a_object.c
index 0de3141..30a656d 100644
--- a/crypto/asn1/a_object.c
+++ b/crypto/asn1/a_object.c
@@ -146,29 +146,29 @@
 ASN1_OBJECT *d2i_ASN1_OBJECT(ASN1_OBJECT **a, const unsigned char **pp,
                              long length)
 {
-    const unsigned char *p;
     long len;
     int tag, xclass;
-    int inf, i;
-    ASN1_OBJECT *ret = NULL;
-    p = *pp;
-    inf = ASN1_get_object(&p, &len, &tag, &xclass, length);
+    const unsigned char *p = *pp;
+    int inf = ASN1_get_object(&p, &len, &tag, &xclass, length);
     if (inf & 0x80) {
-        i = ASN1_R_BAD_OBJECT_HEADER;
-        goto err;
+        OPENSSL_PUT_ERROR(ASN1, ASN1_R_BAD_OBJECT_HEADER);
+        return NULL;
     }
 
-    if (tag != V_ASN1_OBJECT) {
-        i = ASN1_R_EXPECTING_AN_OBJECT;
-        goto err;
+    if (inf & V_ASN1_CONSTRUCTED) {
+        OPENSSL_PUT_ERROR(ASN1, ASN1_R_TYPE_NOT_PRIMITIVE);
+        return NULL;
     }
-    ret = c2i_ASN1_OBJECT(a, &p, len);
-    if (ret)
+
+    if (tag != V_ASN1_OBJECT || xclass != V_ASN1_UNIVERSAL) {
+        OPENSSL_PUT_ERROR(ASN1, ASN1_R_EXPECTING_AN_OBJECT);
+        return NULL;
+    }
+    ASN1_OBJECT *ret = c2i_ASN1_OBJECT(a, &p, len);
+    if (ret) {
         *pp = p;
+    }
     return ret;
- err:
-    OPENSSL_PUT_ERROR(ASN1, i);
-    return (NULL);
 }
 
 ASN1_OBJECT *c2i_ASN1_OBJECT(ASN1_OBJECT **a, const unsigned char **pp,
diff --git a/crypto/asn1/asn1_test.cc b/crypto/asn1/asn1_test.cc
index 1b0c11c..ab9cb01 100644
--- a/crypto/asn1/asn1_test.cc
+++ b/crypto/asn1/asn1_test.cc
@@ -266,7 +266,7 @@
   EXPECT_FALSE(val->value.ptr);
 }
 
-TEST(ASN1Test, ASN1ObjectReuse) {
+TEST(ASN1Test, ParseASN1Object) {
   // 1.2.840.113554.4.1.72585.2, an arbitrary unknown OID.
   static const uint8_t kOID[] = {0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12,
                                  0x04, 0x01, 0x84, 0xb7, 0x09, 0x02};
@@ -277,16 +277,48 @@
   // OBJECT_IDENTIFIER { 1.3.101.112 }
   static const uint8_t kDER[] = {0x06, 0x03, 0x2b, 0x65, 0x70};
   const uint8_t *ptr = kDER;
+  // Parse an |ASN1_OBJECT| with object reuse.
   EXPECT_TRUE(d2i_ASN1_OBJECT(&obj, &ptr, sizeof(kDER)));
   EXPECT_EQ(NID_ED25519, OBJ_obj2nid(obj));
   ASN1_OBJECT_free(obj);
 
-  // Repeat the test, this time overriding a static |ASN1_OBJECT|.
+  // Repeat the test, this time overriding a static |ASN1_OBJECT|. It should
+  // detect this and construct a new one.
   obj = OBJ_nid2obj(NID_rsaEncryption);
   ptr = kDER;
   EXPECT_TRUE(d2i_ASN1_OBJECT(&obj, &ptr, sizeof(kDER)));
   EXPECT_EQ(NID_ED25519, OBJ_obj2nid(obj));
   ASN1_OBJECT_free(obj);
+
+  const std::vector<uint8_t> kInvalidObjects[] = {
+      // No tag header.
+      {},
+      // No length.
+      {0x06},
+      // Truncated contents.
+      {0x06, 0x01},
+      // An OID may not be empty.
+      {0x06, 0x00},
+      // The last byte may not be a continuation byte (high bit set).
+      {0x06, 0x03, 0x2b, 0x65, 0xf0},
+      // Each component must be minimally-encoded.
+      {0x06, 0x03, 0x2b, 0x65, 0x80, 0x70},
+      {0x06, 0x03, 0x80, 0x2b, 0x65, 0x70},
+      // Wrong tag number.
+      {0x01, 0x03, 0x2b, 0x65, 0x70},
+      // Wrong tag class.
+      {0x86, 0x03, 0x2b, 0x65, 0x70},
+      // Element is constructed.
+      {0x26, 0x03, 0x2b, 0x65, 0x70},
+  };
+  for (const auto &invalid : kInvalidObjects) {
+    SCOPED_TRACE(Bytes(invalid));
+    ptr = invalid.data();
+    obj = d2i_ASN1_OBJECT(nullptr, &ptr, invalid.size());
+    EXPECT_FALSE(obj);
+    ASN1_OBJECT_free(obj);
+    ERR_clear_error();
+  }
 }
 
 TEST(ASN1Test, BitString) {