Refactor HPKE API to include explicit length parameters.

Bug: 275
Change-Id: I724e9315b860e230e8fed92de34d89a875ef043c
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/46184
Reviewed-by: David Benjamin <davidben@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
diff --git a/crypto/err/evp.errordata b/crypto/err/evp.errordata
index d0f09d4..a581b1b 100644
--- a/crypto/err/evp.errordata
+++ b/crypto/err/evp.errordata
@@ -9,6 +9,7 @@
 EVP,107,EXPECTING_AN_RSA_KEY
 EVP,108,EXPECTING_A_DSA_KEY
 EVP,109,ILLEGAL_OR_UNSUPPORTED_PADDING_MODE
+EVP,137,INVALID_BUFFER_SIZE
 EVP,110,INVALID_DIGEST_LENGTH
 EVP,111,INVALID_DIGEST_TYPE
 EVP,112,INVALID_KEYBITS
diff --git a/crypto/hpke/hpke.c b/crypto/hpke/hpke.c
index d3e9c7c..29f60fc 100644
--- a/crypto/hpke/hpke.c
+++ b/crypto/hpke/hpke.c
@@ -17,6 +17,7 @@
 
 #include <openssl/aead.h>
 #include <openssl/bytestring.h>
+#include <openssl/curve25519.h>
 #include <openssl/digest.h>
 #include <openssl/err.h>
 #include <openssl/evp_errors.h>
@@ -316,27 +317,44 @@
   EVP_AEAD_CTX_cleanup(&ctx->aead_ctx);
 }
 
-int EVP_HPKE_CTX_setup_base_s_x25519(
-    EVP_HPKE_CTX *hpke, uint8_t out_enc[X25519_PUBLIC_VALUE_LEN],
-    uint16_t kdf_id, uint16_t aead_id,
-    const uint8_t peer_public_value[X25519_PUBLIC_VALUE_LEN],
-    const uint8_t *info, size_t info_len) {
+int EVP_HPKE_CTX_setup_base_s_x25519(EVP_HPKE_CTX *hpke, uint8_t *out_enc,
+                                     size_t out_enc_len, uint16_t kdf_id,
+                                     uint16_t aead_id,
+                                     const uint8_t *peer_public_value,
+                                     size_t peer_public_value_len,
+                                     const uint8_t *info, size_t info_len) {
+  if (out_enc_len != X25519_PUBLIC_VALUE_LEN) {
+    OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_BUFFER_SIZE);
+    return 0;
+  }
+
   // The GenerateKeyPair() step technically belongs in the KEM's Encap()
   // function, but we've moved it up a layer to make it easier for tests to
   // inject an ephemeral keypair.
   uint8_t ephemeral_private[X25519_PRIVATE_KEY_LEN];
   X25519_keypair(out_enc, ephemeral_private);
   return EVP_HPKE_CTX_setup_base_s_x25519_for_test(
-      hpke, kdf_id, aead_id, peer_public_value, info, info_len,
-      ephemeral_private, out_enc);
+      hpke, kdf_id, aead_id, peer_public_value, peer_public_value_len, info,
+      info_len, ephemeral_private, sizeof(ephemeral_private), out_enc,
+      out_enc_len);
 }
 
 int EVP_HPKE_CTX_setup_base_s_x25519_for_test(
     EVP_HPKE_CTX *hpke, uint16_t kdf_id, uint16_t aead_id,
-    const uint8_t peer_public_value[X25519_PUBLIC_VALUE_LEN],
-    const uint8_t *info, size_t info_len,
-    const uint8_t ephemeral_private[X25519_PRIVATE_KEY_LEN],
-    const uint8_t ephemeral_public[X25519_PUBLIC_VALUE_LEN]) {
+    const uint8_t *peer_public_value, size_t peer_public_value_len,
+    const uint8_t *info, size_t info_len, const uint8_t *ephemeral_private,
+    size_t ephemeral_private_len, const uint8_t *ephemeral_public,
+    size_t ephemeral_public_len) {
+  if (peer_public_value_len != X25519_PUBLIC_VALUE_LEN) {
+    OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_PEER_KEY);
+    return 0;
+  }
+  if (ephemeral_private_len != X25519_PRIVATE_KEY_LEN ||
+      ephemeral_public_len != X25519_PUBLIC_VALUE_LEN) {
+    OPENSSL_PUT_ERROR(EVP, EVP_R_DECODE_ERROR);
+    return 0;
+  }
+
   hpke->is_sender = 1;
   hpke->kdf_id = kdf_id;
   hpke->aead_id = aead_id;
@@ -355,12 +373,23 @@
   return 1;
 }
 
