Give X509 an ASN1_ITEM again

I mistakenly thought no one needed X509 as an ASN1_ITEM, but that wasn't
true. wpa_supplicant relies on this. Restore this and add a test for it.

As with the rest of the rewrite, this is currently a little tedious. I'm
hoping that, as the internals are rewritten with CBS and CBB, we can
establish some cleaner patterns and abstractions.

Bug: 547
Change-Id: I761ee058f8ec916b2ec7f4730a764d46d72f1f10
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/58285
Commit-Queue: Bob Beck <bbe@google.com>
Auto-Submit: 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 cd26528..5c43793 100644
--- a/crypto/asn1/asn1_test.cc
+++ b/crypto/asn1/asn1_test.cc
@@ -28,8 +28,10 @@
 #include <openssl/err.h>
 #include <openssl/mem.h>
 #include <openssl/obj.h>
+#include <openssl/pem.h>
 #include <openssl/span.h>
 #include <openssl/time.h>
+#include <openssl/x509.h>
 #include <openssl/x509v3.h>
 
 #include "../test/test_util.h"
@@ -2759,4 +2761,93 @@
   TestSerialize(obj.get(), i2d_OPTIONAL_CHOICE, kTrue);
 }
 
+struct EMBED_X509 {
+  X509 *x509;
+  X509 *x509_opt;
+  STACK_OF(X509) *x509_seq;
+};
+
+DECLARE_ASN1_FUNCTIONS(EMBED_X509)
+ASN1_SEQUENCE(EMBED_X509) = {
+    ASN1_SIMPLE(EMBED_X509, x509, X509),
+    ASN1_EXP_OPT(EMBED_X509, x509_opt, X509, 0),
+    ASN1_IMP_SEQUENCE_OF_OPT(EMBED_X509, x509_seq, X509, 1),
+} ASN1_SEQUENCE_END(EMBED_X509)
+IMPLEMENT_ASN1_FUNCTIONS(EMBED_X509)
+
+// Test that X.509 types defined in this library can be embedded into other
+// types, as we rewrite them away from the templating system.
+TEST(ASN1Test, EmbedX509) {
+  // Set up a test certificate.
+  static const char kTestCert[] = R"(
+-----BEGIN CERTIFICATE-----
+MIIBzzCCAXagAwIBAgIJANlMBNpJfb/rMAkGByqGSM49BAEwRTELMAkGA1UEBhMC
+QVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdp
+dHMgUHR5IEx0ZDAeFw0xNDA0MjMyMzIxNTdaFw0xNDA1MjMyMzIxNTdaMEUxCzAJ
+BgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5l
+dCBXaWRnaXRzIFB0eSBMdGQwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATmK2ni
+v2Wfl74vHg2UikzVl2u3qR4NRvvdqakendy6WgHn1peoChj5w8SjHlbifINI2xYa
+HPUdfvGULUvPciLBo1AwTjAdBgNVHQ4EFgQUq4TSrKuV8IJOFngHVVdf5CaNgtEw
+HwYDVR0jBBgwFoAUq4TSrKuV8IJOFngHVVdf5CaNgtEwDAYDVR0TBAUwAwEB/zAJ
+BgcqhkjOPQQBA0gAMEUCIQDyoDVeUTo2w4J5m+4nUIWOcAZ0lVfSKXQA9L4Vh13E
+BwIgfB55FGohg/B6dGh5XxSZmmi08cueFV7mHzJSYV51yRQ=
+-----END CERTIFICATE-----
+)";
+  bssl::UniquePtr<BIO> bio(BIO_new_mem_buf(kTestCert, sizeof(kTestCert)));
+  ASSERT_TRUE(bio);
+  bssl::UniquePtr<X509> cert(PEM_read_bio_X509(bio.get(), nullptr, nullptr, nullptr));
+  ASSERT_TRUE(cert);
+  uint8_t *cert_der = nullptr;
+  int cert_len = i2d_X509(cert.get(), &cert_der);
+  ASSERT_GT(cert_len, 0);
+  bssl::UniquePtr<uint8_t> free_cert_der(cert_der);
+
+  std::unique_ptr<EMBED_X509, decltype(&EMBED_X509_free)> obj(nullptr,
+                                                              EMBED_X509_free);
+
+  // Test only the first field present.
+  bssl::ScopedCBB cbb;
+  ASSERT_TRUE(CBB_init(cbb.get(), 64));
+  CBB seq;
+  ASSERT_TRUE(CBB_add_asn1(cbb.get(), &seq, CBS_ASN1_SEQUENCE));
+  ASSERT_TRUE(CBB_add_bytes(&seq, cert_der, cert_len));
+  ASSERT_TRUE(CBB_flush(cbb.get()));
+  const uint8_t *ptr = CBB_data(cbb.get());
+  obj.reset(d2i_EMBED_X509(nullptr, &ptr, CBB_len(cbb.get())));
+  ASSERT_TRUE(obj);
+  ASSERT_TRUE(obj->x509);
+  EXPECT_EQ(X509_cmp(obj->x509, cert.get()), 0);
+  EXPECT_FALSE(obj->x509_opt);
+  EXPECT_FALSE(obj->x509_seq);
+  TestSerialize(obj.get(), i2d_EMBED_X509,
+                {CBB_data(cbb.get()), CBB_len(cbb.get())});
+
+  // Test all fields present.
+  cbb.Reset();
+  ASSERT_TRUE(CBB_init(cbb.get(), 64));
+  ASSERT_TRUE(CBB_add_asn1(cbb.get(), &seq, CBS_ASN1_SEQUENCE));
+  ASSERT_TRUE(CBB_add_bytes(&seq, cert_der, cert_len));
+  CBB child;
+  ASSERT_TRUE(CBB_add_asn1(
+      &seq, &child, CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0));
+  ASSERT_TRUE(CBB_add_bytes(&child, cert_der, cert_len));
+  ASSERT_TRUE(CBB_add_asn1(
+      &seq, &child, CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 1));
+  ASSERT_TRUE(CBB_add_bytes(&child, cert_der, cert_len));
+  ASSERT_TRUE(CBB_add_bytes(&child, cert_der, cert_len));
+  ASSERT_TRUE(CBB_flush(cbb.get()));
+  ptr = CBB_data(cbb.get());
+  obj.reset(d2i_EMBED_X509(nullptr, &ptr, CBB_len(cbb.get())));
+  ASSERT_TRUE(obj);
+  ASSERT_TRUE(obj->x509);
+  EXPECT_EQ(X509_cmp(obj->x509, cert.get()), 0);
+  ASSERT_TRUE(obj->x509_opt);
+  EXPECT_EQ(X509_cmp(obj->x509_opt, cert.get()), 0);
+  ASSERT_EQ(sk_X509_num(obj->x509_seq), 2u);
+  EXPECT_EQ(X509_cmp(sk_X509_value(obj->x509_seq, 0), cert.get()), 0);
+  EXPECT_EQ(X509_cmp(sk_X509_value(obj->x509_seq, 1), cert.get()), 0);
+  TestSerialize(obj.get(), i2d_EMBED_X509,
+                {CBB_data(cbb.get()), CBB_len(cbb.get())});
+}
+
 #endif  // !WINDOWS || !SHARED_LIBRARY
