shithub: tlsclient

ref: 94f2907dc40a6415a10c252cb9ba3971f1f7e838
dir: /third_party/boringssl/src/crypto/hpke/hpke_test.cc/

View raw version
/* Copyright (c) 2020, Google Inc.
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */

#include <openssl/hpke.h>

#include <cstdint>
#include <limits>
#include <string>
#include <vector>

#include <gtest/gtest.h>

#include <openssl/base.h>
#include <openssl/curve25519.h>
#include <openssl/digest.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/rand.h>
#include <openssl/sha.h>
#include <openssl/span.h>

#include "../test/file_test.h"
#include "../test/test_util.h"


BSSL_NAMESPACE_BEGIN
namespace {

const decltype(&EVP_hpke_x25519_hkdf_sha256) kAllKEMs[] = {
    &EVP_hpke_p256_hkdf_sha256,
    &EVP_hpke_x25519_hkdf_sha256,
};

const decltype(&EVP_hpke_aes_128_gcm) kAllAEADs[] = {
    &EVP_hpke_aes_128_gcm,
    &EVP_hpke_aes_256_gcm,
    &EVP_hpke_chacha20_poly1305,
};

const decltype(&EVP_hpke_hkdf_sha256) kAllKDFs[] = {
    &EVP_hpke_hkdf_sha256,
};

// HPKETestVector corresponds to one array member in the published
// test-vectors.json.
class HPKETestVector {
 public:
  explicit HPKETestVector() = default;
  ~HPKETestVector() = default;

  bool ReadFromFileTest(FileTest *t);

  void Verify() const {
    const EVP_HPKE_KEM *kem = GetKEM();
    const EVP_HPKE_AEAD *aead = GetAEAD();
    ASSERT_TRUE(aead);
    const EVP_HPKE_KDF *kdf = GetKDF();
    ASSERT_TRUE(kdf);

    // Test the sender.
    ScopedEVP_HPKE_CTX sender_ctx;
    uint8_t enc[EVP_HPKE_MAX_ENC_LENGTH];
    size_t enc_len = 0;

    // X25519 uses the secret key directly. P-256 uses the IKM to derive a key.
    bssl::Span<const uint8_t> secret_input = secret_key_e_;
    if (kem_id_ == EVP_HPKE_DHKEM_P256_HKDF_SHA256) {
      secret_input = ikm_e_;
    }

    switch (mode_) {
      case Mode::kBase:
        ASSERT_TRUE(EVP_HPKE_CTX_setup_sender_with_seed_for_testing(
            sender_ctx.get(), enc, &enc_len, sizeof(enc), kem, kdf, aead,
            public_key_r_.data(), public_key_r_.size(), info_.data(),
            info_.size(), secret_input.data(), secret_input.size()));
        break;
      case Mode::kAuth: {
        ScopedEVP_HPKE_KEY sender_key;
        ASSERT_TRUE(EVP_HPKE_KEY_init(
            sender_key.get(), kem, secret_key_s_.data(), secret_key_s_.size()));
        ASSERT_TRUE(EVP_HPKE_CTX_setup_auth_sender_with_seed_for_testing(
            sender_ctx.get(), enc, &enc_len, sizeof(enc), sender_key.get(), kdf,
            aead, public_key_r_.data(), public_key_r_.size(), info_.data(),
            info_.size(), secret_input.data(), secret_input.size()));
        break;
      }
    }

    EXPECT_EQ(Bytes(enc, enc_len), Bytes(public_key_e_));
    VerifySender(sender_ctx.get());

    // Test the recipient.
    ScopedEVP_HPKE_KEY base_key;
    ASSERT_TRUE(EVP_HPKE_KEY_init(base_key.get(), kem, secret_key_r_.data(),
                                  secret_key_r_.size()));

    enum class CopyMode { kOriginal, kCopy, kMove };
    for (CopyMode copy :
         {CopyMode::kOriginal, CopyMode::kCopy, CopyMode::kMove}) {
      SCOPED_TRACE(static_cast<int>(copy));
      const EVP_HPKE_KEY *key = base_key.get();
      ScopedEVP_HPKE_KEY key_copy;
      switch (copy) {
        case CopyMode::kOriginal:
          break;
        case CopyMode::kCopy:
          ASSERT_TRUE(EVP_HPKE_KEY_copy(key_copy.get(), base_key.get()));
          key = key_copy.get();
          break;
        case CopyMode::kMove:
          EVP_HPKE_KEY_move(key_copy.get(), base_key.get());
          key = key_copy.get();
          break;
      }

      uint8_t public_key[EVP_HPKE_MAX_PUBLIC_KEY_LENGTH];
      size_t public_key_len;
      ASSERT_TRUE(EVP_HPKE_KEY_public_key(key, public_key, &public_key_len,
                                          sizeof(public_key)));
      EXPECT_EQ(Bytes(public_key, public_key_len), Bytes(public_key_r_));

      uint8_t private_key[EVP_HPKE_MAX_PRIVATE_KEY_LENGTH];
      size_t private_key_len;
      ASSERT_TRUE(EVP_HPKE_KEY_private_key(key, private_key, &private_key_len,
                                           sizeof(private_key)));
      EXPECT_EQ(Bytes(private_key, private_key_len), Bytes(secret_key_r_));

      // Set up the recipient.
      ScopedEVP_HPKE_CTX recipient_ctx;
      switch (mode_) {
        case Mode::kBase:
          ASSERT_TRUE(EVP_HPKE_CTX_setup_recipient(recipient_ctx.get(), key,
                                                   kdf, aead, enc, enc_len,
                                                   info_.data(), info_.size()));
          break;
        case Mode::kAuth:
          ASSERT_TRUE(EVP_HPKE_CTX_setup_auth_recipient(
              recipient_ctx.get(), key, kdf, aead, enc, enc_len, info_.data(),
              info_.size(), public_key_s_.data(), public_key_s_.size()));
          break;
      }

      VerifyRecipient(recipient_ctx.get());
    }
  }

