Add DH_compute_key_padded.

OpenSSL has a fixed-width version of DH_compute_key nowadays. Searching
around callers of DH_compute_key, many of them go back and re-pad the
secret anyway. Uses of DH should migrate to modern primitives but, in
the meantime, DH_compute_key_padded seems worthwhile for OpenSSL
compatibility and giving fixed-width users a function to avoid the
timing leak.

Bump BORINGSSL_API_VERSION since one of the uses is in wpa_supplicant
and they like to compile against a wide range of Android revisions.

Update-Note: No compatibility impact, but callers that use
DH_compute_key and then fix up the removed leading zeros can switch to
this function. Then they should migrate to something else.

Change-Id: Icf8b2ace3972fa174a0f08ece39710f7599f96f2
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/45004
Commit-Queue: David Benjamin <davidben@google.com>
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/crypto/dh_extra/dh_test.cc b/crypto/dh_extra/dh_test.cc
index c77e7e4..7933a8c 100644
--- a/crypto/dh_extra/dh_test.cc
+++ b/crypto/dh_extra/dh_test.cc
@@ -71,6 +71,7 @@
 #include <openssl/mem.h>
 
 #include "../internal.h"
+#include "../test/test_util.h"
 
 
 static bool RunBasicTests();
@@ -443,3 +444,41 @@
 
   return true;
 }
+
+TEST(DHTest, LeadingZeros) {
+  bssl::UniquePtr<BIGNUM> p(BN_get_rfc3526_prime_1536(nullptr));
+  ASSERT_TRUE(p);
+  bssl::UniquePtr<BIGNUM> g(BN_new());
+  ASSERT_TRUE(g);
+  ASSERT_TRUE(BN_set_word(g.get(), 2));
+
+  bssl::UniquePtr<DH> dh(DH_new());
+  ASSERT_TRUE(dh);
+  ASSERT_TRUE(DH_set0_pqg(dh.get(), p.get(), /*q=*/nullptr, g.get()));
+  p.release();
+  g.release();
+
+  // These values are far too small to be reasonable Diffie-Hellman keys, but
+  // they are an easy way to get a shared secret with leading zeros.
+  bssl::UniquePtr<BIGNUM> priv_key(BN_new()), peer_key(BN_new());
+  ASSERT_TRUE(priv_key);
+  ASSERT_TRUE(BN_set_word(priv_key.get(), 2));
+  ASSERT_TRUE(peer_key);
+  ASSERT_TRUE(BN_set_word(peer_key.get(), 3));
+  ASSERT_TRUE(DH_set0_key(dh.get(), /*pub_key=*/nullptr, priv_key.get()));
+  priv_key.release();
+
+  uint8_t padded[192] = {0};
+  padded[191] = 9;
+  static const uint8_t kTruncated[] = {9};
+  EXPECT_EQ(int(sizeof(padded)), DH_size(dh.get()));
+
+  std::vector<uint8_t> buf(DH_size(dh.get()));
+  int len = DH_compute_key(buf.data(), peer_key.get(), dh.get());
+  ASSERT_GT(len, 0);
+  EXPECT_EQ(Bytes(buf.data(), len), Bytes(kTruncated));
+
+  len = DH_compute_key_padded(buf.data(), peer_key.get(), dh.get());
+  ASSERT_GT(len, 0);
+  EXPECT_EQ(Bytes(buf.data(), len), Bytes(padded));
+}
diff --git a/crypto/fipsmodule/dh/dh.c b/crypto/fipsmodule/dh/dh.c
index 05acbe2..0d437ee 100644
--- a/crypto/fipsmodule/dh/dh.c
+++ b/crypto/fipsmodule/dh/dh.c
@@ -321,6 +321,27 @@
   return ret;
 }
 
