Add a test for OPTIONAL CHOICE values

An in-progress rewrite of tasn_dec.c accidentally broke this, so add a
regression test.

Bug: 548
Change-Id: Iac6a23acbc08459187c96a2f6471f0aa97d445a1
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/58125
Auto-Submit: David Benjamin <davidben@google.com>
Reviewed-by: Bob Beck <bbe@google.com>
Commit-Queue: Bob Beck <bbe@google.com>
diff --git a/crypto/asn1/asn1_test.cc b/crypto/asn1/asn1_test.cc
index 47d2356..36ad676 100644
--- a/crypto/asn1/asn1_test.cc
+++ b/crypto/asn1/asn1_test.cc
@@ -2657,4 +2657,66 @@
   TestSerialize(obj.get(), i2d_DOUBLY_TAGGED, kTrueEmpty);
 }
 
+#define CHOICE_TYPE_OCT 0
+#define CHOICE_TYPE_BOOL 1
+
+struct CHOICE_TYPE {
+  int type;
+  union {
+    ASN1_OCTET_STRING *oct;
+    ASN1_BOOLEAN b;
+  } value;
+};
+
+DECLARE_ASN1_FUNCTIONS(CHOICE_TYPE)
+ASN1_CHOICE(CHOICE_TYPE) = {
+    ASN1_SIMPLE(CHOICE_TYPE, value.oct, ASN1_OCTET_STRING),
+    ASN1_SIMPLE(CHOICE_TYPE, value.b, ASN1_BOOLEAN),
+} ASN1_CHOICE_END(CHOICE_TYPE)
+IMPLEMENT_ASN1_FUNCTIONS(CHOICE_TYPE)
+
+struct OPTIONAL_CHOICE {
+  CHOICE_TYPE *choice;
+};
+
+DECLARE_ASN1_FUNCTIONS(OPTIONAL_CHOICE)
+ASN1_SEQUENCE(OPTIONAL_CHOICE) = {
+    ASN1_OPT(OPTIONAL_CHOICE, choice, CHOICE_TYPE),
+} ASN1_SEQUENCE_END(OPTIONAL_CHOICE)
+IMPLEMENT_ASN1_FUNCTIONS(OPTIONAL_CHOICE)
+
+TEST(ASN1Test, OptionalChoice) {
+  std::unique_ptr<OPTIONAL_CHOICE, decltype(&OPTIONAL_CHOICE_free)> obj(
+      nullptr, OPTIONAL_CHOICE_free);
+
+  // Value omitted.
+  static const uint8_t kOmitted[] = {0x30, 0x00};
+  const uint8_t *inp = kOmitted;
+  obj.reset(d2i_OPTIONAL_CHOICE(nullptr, &inp, sizeof(kOmitted)));
+  ASSERT_TRUE(obj);
+  EXPECT_FALSE(obj->choice);
+  TestSerialize(obj.get(), i2d_OPTIONAL_CHOICE, kOmitted);
+
+  // Value is present as an OCTET STRING.
+  static const uint8_t kOct[] = {0x30, 0x02, 0x04, 0x00};
+  inp = kOct;
+  obj.reset(d2i_OPTIONAL_CHOICE(nullptr, &inp, sizeof(kOct)));
+  ASSERT_TRUE(obj);
+  ASSERT_TRUE(obj->choice);
+  ASSERT_EQ(obj->choice->type, CHOICE_TYPE_OCT);
+  ASSERT_TRUE(obj->choice->value.oct);
+  EXPECT_EQ(ASN1_STRING_length(obj->choice->value.oct), 0);
+  TestSerialize(obj.get(), i2d_OPTIONAL_CHOICE, kOct);
+
+  // Value is present as TRUE.
+  static const uint8_t kTrue[] = {0x30, 0x03, 0x01, 0x01, 0xff};
+  inp = kTrue;
+  obj.reset(d2i_OPTIONAL_CHOICE(nullptr, &inp, sizeof(kTrue)));
+  ASSERT_TRUE(obj);
+  ASSERT_TRUE(obj->choice);
+  ASSERT_EQ(obj->choice->type, CHOICE_TYPE_BOOL);
+  EXPECT_EQ(obj->choice->value.b, ASN1_BOOLEAN_TRUE);
+  TestSerialize(obj.get(), i2d_OPTIONAL_CHOICE, kTrue);
+}
+
 #endif  // !WINDOWS || !SHARED_LIBRARY