-int EVP_HPKE_CTX_setup_base_r_x25519(
-    EVP_HPKE_CTX *hpke, uint16_t kdf_id, uint16_t aead_id,
-    const uint8_t enc[X25519_PUBLIC_VALUE_LEN],
-    const uint8_t public_key[X25519_PUBLIC_VALUE_LEN],
-    const uint8_t private_key[X25519_PRIVATE_KEY_LEN], const uint8_t *info,
-    size_t info_len) {
+int EVP_HPKE_CTX_setup_base_r_x25519(EVP_HPKE_CTX *hpke, uint16_t kdf_id,
+                                     uint16_t aead_id, const uint8_t *enc,
+                                     size_t enc_len, const uint8_t *public_key,
+                                     size_t public_key_len,
+                                     const uint8_t *private_key,
+                                     size_t private_key_len,
+                                     const uint8_t *info, size_t info_len) {
+  if (enc_len != X25519_PUBLIC_VALUE_LEN) {
+    OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_PEER_KEY);
+    return 0;
+  }
+  if (public_key_len != X25519_PUBLIC_VALUE_LEN ||
+      private_key_len != X25519_PRIVATE_KEY_LEN) {
+    OPENSSL_PUT_ERROR(EVP, EVP_R_DECODE_ERROR);
+    return 0;
+  }
+
   hpke->is_sender = 0;
   hpke->kdf_id = kdf_id;
   hpke->aead_id = aead_id;
@@ -378,29 +407,47 @@
   return 1;
 }
 
-int EVP_HPKE_CTX_setup_psk_s_x25519(
-    EVP_HPKE_CTX *hpke, uint8_t out_enc[X25519_PUBLIC_VALUE_LEN],
-    uint16_t kdf_id, uint16_t aead_id,
-    const uint8_t peer_public_value[X25519_PUBLIC_VALUE_LEN],
-    const uint8_t *info, size_t info_len, const uint8_t *psk, size_t psk_len,
-    const uint8_t *psk_id, size_t psk_id_len) {
+int EVP_HPKE_CTX_setup_psk_s_x25519(EVP_HPKE_CTX *hpke, uint8_t *out_enc,
+                                    size_t out_enc_len, uint16_t kdf_id,
+                                    uint16_t aead_id,
+                                    const uint8_t *peer_public_value,
+                                    size_t peer_public_value_len,
+                                    const uint8_t *info, size_t info_len,
+                                    const uint8_t *psk, size_t psk_len,
+                                    const uint8_t *psk_id, size_t psk_id_len) {
+  if (out_enc_len != X25519_PUBLIC_VALUE_LEN) {
+    OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_BUFFER_SIZE);
+    return 0;
+  }
+
   // The GenerateKeyPair() step technically belongs in the KEM's Encap()
   // function, but we've moved it up a layer to make it easier for tests to
   // inject an ephemeral keypair.
   uint8_t ephemeral_private[X25519_PRIVATE_KEY_LEN];
   X25519_keypair(out_enc, ephemeral_private);
   return EVP_HPKE_CTX_setup_psk_s_x25519_for_test(
-      hpke, kdf_id, aead_id, peer_public_value, info, info_len, psk, psk_len,
-      psk_id, psk_id_len, ephemeral_private, out_enc);
+      hpke, kdf_id, aead_id, peer_public_value, peer_public_value_len, info,
+      info_len, psk, psk_len, psk_id, psk_id_len, ephemeral_private,
+      sizeof(ephemeral_private), out_enc, out_enc_len);
 }
 
 int EVP_HPKE_CTX_setup_psk_s_x25519_for_test(
     EVP_HPKE_CTX *hpke, uint16_t kdf_id, uint16_t aead_id,
-    const uint8_t peer_public_value[X25519_PUBLIC_VALUE_LEN],
+    const uint8_t *peer_public_value, size_t peer_public_value_len,
     const uint8_t *info, size_t info_len, const uint8_t *psk, size_t psk_len,
-    const uint8_t *psk_id, size_t psk_id_len,
-    const uint8_t ephemeral_private[X25519_PRIVATE_KEY_LEN],
-    const uint8_t ephemeral_public[X25519_PUBLIC_VALUE_LEN]) {
+    const uint8_t *psk_id, size_t psk_id_len, const uint8_t *ephemeral_private,
+    size_t ephemeral_private_len, const uint8_t *ephemeral_public,
+    size_t ephemeral_public_len) {
+  if (peer_public_value_len != X25519_PUBLIC_VALUE_LEN) {
+    OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_PEER_KEY);
+    return 0;
+  }
+  if (ephemeral_private_len != X25519_PRIVATE_KEY_LEN ||
+      ephemeral_public_len != X25519_PUBLIC_VALUE_LEN) {
+    OPENSSL_PUT_ERROR(EVP, EVP_R_DECODE_ERROR);
+    return 0;
+  }
+
   hpke->is_sender = 1;
   hpke->kdf_id = kdf_id;
   hpke->aead_id = aead_id;