 private:
  const EVP_HPKE_KEM *GetKEM() const {
    for (const auto kem : kAllKEMs) {
      if (EVP_HPKE_KEM_id(kem()) == kem_id_) {
        return kem();
      }
    }
    return nullptr;
  }

  const EVP_HPKE_AEAD *GetAEAD() const {
    for (const auto aead : kAllAEADs) {
      if (EVP_HPKE_AEAD_id(aead()) == aead_id_) {
        return aead();
      }
    }
    return nullptr;
  }

  const EVP_HPKE_KDF *GetKDF() const {
    for (const auto kdf : kAllKDFs) {
      if (EVP_HPKE_KDF_id(kdf()) == kdf_id_) {
        return kdf();
      }
    }
    return nullptr;
  }

  void VerifySender(EVP_HPKE_CTX *ctx) const {
    for (const Encryption &task : encryptions_) {
      std::vector<uint8_t> encrypted(task.plaintext.size() +
                                     EVP_HPKE_CTX_max_overhead(ctx));
      size_t encrypted_len;
      ASSERT_TRUE(EVP_HPKE_CTX_seal(ctx, encrypted.data(), &encrypted_len,
                                    encrypted.size(), task.plaintext.data(),
                                    task.plaintext.size(), task.aad.data(),
                                    task.aad.size()));

      ASSERT_EQ(Bytes(encrypted.data(), encrypted_len), Bytes(task.ciphertext));
    }
    VerifyExports(ctx);
  }

  void VerifyRecipient(EVP_HPKE_CTX *ctx) const {
    for (const Encryption &task : encryptions_) {
      std::vector<uint8_t> decrypted(task.ciphertext.size());
      size_t decrypted_len;
      ASSERT_TRUE(EVP_HPKE_CTX_open(ctx, decrypted.data(), &decrypted_len,
                                    decrypted.size(), task.ciphertext.data(),
                                    task.ciphertext.size(), task.aad.data(),
                                    task.aad.size()));

      ASSERT_EQ(Bytes(decrypted.data(), decrypted_len), Bytes(task.plaintext));
    }
    VerifyExports(ctx);
  }

