Support the allocating case of i2d_ASN1_{BOOLEAN,OBJECT}.

Imported from upstream's 0971432f6f6d8b40d797133621809bd31eb7bf4e and
7d4c97add12cfa5d4589880b09d6139c3203e2f4, but with missing tests added. Along
the way, make Bytes work with any Span<const uint8_t>-convertable type.

Change-Id: If365f981fe8a8274e12000309ffd99b1bb719842
Reviewed-on: https://boringssl-review.googlesource.com/31086
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/crypto/asn1/a_bool.c b/crypto/asn1/a_bool.c
index 64a079e..34bbd15 100644
--- a/crypto/asn1/a_bool.c
+++ b/crypto/asn1/a_bool.c
@@ -62,17 +62,30 @@
 int i2d_ASN1_BOOLEAN(int a, unsigned char **pp)
 {
     int r;
-    unsigned char *p;
+    unsigned char *p, *allocated = NULL;
 
     r = ASN1_object_size(0, 1, V_ASN1_BOOLEAN);
     if (pp == NULL)
         return (r);
-    p = *pp;
+
+    if (*pp == NULL) {
+        if ((p = allocated = OPENSSL_malloc(r)) == NULL) {
+            OPENSSL_PUT_ERROR(ASN1, ERR_R_MALLOC_FAILURE);
+            return 0;
+        }
+    } else {
+        p = *pp;
+    }
 
     ASN1_put_object(&p, 0, 1, V_ASN1_BOOLEAN, V_ASN1_UNIVERSAL);
-    *(p++) = (unsigned char)a;
-    *pp = p;
-    return (r);
+    *p = (unsigned char)a;
+
+    /*
+     * If a new buffer was allocated, just return it back.
+     * If not, return the incremented buffer pointer.
+     */
+    *pp = allocated != NULL ? allocated : p + 1;
+    return r;
 }
 
 int d2i_ASN1_BOOLEAN(int *a, const unsigned char **pp, long length)
diff --git a/crypto/asn1/a_object.c b/crypto/asn1/a_object.c
index 005e37d..97335bf 100644
--- a/crypto/asn1/a_object.c
+++ b/crypto/asn1/a_object.c
@@ -68,7 +68,7 @@
 
 int i2d_ASN1_OBJECT(ASN1_OBJECT *a, unsigned char **pp)
 {
-    unsigned char *p;
+    unsigned char *p, *allocated = NULL;
     int objsize;
 
     if ((a == NULL) || (a->data == NULL))
@@ -78,13 +78,24 @@
     if (pp == NULL || objsize == -1)
         return objsize;
 
-    p = *pp;
+    if (*pp == NULL) {
+        if ((p = allocated = OPENSSL_malloc(objsize)) == NULL) {
+            OPENSSL_PUT_ERROR(ASN1, ERR_R_MALLOC_FAILURE);
+            return 0;
+        }
+    } else {
+        p = *pp;
+    }
+
     ASN1_put_object(&p, 0, a->length, V_ASN1_OBJECT, V_ASN1_UNIVERSAL);
     OPENSSL_memcpy(p, a->data, a->length);
-    p += a->length;
 
-    *pp = p;
-    return (objsize);
+    /*
+     * If a new buffer was allocated, just return it back.
+     * If not, return the incremented buffer pointer.
+     */
+    *pp = allocated != NULL ? allocated : p + a->length;
+    return objsize;
 }
 
 int i2t_ASN1_OBJECT(char *buf, int buf_len, ASN1_OBJECT *a)
diff --git a/crypto/asn1/asn1_test.cc b/crypto/asn1/asn1_test.cc
index 1cca36c..ff80e49 100644
--- a/crypto/asn1/asn1_test.cc
+++ b/crypto/asn1/asn1_test.cc
@@ -24,6 +24,8 @@
 #include <openssl/bytestring.h>
 #include <openssl/err.h>
 #include <openssl/mem.h>
+#include <openssl/obj.h>
+#include <openssl/span.h>
 
 #include "../test/test_util.h"
 
@@ -148,3 +150,37 @@
   // is too deep, so the |ASN1_R_NESTED_TOO_DEEP| entry drops off the queue.
   ASN1_LINKED_LIST_free(list);
 }