@@ -420,12 +467,21 @@
 }
 
 int EVP_HPKE_CTX_setup_psk_r_x25519(
-    EVP_HPKE_CTX *hpke, uint16_t kdf_id, uint16_t aead_id,
-    const uint8_t enc[X25519_PUBLIC_VALUE_LEN],
-    const uint8_t public_key[X25519_PUBLIC_VALUE_LEN],
-    const uint8_t private_key[X25519_PRIVATE_KEY_LEN], const uint8_t *info,
+    EVP_HPKE_CTX *hpke, uint16_t kdf_id, uint16_t aead_id, const uint8_t *enc,
+    size_t enc_len, const uint8_t *public_key, size_t public_key_len,
+    const uint8_t *private_key, size_t private_key_len, const uint8_t *info,
     size_t info_len, const uint8_t *psk, size_t psk_len, const uint8_t *psk_id,
     size_t psk_id_len) {
+  if (enc_len != X25519_PUBLIC_VALUE_LEN) {
+    OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_PEER_KEY);
+    return 0;
+  }
+  if (public_key_len != X25519_PUBLIC_VALUE_LEN ||
+      private_key_len != X25519_PRIVATE_KEY_LEN) {
+    OPENSSL_PUT_ERROR(EVP, EVP_R_DECODE_ERROR);
+    return 0;
+  }
+
   hpke->is_sender = 0;
   hpke->kdf_id = kdf_id;
   hpke->aead_id = aead_id;
diff --git a/crypto/hpke/hpke_test.cc b/crypto/hpke/hpke_test.cc
index c007b3d..d3f841c 100644
--- a/crypto/hpke/hpke_test.cc
+++ b/crypto/hpke/hpke_test.cc
@@ -62,13 +62,15 @@
         // Set up the sender.
         ASSERT_TRUE(EVP_HPKE_CTX_setup_base_s_x25519_for_test(
             sender_ctx.get(), kdf_id_, aead_id_, public_key_r_.data(),
-            info_.data(), info_.size(), secret_key_e_.data(),
-            public_key_e_.data()));
+            public_key_r_.size(), info_.data(), info_.size(),
+            secret_key_e_.data(), secret_key_e_.size(), public_key_e_.data(),
+            public_key_e_.size()));
 
         // Set up the receiver.
         ASSERT_TRUE(EVP_HPKE_CTX_setup_base_r_x25519(
             receiver_ctx.get(), kdf_id_, aead_id_, public_key_e_.data(),
-            public_key_r_.data(), secret_key_r_.data(), info_.data(),
+            public_key_e_.size(), public_key_r_.data(), public_key_r_.size(),
+            secret_key_r_.data(), secret_key_r_.size(), info_.data(),
             info_.size()));
         break;
 
@@ -80,14 +82,15 @@
         // Set up the sender.
         ASSERT_TRUE(EVP_HPKE_CTX_setup_psk_s_x25519_for_test(
             sender_ctx.get(), kdf_id_, aead_id_, public_key_r_.data(),
-            info_.data(), info_.size(), psk_.data(), psk_.size(),
-            psk_id_.data(), psk_id_.size(), secret_key_e_.data(),
-            public_key_e_.data()));
+            public_key_r_.size(), info_.data(), info_.size(), psk_.data(),
+            psk_.size(), psk_id_.data(), psk_id_.size(), secret_key_e_.data(),
+            secret_key_e_.size(), public_key_e_.data(), public_key_e_.size()));
 
         // Set up the receiver.
         ASSERT_TRUE(EVP_HPKE_CTX_setup_psk_r_x25519(
             receiver_ctx.get(), kdf_id_, aead_id_, public_key_e_.data(),
-            public_key_r_.data(), secret_key_r_.data(), info_.data(),
+            public_key_e_.size(), public_key_r_.data(), public_key_r_.size(),
+            secret_key_r_.data(), secret_key_r_.size(), info_.data(),
             info_.size(), psk_.data(), psk_.size(), psk_id_.data(),
             psk_id_.size()));
         break;
@@ -282,14 +285,15 @@
           ScopedEVP_HPKE_CTX sender_ctx;
           uint8_t enc[X25519_PUBLIC_VALUE_LEN];
           ASSERT_TRUE(EVP_HPKE_CTX_setup_base_s_x25519(
-              sender_ctx.get(), enc, kdf_id, aead_id, public_key_r, info.data(),
-              info.size()));
+              sender_ctx.get(), enc, sizeof(enc), kdf_id, aead_id, 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_id, aead_id, enc, public_key_r,
-              secret_key_r, info.data(), info.size()));
+              receiver_ctx.get(), kdf_id, aead_id, enc, sizeof(enc),
+              public_key_r, sizeof(public_key_r), secret_key_r,
+              sizeof(secret_key_r), info.data(), info.size()));
 
           const char kCleartextPayload[] = "foobar";
 
