Add some tests for optional and default ASN1_BOOLEAN.

ASN1_BOOLEAN has these ASN1_FBOOLEAN and ASN1_TBOOLEAN variants that
behave slightly strangely. Add some tests to ensure we don't break them
in the rewrite.

In doing so, fix a bug: ASN1_BOOLEAN canonically represents TRUE as
0xff, to match DER. But ASN1_TBOOLEAN is initialized with it->size,
which is 1, not 0xff. Fix it to be 0xff. (This shouldn't actually matter
because the encoder is lax and ASN1_TBOOLEAN doesn't encode TRUE
anyway.)

Bug: 548
Change-Id: I4e7fdc2a3bc87603eaf04a7668359006a1480c2e
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/56187
Commit-Queue: David Benjamin <davidben@google.com>
Reviewed-by: Bob Beck <bbe@google.com>
diff --git a/crypto/asn1/asn1_test.cc b/crypto/asn1/asn1_test.cc
index 268280f..99d7d30 100644
--- a/crypto/asn1/asn1_test.cc
+++ b/crypto/asn1/asn1_test.cc
@@ -2526,4 +2526,78 @@
   }
 }
 
+struct BOOLEANS {
+  ASN1_BOOLEAN required;
+  ASN1_BOOLEAN optional;
+  ASN1_BOOLEAN default_true;
+  ASN1_BOOLEAN default_false;
+};
+
+DECLARE_ASN1_FUNCTIONS(BOOLEANS)
+ASN1_SEQUENCE(BOOLEANS) = {
+    ASN1_SIMPLE(BOOLEANS, required, ASN1_BOOLEAN),
+    ASN1_IMP_OPT(BOOLEANS, optional, ASN1_BOOLEAN, 1),
+    // Although not actually optional, |ASN1_TBOOLEAN| and |ASN1_FBOOLEAN| need
+    // to be marked optional in the template.
+    ASN1_IMP_OPT(BOOLEANS, default_true, ASN1_TBOOLEAN, 2),
+    ASN1_IMP_OPT(BOOLEANS, default_false, ASN1_FBOOLEAN, 3),
+} ASN1_SEQUENCE_END(BOOLEANS)
+IMPLEMENT_ASN1_FUNCTIONS(BOOLEANS)
+
+TEST(ASN1Test, OptionalAndDefaultBooleans) {
+  std::unique_ptr<BOOLEANS, decltype(&BOOLEANS_free)> obj(nullptr,
+                                                          BOOLEANS_free);
+
+  // A default-constructed object should use, respectively, omitted, omitted,
+  // TRUE, FALSE.
+  //
+  // TODO(davidben): Is the first one a bug? It seems more consistent for a
+  // required BOOLEAN default to FALSE. |FOO_new| typically default-initializes
+  // fields valid states. (Though there are exceptions. CHOICE, ANY, and OBJECT
+  // IDENTIFIER are default-initialized to something invalid.)
+  obj.reset(BOOLEANS_new());
+  ASSERT_TRUE(obj);
+  EXPECT_EQ(obj->required, ASN1_BOOLEAN_NONE);
+  EXPECT_EQ(obj->optional, ASN1_BOOLEAN_NONE);
+  EXPECT_EQ(obj->default_true, ASN1_BOOLEAN_TRUE);
+  EXPECT_EQ(obj->default_false, ASN1_BOOLEAN_FALSE);
+
+  // Trying to serialize this should fail, because |obj->required| is omitted.
+  EXPECT_EQ(-1, i2d_BOOLEANS(obj.get(), nullptr));
+
+  // Otherwise, this object is serializable. Most fields are omitted, due to
+  // them being optional or defaulted.
+  static const uint8_t kFieldsOmitted[] = {0x30, 0x03, 0x01, 0x01, 0x00};
+  obj->required = 0;
+  TestSerialize(obj.get(), i2d_BOOLEANS, kFieldsOmitted);
+
+  const uint8_t *der = kFieldsOmitted;
+  obj.reset(d2i_BOOLEANS(nullptr, &der, sizeof(kFieldsOmitted)));
+  ASSERT_TRUE(obj);
+  EXPECT_EQ(obj->required, ASN1_BOOLEAN_FALSE);
+  EXPECT_EQ(obj->optional, ASN1_BOOLEAN_NONE);
+  EXPECT_EQ(obj->default_true, ASN1_BOOLEAN_TRUE);
+  EXPECT_EQ(obj->default_false, ASN1_BOOLEAN_FALSE);
+
+  // Include the optinonal fields instead.
+  static const uint8_t kFieldsIncluded[] = {0x30, 0x0c, 0x01, 0x01, 0xff,
+                                            0x81, 0x01, 0x00, 0x82, 0x01,
+                                            0x00, 0x83, 0x01, 0xff};
+  obj->required = ASN1_BOOLEAN_TRUE;
+  obj->optional = ASN1_BOOLEAN_FALSE;
+  obj->default_true = ASN1_BOOLEAN_FALSE;
+  obj->default_false = ASN1_BOOLEAN_TRUE;
+  TestSerialize(obj.get(), i2d_BOOLEANS, kFieldsIncluded);
+
+  der = kFieldsIncluded;
+  obj.reset(d2i_BOOLEANS(nullptr, &der, sizeof(kFieldsIncluded)));
+  ASSERT_TRUE(obj);
+  EXPECT_EQ(obj->required, ASN1_BOOLEAN_TRUE);
+  EXPECT_EQ(obj->optional, ASN1_BOOLEAN_FALSE);
+  EXPECT_EQ(obj->default_true, ASN1_BOOLEAN_FALSE);
+  EXPECT_EQ(obj->default_false, ASN1_BOOLEAN_TRUE);
+
+  // TODO(https://crbug.com/boringssl/354): Reject explicit DEFAULTs.
+}
+
 #endif  // !WINDOWS || !SHARED_LIBRARY
diff --git a/crypto/asn1/tasn_typ.c b/crypto/asn1/tasn_typ.c
index a9409e2..ebeda54 100644
--- a/crypto/asn1/tasn_typ.c
+++ b/crypto/asn1/tasn_typ.c
@@ -107,9 +107,8 @@
                                      DIRECTORYSTRING)
 
 // Three separate BOOLEAN type: normal, DEFAULT TRUE and DEFAULT FALSE
-// TODO(davidben): ASN1_TBOOLEAN should be ASN1_BOOLEAN_TRUE, or 0xff.
 IMPLEMENT_ASN1_TYPE_ex(ASN1_BOOLEAN, ASN1_BOOLEAN, ASN1_BOOLEAN_NONE)
-IMPLEMENT_ASN1_TYPE_ex(ASN1_TBOOLEAN, ASN1_BOOLEAN, 1)
+IMPLEMENT_ASN1_TYPE_ex(ASN1_TBOOLEAN, ASN1_BOOLEAN, ASN1_BOOLEAN_TRUE)
 IMPLEMENT_ASN1_TYPE_ex(ASN1_FBOOLEAN, ASN1_BOOLEAN, ASN1_BOOLEAN_FALSE)
 
 ASN1_ITEM_TEMPLATE(ASN1_SEQUENCE_ANY) =