Re-add |BN_bn2mpi| and |BN_mpi2bn| from OpenSSL at fd682e4c.

This benefits mainly M2Crypto.

Change-Id: I29bd0fa31b218760055ba467673f3882e46010c7
Reviewed-on: https://boringssl-review.googlesource.com/5722
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/crypto/bn/bn_test.cc b/crypto/bn/bn_test.cc
index e1588f7..74299c5 100644
--- a/crypto/bn/bn_test.cc
+++ b/crypto/bn/bn_test.cc
@@ -82,6 +82,7 @@
 #include <openssl/mem.h>
 
 #include "../crypto/test/scoped_types.h"
+#include "../crypto/test/test_util.h"
 
 
 // This program tests the BIGNUM implementation. It takes an optional -bc
@@ -121,6 +122,7 @@
 static bool test_dec2bn(BN_CTX *ctx);
 static bool test_hex2bn(BN_CTX *ctx);
 static bool test_asc2bn(BN_CTX *ctx);
+static bool test_mpi();
 static bool test_rand();
 static bool test_asn1();
 
@@ -316,6 +318,7 @@
       !test_dec2bn(ctx.get()) ||
       !test_hex2bn(ctx.get()) ||
       !test_asc2bn(ctx.get()) ||
+      !test_mpi() ||
       !test_rand() ||
       !test_asn1()) {
     return 1;
@@ -1582,6 +1585,63 @@
   return true;
 }
 
+struct MPITest {
+  const char *base10;
+  const char *mpi;
+  size_t mpi_len;
+};
+
+static const MPITest kMPITests[] = {
+  { "0", "\x00\x00\x00\x00", 4 },
+  { "1", "\x00\x00\x00\x01\x01", 5 },
+  { "-1", "\x00\x00\x00\x01\x81", 5 },
+  { "128", "\x00\x00\x00\x02\x00\x80", 6 },
+  { "256", "\x00\x00\x00\x02\x01\x00", 6 },
+  { "-256", "\x00\x00\x00\x02\x81\x00", 6 },
+};
+
+static bool test_mpi() {
+  uint8_t scratch[8];
+
+  for (size_t i = 0; i < sizeof(kMPITests) / sizeof(kMPITests[0]); i++) {
+    const MPITest &test = kMPITests[i];
+    ScopedBIGNUM bn(ASCIIToBIGNUM(test.base10));
+    const size_t mpi_len = BN_bn2mpi(bn.get(), NULL);
+    if (mpi_len > sizeof(scratch)) {
+      fprintf(stderr, "MPI test #%u: MPI size is too large to test.\n",
+              (unsigned)i);
+      return false;
+    }
+
+    const size_t mpi_len2 = BN_bn2mpi(bn.get(), scratch);
+    if (mpi_len != mpi_len2) {
+      fprintf(stderr, "MPI test #%u: length changes.\n", (unsigned)i);
+      return false;
+    }
+
+    if (mpi_len != test.mpi_len ||
+        memcmp(test.mpi, scratch, mpi_len) != 0) {
+      fprintf(stderr, "MPI test #%u failed:\n", (unsigned)i);
+      hexdump(stderr, "Expected: ", test.mpi, test.mpi_len);
+      hexdump(stderr, "Got:      ", scratch, mpi_len);
+      return false;
+    }
+
+    ScopedBIGNUM bn2(BN_mpi2bn(scratch, mpi_len, NULL));
+    if (bn2.get() == nullptr) {
+      fprintf(stderr, "MPI test #%u: failed to parse\n", (unsigned)i);
+      return false;
+    }
+
+    if (BN_cmp(bn.get(), bn2.get()) != 0) {
+      fprintf(stderr, "MPI test #%u: wrong result\n", (unsigned)i);
+      return false;
+    }
+  }
+
+  return true;
+}
+
 static bool test_rand() {
   ScopedBIGNUM bn(BN_new());
   if (!bn) {
diff --git a/crypto/bn/convert.c b/crypto/bn/convert.c
index 0f2f2a5..0122709 100644
--- a/crypto/bn/convert.c
+++ b/crypto/bn/convert.c
@@ -517,3 +517,81 @@
       return BN_MASK2;
   }
 }
