Rewrite i2o_ECPublicKey with CBB_finish_i2d.

Less code, and internally handles overflows. (Although this one cannot
overflow.)

Bug: 516
Change-Id: I3c2718075689d2815a43534a578a323c52787223
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/55452
Reviewed-by: Bob Beck <bbe@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
diff --git a/crypto/ec_extra/ec_asn1.c b/crypto/ec_extra/ec_asn1.c
index 43132a6..ee75165 100644
--- a/crypto/ec_extra/ec_asn1.c
+++ b/crypto/ec_extra/ec_asn1.c
@@ -518,42 +518,18 @@
 }
 
 int i2o_ECPublicKey(const EC_KEY *key, uint8_t **outp) {
-  size_t buf_len = 0;
-  int new_buffer = 0;
-
   if (key == NULL) {
     OPENSSL_PUT_ERROR(EC, ERR_R_PASSED_NULL_PARAMETER);
     return 0;
   }
-
-  buf_len = EC_POINT_point2oct(key->group, key->pub_key, key->conv_form, NULL,
-                               0, NULL);
-
-  if (outp == NULL || buf_len == 0) {
-    // out == NULL => just return the length of the octet string
-    return buf_len;
+  CBB cbb;
+  if (!CBB_init(&cbb, 0) ||  //
+      !EC_POINT_point2cbb(&cbb, key->group, key->pub_key, key->conv_form,
+                          NULL)) {
+    CBB_cleanup(&cbb);
+    return -1;
   }
-
-  if (*outp == NULL) {
-    *outp = OPENSSL_malloc(buf_len);
-    if (*outp == NULL) {
-      OPENSSL_PUT_ERROR(EC, ERR_R_MALLOC_FAILURE);
-      return 0;
-    }
-    new_buffer = 1;
-  }
-  if (!EC_POINT_point2oct(key->group, key->pub_key, key->conv_form, *outp,
-                          buf_len, NULL)) {
-    OPENSSL_PUT_ERROR(EC, ERR_R_EC_LIB);
-    if (new_buffer) {
-      OPENSSL_free(*outp);
-      *outp = NULL;
-    }
-    return 0;
-  }
-
-  if (!new_buffer) {
-    *outp += buf_len;
-  }
-  return buf_len;
+  int ret = CBB_finish_i2d(&cbb, outp);
+  // Historically, this function used the wrong return value on error.
+  return ret > 0 ? ret : 0;
 }
diff --git a/include/openssl/ec_key.h b/include/openssl/ec_key.h
index ee9c9f0..00986cf 100644
--- a/include/openssl/ec_key.h
+++ b/include/openssl/ec_key.h
@@ -361,7 +361,7 @@
                                        long len);
 
 // i2o_ECPublicKey marshals an EC point from |key|, as described in
-// |i2d_SAMPLE|.
+// |i2d_SAMPLE|, except it returns zero on error instead of a negative value.
 //
 // Use |EC_POINT_point2cbb| instead.
 OPENSSL_EXPORT int i2o_ECPublicKey(const EC_KEY *key, unsigned char **outp);