Add some CBB-based functions for crypto/x509 and crypto/asn1 types

For now, they forward to the i2d/i2c functions instead of being
CBB-based themselves. I've added a tag parameter to the crypto/asn1 ones
in anticipation of them being used for implicit tagging, once the rest
of the legacy parser becomes CBB-based.

But we already have enough things that cross the line that this seems
worthwhile.

Bug: 42290417
Change-Id: Ide588fe04d3d5862ca003ab94b068c009eadf2b1
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/78448
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
Auto-Submit: David Benjamin <davidben@google.com>
diff --git a/crypto/asn1/a_bitstr.cc b/crypto/asn1/a_bitstr.cc
index 82df5c8..6bdd726 100644
--- a/crypto/asn1/a_bitstr.cc
+++ b/crypto/asn1/a_bitstr.cc
@@ -17,6 +17,7 @@
 #include <limits.h>
 #include <string.h>
 
+#include <openssl/bytestring.h>
 #include <openssl/err.h>
 #include <openssl/mem.h>
 
@@ -94,6 +95,21 @@
   return ret;
 }
 
+int asn1_marshal_bit_string(CBB *out, const ASN1_BIT_STRING *in,
+                            CBS_ASN1_TAG tag) {
+  int len = i2c_ASN1_BIT_STRING(in, nullptr);
+  if (len <= 0) {
+    return 0;
+  }
+  tag = tag == 0 ? CBS_ASN1_BITSTRING : tag;
+  CBB child;
+  uint8_t *ptr;
+  return CBB_add_asn1(out, &child, tag) &&                         //
+         CBB_add_space(&child, &ptr, static_cast<size_t>(len)) &&  //
+         i2c_ASN1_BIT_STRING(in, &ptr) == len &&                   //
+         CBB_flush(out);
+}
+
 ASN1_BIT_STRING *c2i_ASN1_BIT_STRING(ASN1_BIT_STRING **a,
                                      const unsigned char **pp, long len) {
   ASN1_BIT_STRING *ret = NULL;
diff --git a/crypto/asn1/a_int.cc b/crypto/asn1/a_int.cc
index 7b0e28b5..e641705 100644
--- a/crypto/asn1/a_int.cc
+++ b/crypto/asn1/a_int.cc
@@ -23,6 +23,7 @@
 #include <openssl/mem.h>
 
 #include "../internal.h"
+#include "internal.h"
 
 
 ASN1_INTEGER *ASN1_INTEGER_dup(const ASN1_INTEGER *x) {
@@ -72,6 +73,20 @@
   return 1;
 }
 
+int asn1_marshal_integer(CBB *out, const ASN1_INTEGER *in, CBS_ASN1_TAG tag) {
+  int len = i2c_ASN1_INTEGER(in, nullptr);
+  if (len <= 0) {
+    return 0;
+  }
+  tag = tag == 0 ? CBS_ASN1_INTEGER : tag;
+  CBB child;
+  uint8_t *ptr;
+  return CBB_add_asn1(out, &child, tag) &&     //
+         CBB_add_space(&child, &ptr, static_cast<size_t>(len)) &&   //
+         i2c_ASN1_INTEGER(in, &ptr) == len &&  //
+         CBB_flush(out);
+}
+
 int i2c_ASN1_INTEGER(const ASN1_INTEGER *in, unsigned char **outp) {
   if (in == NULL) {
     return 0;
diff --git a/crypto/asn1/internal.h b/crypto/asn1/internal.h
index 8d863fc..2baeb12 100644
--- a/crypto/asn1/internal.h
+++ b/crypto/asn1/internal.h
@@ -180,6 +180,17 @@
 int asn1_bit_string_length(const ASN1_BIT_STRING *str,
                            uint8_t *out_padding_bits);
 
+// asn1_marshal_bit_string marshals |in| as a DER-encoded, ASN.1 BIT STRING and
+// writes the result to |out|. It returns one on success and zero on error. If
+// |tag| is non-zero, the tag is replaced with |tag|.
+int asn1_marshal_bit_string(CBB *out, const ASN1_BIT_STRING *in,
+                            CBS_ASN1_TAG tag);
+
+// asn1_marshal_integer marshals |in| as a DER-encoded, ASN.1 INTEGER and writes
+// the result to |out|. It returns one on success and zero on error. If |tag| is
+// non-zero, the tag is replaced with |tag|.
+int asn1_marshal_integer(CBB *out, const ASN1_INTEGER *in, CBS_ASN1_TAG tag);
+
 typedef struct {
   int nid;
   long minsize;
diff --git a/crypto/pkcs7/pkcs7_x509.cc b/crypto/pkcs7/pkcs7_x509.cc
index 8484c38..aabc9a2 100644
--- a/crypto/pkcs7/pkcs7_x509.cc
+++ b/crypto/pkcs7/pkcs7_x509.cc
@@ -26,6 +26,8 @@
 #include <openssl/stack.h>
 #include <openssl/x509.h>
 
+#include "../asn1/internal.h"
+#include "../x509/internal.h"
 #include "../internal.h"
 #include "internal.h"
 
@@ -431,24 +433,16 @@
   const struct signer_info_data *const si_data =
       reinterpret_cast<const struct signer_info_data *>(arg);
 
-  int ret = 0;
-  uint8_t *subject_bytes = NULL;
-  uint8_t *serial_bytes = NULL;
-
-  const int subject_len =
-      i2d_X509_NAME(X509_get_subject_name(si_data->sign_cert), &subject_bytes);
-  const int serial_len = i2d_ASN1_INTEGER(
-      (ASN1_INTEGER *)X509_get0_serialNumber(si_data->sign_cert),
-      &serial_bytes);
-
   CBB seq, issuer_and_serial, signing_algo, null, signature;
-  if (subject_len < 0 || serial_len < 0 ||
-      !CBB_add_asn1(out, &seq, CBS_ASN1_SEQUENCE) ||
+  if (!CBB_add_asn1(out, &seq, CBS_ASN1_SEQUENCE) ||
       // version
       !CBB_add_asn1_uint64(&seq, 1) ||
       !CBB_add_asn1(&seq, &issuer_and_serial, CBS_ASN1_SEQUENCE) ||
-      !CBB_add_bytes(&issuer_and_serial, subject_bytes, subject_len) ||
-      !CBB_add_bytes(&issuer_and_serial, serial_bytes, serial_len) ||
+      !x509_marshal_name(&issuer_and_serial,
+                         X509_get_subject_name(si_data->sign_cert)) ||
+      !asn1_marshal_integer(&issuer_and_serial,
+                            X509_get0_serialNumber(si_data->sign_cert),
+                            /*tag=*/0) ||
       !write_sha256_ai(&seq, NULL) ||
       !CBB_add_asn1(&seq, &signing_algo, CBS_ASN1_SEQUENCE) ||
       !OBJ_nid2cbb(&signing_algo, NID_rsaEncryption) ||
@@ -456,15 +450,10 @@
       !CBB_add_asn1(&seq, &signature, CBS_ASN1_OCTETSTRING) ||
       !CBB_add_bytes(&signature, si_data->signature, si_data->signature_len) ||
       !CBB_flush(out)) {
-    goto out;
+    return 0;
   }
 
-  ret = 1;
-
-out:
-  OPENSSL_free(subject_bytes);
-  OPENSSL_free(serial_bytes);
-  return ret;
+  return 1;
 }
 
 PKCS7 *PKCS7_sign(X509 *sign_cert, EVP_PKEY *pkey, STACK_OF(X509) *certs,
diff --git a/crypto/x509/internal.h b/crypto/x509/internal.h
index 1794d16..1fab446 100644
--- a/crypto/x509/internal.h
+++ b/crypto/x509/internal.h
@@ -547,6 +547,18 @@
 // TODO(https://crbug.com/boringssl/695): Remove this.
 int DIST_POINT_set_dpname(DIST_POINT_NAME *dpn, X509_NAME *iname);
 
+// x509_marshal_name marshals |in| as a DER-encoded, X.509 Name and writes the
+// result to |out|. It returns one on success and zero on error.
+//
+// TODO(https://crbug.com/boringssl/407): This function should be const and
+// thread-safe but is currently neither in some cases, notably if |in| was
+// mutated.
+int x509_marshal_name(CBB *out, X509_NAME *in);
+
+// x509_marshal_algorithm marshals |in| as a DER-encoded, AlgorithmIdentifier
+// and writes the result to |out|. It returns one on success and zero on error.
+int x509_marshal_algorithm(CBB *out, const X509_ALGOR *in);
+
 
 #if defined(__cplusplus)
 }  // extern C
diff --git a/crypto/x509/x_algor.cc b/crypto/x509/x_algor.cc
index 0a1162e..60eb006 100644
--- a/crypto/x509/x_algor.cc
+++ b/crypto/x509/x_algor.cc
@@ -20,6 +20,7 @@
 #include <openssl/obj.h>
 
 #include "../asn1/internal.h"
+#include "internal.h"
 
 
 ASN1_SEQUENCE(X509_ALGOR) = {
@@ -105,3 +106,11 @@
   }
   return ASN1_TYPE_cmp(a->parameter, b->parameter);
 }
+
+int x509_marshal_algorithm(CBB *out, const X509_ALGOR *in) {
+  uint8_t *ptr;
+  int len = i2d_X509_ALGOR(in, NULL);
+  return len > 0 &&  //
+         CBB_add_space(out, &ptr, static_cast<size_t>(len)) &&
+         i2d_X509_ALGOR(in, &ptr) == len;
+}
diff --git a/crypto/x509/x_name.cc b/crypto/x509/x_name.cc
index 16e60e4..035a03a 100644
--- a/crypto/x509/x_name.cc
+++ b/crypto/x509/x_name.cc
@@ -490,3 +490,12 @@
   }
   return 1;
 }
+
+int x509_marshal_name(CBB *out, X509_NAME *in) {
+  int len = i2d_X509_NAME(in, nullptr);
+  if (len <= 0) {
+    return 0;
+  }
+  uint8_t *ptr;
+  return CBB_add_space(out, &ptr, len) && i2d_X509_NAME(in, &ptr) == len;
+}
diff --git a/crypto/x509/x_x509.cc b/crypto/x509/x_x509.cc
index ebb20d5..098c757 100644
--- a/crypto/x509/x_x509.cc
+++ b/crypto/x509/x_x509.cc
@@ -223,42 +223,26 @@
     return -1;
   }
 