diff --git a/crypto/x509/x_x509.c b/crypto/x509/x_x509.c
index 1aa2d2e..31dbebe 100644
--- a/crypto/x509/x_x509.c
+++ b/crypto/x509/x_x509.c
@@ -301,6 +301,55 @@
   return -1;
 }
 
+static int x509_new_cb(ASN1_VALUE **pval, const ASN1_ITEM *it) {
+  *pval = (ASN1_VALUE *)X509_new();
+  return *pval != NULL;
+}
+
+static void x509_free_cb(ASN1_VALUE **pval, const ASN1_ITEM *it) {
+  X509_free((X509 *)*pval);
+  *pval = NULL;
+}
+
+static int x509_d2i_cb(ASN1_VALUE **pval, const unsigned char **in, long len,
+                       const ASN1_ITEM *it, int opt, ASN1_TLC *ctx) {
+  if (len < 0) {
+    OPENSSL_PUT_ERROR(ASN1, ASN1_R_BUFFER_TOO_SMALL);
+    return 0;
+  }
+
+  CBS cbs;
+  CBS_init(&cbs, *in, len);
+  if (opt && !CBS_peek_asn1_tag(&cbs, CBS_ASN1_SEQUENCE)) {
+    return -1;
+  }
+
+  X509 *ret = x509_parse(&cbs, NULL);
+  if (ret == NULL) {
+    return 0;
+  }
+
+  *in = CBS_data(&cbs);
+  X509_free((X509 *)*pval);
+  *pval = (ASN1_VALUE *)ret;
+  return 1;
+}
+
+static int x509_i2d_cb(ASN1_VALUE **pval, unsigned char **out,
+                       const ASN1_ITEM *it) {
+  return i2d_X509((X509 *)*pval, out);
+}
+
+static const ASN1_EXTERN_FUNCS x509_extern_funcs = {
+    x509_new_cb,
+    x509_free_cb,
+    /*asn1_ex_clear=*/NULL,
+    x509_d2i_cb,
+    x509_i2d_cb,
+};
+
+IMPLEMENT_EXTERN_ASN1(X509, V_ASN1_SEQUENCE, x509_extern_funcs)
+
 X509 *X509_dup(X509 *x509) {
   uint8_t *der = NULL;
   int len = i2d_X509(x509, &der);
diff --git a/include/openssl/x509.h b/include/openssl/x509.h
index 9a49b40..4141007 100644
--- a/include/openssl/x509.h
+++ b/include/openssl/x509.h
@@ -118,6 +118,10 @@
 
 DEFINE_STACK_OF(X509)
 
+// X509 is an |ASN1_ITEM| whose ASN.1 type is X.509 Certificate (RFC 5280) and C
+// type is |X509*|.
+DECLARE_ASN1_ITEM(X509)
+
 // X509_up_ref adds one to the reference count of |x509| and returns one.
 OPENSSL_EXPORT int X509_up_ref(X509 *x509);