Switch HPKE to a three-parameter output buffer.

This is a little tedious but aligns with some of our other
variable-length parameters. This is in preparation for making the HPKE
APIs KEM-agnostic, so we don't need to make so many variations on the
HPKE functions for each KEM. (Especially if we ever need to implement
SetupPSK*, SetupAuth*, or SetupAuthPSK*.)

Bug: 410
Change-Id: I0625580b15358ab1f02b7835122256e8f058a779
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/47328
Commit-Queue: David Benjamin <davidben@google.com>
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/crypto/hpke/hpke.c b/crypto/hpke/hpke.c
index 3e98159..09bd020 100644
--- a/crypto/hpke/hpke.c
+++ b/crypto/hpke/hpke.c
@@ -292,7 +292,7 @@
 }
 
 int EVP_HPKE_CTX_setup_base_s_x25519(EVP_HPKE_CTX *hpke, uint8_t *out_enc,
-                                     size_t out_enc_len,
+                                     size_t *out_enc_len, size_t max_enc,
                                      const EVP_HPKE_KDF *kdf,
                                      const EVP_HPKE_AEAD *aead,
                                      const uint8_t *peer_public_value,
@@ -301,17 +301,17 @@
   uint8_t seed[X25519_PRIVATE_KEY_LEN];
   RAND_bytes(seed, sizeof(seed));
   return EVP_HPKE_CTX_setup_base_s_x25519_with_seed_for_testing(
-      hpke, out_enc, out_enc_len, kdf, aead, peer_public_value,
+      hpke, out_enc, out_enc_len, max_enc, kdf, aead, peer_public_value,
       peer_public_value_len, info, info_len, seed, sizeof(seed));
 }
 
 int EVP_HPKE_CTX_setup_base_s_x25519_with_seed_for_testing(
-    EVP_HPKE_CTX *hpke, uint8_t *out_enc, size_t out_enc_len,
+    EVP_HPKE_CTX *hpke, uint8_t *out_enc, size_t *out_enc_len, size_t max_enc,
     const EVP_HPKE_KDF *kdf, const EVP_HPKE_AEAD *aead,
     const uint8_t *peer_public_value, size_t peer_public_value_len,
     const uint8_t *info, size_t info_len, const uint8_t *seed,
     size_t seed_len) {
-  if (out_enc_len != X25519_PUBLIC_VALUE_LEN) {
+  if (max_enc < X25519_PUBLIC_VALUE_LEN) {
     OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_BUFFER_SIZE);
     return 0;
   }
@@ -334,6 +334,7 @@
                          info_len)) {
     return 0;
   }
+  *out_enc_len = X25519_PUBLIC_VALUE_LEN;
   return 1;
 }
 
diff --git a/crypto/hpke/hpke_test.cc b/crypto/hpke/hpke_test.cc
index 82ba229..c7bc2c4 100644
--- a/crypto/hpke/hpke_test.cc
+++ b/crypto/hpke/hpke_test.cc
@@ -63,16 +63,17 @@
     // Set up the sender.
     ScopedEVP_HPKE_CTX sender_ctx;
     uint8_t enc[X25519_PUBLIC_VALUE_LEN];
+    size_t enc_len;
     ASSERT_TRUE(EVP_HPKE_CTX_setup_base_s_x25519_with_seed_for_testing(
-        sender_ctx.get(), enc, sizeof(enc), kdf, aead, public_key_r_.data(),
-        public_key_r_.size(), info_.data(), info_.size(), secret_key_e_.data(),
-        secret_key_e_.size()));
-    EXPECT_EQ(Bytes(enc), Bytes(public_key_e_));
+        sender_ctx.get(), enc, &enc_len, sizeof(enc), kdf, aead,
+        public_key_r_.data(), public_key_r_.size(), info_.data(), info_.size(),
+        secret_key_e_.data(), secret_key_e_.size()));
+    EXPECT_EQ(Bytes(enc, enc_len), Bytes(public_key_e_));
 
     // Set up the receiver.
     ScopedEVP_HPKE_CTX receiver_ctx;
     ASSERT_TRUE(EVP_HPKE_CTX_setup_base_r_x25519(
-        receiver_ctx.get(), kdf, aead, enc, sizeof(enc), public_key_r_.data(),
+        receiver_ctx.get(), kdf, aead, enc, enc_len, public_key_r_.data(),
         public_key_r_.size(), secret_key_r_.data(), secret_key_r_.size(),
         info_.data(), info_.size()));
 