+
+template <typename T>
+void TestSerialize(T obj, int (*i2d_func)(T a, uint8_t **pp),
+                   bssl::Span<const uint8_t> expected) {
+  int len = static_cast<int>(expected.size());
+  ASSERT_EQ(i2d_func(obj, nullptr), len);
+
+  std::vector<uint8_t> buf(expected.size());
+  uint8_t *ptr = buf.data();
+  ASSERT_EQ(i2d_func(obj, &ptr), len);
+  EXPECT_EQ(ptr, buf.data() + buf.size());
+  EXPECT_EQ(Bytes(expected), Bytes(buf));
+
+  // Test the allocating version.
+  ptr = nullptr;
+  ASSERT_EQ(i2d_func(obj, &ptr), len);
+  EXPECT_EQ(Bytes(expected), Bytes(ptr, expected.size()));
+  OPENSSL_free(ptr);
+}
+
+TEST(ASN1Test, SerializeObject) {
+  static const uint8_t kDER[] = {0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+                                 0xf7, 0x0d, 0x01, 0x01, 0x01};
+  const ASN1_OBJECT *obj = OBJ_nid2obj(NID_rsaEncryption);
+  TestSerialize(const_cast<ASN1_OBJECT *>(obj), i2d_ASN1_OBJECT, kDER);
+}
+
+TEST(ASN1Test, SerializeBoolean) {
+  static const uint8_t kTrue[] = {0x01, 0x01, 0xff};
+  TestSerialize(0xff, i2d_ASN1_BOOLEAN, kTrue);
+
+  static const uint8_t kFalse[] = {0x01, 0x01, 0x00};
+  TestSerialize(0x00, i2d_ASN1_BOOLEAN, kFalse);
+}
diff --git a/crypto/test/test_util.cc b/crypto/test/test_util.cc
index 4ad777f..29be702 100644
--- a/crypto/test/test_util.cc
+++ b/crypto/test/test_util.cc
@@ -30,15 +30,15 @@
 }
 
 std::ostream &operator<<(std::ostream &os, const Bytes &in) {
-  if (in.len == 0) {
+  if (in.span_.empty()) {
     return os << "<empty Bytes>";
   }
 
   // Print a byte slice as hex.
   static const char hex[] = "0123456789abcdef";
-  for (size_t i = 0; i < in.len; i++) {
-    os << hex[in.data[i] >> 4];
-    os << hex[in.data[i] & 0xf];
+  for (uint8_t b : in.span_) {
+    os << hex[b >> 4];
+    os << hex[b & 0xf];
   }
   return os;
 }
diff --git a/crypto/test/test_util.h b/crypto/test/test_util.h
index 9c9ef58..2ef3f19 100644
--- a/crypto/test/test_util.h
+++ b/crypto/test/test_util.h
@@ -24,6 +24,8 @@
 #include <string>
 #include <vector>
 
+#include <openssl/span.h>
+
 #include "../internal.h"
 
 
@@ -35,26 +37,22 @@
 // allows it to be used in EXPECT_EQ macros.
 struct Bytes {
   Bytes(const uint8_t *data_arg, size_t len_arg)
-      : data(data_arg), len(len_arg) {}
+      : span_(data_arg, len_arg) {}
   Bytes(const char *data_arg, size_t len_arg)
-      : data(reinterpret_cast<const uint8_t *>(data_arg)), len(len_arg) {}
+      : span_(reinterpret_cast<const uint8_t *>(data_arg), len_arg) {}
 
   explicit Bytes(const char *str)
-      : data(reinterpret_cast<const uint8_t *>(str)), len(strlen(str)) {}
+      : span_(reinterpret_cast<const uint8_t *>(str), strlen(str)) {}
   explicit Bytes(const std::string &str)
-      : data(reinterpret_cast<const uint8_t *>(str.data())), len(str.size()) {}
-  explicit Bytes(const std::vector<uint8_t> &vec)
-      : data(vec.data()), len(vec.size()) {}
+      : span_(reinterpret_cast<const uint8_t *>(str.data()), str.size()) {}
+  explicit Bytes(bssl::Span<const uint8_t> span)
+      : span_(span) {}
 
-  template <size_t N>
-  explicit Bytes(const uint8_t (&array)[N]) : data(array), len(N) {}
-
-  const uint8_t *data;
-  size_t len;
+  bssl::Span<const uint8_t> span_;
 };
 
 inline bool operator==(const Bytes &a, const Bytes &b) {
-  return a.len == b.len && OPENSSL_memcmp(a.data, b.data, a.len) == 0;
+  return a.span_ == b.span_;
 }
 
 inline bool operator!=(const Bytes &a, const Bytes &b) { return !(a == b); }