Add the suite of EC_KEY and EC_POINT serializers.

OpenSSL added a bunch of these. oct2priv is a little weird (see
https://crbug.com/boringssl/534), but I've made it match OpenSSL and
set_private_key for now. But I think we should reduce the state-space a
bit.

EC_KEY_oct2priv behaves slightly differently from upstream OpenSSL in
one way: we reject inputs that aren't exactly the right size. This
matches the OpenSSL documentation (the OCTET STRING inside an
ECPrivateKey, per spec, is fixed-width), but not OpenSSL's behavior.

Update-note: see go/xshow when incorporating this change internally.
Change-Id: I33863d773ac4c7f3eabf4ffda157e8250c7fdbd9
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/55066
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
diff --git a/crypto/fipsmodule/ec/ec_key.c b/crypto/fipsmodule/ec/ec_key.c
index 3b3da24..31a097e 100644
--- a/crypto/fipsmodule/ec/ec_key.c
+++ b/crypto/fipsmodule/ec/ec_key.c
@@ -409,24 +409,70 @@
 }
 
 size_t EC_KEY_key2buf(const EC_KEY *key, point_conversion_form_t form,
-                      unsigned char **out_buf, BN_CTX *ctx) {
+                      uint8_t **out_buf, BN_CTX *ctx) {
   if (key == NULL || key->pub_key == NULL || key->group == NULL) {
+    OPENSSL_PUT_ERROR(EC, EC_R_MISSING_PARAMETERS);
     return 0;
   }
 
-  const size_t len =
-      EC_POINT_point2oct(key->group, key->pub_key, form, NULL, 0, ctx);
+  return EC_POINT_point2buf(key->group, key->pub_key, form, out_buf, ctx);
+}
+
+int EC_KEY_oct2priv(EC_KEY *key, const uint8_t *in, size_t len) {
+  if (key->group == NULL) {
+    OPENSSL_PUT_ERROR(EC, EC_R_MISSING_PARAMETERS);
+    return 0;
+  }
+
+  if (len != BN_num_bytes(EC_GROUP_get0_order(key->group))) {
+    OPENSSL_PUT_ERROR(EC, EC_R_DECODE_ERROR);
+    return 0;
+  }
+
+  BIGNUM *priv_key = BN_bin2bn(in, len, NULL);
+  int ok = priv_key != NULL &&  //
+           EC_KEY_set_private_key(key, priv_key);
+  BN_free(priv_key);
+  return ok;
+}
+
+size_t EC_KEY_priv2oct(const EC_KEY *key, uint8_t *out, size_t max_out) {
+  if (key->group == NULL || key->priv_key == NULL) {
+    OPENSSL_PUT_ERROR(EC, EC_R_MISSING_PARAMETERS);
+    return 0;
+  }
+
+  size_t len = BN_num_bytes(EC_GROUP_get0_order(key->group));
+  if (out == NULL) {
+    return len;
+  }
+
+  if (max_out < len) {
+    OPENSSL_PUT_ERROR(EC, EC_R_BUFFER_TOO_SMALL);
+    return 0;
+  }
+
+  size_t bytes_written;
+  ec_scalar_to_bytes(key->group, out, &bytes_written, &key->priv_key->scalar);
+  assert(bytes_written == len);
+  return len;
+}
+
+size_t EC_KEY_priv2buf(const EC_KEY *key, uint8_t **out_buf) {
+  *out_buf = NULL;
+  size_t len = EC_KEY_priv2oct(key, NULL, 0);
   if (len == 0) {
     return 0;
   }
 
   uint8_t *buf = OPENSSL_malloc(len);
   if (buf == NULL) {
+    OPENSSL_PUT_ERROR(EC, ERR_R_MALLOC_FAILURE);
     return 0;
   }
 
-  if (EC_POINT_point2oct(key->group, key->pub_key, form, buf, len, ctx) !=
-      len) {
+  len = EC_KEY_priv2oct(key, buf, len);
+  if (len == 0) {
     OPENSSL_free(buf);
     return 0;
   }
diff --git a/crypto/fipsmodule/ec/ec_test.cc b/crypto/fipsmodule/ec/ec_test.cc
index c54adb5..12430b8 100644
--- a/crypto/fipsmodule/ec/ec_test.cc
+++ b/crypto/fipsmodule/ec/ec_test.cc
@@ -105,6 +105,20 @@
   0x37, 0xbf, 0x51, 0xf5,
 };
 
+static const uint8_t kECKeyWithZerosPublic[] = {
+    0x04, 0x6b, 0x17, 0xd1, 0xf2, 0xe1, 0x2c, 0x42, 0x47, 0xf8, 0xbc,
+    0xe6, 0xe5, 0x63, 0xa4, 0x40, 0xf2, 0x77, 0x03, 0x7d, 0x81, 0x2d,
+    0xeb, 0x33, 0xa0, 0xf4, 0xa1, 0x39, 0x45, 0xd8, 0x98, 0xc2, 0x96,
+    0x4f, 0xe3, 0x42, 0xe2, 0xfe, 0x1a, 0x7f, 0x9b, 0x8e, 0xe7, 0xeb,
+    0x4a, 0x7c, 0x0f, 0x9e, 0x16, 0x2b, 0xce, 0x33, 0x57, 0x6b, 0x31,
+    0x5e, 0xce, 0xcb, 0xb6, 0x40, 0x68, 0x37, 0xbf, 0x51, 0xf5,
+};
+
+static const uint8_t kECKeyWithZerosRawPrivate[] = {
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01};
+
 // DecodeECPrivateKey decodes |in| as an ECPrivateKey structure and returns the
 // result or nullptr on error.
 static bssl::UniquePtr<EC_KEY> DecodeECPrivateKey(const uint8_t *in,
@@ -191,11 +205,52 @@
   EXPECT_TRUE(EncodeECPrivateKey(&out, key.get()));
   EXPECT_EQ(Bytes(kECKeyWithZeros), Bytes(out.data(), out.size()));
 
+  // Check the private key encodes correctly, including with the leading zeros.
+  EXPECT_EQ(32u, EC_KEY_priv2oct(key.get(), nullptr, 0));
+  uint8_t buf[32];
+  ASSERT_EQ(32u, EC_KEY_priv2oct(key.get(), buf, sizeof(buf)));
+  EXPECT_EQ(Bytes(buf), Bytes(kECKeyWithZerosRawPrivate));
+
+  // Buffer too small.
+  EXPECT_EQ(0u, EC_KEY_priv2oct(key.get(), buf, sizeof(buf) - 1));
+
+  // Extra space in buffer.
+  uint8_t large_buf[33];
+  ASSERT_EQ(32u, EC_KEY_priv2oct(key.get(), large_buf, sizeof(large_buf)));
+  EXPECT_EQ(Bytes(buf), Bytes(kECKeyWithZerosRawPrivate));
+
+  // Allocating API.
+  uint8_t *buf_alloc;
+  size_t len = EC_KEY_priv2buf(key.get(), &buf_alloc);
+  ASSERT_GT(len, 0u);
+  bssl::UniquePtr<uint8_t> free_buf_alloc(buf_alloc);
+  EXPECT_EQ(Bytes(buf_alloc, len), Bytes(kECKeyWithZerosRawPrivate));
+
   // Keys without leading zeros also parse, but they encode correctly.
   key = DecodeECPrivateKey(kECKeyMissingZeros, sizeof(kECKeyMissingZeros));
   ASSERT_TRUE(key);
   EXPECT_TRUE(EncodeECPrivateKey(&out, key.get()));
   EXPECT_EQ(Bytes(kECKeyWithZeros), Bytes(out.data(), out.size()));
+
+  // Test the key can be constructed with |EC_KEY_oct2*|.
+  key.reset(EC_KEY_new_by_curve_name(NID_X9_62_prime256v1));
+  ASSERT_TRUE(key);
+  ASSERT_TRUE(EC_KEY_oct2key(key.get(), kECKeyWithZerosPublic,
+                             sizeof(kECKeyWithZerosPublic), nullptr));
+  ASSERT_TRUE(EC_KEY_oct2priv(key.get(), kECKeyWithZerosRawPrivate,
+                              sizeof(kECKeyWithZerosRawPrivate)));
+  EXPECT_TRUE(EncodeECPrivateKey(&out, key.get()));
+  EXPECT_EQ(Bytes(kECKeyWithZeros), Bytes(out.data(), out.size()));
+
+  // |EC_KEY_oct2priv|'s format is fixed-width and must match the group order.
+  key.reset(EC_KEY_new_by_curve_name(NID_X9_62_prime256v1));
+  ASSERT_TRUE(key);
+  EXPECT_FALSE(EC_KEY_oct2priv(key.get(), kECKeyWithZerosRawPrivate + 1,
+                               sizeof(kECKeyWithZerosRawPrivate) - 1));
+  uint8_t padded[sizeof(kECKeyWithZerosRawPrivate) + 1] = {0};
+  memcpy(padded + 1, kECKeyWithZerosRawPrivate,
+         sizeof(kECKeyWithZerosRawPrivate));
+  EXPECT_FALSE(EC_KEY_oct2priv(key.get(), padded, sizeof(padded)));
 }
 
 TEST(ECTest, SpecifiedCurve) {
diff --git a/crypto/fipsmodule/ec/oct.c b/crypto/fipsmodule/ec/oct.c
index 7032635..dd2e007 100644
--- a/crypto/fipsmodule/ec/oct.c
+++ b/crypto/fipsmodule/ec/oct.c
@@ -91,9 +91,9 @@
 
 size_t ec_point_to_bytes(const EC_GROUP *group, const EC_AFFINE *point,
                          point_conversion_form_t form, uint8_t *buf,
-                         size_t len) {
+                         size_t max_out) {
   size_t output_len = ec_point_byte_len(group, form);
-  if (len < output_len) {
+  if (max_out < output_len) {
     OPENSSL_PUT_ERROR(EC, EC_R_BUFFER_TOO_SMALL);
     return 0;
   }
@@ -210,7 +210,7 @@
 
 size_t EC_POINT_point2oct(const EC_GROUP *group, const EC_POINT *point,
                           point_conversion_form_t form, uint8_t *buf,
-                          size_t len, BN_CTX *ctx) {
+                          size_t max_out, BN_CTX *ctx) {
   if (EC_GROUP_cmp(group, point->group, NULL) != 0) {
     OPENSSL_PUT_ERROR(EC, EC_R_INCOMPATIBLE_OBJECTS);
     return 0;
@@ -228,7 +228,29 @@
   if (!ec_jacobian_to_affine(group, &affine, &point->raw)) {
     return 0;
   }
-  return ec_point_to_bytes(group, &affine, form, buf, len);
+  return ec_point_to_bytes(group, &affine, form, buf, max_out);
+}
+
+size_t EC_POINT_point2buf(const EC_GROUP *group, const EC_POINT *point,
+                          point_conversion_form_t form, uint8_t **out_buf,
+                          BN_CTX *ctx) {
+  *out_buf = NULL;
+  size_t len = EC_POINT_point2oct(group, point, form, NULL, 0, ctx);
+  if (len == 0) {
+    return 0;
+  }
+  uint8_t *buf = OPENSSL_malloc(len);
+  if (buf == NULL) {
+    OPENSSL_PUT_ERROR(EC, ERR_R_MALLOC_FAILURE);
+    return 0;
+  }
+  len = EC_POINT_point2oct(group, point, form, buf, len, ctx);
+  if (len == 0) {
+    OPENSSL_free(buf);
+    return 0;
+  }
+  *out_buf = buf;
+  return len;
 }
 
 int EC_POINT_set_compressed_coordinates_GFp(const EC_GROUP *group,
diff --git a/include/openssl/ec.h b/include/openssl/ec.h
index 8339bfb..63f0c6f 100644
--- a/include/openssl/ec.h
+++ b/include/openssl/ec.h
@@ -253,13 +253,23 @@
                                                    BN_CTX *ctx);
 
 // EC_POINT_point2oct serialises |point| into the X9.62 form given by |form|
-// into, at most, |len| bytes at |buf|. It returns the number of bytes written
-// or zero on error if |buf| is non-NULL, else the number of bytes needed. The
-// |ctx| argument may be used if not NULL.
+// into, at most, |max_out| bytes at |buf|. It returns the number of bytes
+// written or zero on error if |buf| is non-NULL, else the number of bytes
+// needed. The |ctx| argument may be used if not NULL.
 OPENSSL_EXPORT size_t EC_POINT_point2oct(const EC_GROUP *group,
                                          const EC_POINT *point,
                                          point_conversion_form_t form,
-                                         uint8_t *buf, size_t len, BN_CTX *ctx);
+                                         uint8_t *buf, size_t max_out,
+                                         BN_CTX *ctx);
+
+// EC_POINT_point2buf serialises |point| into the X9.62 form given by |form| to
+// a newly-allocated buffer and sets |*out_buf| to point to it. It returns the
+// length of the result on success or zero on error. The caller must release
+// |*out_buf| with |OPENSSL_free| when done.
+OPENSSL_EXPORT size_t EC_POINT_point2buf(const EC_GROUP *group,
+                                         const EC_POINT *point,
+                                         point_conversion_form_t form,
+                                         uint8_t **out_buf, BN_CTX *ctx);
 
 // EC_POINT_point2cbb behaves like |EC_POINT_point2oct| but appends the
 // serialised point to |cbb|. It returns one on success and zero on error.
diff --git a/include/openssl/ec_key.h b/include/openssl/ec_key.h
index 3143290..ee9c9f0 100644
--- a/include/openssl/ec_key.h
+++ b/include/openssl/ec_key.h
@@ -186,12 +186,31 @@
 OPENSSL_EXPORT int EC_KEY_oct2key(EC_KEY *key, const uint8_t *in, size_t len,
                                   BN_CTX *ctx);
 
-// EC_KEY_key2buf encodes the public key in |key| to an allocated octet string
-// and sets |*out_buf| to point to it. It returns the length of the encoded
-// octet string or zero if an error occurred.
+// EC_KEY_key2buf behaves like |EC_POINT_point2buf|, except it encodes the
+// public key in |key|.
 OPENSSL_EXPORT size_t EC_KEY_key2buf(const EC_KEY *key,
                                      point_conversion_form_t form,
-                                     unsigned char **out_buf, BN_CTX *ctx);
+                                     uint8_t **out_buf, BN_CTX *ctx);
+
+// EC_KEY_oct2priv decodes a big-endian, zero-padded integer from |len| bytes
+// from |in| and sets |key|'s private key to the result. It returns one on
+// success and zero on error. The input must be padded to the size of |key|'s
+// group order.
+OPENSSL_EXPORT int EC_KEY_oct2priv(EC_KEY *key, const uint8_t *in, size_t len);
+
+// EC_KEY_priv2oct serializes |key|'s private key as a big-endian integer,
+// zero-padded to the size of |key|'s group order and writes the result to at
+// most |max_out| bytes of |out|. It returns the number of bytes written on
+// success and zero on error. If |out| is NULL, it returns the number of bytes
+// needed without writing anything.
+OPENSSL_EXPORT size_t EC_KEY_priv2oct(const EC_KEY *key, uint8_t *out,
+                                      size_t max_out);
+
+// EC_KEY_priv2buf behaves like |EC_KEY_priv2oct| but sets |*out_buf| to a
+// newly-allocated buffer containing the result. It returns the size of the
+// result on success and zero on error. The caller must release |*out_buf| with
+// |OPENSSL_free| when done.
+OPENSSL_EXPORT size_t EC_KEY_priv2buf(const EC_KEY *key, uint8_t **out_buf);
 
 
 // Key generation.