@@ -265,14 +266,15 @@
           // Set up the sender.
           ScopedEVP_HPKE_CTX sender_ctx;
           uint8_t enc[X25519_PUBLIC_VALUE_LEN];
+          size_t enc_len;
           ASSERT_TRUE(EVP_HPKE_CTX_setup_base_s_x25519(
-              sender_ctx.get(), enc, sizeof(enc), kdf(), aead(), public_key_r,
-              sizeof(public_key_r), info.data(), info.size()));
+              sender_ctx.get(), enc, &enc_len, sizeof(enc), kdf(), aead(),
+              public_key_r, sizeof(public_key_r), info.data(), info.size()));
 
           // Set up the receiver.
           ScopedEVP_HPKE_CTX receiver_ctx;
           ASSERT_TRUE(EVP_HPKE_CTX_setup_base_r_x25519(
-              receiver_ctx.get(), kdf(), aead(), enc, sizeof(enc), public_key_r,
+              receiver_ctx.get(), kdf(), aead(), enc, enc_len, public_key_r,
               sizeof(public_key_r), secret_key_r, sizeof(secret_key_r),
               info.data(), info.size()));
 
@@ -328,9 +330,10 @@
       // Set up the sender, passing in kSmallOrderPoint as |peer_public_value|.
       ScopedEVP_HPKE_CTX sender_ctx;
       uint8_t enc[X25519_PUBLIC_VALUE_LEN];
+      size_t enc_len;
       ASSERT_FALSE(EVP_HPKE_CTX_setup_base_s_x25519(
-          sender_ctx.get(), enc, sizeof(enc), kdf(), aead(), kSmallOrderPoint,
-          sizeof(kSmallOrderPoint), nullptr, 0));
+          sender_ctx.get(), enc, &enc_len, sizeof(enc), kdf(), aead(),
+          kSmallOrderPoint, sizeof(kSmallOrderPoint), nullptr, 0));
 
       // Set up the receiver, passing in kSmallOrderPoint as |enc|.
       ScopedEVP_HPKE_CTX receiver_ctx;
@@ -381,8 +384,9 @@
   // Set up the sender.
   ScopedEVP_HPKE_CTX sender_ctx;
   uint8_t enc[X25519_PUBLIC_VALUE_LEN];
+  size_t enc_len;
   ASSERT_TRUE(EVP_HPKE_CTX_setup_base_s_x25519(
-      sender_ctx.get(), enc, sizeof(enc), EVP_hpke_hkdf_sha256(),
+      sender_ctx.get(), enc, &enc_len, sizeof(enc), EVP_hpke_hkdf_sha256(),
       EVP_hpke_aes_128_gcm(), public_key_r, sizeof(public_key_r), nullptr, 0));
 
   // Call Open() on the sender.
@@ -393,15 +397,16 @@
                                  kMockCiphertextLen, nullptr, 0));
 }
 