-  CBB cbb, cert;
-  int len;
-  if (!CBB_init(&cbb, 64) ||  //
-      !CBB_add_asn1(&cbb, &cert, CBS_ASN1_SEQUENCE)) {
-    goto err;
+  bssl::ScopedCBB cbb;
+  CBB cert;
+  if (!CBB_init(cbb.get(), 64) ||  //
+      !CBB_add_asn1(cbb.get(), &cert, CBS_ASN1_SEQUENCE)) {
+    return -1;
   }
 
   // TODO(crbug.com/boringssl/443): When the rest of the library is decoupled
   // from the tasn_*.c implementation, replace this with |CBS|-based functions.
   uint8_t *out;
-  len = i2d_X509_CINF(x509->cert_info, NULL);
+  int len = i2d_X509_CINF(x509->cert_info, NULL);
   if (len < 0 ||  //
-      !CBB_add_space(&cert, &out, (size_t)len) ||
-      i2d_X509_CINF(x509->cert_info, &out) != len) {
-    goto err;
+      !CBB_add_space(&cert, &out, static_cast<size_t>(len)) ||
+      i2d_X509_CINF(x509->cert_info, &out) != len ||
+      !x509_marshal_algorithm(&cert, x509->sig_alg) ||
+      !asn1_marshal_bit_string(&cert, x509->signature, /*tag=*/0)) {
+    return -1;
   }
 
-  len = i2d_X509_ALGOR(x509->sig_alg, NULL);
-  if (len < 0 ||  //
-      !CBB_add_space(&cert, &out, (size_t)len) ||
-      i2d_X509_ALGOR(x509->sig_alg, &out) != len) {
-    goto err;
-  }
-
-  len = i2d_ASN1_BIT_STRING(x509->signature, NULL);
-  if (len < 0 ||  //
-      !CBB_add_space(&cert, &out, (size_t)len) ||
-      i2d_ASN1_BIT_STRING(x509->signature, &out) != len) {
-    goto err;
-  }
-
-  return CBB_finish_i2d(&cbb, outp);
-
-err:
-  CBB_cleanup(&cbb);
-  return -1;
+  return CBB_finish_i2d(cbb.get(), outp);
 }
 
 static int x509_new_cb(ASN1_VALUE **pval, const ASN1_ITEM *it) {