+int DH_compute_key_padded(unsigned char *out, const BIGNUM *peers_key, DH *dh) {
+  BN_CTX *ctx = BN_CTX_new();
+  if (ctx == NULL) {
+    return -1;
+  }
+  BN_CTX_start(ctx);
+
+  int dh_size = DH_size(dh);
+  int ret = -1;
+  BIGNUM *shared_key = BN_CTX_get(ctx);
+  if (shared_key &&
+      dh_compute_key(dh, shared_key, peers_key, ctx) &&
+      BN_bn2bin_padded(out, dh_size, shared_key)) {
+    ret = dh_size;
+  }
+
+  BN_CTX_end(ctx);
+  BN_CTX_free(ctx);
+  return ret;
+}
+
 int DH_compute_key(unsigned char *out, const BIGNUM *peers_key, DH *dh) {
   BN_CTX *ctx = BN_CTX_new();
   if (ctx == NULL) {
@@ -349,29 +370,19 @@
     return 0;
   }
 
-  BN_CTX *ctx = BN_CTX_new();
-  if (ctx == NULL) {
-    return 0;
-  }
-  BN_CTX_start(ctx);
-
   int ret = 0;
-  BIGNUM *shared_key = BN_CTX_get(ctx);
-  const size_t p_len = BN_num_bytes(dh->p);
-  uint8_t *shared_bytes = OPENSSL_malloc(p_len);
+  const size_t dh_len = DH_size(dh);
+  uint8_t *shared_bytes = OPENSSL_malloc(dh_len);
   unsigned out_len_unsigned;
-  if (!shared_key ||
-      !shared_bytes ||
-      !dh_compute_key(dh, shared_key, peers_key, ctx) ||
-      // |DH_compute_key| doesn't pad the output. SP 800-56A is ambiguous about
-      // whether the output should be padded prior to revision three. But
-      // revision three, section C.1, awkwardly specifies padding to the length
-      // of p.
+  if (!shared_bytes ||
+      // SP 800-56A is ambiguous about whether the output should be padded prior
+      // to revision three. But revision three, section C.1, awkwardly specifies
+      // padding to the length of p.
       //
       // Also, padded output avoids side-channels, so is always strongly
       // advisable.
-      !BN_bn2bin_padded(shared_bytes, p_len, shared_key) ||
-      !EVP_Digest(shared_bytes, p_len, out, &out_len_unsigned, digest, NULL) ||
+      DH_compute_key_padded(shared_bytes, peers_key, dh) != (int)dh_len ||
+      !EVP_Digest(shared_bytes, dh_len, out, &out_len_unsigned, digest, NULL) ||
       out_len_unsigned != digest_len) {
     goto err;
   }
@@ -380,8 +391,6 @@
   ret = 1;
 
  err:
-  BN_CTX_end(ctx);
-  BN_CTX_free(ctx);
   OPENSSL_free(shared_bytes);
   return ret;
 }
diff --git a/include/openssl/base.h b/include/openssl/base.h
index 6ecb3d3..b678306 100644
--- a/include/openssl/base.h
+++ b/include/openssl/base.h
@@ -191,7 +191,7 @@
 // A consumer may use this symbol in the preprocessor to temporarily build
 // against multiple revisions of BoringSSL at the same time. It is not
 // recommended to do so for longer than is necessary.
-#define BORINGSSL_API_VERSION 13
+#define BORINGSSL_API_VERSION 14
 
 #if defined(BORINGSSL_SHARED_LIBRARY)
 
diff --git a/include/openssl/dh.h b/include/openssl/dh.h
index 52d831d..a4e2e71 100644
--- a/include/openssl/dh.h
+++ b/include/openssl/dh.h
@@ -163,20 +163,24 @@
 // |dh|. It returns one on success and zero on error.
 OPENSSL_EXPORT int DH_generate_key(DH *dh);
 
-// DH_compute_key calculates the shared key between |dh| and |peers_key| and
-// writes it as a big-endian integer into |out|, which must have |DH_size|
-// bytes of space. It returns the number of bytes written, or a negative number
-// on error.
+// DH_compute_key_padded calculates the shared key between |dh| and |peers_key|
+// and writes it as a big-endian integer into |out|, padded up to |DH_size|
+// bytes. It returns the number of bytes written, which is always |DH_size|, or
+// a negative number on error. |out| must have |DH_size| bytes of space.
 //
-// Note the output may be shorter than |DH_size| bytes. Contrary to PKCS #3,
-// this function returns a variable-length shared key with leading zeros
-// removed. This may result in sporadic key mismatch and, if |dh| is reused,
-// side channel attacks such as https://raccoon-attack.com/.
+// WARNING: this differs from the usual BoringSSL return-value convention.
 //
-// This is a legacy algorithm, so we do not provide a fixed-width variant. Use
-// X25519 or ECDH with P-256 instead.
-OPENSSL_EXPORT int DH_compute_key(uint8_t *out, const BIGNUM *peers_key,
-                                  DH *dh);
+// Note this function differs from |DH_compute_key| in that it preserves leading
+// zeros in the secret. This function is the preferred variant. It matches PKCS
+// #3 and avoids some side channel attacks. However, the two functions are not
+// drop-in replacements for each other. Using a different variant than the
+// application expects will result in sporadic key mismatches.
+//
+// Callers that expect a fixed-width secret should use this function over
+// |DH_compute_key|. Callers that use either function should migrate to a modern
+// primitive such as X25519 or ECDH with P-256 instead.
+OPENSSL_EXPORT int DH_compute_key_padded(uint8_t *out, const BIGNUM *peers_key,
+                                         DH *dh);
 
 // DH_compute_key_hashed calculates the shared key between |dh| and |peers_key|
 // and hashes it with the given |digest|. If the hash output is less than
@@ -185,7 +189,7 @@
 // returns one on success or zero on error.
 //
 // NOTE: this follows the usual BoringSSL return-value convention, but that's
-// different from |DH_compute_key|, above.
+// different from |DH_compute_key| and |DH_compute_key_padded|.
 OPENSSL_EXPORT int DH_compute_key_hashed(DH *dh, uint8_t *out, size_t *out_len,
                                          size_t max_out_len,
                                          const BIGNUM *peers_key,
@@ -278,6 +282,28 @@
 // Use |DH_marshal_parameters| instead.
 OPENSSL_EXPORT int i2d_DHparams(const DH *in, unsigned char **outp);
 
+// DH_compute_key behaves like |DH_compute_key_padded| but, contrary to PKCS #3,
+// returns a variable-length shared key with leading zeros. It returns the
+// number of bytes written, or a negative number on error. |out| must have
+// |DH_size| bytes of space.
+//
+// WARNING: this differs from the usual BoringSSL return-value convention.
+//
+// Note this function's running time and memory access pattern leaks information
+// about the shared secret. Particularly if |dh| is reused, this may result in
+// side channel attacks such as https://raccoon-attack.com/.
+//
+// |DH_compute_key_padded| is the preferred variant and avoids the above
+// attacks. However, the two functions are not drop-in replacements for each
+// other. Using a different variant than the application expects will result in
+// sporadic key mismatches.
+//
+// Callers that expect a fixed-width secret should use |DH_compute_key_padded|
+// instead. Callers that use either function should migrate to a modern
+// primitive such as X25519 or ECDH with P-256 instead.
+OPENSSL_EXPORT int DH_compute_key(uint8_t *out, const BIGNUM *peers_key,
+                                  DH *dh);
+
 
 struct dh_st {
   BIGNUM *p;