  void VerifyExports(EVP_HPKE_CTX *ctx) const {
    for (const Export &task : exports_) {
      std::vector<uint8_t> exported_secret(task.export_length);

      ASSERT_TRUE(EVP_HPKE_CTX_export(
          ctx, exported_secret.data(), exported_secret.size(),
          task.exporter_context.data(), task.exporter_context.size()));
      ASSERT_EQ(Bytes(exported_secret), Bytes(task.exported_value));
    }
  }

  enum class Mode {
    kBase = 0,
    kAuth = 2,
  };

  struct Encryption {
    std::vector<uint8_t> aad;
    std::vector<uint8_t> ciphertext;
    std::vector<uint8_t> plaintext;
  };

  struct Export {
    std::vector<uint8_t> exporter_context;
    size_t export_length;
    std::vector<uint8_t> exported_value;
  };

  Mode mode_;
  uint16_t kem_id_;
  uint16_t kdf_id_;
  uint16_t aead_id_;
  std::vector<uint8_t> context_;
  std::vector<uint8_t> info_;
  std::vector<uint8_t> public_key_e_;
  std::vector<uint8_t> secret_key_e_;
  std::vector<uint8_t> ikm_e_;
  std::vector<uint8_t> public_key_r_;
  std::vector<uint8_t> secret_key_r_;
  std::vector<uint8_t> ikm_r_;
  std::vector<uint8_t> public_key_s_;
  std::vector<uint8_t> secret_key_s_;
  std::vector<Encryption> encryptions_;
  std::vector<Export> exports_;
};

// Match FileTest's naming scheme for duplicated attribute names.
std::string BuildAttrName(const std::string &name, int iter) {
  return iter == 1 ? name : name + "/" + std::to_string(iter);
}

// Parses |s| as an unsigned integer of type T and writes the value to |out|.
// Returns true on success. If the integer value exceeds the maximum T value,
// returns false.
template <typename T>
bool ParseIntSafe(T *out, const std::string &s) {
  T value = 0;
  for (char c : s) {
    if (c < '0' || c > '9') {
      return false;
    }
    if (value > (std::numeric_limits<T>::max() - (c - '0')) / 10) {
      return false;
    }
    value = 10 * value + (c - '0');
  }
  *out = value;
  return true;
}

// Read the |key| attribute from |file_test| and convert it to an integer.
template <typename T>
bool FileTestReadInt(FileTest *file_test, T *out, const std::string &key) {
  std::string s;
  return file_test->GetAttribute(&s, key) && ParseIntSafe(out, s);
}


bool HPKETestVector::ReadFromFileTest(FileTest *t) {
  uint8_t mode = 0;
  if (!FileTestReadInt(t, &mode, "mode") ||
      !FileTestReadInt(t, &kem_id_, "kem_id") ||
      !FileTestReadInt(t, &kdf_id_, "kdf_id") ||
      !FileTestReadInt(t, &aead_id_, "aead_id") ||
      !t->GetBytes(&info_, "info") ||  //
      !t->GetBytes(&secret_key_r_, "skRm") ||
      !t->GetBytes(&public_key_r_, "pkRm") ||
      !t->GetBytes(&ikm_r_, "ikmR") ||  //
      !t->GetBytes(&secret_key_e_, "skEm") ||
      !t->GetBytes(&public_key_e_, "pkEm") ||  //
      !t->GetBytes(&ikm_e_, "ikmE")) {
    return false;
  }

  switch (mode) {
    case static_cast<int>(Mode::kBase):
      mode_ = Mode::kBase;
      break;
    case static_cast<int>(Mode::kAuth):
      mode_ = Mode::kAuth;
      if (!t->GetBytes(&secret_key_s_, "skSm") ||
          !t->GetBytes(&public_key_s_, "pkSm")) {
        return false;
      }
      break;
    default:
      return false;
  }

  for (int i = 1; t->HasAttribute(BuildAttrName("aad", i)); i++) {
    Encryption encryption;
    if (!t->GetBytes(&encryption.aad, BuildAttrName("aad", i)) ||
        !t->GetBytes(&encryption.ciphertext, BuildAttrName("ct", i)) ||
        !t->GetBytes(&encryption.plaintext, BuildAttrName("pt", i))) {
      return false;
    }
    encryptions_.push_back(std::move(encryption));
  }

  for (int i = 1; t->HasAttribute(BuildAttrName("exporter_context", i)); i++) {
    Export exp;
    if (!t->GetBytes(&exp.exporter_context,
                     BuildAttrName("exporter_context", i)) ||
        !FileTestReadInt(t, &exp.export_length, BuildAttrName("L", i)) ||
        !t->GetBytes(&exp.exported_value, BuildAttrName("exported_value", i))) {
      return false;
    }
    exports_.push_back(std::move(exp));
  }
  return true;
}

}  // namespace