+
+size_t BN_bn2mpi(const BIGNUM *in, uint8_t *out) {
+  const size_t bits = BN_num_bits(in);
+  const size_t bytes = (bits + 7) / 8;
+  /* If the number of bits is a multiple of 8, i.e. if the MSB is set,
+   * prefix with a zero byte. */
+  int extend = 0;
+  if (bytes != 0 && (bits & 0x07) == 0) {
+    extend = 1;
+  }
+
+  const size_t len = bytes + extend;
+  if (len < bytes ||
+      4 + len < len ||
+      (len & 0xffffffff) != len) {
+    /* If we cannot represent the number then we emit zero as the interface
+     * doesn't allow an error to be signalled. */
+    if (out) {
+      memset(out, 0, 4);
+    }
+    return 4;
+  }
+
+  if (out == NULL) {
+    return 4 + len;
+  }
+
+  out[0] = len >> 24;
+  out[1] = len >> 16;
+  out[2] = len >> 8;
+  out[3] = len;
+  if (extend) {
+    out[4] = 0;
+  }
+  BN_bn2bin(in, out + 4 + extend);
+  if (in->neg && len > 0) {
+    out[4] |= 0x80;
+  }
+  return len + 4;
+}
+
+BIGNUM *BN_mpi2bn(const uint8_t *in, size_t len, BIGNUM *out) {
+  if (len < 4) {
+    OPENSSL_PUT_ERROR(BN, BN_R_BAD_ENCODING);
+    return NULL;
+  }
+  const size_t in_len = ((size_t)in[0] << 24) |
+                        ((size_t)in[1] << 16) |
+                        ((size_t)in[2] << 8) |
+                        ((size_t)in[3]);
+  if (in_len != len - 4) {
+    OPENSSL_PUT_ERROR(BN, BN_R_BAD_ENCODING);
+    return NULL;
+  }
+
+  if (out == NULL) {
+    out = BN_new();
+  }
+  if (out == NULL) {
+    OPENSSL_PUT_ERROR(BN, ERR_R_MALLOC_FAILURE);
+    return NULL;
+  }
+
+  if (in_len == 0) {
+    BN_zero(out);
+    return out;
+  }
+
+  in += 4;
+  if (BN_bin2bn(in, in_len, out) == NULL) {
+    return NULL;
+  }
+  out->neg = ((*in) & 0x80) != 0;
+  if (out->neg) {
+    BN_clear_bit(out, BN_num_bits(out) - 1);
+  }
+  return out;
+}
diff --git a/include/openssl/bn.h b/include/openssl/bn.h
index 70c0546..7edb778 100644
--- a/include/openssl/bn.h
+++ b/include/openssl/bn.h
@@ -794,6 +794,25 @@
                                     BN_CTX *ctx, BN_MONT_CTX *m_ctx);
 
 
+/* Deprecated functions */
+
+/* BN_bn2mpi serialises the value of |in| to |out|, using a format that consists
+ * of the number's length in bytes represented as a 4-byte big-endian number,
+ * and the number itself in big-endian format, where the most significant bit
+ * signals a negative number. (The representation of numbers with the MSB set is
+ * prefixed with null byte). |out| must have sufficient space available; to
+ * find the needed amount of space, call the function with |out| set to NULL. */
+OPENSSL_EXPORT size_t BN_bn2mpi(const BIGNUM *in, uint8_t *out);
+
+/* BN_bin2bn parses |len| bytes from |in| and returns the resulting value. The
+ * bytes at |in| are expected to be in the format emitted by |BN_bn2mpi|.
+ *
+ * If |out| is NULL then a fresh |BIGNUM| is allocated and returned, otherwise
+ * |out| is reused and returned. On error, NULL is returned and the error queue
+ * is updated. */
+OPENSSL_EXPORT BIGNUM *BN_mpi2bn(const uint8_t *in, size_t len, BIGNUM *out);
+
+
 /* Private functions */
 
 struct bignum_st {