@@ -347,14 +351,15 @@
       ScopedEVP_HPKE_CTX sender_ctx;
       uint8_t enc[X25519_PUBLIC_VALUE_LEN];
       ASSERT_FALSE(EVP_HPKE_CTX_setup_base_s_x25519(
-          sender_ctx.get(), enc, kdf_id, aead_id, kSmallOrderPoint, nullptr,
-          0));
+          sender_ctx.get(), enc, sizeof(enc), kdf_id, aead_id, kSmallOrderPoint,
+          sizeof(kSmallOrderPoint), nullptr, 0));
 
       // Set up the receiver, passing in kSmallOrderPoint as |enc|.
       ScopedEVP_HPKE_CTX receiver_ctx;
       ASSERT_FALSE(EVP_HPKE_CTX_setup_base_r_x25519(
-          receiver_ctx.get(), kdf_id, aead_id, kSmallOrderPoint, public_key_r,
-          secret_key_r, nullptr, 0));
+          receiver_ctx.get(), kdf_id, aead_id, kSmallOrderPoint,
+          sizeof(kSmallOrderPoint), public_key_r, sizeof(public_key_r),
+          secret_key_r, sizeof(secret_key_r), nullptr, 0));
     }
   }
 }
@@ -373,7 +378,8 @@
   ScopedEVP_HPKE_CTX receiver_ctx;
   ASSERT_TRUE(EVP_HPKE_CTX_setup_base_r_x25519(
       receiver_ctx.get(), EVP_HPKE_HKDF_SHA256, EVP_HPKE_AEAD_AES_GCM_128,
-      kMockEnc, public_key_r, secret_key_r, nullptr, 0));
+      kMockEnc, sizeof(kMockEnc), public_key_r, sizeof(public_key_r),
+      secret_key_r, sizeof(secret_key_r), nullptr, 0));
 
   // Call Seal() on the receiver.
   size_t ciphertext_len;
@@ -398,8 +404,9 @@
   ScopedEVP_HPKE_CTX sender_ctx;
   uint8_t enc[X25519_PUBLIC_VALUE_LEN];
   ASSERT_TRUE(EVP_HPKE_CTX_setup_base_s_x25519(
-      sender_ctx.get(), enc, EVP_HPKE_HKDF_SHA256, EVP_HPKE_AEAD_AES_GCM_128,
-      public_key_r, nullptr, 0));
+      sender_ctx.get(), enc, sizeof(enc), EVP_HPKE_HKDF_SHA256,
+      EVP_HPKE_AEAD_AES_GCM_128, public_key_r, sizeof(public_key_r), nullptr,
+      0));
 
   // Call Open() on the sender.
   uint8_t cleartext[128];
@@ -432,24 +439,10 @@
 
       ScopedEVP_HPKE_CTX sender_ctx;
       uint8_t enc[X25519_PUBLIC_VALUE_LEN];
-      ASSERT_EQ(EVP_HPKE_CTX_setup_psk_s_x25519(
-                    sender_ctx.get(), enc, EVP_HPKE_HKDF_SHA256,
-                    EVP_HPKE_AEAD_AES_GCM_128, public_key_r, nullptr, 0,
-                    psk.data(), psk.size(), psk_id.data(), psk_id.size()),
-                kExpectSuccess);
-
-      if (!kExpectSuccess) {
-        uint32_t err = ERR_get_error();
-        EXPECT_EQ(ERR_LIB_EVP, ERR_GET_LIB(err));
-        EXPECT_EQ(EVP_R_EMPTY_PSK, ERR_GET_REASON(err));
-      }
-      ERR_clear_error();
-
-      ScopedEVP_HPKE_CTX receiver_ctx;
       ASSERT_EQ(
-          EVP_HPKE_CTX_setup_psk_r_x25519(
-              receiver_ctx.get(), EVP_HPKE_HKDF_SHA256,
-              EVP_HPKE_AEAD_AES_GCM_128, kMockEnc, public_key_r, secret_key_r,
+          EVP_HPKE_CTX_setup_psk_s_x25519(
+              sender_ctx.get(), enc, sizeof(enc), EVP_HPKE_HKDF_SHA256,
+              EVP_HPKE_AEAD_AES_GCM_128, public_key_r, sizeof(public_key_r),
               nullptr, 0, psk.data(), psk.size(), psk_id.data(), psk_id.size()),
           kExpectSuccess);
 
@@ -459,10 +452,185 @@
         EXPECT_EQ(EVP_R_EMPTY_PSK, ERR_GET_REASON(err));
       }
       ERR_clear_error();