TEST(HPKETest, VerifyTestVectors) {
  FileTestGTest("crypto/hpke/hpke_test_vectors.txt", [](FileTest *t) {
    HPKETestVector test_vec;
    EXPECT_TRUE(test_vec.ReadFromFileTest(t));
    test_vec.Verify();
  });
}

// The test vectors used fixed sender ephemeral keys, while HPKE itself
// generates new keys for each context. Test this codepath by checking we can
// decrypt our own messages.
TEST(HPKETest, RoundTrip) {
  const uint8_t info_a[] = {1, 1, 2, 3, 5, 8};
  const uint8_t info_b[] = {42, 42, 42};
  const uint8_t ad_a[] = {1, 2, 4, 8, 16};
  const uint8_t ad_b[] = {7};
  Span<const uint8_t> info_values[] = {{nullptr, 0}, info_a, info_b};
  Span<const uint8_t> ad_values[] = {{nullptr, 0}, ad_a, ad_b};

  for (const auto kem : kAllKEMs) {
    SCOPED_TRACE(EVP_HPKE_KEM_id(kem()));

    // Generate the recipient's keypair.
    ScopedEVP_HPKE_KEY key;
    ASSERT_TRUE(EVP_HPKE_KEY_generate(key.get(), kem()));
    uint8_t public_key_r[EVP_HPKE_MAX_PUBLIC_KEY_LENGTH];
    size_t public_key_r_len;
    ASSERT_TRUE(EVP_HPKE_KEY_public_key(
        key.get(), public_key_r, &public_key_r_len, sizeof(public_key_r)));

    // Generate the sender's keypair, for auth modes.
    ScopedEVP_HPKE_KEY sender_key;
    ASSERT_TRUE(EVP_HPKE_KEY_generate(sender_key.get(), kem()));
    uint8_t public_key_s[EVP_HPKE_MAX_PUBLIC_KEY_LENGTH];
    size_t public_key_s_len;
    ASSERT_TRUE(EVP_HPKE_KEY_public_key(sender_key.get(), public_key_s,
                                        &public_key_s_len,
                                        sizeof(public_key_s)));

    for (const auto kdf : kAllKDFs) {
      SCOPED_TRACE(EVP_HPKE_KDF_id(kdf()));
      for (const auto aead : kAllAEADs) {
        SCOPED_TRACE(EVP_HPKE_AEAD_id(aead()));
        for (const Span<const uint8_t> &info : info_values) {
          SCOPED_TRACE(Bytes(info));
          for (const Span<const uint8_t> &ad : ad_values) {
            SCOPED_TRACE(Bytes(ad));

            auto check_messages = [&](EVP_HPKE_CTX *sender_ctx,
                                      EVP_HPKE_CTX *recipient_ctx) {
              const char kCleartextPayload[] = "foobar";

              // Have sender encrypt message for the recipient.
              std::vector<uint8_t> ciphertext(
                  sizeof(kCleartextPayload) +
                  EVP_HPKE_CTX_max_overhead(sender_ctx));
              size_t ciphertext_len;
              ASSERT_TRUE(EVP_HPKE_CTX_seal(
                  sender_ctx, ciphertext.data(), &ciphertext_len,
                  ciphertext.size(),
                  reinterpret_cast<const uint8_t *>(kCleartextPayload),
                  sizeof(kCleartextPayload), ad.data(), ad.size()));

              // Have recipient decrypt the message.
              std::vector<uint8_t> cleartext(ciphertext.size());
              size_t cleartext_len;
              ASSERT_TRUE(EVP_HPKE_CTX_open(recipient_ctx, cleartext.data(),
                                            &cleartext_len, cleartext.size(),
                                            ciphertext.data(), ciphertext_len,
                                            ad.data(), ad.size()));

              // Verify that decrypted message matches the original.
              ASSERT_EQ(Bytes(cleartext.data(), cleartext_len),
                        Bytes(kCleartextPayload, sizeof(kCleartextPayload)));
            };

            // Test the base mode.
            {
              ScopedEVP_HPKE_CTX sender_ctx;
              uint8_t enc[EVP_HPKE_MAX_PUBLIC_KEY_LENGTH];
              size_t enc_len;
              ASSERT_TRUE(EVP_HPKE_CTX_setup_sender(
                  sender_ctx.get(), enc, &enc_len, sizeof(enc), kem(), kdf(),
                  aead(), public_key_r, public_key_r_len, info.data(),
                  info.size()));

              ScopedEVP_HPKE_CTX recipient_ctx;
              ASSERT_TRUE(EVP_HPKE_CTX_setup_recipient(
                  recipient_ctx.get(), key.get(), kdf(), aead(), enc, enc_len,
                  info.data(), info.size()));

              check_messages(sender_ctx.get(), recipient_ctx.get());
            }

            // Test the auth mode.
            {
              ScopedEVP_HPKE_CTX sender_ctx;
              uint8_t enc[EVP_HPKE_MAX_PUBLIC_KEY_LENGTH];
              size_t enc_len;
              ASSERT_TRUE(EVP_HPKE_CTX_setup_auth_sender(
                  sender_ctx.get(), enc, &enc_len, sizeof(enc),
                  sender_key.get(), kdf(), aead(), public_key_r,
                  public_key_r_len, info.data(), info.size()));

              ScopedEVP_HPKE_CTX recipient_ctx;
              ASSERT_TRUE(EVP_HPKE_CTX_setup_auth_recipient(
                  recipient_ctx.get(), key.get(), kdf(), aead(), enc, enc_len,
                  info.data(), info.size(), public_key_s, public_key_s_len));

              check_messages(sender_ctx.get(), recipient_ctx.get());
            }
          }
        }
      }
    }
  }
}