-TEST(HPKETest, SetupSenderWrongLengthEnc) {
+TEST(HPKETest, SetupSenderBufferTooSmall) {
   uint8_t secret_key_r[X25519_PRIVATE_KEY_LEN];
   uint8_t public_key_r[X25519_PUBLIC_VALUE_LEN];
   X25519_keypair(public_key_r, secret_key_r);
 
   ScopedEVP_HPKE_CTX sender_ctx;
-  uint8_t bogus_enc[X25519_PUBLIC_VALUE_LEN + 5];
+  uint8_t enc[X25519_PUBLIC_VALUE_LEN - 1];
+  size_t enc_len;
   ASSERT_FALSE(EVP_HPKE_CTX_setup_base_s_x25519(
-      sender_ctx.get(), bogus_enc, sizeof(bogus_enc), EVP_hpke_hkdf_sha256(),
+      sender_ctx.get(), enc, &enc_len, sizeof(enc), EVP_hpke_hkdf_sha256(),
       EVP_hpke_aes_128_gcm(), public_key_r, sizeof(public_key_r), nullptr, 0));
   uint32_t err = ERR_get_error();
   EXPECT_EQ(ERR_LIB_EVP, ERR_GET_LIB(err));
@@ -409,6 +414,22 @@
   ERR_clear_error();
 }
 
+TEST(HPKETest, SetupSenderBufferTooLarge) {
+  uint8_t secret_key_r[X25519_PRIVATE_KEY_LEN];
+  uint8_t public_key_r[X25519_PUBLIC_VALUE_LEN];
+  X25519_keypair(public_key_r, secret_key_r);
+
+  // Too large of an output buffer is fine because the function reports the
+  // actual length.
+  ScopedEVP_HPKE_CTX sender_ctx;
+  uint8_t enc[X25519_PUBLIC_VALUE_LEN + 1];
+  size_t enc_len;
+  EXPECT_TRUE(EVP_HPKE_CTX_setup_base_s_x25519(
+      sender_ctx.get(), enc, &enc_len, sizeof(enc), EVP_hpke_hkdf_sha256(),
+      EVP_hpke_aes_128_gcm(), public_key_r, sizeof(public_key_r), nullptr, 0));
+  EXPECT_EQ(size_t{X25519_PUBLIC_VALUE_LEN}, enc_len);
+}
+
 TEST(HPKETest, SetupReceiverWrongLengthEnc) {
   uint8_t private_key[X25519_PRIVATE_KEY_LEN];
   uint8_t public_key[X25519_PUBLIC_VALUE_LEN];
@@ -431,8 +452,9 @@
   const uint8_t bogus_public_key_r[X25519_PRIVATE_KEY_LEN + 5] = {0xff};
   ScopedEVP_HPKE_CTX sender_ctx;
   uint8_t enc[X25519_PUBLIC_VALUE_LEN];
+  size_t enc_len;
   ASSERT_FALSE(EVP_HPKE_CTX_setup_base_s_x25519(
-      sender_ctx.get(), enc, sizeof(enc), EVP_hpke_hkdf_sha256(),
+      sender_ctx.get(), enc, &enc_len, sizeof(enc), EVP_hpke_hkdf_sha256(),
       EVP_hpke_aes_128_gcm(), bogus_public_key_r, sizeof(bogus_public_key_r),
       nullptr, 0));
   uint32_t err = ERR_get_error();
diff --git a/crypto/hpke/internal.h b/crypto/hpke/internal.h
index 6dee25a..db98d72 100644
--- a/crypto/hpke/internal.h
+++ b/crypto/hpke/internal.h
@@ -89,11 +89,12 @@
 // recipient's public key). It returns one on success, and zero otherwise. Note
 // that this function will fail if |peer_public_value| is invalid.
 //
-// This function writes the encapsulated shared secret, a Diffie-Hellman public
-// key, to |out_enc|. It will fail if the buffer's size in |out_enc_len| is not
-// exactly |X25519_PUBLIC_VALUE_LEN|.
+// This function writes the encapsulated shared secret to |out_enc| and sets
+// |*out_enc_len| to the number of bytes written. It writes at most |max_enc|
+// bytes and fails if the buffer is too small. |max_enc| must be at least
+// |X25519_PUBLIC_VALUE_LEN| to ensure the buffer is large enough.
 OPENSSL_EXPORT int EVP_HPKE_CTX_setup_base_s_x25519(
-    EVP_HPKE_CTX *hpke, uint8_t *out_enc, size_t out_enc_len,
+    EVP_HPKE_CTX *hpke, uint8_t *out_enc, size_t *out_enc_len, size_t max_enc,
     const EVP_HPKE_KDF *kdf, const EVP_HPKE_AEAD *aead,
     const uint8_t *peer_public_value, size_t peer_public_value_len,
     const uint8_t *info, size_t info_len);
@@ -102,7 +103,7 @@
 // |EVP_HPKE_CTX_setup_base_s_x25519|, but takes a seed value to behave
 // deterministically. This seed is the sender's ephemeral X25519 key.
 OPENSSL_EXPORT int EVP_HPKE_CTX_setup_base_s_x25519_with_seed_for_testing(
-    EVP_HPKE_CTX *hpke, uint8_t *out_enc, size_t out_enc_len,
+    EVP_HPKE_CTX *hpke, uint8_t *out_enc, size_t *out_enc_len, size_t max_enc,
     const EVP_HPKE_KDF *kdf, const EVP_HPKE_AEAD *aead,
     const uint8_t *peer_public_value, size_t peer_public_value_len,
     const uint8_t *info, size_t info_len, const uint8_t *seed, size_t seed_len);