+
+      ScopedEVP_HPKE_CTX receiver_ctx;
+      ASSERT_EQ(EVP_HPKE_CTX_setup_psk_r_x25519(
+                    receiver_ctx.get(), EVP_HPKE_HKDF_SHA256,
+                    EVP_HPKE_AEAD_AES_GCM_128, kMockEnc, sizeof(kMockEnc),
+                    public_key_r, sizeof(public_key_r), secret_key_r,
+                    sizeof(secret_key_r), nullptr, 0, psk.data(), psk.size(),
+                    psk_id.data(), psk_id.size()),
+                kExpectSuccess);
+
+      if (!kExpectSuccess) {
+        uint32_t err = ERR_get_error();
+        EXPECT_EQ(ERR_LIB_EVP, ERR_GET_LIB(err));
+        EXPECT_EQ(EVP_R_EMPTY_PSK, ERR_GET_REASON(err));
+      }
+      ERR_clear_error();
     }
   }
 }
 
+TEST(HPKETest, SetupSenderWrongLengthEnc) {
+  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];
+  {
+    ASSERT_FALSE(EVP_HPKE_CTX_setup_base_s_x25519(
+        sender_ctx.get(), bogus_enc, sizeof(bogus_enc), EVP_HPKE_HKDF_SHA256,
+        EVP_HPKE_AEAD_AES_GCM_128, public_key_r, sizeof(public_key_r), nullptr,
+        0));
+    uint32_t err = ERR_get_error();
+    EXPECT_EQ(ERR_LIB_EVP, ERR_GET_LIB(err));
+    EXPECT_EQ(EVP_R_INVALID_BUFFER_SIZE, ERR_GET_REASON(err));
+    ERR_clear_error();
+  }
+  {
+    const uint8_t psk[] = {1, 2, 3, 4};
+    const uint8_t psk_id[] = {1, 2, 3, 4};
+    ASSERT_FALSE(EVP_HPKE_CTX_setup_psk_s_x25519(
+        sender_ctx.get(), bogus_enc, sizeof(bogus_enc), EVP_HPKE_HKDF_SHA256,
+        EVP_HPKE_AEAD_AES_GCM_128, public_key_r, sizeof(public_key_r), nullptr,
+        0, psk, sizeof(psk), psk_id, sizeof(psk_id)));
+    uint32_t err = ERR_get_error();
+    EXPECT_EQ(ERR_LIB_EVP, ERR_GET_LIB(err));
+    EXPECT_EQ(EVP_R_INVALID_BUFFER_SIZE, ERR_GET_REASON(err));
+    ERR_clear_error();
+  }
+}
+
+TEST(HPKETest, SetupReceiverWrongLengthEnc) {
+  uint8_t private_key[X25519_PRIVATE_KEY_LEN];
+  uint8_t public_key[X25519_PUBLIC_VALUE_LEN];
+  X25519_keypair(public_key, private_key);
+
+  const uint8_t bogus_enc[X25519_PUBLIC_VALUE_LEN + 5] = {0xff};
+
+  ScopedEVP_HPKE_CTX receiver_ctx;
+  {
+    ASSERT_FALSE(EVP_HPKE_CTX_setup_base_r_x25519(
+        receiver_ctx.get(), EVP_HPKE_HKDF_SHA256, EVP_HPKE_AEAD_AES_GCM_128,
+        bogus_enc, sizeof(bogus_enc), public_key, sizeof(public_key),
+        private_key, sizeof(private_key), nullptr, 0));
+    uint32_t err = ERR_get_error();
+    EXPECT_EQ(ERR_LIB_EVP, ERR_GET_LIB(err));
+    EXPECT_EQ(EVP_R_INVALID_PEER_KEY, ERR_GET_REASON(err));
+    ERR_clear_error();
+  }
+  {
+    const uint8_t psk[] = {1, 2, 3, 4};
+    const uint8_t psk_id[] = {1, 2, 3, 4};
+    ASSERT_FALSE(EVP_HPKE_CTX_setup_psk_r_x25519(
+        receiver_ctx.get(), EVP_HPKE_HKDF_SHA256, EVP_HPKE_AEAD_AES_GCM_128,
+        bogus_enc, sizeof(bogus_enc), public_key, sizeof(public_key),
+        private_key, sizeof(private_key), nullptr, 0, psk, sizeof(psk), psk_id,
+        sizeof(psk_id)));
+    uint32_t err = ERR_get_error();
+    EXPECT_EQ(ERR_LIB_EVP, ERR_GET_LIB(err));
+    EXPECT_EQ(EVP_R_INVALID_PEER_KEY, ERR_GET_REASON(err));
+    ERR_clear_error();
+  }
+}
+
+TEST(HPKETest, SetupSenderWrongLengthPeerPublicValue) {
+  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];
+  {
+    ASSERT_FALSE(EVP_HPKE_CTX_setup_base_s_x25519(
+        sender_ctx.get(), enc, sizeof(enc), EVP_HPKE_HKDF_SHA256,
+        EVP_HPKE_AEAD_AES_GCM_128, bogus_public_key_r,
+        sizeof(bogus_public_key_r), nullptr, 0));
+    uint32_t err = ERR_get_error();
+    EXPECT_EQ(ERR_LIB_EVP, ERR_GET_LIB(err));
+    EXPECT_EQ(EVP_R_INVALID_PEER_KEY, ERR_GET_REASON(err));
+    ERR_clear_error();
+  }
+  {
+    const uint8_t psk[] = {1, 2, 3, 4};
+    const uint8_t psk_id[] = {1, 2, 3, 4};
+
+    ASSERT_FALSE(EVP_HPKE_CTX_setup_psk_s_x25519(
+        sender_ctx.get(), enc, sizeof(enc), EVP_HPKE_HKDF_SHA256,
+        EVP_HPKE_AEAD_AES_GCM_128, bogus_public_key_r,
+        sizeof(bogus_public_key_r), nullptr, 0, psk, sizeof(psk), psk_id,
+        sizeof(psk_id)));
+    uint32_t err = ERR_get_error();
+    EXPECT_EQ(ERR_LIB_EVP, ERR_GET_LIB(err));
+    EXPECT_EQ(EVP_R_INVALID_PEER_KEY, ERR_GET_REASON(err));
+    ERR_clear_error();
+  }
+}
+
+TEST(HPKETest, SetupReceiverWrongLengthKeys) {
+  uint8_t private_key[X25519_PRIVATE_KEY_LEN];
+  uint8_t public_key[X25519_PUBLIC_VALUE_LEN];
+  X25519_keypair(public_key, private_key);
+
+  uint8_t unused[X25519_PRIVATE_KEY_LEN];
+  uint8_t enc[X25519_PUBLIC_VALUE_LEN];
+  X25519_keypair(enc, unused);
+
+  const uint8_t bogus_public_key[X25519_PUBLIC_VALUE_LEN + 5] = {0xff};
+  const uint8_t bogus_private_key[X25519_PUBLIC_VALUE_LEN + 5] = {0xff};
+
+  ScopedEVP_HPKE_CTX receiver_ctx;
+  {
+    // Test base mode with |bogus_public_key|.
+    ASSERT_FALSE(EVP_HPKE_CTX_setup_base_r_x25519(
+        receiver_ctx.get(), EVP_HPKE_HKDF_SHA256, EVP_HPKE_AEAD_AES_GCM_128,
+        enc, sizeof(enc), bogus_public_key, sizeof(bogus_public_key),
+        private_key, sizeof(private_key), nullptr, 0));
+    uint32_t err = ERR_get_error();
+    EXPECT_EQ(ERR_LIB_EVP, ERR_GET_LIB(err));
+    EXPECT_EQ(EVP_R_DECODE_ERROR, ERR_GET_REASON(err));
+    ERR_clear_error();
+  }
+  {
+    // Test base mode with |bogus_private_key|.
+    ASSERT_FALSE(EVP_HPKE_CTX_setup_base_r_x25519(
+        receiver_ctx.get(), EVP_HPKE_HKDF_SHA256, EVP_HPKE_AEAD_AES_GCM_128,
+        enc, sizeof(enc), public_key, sizeof(public_key), bogus_private_key,
+        sizeof(bogus_private_key), nullptr, 0));
+    uint32_t err = ERR_get_error();
+    EXPECT_EQ(ERR_LIB_EVP, ERR_GET_LIB(err));
+    EXPECT_EQ(EVP_R_DECODE_ERROR, ERR_GET_REASON(err));
+    ERR_clear_error();
+  }
+  {
+    // Test PSK mode with |bogus_public_key|.
+    const uint8_t psk[] = {1, 2, 3, 4};
+    const uint8_t psk_id[] = {1, 2, 3, 4};
+    ASSERT_FALSE(EVP_HPKE_CTX_setup_psk_r_x25519(
+        receiver_ctx.get(), EVP_HPKE_HKDF_SHA256, EVP_HPKE_AEAD_AES_GCM_128,
+        enc, sizeof(enc), bogus_public_key, sizeof(bogus_public_key),
+        private_key, sizeof(private_key), nullptr, 0, psk, sizeof(psk), psk_id,
+        sizeof(psk_id)));
+    uint32_t err = ERR_get_error();
+    EXPECT_EQ(ERR_LIB_EVP, ERR_GET_LIB(err));
+    EXPECT_EQ(EVP_R_DECODE_ERROR, ERR_GET_REASON(err));
+    ERR_clear_error();
+  }
+  {
+    // Test PSK mode with |bogus_private_key|.
+    const uint8_t psk[] = {1, 2, 3, 4};
+    const uint8_t psk_id[] = {1, 2, 3, 4};
+    ASSERT_FALSE(EVP_HPKE_CTX_setup_psk_r_x25519(
+        receiver_ctx.get(), EVP_HPKE_HKDF_SHA256, EVP_HPKE_AEAD_AES_GCM_128,
+        enc, sizeof(enc), public_key, sizeof(public_key), bogus_private_key,
+        sizeof(bogus_private_key), nullptr, 0, psk, sizeof(psk), psk_id,
+        sizeof(psk_id)));
+    uint32_t err = ERR_get_error();
+    EXPECT_EQ(ERR_LIB_EVP, ERR_GET_LIB(err));
+    EXPECT_EQ(EVP_R_DECODE_ERROR, ERR_GET_REASON(err));
+    ERR_clear_error();
+  }
+}
+
 TEST(HPKETest, InternalParseIntSafe) {
   uint8_t u8 = 0xff;
   ASSERT_FALSE(ParseIntSafe(&u8, "-1"));
diff --git a/crypto/hpke/internal.h b/crypto/hpke/internal.h
index 3d2f4ba..51a3313 100644
--- a/crypto/hpke/internal.h
+++ b/crypto/hpke/internal.h
@@ -86,32 +86,35 @@
 // 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 to |out_enc|.
+// 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|.
 OPENSSL_EXPORT int EVP_HPKE_CTX_setup_base_s_x25519(
-    EVP_HPKE_CTX *hpke, uint8_t out_enc[X25519_PUBLIC_VALUE_LEN],
-    uint16_t kdf_id, uint16_t aead_id,
-    const uint8_t peer_public_value[X25519_PUBLIC_VALUE_LEN],
-    const uint8_t *info, size_t info_len);
+    EVP_HPKE_CTX *hpke, uint8_t *out_enc, size_t out_enc_len, uint16_t kdf_id,
+    uint16_t aead_id, const uint8_t *peer_public_value,
+    size_t peer_public_value_len, const uint8_t *info, size_t info_len);
 
 // EVP_HPKE_CTX_setup_base_s_x25519_for_test behaves like
 // |EVP_HPKE_CTX_setup_base_s_x25519|, but takes a pre-generated ephemeral