// Verify that the DH operations inside Encap() and Decap() both fail when the
// public key is on a small-order point in the curve.
TEST(HPKETest, X25519EncapSmallOrderPoint) {
  // Borrowed from X25519Test.SmallOrder.
  static const uint8_t kSmallOrderPoint[32] = {
      0xe0, 0xeb, 0x7a, 0x7c, 0x3b, 0x41, 0xb8, 0xae, 0x16, 0x56, 0xe3,
      0xfa, 0xf1, 0x9f, 0xc4, 0x6a, 0xda, 0x09, 0x8d, 0xeb, 0x9c, 0x32,
      0xb1, 0xfd, 0x86, 0x62, 0x05, 0x16, 0x5f, 0x49, 0xb8,
  };
  static const uint8_t kValidPoint[32] = {
      0xe6, 0xdb, 0x68, 0x67, 0x58, 0x30, 0x30, 0xdb, 0x35, 0x94, 0xc1,
      0xa4, 0x24, 0xb1, 0x5f, 0x7c, 0x72, 0x66, 0x24, 0xec, 0x26, 0xb3,
      0x35, 0x3b, 0x10, 0xa9, 0x03, 0xa6, 0xd0, 0xab, 0x1c, 0x4c,
  };

  ScopedEVP_HPKE_KEY key;
  ASSERT_TRUE(EVP_HPKE_KEY_generate(key.get(), EVP_hpke_x25519_hkdf_sha256()));

  for (const auto kdf : kAllKDFs) {
    SCOPED_TRACE(EVP_HPKE_KDF_id(kdf()));
    for (const auto aead : kAllAEADs) {
      SCOPED_TRACE(EVP_HPKE_AEAD_id(aead()));
      // Set up the sender, passing in kSmallOrderPoint as |peer_public_key|.
      ScopedEVP_HPKE_CTX sender_ctx;
      uint8_t enc[X25519_PUBLIC_VALUE_LEN];
      size_t enc_len;
      EXPECT_FALSE(EVP_HPKE_CTX_setup_sender(
          sender_ctx.get(), enc, &enc_len, sizeof(enc),
          EVP_hpke_x25519_hkdf_sha256(), kdf(), aead(), kSmallOrderPoint,
          sizeof(kSmallOrderPoint), nullptr, 0));

      // Likewise with auth.
      EXPECT_FALSE(EVP_HPKE_CTX_setup_auth_sender(
          sender_ctx.get(), enc, &enc_len, sizeof(enc), key.get(), kdf(),
          aead(), kSmallOrderPoint, sizeof(kSmallOrderPoint), nullptr, 0));

      // Set up the recipient, passing in kSmallOrderPoint as |enc|.
      ScopedEVP_HPKE_CTX recipient_ctx;
      EXPECT_FALSE(EVP_HPKE_CTX_setup_recipient(
          recipient_ctx.get(), key.get(), kdf(), aead(), kSmallOrderPoint,
          sizeof(kSmallOrderPoint), nullptr, 0));

      // Likewise with auth. With auth, a small-order point could appear as
      // either |enc| or the peer public key.
      EXPECT_FALSE(EVP_HPKE_CTX_setup_auth_recipient(
          recipient_ctx.get(), key.get(), kdf(), aead(), kSmallOrderPoint,
          sizeof(kSmallOrderPoint), nullptr, 0, kValidPoint,
          sizeof(kValidPoint)));
      EXPECT_FALSE(EVP_HPKE_CTX_setup_auth_recipient(
          recipient_ctx.get(), key.get(), kdf(), aead(), kValidPoint,
          sizeof(kValidPoint), nullptr, 0, kSmallOrderPoint,
          sizeof(kSmallOrderPoint)));
    }
  }
}

// Test that Seal() fails when the context has been initialized as a recipient.
TEST(HPKETest, RecipientInvalidSeal) {
  const uint8_t kMockEnc[X25519_PUBLIC_VALUE_LEN] = {0xff};
  const char kCleartextPayload[] = "foobar";

  ScopedEVP_HPKE_KEY key;
  ASSERT_TRUE(EVP_HPKE_KEY_generate(key.get(), EVP_hpke_x25519_hkdf_sha256()));

  // Set up the recipient.
  ScopedEVP_HPKE_CTX recipient_ctx;
  ASSERT_TRUE(EVP_HPKE_CTX_setup_recipient(
      recipient_ctx.get(), key.get(), EVP_hpke_hkdf_sha256(),
      EVP_hpke_aes_128_gcm(), kMockEnc, sizeof(kMockEnc), nullptr, 0));

  // Call Seal() on the recipient.
  size_t ciphertext_len;
  uint8_t ciphertext[100];
  ASSERT_FALSE(EVP_HPKE_CTX_seal(
      recipient_ctx.get(), ciphertext, &ciphertext_len, sizeof(ciphertext),
      reinterpret_cast<const uint8_t *>(kCleartextPayload),
      sizeof(kCleartextPayload), nullptr, 0));
}

// Test that Open() fails when the context has been initialized as a sender.
TEST(HPKETest, SenderInvalidOpen) {
  const uint8_t kMockCiphertext[100] = {0xff};
  const size_t kMockCiphertextLen = 80;

  // Generate the recipient's keypair.
  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);

  // 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_sender(
      sender_ctx.get(), enc, &enc_len, sizeof(enc),
      EVP_hpke_x25519_hkdf_sha256(), EVP_hpke_hkdf_sha256(),
      EVP_hpke_aes_128_gcm(), public_key_r, sizeof(public_key_r), nullptr, 0));

  // Call Open() on the sender.
  uint8_t cleartext[128];
  size_t cleartext_len;
  ASSERT_FALSE(EVP_HPKE_CTX_open(sender_ctx.get(), cleartext, &cleartext_len,
                                 sizeof(cleartext), kMockCiphertext,
                                 kMockCiphertextLen, nullptr, 0));
}

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 enc[X25519_PUBLIC_VALUE_LEN - 1];
  size_t enc_len;
  ASSERT_FALSE(EVP_HPKE_CTX_setup_sender(
      sender_ctx.get(), enc, &enc_len, sizeof(enc),
      EVP_hpke_x25519_hkdf_sha256(), EVP_hpke_hkdf_sha256(),
      EVP_hpke_aes_128_gcm(), public_key_r, sizeof(public_key_r), nullptr, 0));
  EXPECT_TRUE(
      ErrorEquals(ERR_get_error(), ERR_LIB_EVP, EVP_R_INVALID_BUFFER_SIZE));
  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_sender(
      sender_ctx.get(), enc, &enc_len, sizeof(enc),
      EVP_hpke_x25519_hkdf_sha256(), 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, SetupRecipientWrongLengthEnc) {
  ScopedEVP_HPKE_KEY key;
  ASSERT_TRUE(EVP_HPKE_KEY_generate(key.get(), EVP_hpke_x25519_hkdf_sha256()));

  const uint8_t bogus_enc[X25519_PUBLIC_VALUE_LEN + 5] = {0xff};

  ScopedEVP_HPKE_CTX recipient_ctx;
  ASSERT_FALSE(EVP_HPKE_CTX_setup_recipient(
      recipient_ctx.get(), key.get(), EVP_hpke_hkdf_sha256(),
      EVP_hpke_aes_128_gcm(), bogus_enc, sizeof(bogus_enc), nullptr, 0));
  EXPECT_TRUE(
      ErrorEquals(ERR_get_error(), ERR_LIB_EVP, EVP_R_INVALID_PEER_KEY));
  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];
  size_t enc_len;
  ASSERT_FALSE(EVP_HPKE_CTX_setup_sender(
      sender_ctx.get(), enc, &enc_len, sizeof(enc),
      EVP_hpke_x25519_hkdf_sha256(), EVP_hpke_hkdf_sha256(),
      EVP_hpke_aes_128_gcm(), bogus_public_key_r, sizeof(bogus_public_key_r),
      nullptr, 0));
  EXPECT_TRUE(
      ErrorEquals(ERR_get_error(), ERR_LIB_EVP, EVP_R_INVALID_PEER_KEY));
  ERR_clear_error();
}