-// sender key.
+// sender key. The caller ensures that |ephemeral_public| and
+// |ephemeral_private| are a valid keypair.
 OPENSSL_EXPORT int EVP_HPKE_CTX_setup_base_s_x25519_for_test(
     EVP_HPKE_CTX *hpke, uint16_t kdf_id, uint16_t aead_id,
-    const uint8_t peer_public_value[X25519_PUBLIC_VALUE_LEN],
-    const uint8_t *info, size_t info_len,
-    const uint8_t ephemeral_private[X25519_PRIVATE_KEY_LEN],
-    const uint8_t ephemeral_public[X25519_PUBLIC_VALUE_LEN]);
+    const uint8_t *peer_public_value, size_t peer_public_value_len,
+    const uint8_t *info, size_t info_len, const uint8_t *ephemeral_private,
+    size_t ephemeral_private_len, const uint8_t *ephemeral_public,
+    size_t ephemeral_public_len);
 
 // EVP_HPKE_CTX_setup_base_r_x25519 sets up |hpke| as a recipient context that
-// can decrypt messages. |private_key| is the recipient's private key, and |enc|
-// is the encapsulated shared secret from the sender. Note that this function
-// will fail if |enc| is invalid.
+// can decrypt messages. It returns one on success, and zero otherwise.
+//
+// The recipient's keypair is composed of |public_key| and |private_key|, and
+// |enc| is the encapsulated shared secret from the sender. If |enc| is invalid,
+// this function will fail.
 OPENSSL_EXPORT int EVP_HPKE_CTX_setup_base_r_x25519(
-    EVP_HPKE_CTX *hpke, uint16_t kdf_id, uint16_t aead_id,
-    const uint8_t enc[X25519_PUBLIC_VALUE_LEN],
-    const uint8_t public_key[X25519_PUBLIC_VALUE_LEN],
-    const uint8_t private_key[X25519_PRIVATE_KEY_LEN], const uint8_t *info,
+    EVP_HPKE_CTX *hpke, uint16_t kdf_id, uint16_t aead_id, const uint8_t *enc,
+    size_t enc_len, const uint8_t *public_key, size_t public_key_len,
+    const uint8_t *private_key, size_t private_key_len, const uint8_t *info,
     size_t info_len);
 
 // EVP_HPKE_CTX_setup_psk_s_x25519 sets up |hpke| as a sender context that can