TEST(HPKETest, InvalidRecipientKey) {
  const uint8_t private_key[X25519_PUBLIC_VALUE_LEN + 5] = {0xff};
  ScopedEVP_HPKE_KEY key;
  EXPECT_FALSE(EVP_HPKE_KEY_init(key.get(), EVP_hpke_x25519_hkdf_sha256(),
                                 private_key, sizeof(private_key)));
}

TEST(HPKETest, InvalidP256PrivateKey) {
  const uint8_t zero_key[32] = {0};
  ScopedEVP_HPKE_KEY key;
  EXPECT_FALSE(EVP_HPKE_KEY_init(key.get(), EVP_hpke_p256_hkdf_sha256(),
                                 zero_key, sizeof(zero_key)));

  uint8_t all_ones_key[32];
  OPENSSL_memset(all_ones_key, 0xff, sizeof(all_ones_key));
  EXPECT_FALSE(EVP_HPKE_KEY_init(key.get(), EVP_hpke_p256_hkdf_sha256(),
                                 all_ones_key, sizeof(all_ones_key)));
}

TEST(HPKETest, InternalParseIntSafe) {
  uint8_t u8 = 0xff;
  ASSERT_FALSE(ParseIntSafe(&u8, "-1"));

  ASSERT_TRUE(ParseIntSafe(&u8, "0"));
  ASSERT_EQ(u8, 0);

  ASSERT_TRUE(ParseIntSafe(&u8, "255"));
  ASSERT_EQ(u8, 255);

  ASSERT_FALSE(ParseIntSafe(&u8, "256"));

  uint16_t u16 = 0xffff;
  ASSERT_TRUE(ParseIntSafe(&u16, "257"));
  ASSERT_EQ(u16, 257);

  ASSERT_TRUE(ParseIntSafe(&u16, "65535"));
  ASSERT_EQ(u16, 65535);

  ASSERT_FALSE(ParseIntSafe(&u16, "65536"));
}

BSSL_NAMESPACE_END