@@ -124,39 +127,44 @@
 // must be nonempty (|psk_len| and |psk_id_len| must be non-zero), or this
 // function will fail.
 //
-// This function writes the encapsulated shared secret to |out_enc|.
+// 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|.
 OPENSSL_EXPORT int EVP_HPKE_CTX_setup_psk_s_x25519(
-    EVP_HPKE_CTX *hpke, uint8_t out_enc[X25519_PUBLIC_VALUE_LEN],
-    uint16_t kdf_id, uint16_t aead_id,
-    const uint8_t peer_public_value[X25519_PUBLIC_VALUE_LEN],
-    const uint8_t *info, size_t info_len, const uint8_t *psk, size_t psk_len,
-    const uint8_t *psk_id, size_t psk_id_len);
+    EVP_HPKE_CTX *hpke, uint8_t *out_enc, size_t out_enc_len, uint16_t kdf_id,
+    uint16_t aead_id, const uint8_t *peer_public_value,
+    size_t peer_public_value_len, const uint8_t *info, size_t info_len,
+    const uint8_t *psk, size_t psk_len, const uint8_t *psk_id,
+    size_t psk_id_len);
 
 // EVP_HPKE_CTX_setup_psk_s_x25519_for_test behaves like
 // |EVP_HPKE_CTX_setup_psk_s_x25519|, but takes a pre-generated ephemeral sender
-// key.
+// key. The caller ensures that |ephemeral_public| and |ephemeral_private| are a
+// valid keypair.
 OPENSSL_EXPORT int EVP_HPKE_CTX_setup_psk_s_x25519_for_test(
     EVP_HPKE_CTX *hpke, uint16_t kdf_id, uint16_t aead_id,
-    const uint8_t peer_public_value[X25519_PUBLIC_VALUE_LEN],
+    const uint8_t *peer_public_value, size_t peer_public_value_len,
     const uint8_t *info, size_t info_len, const uint8_t *psk, size_t psk_len,
-    const uint8_t *psk_id, size_t psk_id_len,
-    const uint8_t ephemeral_private[X25519_PRIVATE_KEY_LEN],
-    const uint8_t ephemeral_public[X25519_PUBLIC_VALUE_LEN]);
+    const uint8_t *psk_id, size_t psk_id_len, const uint8_t *ephemeral_private,
+    size_t ephemeral_private_len, const uint8_t *ephemeral_public,
+    size_t ephemeral_public_len);
 
 // EVP_HPKE_CTX_setup_psk_r_x25519 sets up |hpke| as a recipient context that
 // can decrypt messages. Future open (decrypt) operations will fail if the
-// sender does not possess the PSK indicated by |psk| and |psk_id|.
-// |private_key| is the recipient's private key, and |enc| is the encapsulated
-// shared secret from the sender. If |enc| is invalid, this function will fail.
+// sender does not possess the PSK indicated by |psk| and |psk_id|. It returns
+// one on success, and zero otherwise.
+//
+// The recipient's keypair is composed of |public_key| and |private_key|, and
+// |enc| is the encapsulated shared secret from the sender. If |enc| is invalid,
+// this function will fail.
 //
 // The PSK and its ID must be provided in |psk| and |psk_id|, respectively. Both
 // must be nonempty (|psk_len| and |psk_id_len| must be non-zero), or this
 // function will fail.
 OPENSSL_EXPORT int EVP_HPKE_CTX_setup_psk_r_x25519(
-    EVP_HPKE_CTX *hpke, uint16_t kdf_id, uint16_t aead_id,
-    const uint8_t enc[X25519_PUBLIC_VALUE_LEN],
-    const uint8_t public_key[X25519_PUBLIC_VALUE_LEN],
-    const uint8_t private_key[X25519_PRIVATE_KEY_LEN], const uint8_t *info,
+    EVP_HPKE_CTX *hpke, uint16_t kdf_id, uint16_t aead_id, const uint8_t *enc,
+    size_t enc_len, const uint8_t *public_key, size_t public_key_len,
+    const uint8_t *private_key, size_t private_key_len, const uint8_t *info,
     size_t info_len, const uint8_t *psk, size_t psk_len, const uint8_t *psk_id,
     size_t psk_id_len);
 
diff --git a/include/openssl/evp_errors.h b/include/openssl/evp_errors.h
index 2482584..8583f52 100644
--- a/include/openssl/evp_errors.h
+++ b/include/openssl/evp_errors.h
@@ -94,5 +94,6 @@
 #define EVP_R_INVALID_PEER_KEY 134
 #define EVP_R_NOT_XOF_OR_INVALID_LENGTH 135
 #define EVP_R_EMPTY_PSK 136
+#define EVP_R_INVALID_BUFFER_SIZE 137
 
 #endif  // OPENSSL_HEADER_EVP_ERRORS_H