// Copyright 2017 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////////

#include "tink/internal/registry_impl.h"

#include <stdint.h>

#include <memory>
#include <sstream>
#include <string>
#include <thread>  // NOLINT(build/c++11)
#include <typeinfo>
#include <utility>

#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/memory/memory.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/string_view.h"
#include "openssl/crypto.h"
#include "tink/aead.h"
#include "tink/aead/aead_wrapper.h"
#include "tink/aead/aes_gcm_key_manager.h"
#include "tink/core/key_manager_impl.h"
#include "tink/core/key_type_manager.h"
#include "tink/core/private_key_manager_impl.h"
#include "tink/core/private_key_type_manager.h"
#include "tink/core/template_util.h"
#include "tink/hybrid/ecies_aead_hkdf_private_key_manager.h"
#include "tink/hybrid/ecies_aead_hkdf_public_key_manager.h"
#include "tink/hybrid_decrypt.h"
#include "tink/input_stream.h"
#include "tink/internal/fips_utils.h"
#include "tink/key_manager.h"
#include "tink/mac.h"
#include "tink/monitoring/monitoring_client_mocks.h"
#include "tink/primitive_set.h"
#include "tink/primitive_wrapper.h"
#include "tink/registry.h"
#include "tink/subtle/aes_gcm_boringssl.h"
#include "tink/subtle/random.h"
#include "tink/util/input_stream_util.h"
#include "tink/util/istream_input_stream.h"
#include "tink/util/protobuf_helper.h"
#include "tink/util/secret_data.h"
#include "tink/util/status.h"
#include "tink/util/statusor.h"
#include "tink/util/test_matchers.h"
#include "tink/util/test_util.h"
#include "proto/aes_ctr_hmac_aead.pb.h"
#include "proto/aes_gcm.pb.h"
#include "proto/common.pb.h"
#include "proto/ecdsa.pb.h"
#include "proto/ecies_aead_hkdf.pb.h"
#include "proto/tink.pb.h"

namespace crypto {
namespace tink {
namespace internal {

namespace {

using ::crypto::tink::test::AddLegacyKey;
using ::crypto::tink::test::AddRawKey;
using ::crypto::tink::test::AddTinkKey;
using ::crypto::tink::test::DummyAead;
using ::crypto::tink::test::IsOk;
using ::crypto::tink::test::StatusIs;
using ::crypto::tink::util::Status;
using ::google::crypto::tink::AesCtrHmacAeadKey;
using ::google::crypto::tink::AesGcmKey;
using ::google::crypto::tink::AesGcmKeyFormat;
using ::google::crypto::tink::EcdsaKeyFormat;
using ::google::crypto::tink::EcdsaPrivateKey;
using ::google::crypto::tink::EcdsaPublicKey;
using ::google::crypto::tink::EcdsaSignatureEncoding;
using ::google::crypto::tink::EcPointFormat;
using ::google::crypto::tink::EllipticCurveType;
using ::google::crypto::tink::HashType;
using ::google::crypto::tink::KeyData;
using ::google::crypto::tink::Keyset;
using ::google::crypto::tink::KeysetInfo;
using ::google::crypto::tink::KeyStatusType;
using ::google::crypto::tink::KeyTemplate;
using ::google::crypto::tink::OutputPrefixType;
using ::portable_proto::MessageLite;
using ::testing::Eq;
using ::testing::HasSubstr;
using ::testing::IsNull;
using ::testing::Not;
using ::testing::SizeIs;

class RegistryTest : public ::testing::Test {
 protected:
  void SetUp() override { Registry::Reset(); }

  void TearDown() override {
    // Reset is needed here to ensure Mock objects get deleted and do not leak.
    Registry::Reset();
  }
};

class TestKeyFactory : public KeyFactory {
 public:
  explicit TestKeyFactory(const std::string& key_type) : key_type_(key_type) {}

  util::StatusOr<std::unique_ptr<portable_proto::MessageLite>> NewKey(
      const MessageLite& key_format) const override {
    return util::Status(absl::StatusCode::kUnknown,
                        "TestKeyFactory cannot produce a key");
  }

  util::StatusOr<std::unique_ptr<portable_proto::MessageLite>> NewKey(
      absl::string_view serialized_key_format) const override {
    return util::Status(absl::StatusCode::kUnknown,
                        "TestKeyFactory cannot produce a key");
  }

  util::StatusOr<std::unique_ptr<KeyData>> NewKeyData(
      absl::string_view serialized_key_format) const override {
    auto key_data = absl::make_unique<KeyData>();
    key_data->set_type_url(key_type_);
    key_data->set_value(std::string(serialized_key_format));
    return std::move(key_data);
  }

 private:
  std::string key_type_;
};

class TestAeadKeyManager : public KeyManager<Aead> {
 public:
  explicit TestAeadKeyManager(const std::string& key_type)
      : key_type_(key_type), key_factory_(key_type) {}

  util::StatusOr<std::unique_ptr<Aead>> GetPrimitive(
      const KeyData& key) const override {
    std::unique_ptr<Aead> aead(new DummyAead(key_type_));
    return std::move(aead);
  }

  util::StatusOr<std::unique_ptr<Aead>> GetPrimitive(
      const MessageLite& key) const override {
    return util::Status(absl::StatusCode::kUnknown,
                        "TestKeyFactory cannot construct an aead");
  }

  uint32_t get_version() const override { return 0; }

  const std::string& get_key_type() const override { return key_type_; }

  const KeyFactory& get_key_factory() const override { return key_factory_; }

 private:
  std::string key_type_;
  TestKeyFactory key_factory_;
};

// A class for testing. We will construct objects from an aead key, so that we
// can check that a keymanager can handle multiple primitives. It is really
// insecure, as it does nothing except provide access to the key.
class AeadVariant {
 public:
  explicit AeadVariant(std::string s) : s_(s) {}

  std::string get() { return s_; }

 private:
  std::string s_;
};

class ExampleKeyTypeManager : public KeyTypeManager<AesGcmKey, AesGcmKeyFormat,
                                                    List<Aead, AeadVariant>> {
 public:
  class AeadFactory : public PrimitiveFactory<Aead> {
   public:
    crypto::tink::util::StatusOr<std::unique_ptr<Aead>> Create(
        const AesGcmKey& key) const override {
      // Ignore the key and returned one with a fixed size for this test.
      return {subtle::AesGcmBoringSsl::New(
          util::SecretDataFromStringView(key.key_value()))};
    }
  };

  class AeadVariantFactory : public PrimitiveFactory<AeadVariant> {
   public:
    crypto::tink::util::StatusOr<std::unique_ptr<AeadVariant>> Create(
        const AesGcmKey& key) const override {
      return absl::make_unique<AeadVariant>(key.key_value());
    }
  };

  ExampleKeyTypeManager()
      : KeyTypeManager(absl::make_unique<AeadFactory>(),
                       absl::make_unique<AeadVariantFactory>()) {}

  google::crypto::tink::KeyData::KeyMaterialType key_material_type()
      const override {
    return google::crypto::tink::KeyData::SYMMETRIC;
  }

  uint32_t get_version() const override { return kVersion; }

  const std::string& get_key_type() const override { return kKeyType; }

  crypto::tink::util::Status ValidateKey(const AesGcmKey& key) const override {
    return util::OkStatus();
  }

  crypto::tink::util::Status ValidateKeyFormat(
      const AesGcmKeyFormat& key_format) const override {
    return util::OkStatus();
  }

  crypto::tink::util::StatusOr<AesGcmKey> CreateKey(
      const AesGcmKeyFormat& key_format) const override {
    AesGcmKey result;
    result.set_key_value(subtle::Random::GetRandomBytes(key_format.key_size()));
    return result;
  }

  crypto::tink::util::StatusOr<AesGcmKey> DeriveKey(
      const AesGcmKeyFormat& key_format,
      InputStream* input_stream) const override {
    // Note: in an actual key type manager we need to do more work, e.g., test
    // that the generated key is long enough.
    crypto::tink::util::StatusOr<std::string> randomness =
        ReadBytesFromStream(key_format.key_size(), input_stream);
    if (!randomness.status().ok()) {
      return randomness.status();
    }
    AesGcmKey key;
    key.set_key_value(randomness.value());
    return key;
  }

  MOCK_METHOD(FipsCompatibility, FipsStatus, (), (const, override));

 private:
  static constexpr int kVersion = 0;
  const std::string kKeyType =
      "type.googleapis.com/google.crypto.tink.AesGcmKey";
};

template <typename P, typename Q = P>
class TestWrapper : public PrimitiveWrapper<P, Q> {
 public:
  TestWrapper() = default;
  crypto::tink::util::StatusOr<std::unique_ptr<Q>> Wrap(
      std::unique_ptr<PrimitiveSet<P>> primitive_set) const override {
    return util::Status(absl::StatusCode::kUnimplemented,
                        "This is a test wrapper.");
  }
};

class AeadVariantWrapper : public PrimitiveWrapper<AeadVariant, AeadVariant> {
 public:
  crypto::tink::util::StatusOr<std::unique_ptr<AeadVariant>> Wrap(
      std::unique_ptr<PrimitiveSet<AeadVariant>> primitive_set) const override {
    return absl::make_unique<AeadVariant>(
        primitive_set->get_primary()->get_primitive().get());
  }
};

class AeadVariantToStringWrapper
    : public PrimitiveWrapper<AeadVariant, std::string> {
 public:
  crypto::tink::util::StatusOr<std::unique_ptr<std::string>> Wrap(
      std::unique_ptr<PrimitiveSet<AeadVariant>> primitive_set) const override {
    return absl::make_unique<std::string>(
        primitive_set->get_primary()->get_primitive().get());
  }
};

void register_test_managers(const std::string& key_type_prefix,
                            int manager_count) {
  for (int i = 0; i < manager_count; i++) {
    std::string key_type = key_type_prefix + std::to_string(i);
    util::Status status = Registry::RegisterKeyManager(
        absl::make_unique<TestAeadKeyManager>(key_type),
        /* new_key_allowed= */ true);
    EXPECT_TRUE(status.ok()) << status;
  }
}

void verify_test_managers(const std::string& key_type_prefix,
                          int manager_count) {
  for (int i = 0; i < manager_count; i++) {
    std::string key_type = key_type_prefix + std::to_string(i);
    auto manager_result = Registry::get_key_manager<Aead>(key_type);
    EXPECT_TRUE(manager_result.ok()) << manager_result.status();
    auto manager = manager_result.value();
    EXPECT_EQ(key_type, manager->get_key_type());
  }
}

TEST_F(RegistryTest, testRegisterKeyManagerMoreRestrictiveNewKeyAllowed) {
  std::string key_type = "some_key_type";
  KeyTemplate key_template;
  key_template.set_type_url(key_type);

  // Register the key manager with new_key_allowed == true and verify that
  // new key data can be created.
  util::Status status = Registry::RegisterKeyManager(
      absl::make_unique<TestAeadKeyManager>(key_type),
      /* new_key_allowed= */ true);
  EXPECT_TRUE(status.ok()) << status;

  auto result_before = Registry::NewKeyData(key_template);
  EXPECT_TRUE(result_before.ok()) << result_before.status();

  // Re-register the key manager with new_key_allowed == false and check the
  // restriction (i.e. new key data cannot be created).
  status = Registry::RegisterKeyManager(
      absl::make_unique<TestAeadKeyManager>(key_type),
      /* new_key_allowed= */ false);
  EXPECT_TRUE(status.ok()) << status;

  auto result_after = Registry::NewKeyData(key_template);
  EXPECT_FALSE(result_after.ok());
  EXPECT_EQ(absl::StatusCode::kInvalidArgument, result_after.status().code());
  EXPECT_PRED_FORMAT2(testing::IsSubstring, key_type,
                      std::string(result_after.status().message()));
  EXPECT_PRED_FORMAT2(testing::IsSubstring, "does not allow",
                      std::string(result_after.status().message()));
}

TEST_F(RegistryTest, testRegisterKeyManagerLessRestrictiveNewKeyAllowed) {
  std::string key_type = "some_key_type";
  KeyTemplate key_template;
  key_template.set_type_url(key_type);

  // Register the key manager with new_key_allowed == false.
  util::Status status = Registry::RegisterKeyManager(
      absl::make_unique<TestAeadKeyManager>(key_type),
      /* new_key_allowed= */ false);
  EXPECT_TRUE(status.ok()) << status;

  // Verify that re-registering the key manager with new_key_allowed == true is
  // not possible and that the restriction still holds after that operation
  // (i.e. new key data cannot be created).
  status = Registry::RegisterKeyManager(
      absl::make_unique<TestAeadKeyManager>(key_type),
      /* new_key_allowed= */ true);
  EXPECT_FALSE(status.ok());
  EXPECT_EQ(absl::StatusCode::kAlreadyExists, status.code()) << status;
  EXPECT_PRED_FORMAT2(testing::IsSubstring, key_type,
                      std::string(status.message()))
      << status;
  EXPECT_PRED_FORMAT2(testing::IsSubstring, "forbidden new key operation",
                      std::string(status.message()))
      << status;

  auto result_after = Registry::NewKeyData(key_template);
  EXPECT_FALSE(result_after.ok());
  EXPECT_EQ(absl::StatusCode::kInvalidArgument, result_after.status().code());
  EXPECT_PRED_FORMAT2(testing::IsSubstring, key_type,
                      std::string(result_after.status().message()));
  EXPECT_PRED_FORMAT2(testing::IsSubstring, "does not allow",
                      std::string(result_after.status().message()));
}

TEST_F(RegistryTest, testConcurrentRegistration) {
  std::string key_type_prefix_a = "key_type_a_";
  std::string key_type_prefix_b = "key_type_b_";
  int count_a = 42;
  int count_b = 72;

  // Register some managers.
  std::thread register_a(register_test_managers, key_type_prefix_a, count_a);
  std::thread register_b(register_test_managers, key_type_prefix_b, count_b);
  register_a.join();
  register_b.join();

  // Check that the managers were registered. Also, keep registering new
  // versions while we check.
  std::thread register_more_a(register_test_managers, key_type_prefix_a,
                              count_a);
  std::thread register_more_b(register_test_managers, key_type_prefix_b,
                              count_b);
  std::thread verify_a(verify_test_managers, key_type_prefix_a, count_a);
  std::thread verify_b(verify_test_managers, key_type_prefix_b, count_b);
  verify_a.join();
  verify_b.join();
  register_more_a.join();
  register_more_b.join();

  // Check that there are no extra managers.
  std::string key_type = key_type_prefix_a + std::to_string(count_a - 1);
  auto manager_result = Registry::get_key_manager<Aead>(key_type);
  EXPECT_TRUE(manager_result.ok()) << manager_result.status();
  EXPECT_EQ(key_type, manager_result.value()->get_key_type());

  key_type = key_type_prefix_a + std::to_string(count_a);
  manager_result = Registry::get_key_manager<Aead>(key_type);
  EXPECT_FALSE(manager_result.ok());
  EXPECT_EQ(absl::StatusCode::kNotFound, manager_result.status().code());
}

TEST_F(RegistryTest, testBasic) {
  std::string key_type_1 = "google.crypto.tink.AesCtrHmacAeadKey";
  std::string key_type_2 = "google.crypto.tink.AesGcmKey";
  auto manager_result = Registry::get_key_manager<Aead>(key_type_1);
  EXPECT_FALSE(manager_result.ok());
  EXPECT_EQ(absl::StatusCode::kNotFound, manager_result.status().code());

  auto status = Registry::RegisterKeyManager(
      absl::make_unique<TestAeadKeyManager>(key_type_1), true);

  EXPECT_TRUE(status.ok()) << status;

  status = Registry::RegisterKeyManager(
      absl::make_unique<TestAeadKeyManager>(key_type_2), true);
  EXPECT_TRUE(status.ok()) << status;

  manager_result = Registry::get_key_manager<Aead>(key_type_1);
  EXPECT_TRUE(manager_result.ok()) << manager_result.status();
  auto manager = manager_result.value();
  EXPECT_TRUE(manager->DoesSupport(key_type_1));
  EXPECT_FALSE(manager->DoesSupport(key_type_2));

  manager_result = Registry::get_key_manager<Aead>(key_type_2);
  EXPECT_TRUE(manager_result.ok()) << manager_result.status();
  manager = manager_result.value();
  EXPECT_TRUE(manager->DoesSupport(key_type_2));
  EXPECT_FALSE(manager->DoesSupport(key_type_1));
}

TEST_F(RegistryTest, testRegisterKeyManager) {
  std::string key_type_1 = AesGcmKeyManager().get_key_type();

  std::unique_ptr<TestAeadKeyManager> null_key_manager = nullptr;
  auto status = Registry::RegisterKeyManager(std::move(null_key_manager), true);
  EXPECT_FALSE(status.ok());
  EXPECT_EQ(absl::StatusCode::kInvalidArgument, status.code()) << status;

  // Register a key manager.
  status = Registry::RegisterKeyManager(
      absl::make_unique<TestAeadKeyManager>(key_type_1), true);
  EXPECT_TRUE(status.ok()) << status;

  // Register the same key manager again, it should work (idempotence).
  status = Registry::RegisterKeyManager(
      absl::make_unique<TestAeadKeyManager>(key_type_1), true);
  EXPECT_TRUE(status.ok()) << status;

  // Try overriding a key manager.
  AesGcmKeyManager key_type_manager;
  status = Registry::RegisterKeyManager(
      crypto::tink::internal::MakeKeyManager<Aead>(&key_type_manager), true);
  EXPECT_FALSE(status.ok());
  EXPECT_EQ(absl::StatusCode::kAlreadyExists, status.code()) << status;

  // Check the key manager is still registered.
  auto manager_result = Registry::get_key_manager<Aead>(key_type_1);
  EXPECT_TRUE(manager_result.ok()) << manager_result.status();
  auto manager = manager_result.value();
  EXPECT_TRUE(manager->DoesSupport(key_type_1));
}

// Tests that if we register a key manager once more after a call to
// get_key_manager, the key manager previously obtained with "get_key_manager()"
// remains valid.
TEST_F(RegistryTest, GetKeyManagerRemainsValid) {
  std::string key_type = AesGcmKeyManager().get_key_type();
  EXPECT_THAT(Registry::RegisterKeyManager(
                  absl::make_unique<TestAeadKeyManager>(key_type), true),
              IsOk());

  crypto::tink::util::StatusOr<const KeyManager<Aead>*> key_manager =
      Registry::get_key_manager<Aead>(key_type);
  ASSERT_THAT(key_manager, IsOk());
  EXPECT_THAT(Registry::RegisterKeyManager(
                  absl::make_unique<TestAeadKeyManager>(key_type), true),
              IsOk());
  EXPECT_THAT(key_manager.value()->get_key_type(), Eq(key_type));
}

TEST_F(RegistryTest, testGettingPrimitives) {
  std::string key_type_1 = "google.crypto.tink.AesCtrHmacAeadKey";
  std::string key_type_2 = "google.crypto.tink.AesGcmKey";
  AesCtrHmacAeadKey dummy_key_1;
  AesGcmKey dummy_key_2;

  // Prepare keyset.
  Keyset keyset;

  uint32_t key_id_1 = 1234543;
  AddTinkKey(key_type_1, key_id_1, dummy_key_1, KeyStatusType::ENABLED,
             KeyData::SYMMETRIC, &keyset);

  uint32_t key_id_2 = 726329;
  AddTinkKey(key_type_2, key_id_2, dummy_key_2, KeyStatusType::DISABLED,
             KeyData::SYMMETRIC, &keyset);

  uint32_t key_id_3 = 7213743;
  AddLegacyKey(key_type_2, key_id_3, dummy_key_2, KeyStatusType::ENABLED,
               KeyData::SYMMETRIC, &keyset);

  uint32_t key_id_4 = 6268492;
  AddRawKey(key_type_1, key_id_4, dummy_key_1, KeyStatusType::ENABLED,
            KeyData::SYMMETRIC, &keyset);

  uint32_t key_id_5 = 42;
  AddRawKey(key_type_2, key_id_5, dummy_key_2, KeyStatusType::ENABLED,
            KeyData::SYMMETRIC, &keyset);

  keyset.set_primary_key_id(key_id_3);

  // Register key managers.
  util::Status status;
  status = Registry::RegisterKeyManager(
      absl::make_unique<TestAeadKeyManager>(key_type_1), true);
  EXPECT_TRUE(status.ok()) << status;
  status = Registry::RegisterKeyManager(
      absl::make_unique<TestAeadKeyManager>(key_type_2), true);
  EXPECT_TRUE(status.ok()) << status;

  // Get and use primitives.
  std::string plaintext = "some data";
  std::string aad = "aad";

  // Key #1.
  {
    auto result = Registry::GetPrimitive<Aead>(keyset.key(0).key_data());
    EXPECT_TRUE(result.ok()) << result.status();
    auto aead = std::move(result.value());
    EXPECT_EQ(DummyAead(key_type_1).Encrypt(plaintext, aad).value(),
              aead->Encrypt(plaintext, aad).value());
  }

  // Key #3.
  {
    auto result = Registry::GetPrimitive<Aead>(keyset.key(2).key_data());
    EXPECT_TRUE(result.ok()) << result.status();
    auto aead = std::move(result.value());
    EXPECT_EQ(DummyAead(key_type_2).Encrypt(plaintext, aad).value(),
              aead->Encrypt(plaintext, aad).value());
  }
}

TEST_F(RegistryTest, testNewKeyData) {
  std::string key_type_1 = "google.crypto.tink.AesCtrHmacAeadKey";
  std::string key_type_2 = "google.crypto.tink.AesGcmKey";
  std::string key_type_3 = "yet/another/keytype";

  // Register key managers.
  util::Status status;
  status = Registry::RegisterKeyManager(
      absl::make_unique<TestAeadKeyManager>(key_type_1),
      /*new_key_allowed=*/true);
  EXPECT_TRUE(status.ok()) << status;
  status = Registry::RegisterKeyManager(
      absl::make_unique<TestAeadKeyManager>(key_type_2),
      /*new_key_allowed=*/true);
  EXPECT_TRUE(status.ok()) << status;
  status = Registry::RegisterKeyManager(
      absl::make_unique<TestAeadKeyManager>(key_type_3),
      /*new_key_allowed=*/false);
  EXPECT_TRUE(status.ok()) << status;

  {  // A supported key type.
    KeyTemplate key_template;
    key_template.set_type_url(key_type_1);
    key_template.set_value("test value 42");
    auto new_key_data_result = Registry::NewKeyData(key_template);
    EXPECT_TRUE(new_key_data_result.ok()) << new_key_data_result.status();
    EXPECT_EQ(key_type_1, new_key_data_result.value()->type_url());
    EXPECT_EQ(key_template.value(), new_key_data_result.value()->value());
  }

  {  // Another supported key type.
    KeyTemplate key_template;
    key_template.set_type_url(key_type_2);
    key_template.set_value("yet another test value 42");
    auto new_key_data_result = Registry::NewKeyData(key_template);
    EXPECT_TRUE(new_key_data_result.ok()) << new_key_data_result.status();
    EXPECT_EQ(key_type_2, new_key_data_result.value()->type_url());
    EXPECT_EQ(key_template.value(), new_key_data_result.value()->value());
  }

  {  // A key type that does not allow NewKey-operations.
    KeyTemplate key_template;
    key_template.set_type_url(key_type_3);
    key_template.set_value("some other value 72");
    auto new_key_data_result = Registry::NewKeyData(key_template);
    EXPECT_FALSE(new_key_data_result.ok());
    EXPECT_EQ(absl::StatusCode::kInvalidArgument,
              new_key_data_result.status().code());
    EXPECT_PRED_FORMAT2(testing::IsSubstring, key_type_3,
                        std::string(new_key_data_result.status().message()));
    EXPECT_PRED_FORMAT2(testing::IsSubstring, "does not allow",
                        std::string(new_key_data_result.status().message()));
  }

  {  // A key type that is not supported.
    KeyTemplate key_template;
    std::string bad_type_url = "some key type that is not supported";
    key_template.set_type_url(bad_type_url);
    key_template.set_value("some totally other value 42");
    auto new_key_data_result = Registry::NewKeyData(key_template);
    EXPECT_FALSE(new_key_data_result.ok());
    EXPECT_EQ(absl::StatusCode::kNotFound, new_key_data_result.status().code());
    EXPECT_PRED_FORMAT2(testing::IsSubstring, bad_type_url,
                        std::string(new_key_data_result.status().message()));
  }
}

TEST_F(RegistryTest, testGetPublicKeyData) {
  // Setup the registry.
  Registry::Reset();
  auto private_key_type_manager =
      absl::make_unique<EciesAeadHkdfPrivateKeyManager>();
  auto public_key_type_manager =
      absl::make_unique<EciesAeadHkdfPublicKeyManager>();

  auto status = Registry::RegisterKeyManager(
      internal::MakePrivateKeyManager<HybridDecrypt>(
          private_key_type_manager.get(), public_key_type_manager.get()),
      true);
  ASSERT_TRUE(status.ok()) << status;
  AesGcmKeyManager key_type_manager;
  status = Registry::RegisterKeyManager(
      crypto::tink::internal::MakeKeyManager<Aead>(&key_type_manager), true);
  ASSERT_TRUE(status.ok()) << status;

  // Get a test private key.
  auto ecies_key = test::GetEciesAesGcmHkdfTestKey(
      EllipticCurveType::NIST_P256, EcPointFormat::UNCOMPRESSED,
      HashType::SHA256, /* aes_gcm_key_size= */ 24);

  // Extract public key data and check.
  auto public_key_data_result = Registry::GetPublicKeyData(
      EciesAeadHkdfPrivateKeyManager().get_key_type(),
      ecies_key.SerializeAsString());
  EXPECT_TRUE(public_key_data_result.ok()) << public_key_data_result.status();
  auto public_key_data = std::move(public_key_data_result.value());
  EXPECT_EQ(EciesAeadHkdfPublicKeyManager().get_key_type(),
            public_key_data->type_url());
  EXPECT_EQ(KeyData::ASYMMETRIC_PUBLIC, public_key_data->key_material_type());
  EXPECT_EQ(ecies_key.public_key().SerializeAsString(),
            public_key_data->value());

  // Try with a wrong key type.
  auto wrong_key_type_result = Registry::GetPublicKeyData(
      AesGcmKeyManager().get_key_type(), ecies_key.SerializeAsString());
  EXPECT_FALSE(wrong_key_type_result.ok());
  EXPECT_EQ(absl::StatusCode::kInvalidArgument,
            wrong_key_type_result.status().code());
  EXPECT_PRED_FORMAT2(testing::IsSubstring, "PrivateKeyFactory",
                      std::string(wrong_key_type_result.status().message()));

  // Try with a bad serialized key.
  auto bad_key_result = Registry::GetPublicKeyData(
      EciesAeadHkdfPrivateKeyManager().get_key_type(),
      "some bad serialized key");
  EXPECT_FALSE(bad_key_result.ok());
  EXPECT_EQ(absl::StatusCode::kInvalidArgument, bad_key_result.status().code());
  EXPECT_PRED_FORMAT2(testing::IsSubstring, "Could not parse",
                      std::string(bad_key_result.status().message()));
}

// Tests that if we register the same type of wrapper twice, the second call
// succeeds.
TEST_F(RegistryTest, RegisterWrapperTwice) {
  EXPECT_TRUE(
      Registry::RegisterPrimitiveWrapper(absl::make_unique<AeadWrapper>())
          .ok());
  EXPECT_TRUE(
      Registry::RegisterPrimitiveWrapper(absl::make_unique<AeadWrapper>())
          .ok());
}

// Tests that if we register the same type of wrapper twice, the second call
// succeeds.
TEST_F(RegistryTest, RegisterTransformingWrapperTwice) {
  EXPECT_TRUE(Registry::RegisterPrimitiveWrapper(
                  absl::make_unique<AeadVariantToStringWrapper>())
                  .ok());
  EXPECT_TRUE(Registry::RegisterPrimitiveWrapper(
                  absl::make_unique<AeadVariantToStringWrapper>())
                  .ok());
}

// Test that if we register a second wrapper, wrapping to the same type as a
// previous wrapper it will fail.
TEST_F(RegistryTest, RegisterTransformingWrapperTwiceMixing) {
  EXPECT_TRUE(Registry::RegisterPrimitiveWrapper(
                  absl::make_unique<AeadVariantToStringWrapper>())
                  .ok());
  // We cannot register a different wrapper creating a std::string.
  EXPECT_THAT(Registry::RegisterPrimitiveWrapper(
                  absl::make_unique<TestWrapper<std::string>>()),
              Not(IsOk()));
  // But one creating an Aead.
  EXPECT_THAT(Registry::RegisterPrimitiveWrapper(
                  absl::make_unique<TestWrapper<AeadVariant>>()),
              IsOk());
}

// Test that if we register a second wrapper, wrapping to the same type as a
// previous wrapper it will fail (order swapped).
TEST_F(RegistryTest, RegisterTransformingWrapperTwiceMixingBackwards) {
  EXPECT_THAT(Registry::RegisterPrimitiveWrapper(
                  absl::make_unique<TestWrapper<std::string>>()),
              IsOk());
  // We cannot register another wrapper producing strings.
  EXPECT_THAT(Registry::RegisterPrimitiveWrapper(
                  absl::make_unique<AeadVariantToStringWrapper>()),
              Not(IsOk()));
}

// Tests that if we register different wrappers for the same primitive twice,
// the second call fails.
TEST_F(RegistryTest, RegisterDifferentWrappers) {
  EXPECT_TRUE(
      Registry::RegisterPrimitiveWrapper(absl::make_unique<AeadWrapper>())
          .ok());
  util::Status result = Registry::RegisterPrimitiveWrapper(
      absl::make_unique<TestWrapper<Aead>>());
  EXPECT_FALSE(result.ok());
  EXPECT_EQ(absl::StatusCode::kAlreadyExists, result.code());
}

// Tests that if we register different wrappers for different primitives, this
// returns ok.
TEST_F(RegistryTest, RegisterDifferentWrappersDifferentPrimitives) {
  EXPECT_TRUE(
      Registry::RegisterPrimitiveWrapper(absl::make_unique<TestWrapper<Aead>>())
          .ok());
  EXPECT_TRUE(
      Registry::RegisterPrimitiveWrapper(absl::make_unique<TestWrapper<Mac>>())
          .ok());
}

// Tests that if we do not register a wrapper, then calls to Wrap
// fail with "No wrapper registered" -- even if there is a wrapper for a
// different primitive registered.
TEST_F(RegistryTest, NoWrapperRegistered) {
  EXPECT_TRUE(
      Registry::RegisterPrimitiveWrapper(absl::make_unique<TestWrapper<Mac>>())
          .ok());

  crypto::tink::util::StatusOr<std::unique_ptr<Aead>> result =
      Registry::Wrap<Aead>(absl::make_unique<PrimitiveSet<Aead>>());
  EXPECT_FALSE(result.ok());
  EXPECT_EQ(absl::StatusCode::kNotFound, result.status().code());
  EXPECT_PRED_FORMAT2(testing::IsSubstring, "No wrapper registered",
                      std::string(result.status().message()));
}

// Tests that if the wrapper fails, the error of the wrapped is forwarded
// in GetWrappedPrimitive.
TEST_F(RegistryTest, WrapperFails) {
  EXPECT_TRUE(
      Registry::RegisterPrimitiveWrapper(absl::make_unique<TestWrapper<Aead>>())
          .ok());

  crypto::tink::util::StatusOr<std::unique_ptr<Aead>> result =
      Registry::Wrap<Aead>(absl::make_unique<PrimitiveSet<Aead>>());
  EXPECT_FALSE(result.ok());
  EXPECT_PRED_FORMAT2(testing::IsSubstring, "This is a test wrapper",
                      std::string(result.status().message()));
}

// Tests that wrapping works as expected in the usual case.
TEST_F(RegistryTest, UsualWrappingTest) {
  KeysetInfo keyset_info;

  keyset_info.add_key_info();
  keyset_info.mutable_key_info(0)->set_output_prefix_type(
      OutputPrefixType::TINK);
  keyset_info.mutable_key_info(0)->set_key_id(1234543);
  keyset_info.mutable_key_info(0)->set_status(KeyStatusType::ENABLED);
  keyset_info.add_key_info();
  keyset_info.mutable_key_info(1)->set_output_prefix_type(
      OutputPrefixType::LEGACY);
  keyset_info.mutable_key_info(1)->set_key_id(726329);
  keyset_info.mutable_key_info(1)->set_status(KeyStatusType::ENABLED);
  keyset_info.add_key_info();
  keyset_info.mutable_key_info(2)->set_output_prefix_type(
      OutputPrefixType::TINK);
  keyset_info.mutable_key_info(2)->set_key_id(7213743);
  keyset_info.mutable_key_info(2)->set_status(KeyStatusType::ENABLED);

  auto primitive_set = absl::make_unique<PrimitiveSet<Aead>>();
  ASSERT_TRUE(primitive_set
                  ->AddPrimitive(absl::make_unique<DummyAead>("aead0"),
                                 keyset_info.key_info(0))
                  .ok());
  ASSERT_TRUE(primitive_set
                  ->AddPrimitive(absl::make_unique<DummyAead>("aead1"),
                                 keyset_info.key_info(1))
                  .ok());
  auto entry_result = primitive_set->AddPrimitive(
      absl::make_unique<DummyAead>("primary_aead"), keyset_info.key_info(2));
  ASSERT_THAT(primitive_set->set_primary(entry_result.value()), IsOk());

  EXPECT_TRUE(
      Registry::RegisterPrimitiveWrapper(absl::make_unique<AeadWrapper>())
          .ok());

  auto aead_result = Registry::Wrap<Aead>(std::move(primitive_set));
  EXPECT_TRUE(aead_result.ok()) << aead_result.status();
  std::unique_ptr<Aead> aead = std::move(aead_result.value());
  std::string plaintext = "some_plaintext";
  std::string aad = "some_aad";

  auto encrypt_result = aead->Encrypt(plaintext, aad);
  EXPECT_TRUE(encrypt_result.ok()) << encrypt_result.status();
  std::string ciphertext = encrypt_result.value();
  EXPECT_PRED_FORMAT2(testing::IsSubstring, "primary_aead", ciphertext);

  auto decrypt_result = aead->Decrypt(ciphertext, aad);
  EXPECT_TRUE(decrypt_result.ok()) << decrypt_result.status();
  EXPECT_EQ(plaintext, decrypt_result.value());

  decrypt_result = aead->Decrypt("some bad ciphertext", aad);
  EXPECT_FALSE(decrypt_result.ok());
  EXPECT_EQ(absl::StatusCode::kInvalidArgument, decrypt_result.status().code());
  EXPECT_PRED_FORMAT2(testing::IsSubstring, "decryption failed",
                      std::string(decrypt_result.status().message()));
}

std::string AddAesGcmKey(uint32_t key_id, OutputPrefixType output_prefix_type,
                         KeyStatusType key_status_type,
                         Keyset& modified_keyset) {
  AesGcmKey key;
  key.set_version(0);
  key.set_key_value(subtle::Random::GetRandomBytes(16));
  KeyData key_data;
  key_data.set_value(key.SerializeAsString());
  key_data.set_type_url("type.googleapis.com/google.crypto.tink.AesGcmKey");
  test::AddKeyData(key_data, key_id, output_prefix_type, key_status_type,
                   &modified_keyset);
  return key.key_value();
}

// Tests that wrapping of a keyset works in the usual case.
TEST_F(RegistryTest, KeysetWrappingTest) {
  if (!IsFipsEnabledInSsl()) {
    GTEST_SKIP() << "Not supported when BoringSSL is not built in FIPS-mode.";
  }

  Keyset keyset;
  std::string raw_key =
      AddAesGcmKey(13, OutputPrefixType::TINK, KeyStatusType::ENABLED, keyset);
  keyset.set_primary_key_id(13);

  auto fips_key_manager = absl::make_unique<ExampleKeyTypeManager>();

  ON_CALL(*fips_key_manager, FipsStatus())
      .WillByDefault(testing::Return(FipsCompatibility::kRequiresBoringCrypto));

  ASSERT_THAT(
      Registry::RegisterKeyTypeManager(std::move(fips_key_manager), true),
      IsOk());
  ASSERT_THAT(Registry::RegisterPrimitiveWrapper(
                  absl::make_unique<AeadVariantWrapper>()),
              IsOk());

  crypto::tink::util::StatusOr<std::unique_ptr<AeadVariant>> aead_variant =
      RegistryImpl::GlobalInstance().WrapKeyset<AeadVariant>(
          keyset, /*annotations=*/{});
  EXPECT_THAT(aead_variant, IsOk());
  EXPECT_THAT(aead_variant.value()->get(), Eq(raw_key));
}

// Tests that wrapping of a keyset works.
TEST_F(RegistryTest, TransformingKeysetWrappingTest) {
  if (kUseOnlyFips) {
    GTEST_SKIP() << "Not supported in FIPS-only mode";
  }

  Keyset keyset;
  std::string raw_key =
      AddAesGcmKey(13, OutputPrefixType::TINK, KeyStatusType::ENABLED, keyset);
  keyset.set_primary_key_id(13);

  ASSERT_THAT(Registry::RegisterKeyTypeManager(
                  absl::make_unique<ExampleKeyTypeManager>(), true),
              IsOk());
  ASSERT_THAT(Registry::RegisterPrimitiveWrapper(
                  absl::make_unique<AeadVariantToStringWrapper>()),
              IsOk());

  crypto::tink::util::StatusOr<std::unique_ptr<std::string>> string_primitive =
      RegistryImpl::GlobalInstance().WrapKeyset<std::string>(
          keyset, /*annotations=*/{});
  EXPECT_THAT(string_primitive, IsOk());
  EXPECT_THAT(*string_primitive.value(), Eq(raw_key));
}

// Tests that when we ask the registry to wrap a PrimitiveSet<Aead> into an
// Aead, but the wrapper is in fact from something else into Aead, we give a
// correct error message.
TEST_F(RegistryTest, TransformingPrimitiveWrapperCustomKeyManager) {
  if (kUseOnlyFips) {
    GTEST_SKIP() << "Not supported in FIPS-only mode";
  }

  ASSERT_THAT(Registry::RegisterKeyTypeManager(
                  absl::make_unique<ExampleKeyTypeManager>(), true),
              IsOk());
  // Register a transforming wrapper taking strings and making Aeads.
  ASSERT_THAT(Registry::RegisterPrimitiveWrapper(
                  absl::make_unique<TestWrapper<std::string, Aead>>()),
              IsOk());

  KeysetInfo keyset_info;
  keyset_info.add_key_info();
  keyset_info.mutable_key_info(0)->set_output_prefix_type(
      OutputPrefixType::TINK);
  keyset_info.mutable_key_info(0)->set_key_id(1234543);
  keyset_info.mutable_key_info(0)->set_status(KeyStatusType::ENABLED);
  keyset_info.set_primary_key_id(1234543);

  auto primitive_set = absl::make_unique<PrimitiveSet<Aead>>();
  ASSERT_TRUE(primitive_set
                  ->AddPrimitive(absl::make_unique<DummyAead>("aead0"),
                                 keyset_info.key_info(0))
                  .ok());

  EXPECT_THAT(Registry::Wrap<Aead>(std::move(primitive_set)).status(),
              StatusIs(absl::StatusCode::kFailedPrecondition,
                       HasSubstr("custom key manager")));
}

// Tests that the error message in GetKeyManager contains the type_id.name() of
// the primitive for which the key manager was actually registered.
TEST_F(RegistryTest, GetKeyManagerErrorMessage) {
  AesGcmKeyManager key_type_manager;
  EXPECT_TRUE(
      Registry::RegisterKeyManager(
          crypto::tink::internal::MakeKeyManager<Aead>(&key_type_manager), true)
          .ok());
  auto result =
      Registry::get_key_manager<int>(AesGcmKeyManager().get_key_type());
  EXPECT_FALSE(result.ok());
  EXPECT_THAT(std::string(result.status().message()),
              HasSubstr(AesGcmKeyManager().get_key_type()));
  // Note: The C++ standard does not guarantee the next line.  If some toolchain
  // update fails it, one can delete it.
  EXPECT_THAT(std::string(result.status().message()),
              HasSubstr(typeid(Aead).name()));
}

TEST_F(RegistryTest, RegisterKeyTypeManager) {
  if (kUseOnlyFips) {
    GTEST_SKIP() << "Not supported in FIPS-only mode";
  }

  EXPECT_THAT(Registry::RegisterKeyTypeManager(
                  absl::make_unique<ExampleKeyTypeManager>(), true),
              IsOk());
}

TEST_F(RegistryTest, RegisterFipsKeyTypeManager) {
  if (!kUseOnlyFips || !IsFipsEnabledInSsl()) {
    GTEST_SKIP() << "Only supported in FIPS-mode with BoringCrypto available.";
  }

  auto fips_key_manager = absl::make_unique<ExampleKeyTypeManager>();

  ON_CALL(*fips_key_manager, FipsStatus())
      .WillByDefault(testing::Return(FipsCompatibility::kRequiresBoringCrypto));

  EXPECT_THAT(
      Registry::RegisterKeyTypeManager(std::move(fips_key_manager), true),
      IsOk());
}

TEST_F(RegistryTest, RegisterFipsKeyTypeManagerNoBoringCrypto) {
  if (!kUseOnlyFips || IsFipsEnabledInSsl()) {
    GTEST_SKIP()
        << "Only supported in FIPS-mode with BoringCrypto not available.";
  }

  auto fips_key_manager = absl::make_unique<ExampleKeyTypeManager>();

  ON_CALL(*fips_key_manager, FipsStatus())
      .WillByDefault(testing::Return(FipsCompatibility::kNotFips));

  EXPECT_THAT(
      Registry::RegisterKeyTypeManager(std::move(fips_key_manager), true),
      StatusIs(absl::StatusCode::kInternal));
}

TEST_F(RegistryTest, KeyTypeManagerGetFirstKeyManager) {
  if (kUseOnlyFips) {
    GTEST_SKIP() << "Not supported in FIPS-only mode";
  }

  EXPECT_THAT(Registry::RegisterKeyTypeManager(
                  absl::make_unique<ExampleKeyTypeManager>(), true),
              IsOk());
  AesGcmKeyFormat format;
  format.set_key_size(16);
  AesGcmKey key = ExampleKeyTypeManager().CreateKey(format).value();
  auto aead = Registry::get_key_manager<Aead>(
                  "type.googleapis.com/google.crypto.tink.AesGcmKey")
                  .value()
                  ->GetPrimitive(key)
                  .value();
  std::string encryption = aead->Encrypt("TESTMESSAGE", "").value();
  std::string decryption = aead->Decrypt(encryption, "").value();
  EXPECT_THAT(decryption, Eq("TESTMESSAGE"));
}

TEST_F(RegistryTest, KeyTypeManagerGetSecondKeyManager) {
  if (kUseOnlyFips) {
    GTEST_SKIP() << "Not supported in FIPS-only mode";
  }

  EXPECT_THAT(Registry::RegisterKeyTypeManager(
                  absl::make_unique<ExampleKeyTypeManager>(), true),
              IsOk());
  AesGcmKeyFormat format;
  format.set_key_size(16);
  AesGcmKey key = ExampleKeyTypeManager().CreateKey(format).value();
  auto aead_variant = Registry::get_key_manager<AeadVariant>(
                          "type.googleapis.com/google.crypto.tink.AesGcmKey")
                          .value()
                          ->GetPrimitive(key)
                          .value();
  EXPECT_THAT(aead_variant->get(), Eq(key.key_value()));
}

TEST_F(RegistryTest, KeyTypeManagerNotSupportedPrimitive) {
  if (kUseOnlyFips) {
    GTEST_SKIP() << "Not supported in FIPS-only mode";
  }

  EXPECT_THAT(Registry::RegisterKeyTypeManager(
                  absl::make_unique<ExampleKeyTypeManager>(), true),
              IsOk());
  EXPECT_THAT(Registry::get_key_manager<Mac>(
                  "type.googleapis.com/google.crypto.tink.AesGcmKey")
                  .status(),
              StatusIs(absl::StatusCode::kInvalidArgument,
                       HasSubstr("not among supported primitives")));
}

// Tests that if we register a key manager once more after a call to
// get_key_manager, the key manager previously obtained with "get_key_manager()"
// remains valid.
TEST_F(RegistryTest, GetKeyManagerRemainsValidForKeyTypeManagers) {
  if (kUseOnlyFips) {
    GTEST_SKIP() << "Not supported in FIPS-only mode";
  }

  EXPECT_THAT(Registry::RegisterKeyTypeManager(
                  absl::make_unique<ExampleKeyTypeManager>(), true),
              IsOk());

  crypto::tink::util::StatusOr<const KeyManager<Aead>*> key_manager =
      Registry::get_key_manager<Aead>(ExampleKeyTypeManager().get_key_type());
  ASSERT_THAT(key_manager, IsOk());
  EXPECT_THAT(Registry::RegisterKeyTypeManager(
                  absl::make_unique<ExampleKeyTypeManager>(), true),
              IsOk());
  EXPECT_THAT(key_manager.value()->get_key_type(),
              Eq(ExampleKeyTypeManager().get_key_type()));
}

TEST_F(RegistryTest, KeyTypeManagerNewKey) {
  if (kUseOnlyFips) {
    GTEST_SKIP() << "Not supported in FIPS-only mode";
  }

  EXPECT_THAT(Registry::RegisterKeyTypeManager(
                  absl::make_unique<ExampleKeyTypeManager>(), true),
              IsOk());

  AesGcmKeyFormat format;
  format.set_key_size(32);
  KeyTemplate key_template;
  key_template.set_type_url("type.googleapis.com/google.crypto.tink.AesGcmKey");
  key_template.set_value(format.SerializeAsString());

  KeyData key_data = *Registry::NewKeyData(key_template).value();
  EXPECT_THAT(key_data.type_url(),
              Eq("type.googleapis.com/google.crypto.tink.AesGcmKey"));
  EXPECT_THAT(key_data.key_material_type(),
              Eq(google::crypto::tink::KeyData::SYMMETRIC));
  AesGcmKey key;
  key.ParseFromString(key_data.value());
  EXPECT_THAT(key.key_value(), SizeIs(32));
}

TEST_F(RegistryTest, KeyTypeManagerNewKeyInvalidSize) {
  if (kUseOnlyFips) {
    GTEST_SKIP() << "Not supported in FIPS-only mode";
  }

  EXPECT_THAT(Registry::RegisterKeyTypeManager(
                  absl::make_unique<ExampleKeyTypeManager>(), true),
              IsOk());

  AesGcmKeyFormat format;
  format.set_key_size(33);
  KeyTemplate key_template;
  key_template.set_type_url("type.googleapis.com/google.crypto.tink.AesGcmKey");
  key_template.set_value(format.SerializeAsString());

  EXPECT_THAT(Registry::NewKeyData(key_template), IsOk());
}

TEST_F(RegistryTest, KeyTypeManagerDeriveKey) {
  if (kUseOnlyFips) {
    GTEST_SKIP() << "Not supported in FIPS-only mode";
  }

  EXPECT_THAT(Registry::RegisterKeyTypeManager(
                  absl::make_unique<ExampleKeyTypeManager>(), true),
              IsOk());

  AesGcmKeyFormat format;
  format.set_key_size(32);
  KeyTemplate key_template;
  key_template.set_type_url("type.googleapis.com/google.crypto.tink.AesGcmKey");
  key_template.set_value(format.SerializeAsString());

  crypto::tink::util::IstreamInputStream input_stream{
      absl::make_unique<std::stringstream>(
          "0123456789012345678901234567890123456789")};

  auto key_data_or =
      RegistryImpl::GlobalInstance().DeriveKey(key_template, &input_stream);
  ASSERT_THAT(key_data_or, IsOk());
  EXPECT_THAT(key_data_or.value().type_url(), Eq(key_template.type_url()));
  AesGcmKey key;
  EXPECT_TRUE(key.ParseFromString(key_data_or.value().value()));
  // 32 byte prefix of above string.
  EXPECT_THAT(key.key_value(), Eq("01234567890123456789012345678901"));
}

// The same, but we register the key manager twice. This should catch some of
// the possible lifetime issues.
TEST_F(RegistryTest, KeyTypeManagerDeriveKeyRegisterTwice) {
  if (kUseOnlyFips) {
    GTEST_SKIP() << "Not supported in FIPS-only mode";
  }

  EXPECT_THAT(Registry::RegisterKeyTypeManager(
                  absl::make_unique<ExampleKeyTypeManager>(), true),
              IsOk());
  EXPECT_THAT(Registry::RegisterKeyTypeManager(
                  absl::make_unique<ExampleKeyTypeManager>(), true),
              IsOk());

  AesGcmKeyFormat format;
  format.set_key_size(32);
  KeyTemplate key_template;
  key_template.set_type_url("type.googleapis.com/google.crypto.tink.AesGcmKey");
  key_template.set_value(format.SerializeAsString());

  crypto::tink::util::IstreamInputStream input_stream{
      absl::make_unique<std::stringstream>(
          "0123456789012345678901234567890123456789")};

  auto key_data_or =
      RegistryImpl::GlobalInstance().DeriveKey(key_template, &input_stream);
  ASSERT_THAT(key_data_or, IsOk());
  EXPECT_THAT(key_data_or.value().type_url(), Eq(key_template.type_url()));
  AesGcmKey key;
  EXPECT_TRUE(key.ParseFromString(key_data_or.value().value()));
  // 32 byte prefix of above string.
  EXPECT_THAT(key.key_value(), Eq("01234567890123456789012345678901"));
}

// Tests that if we register a KeyManager instead of a KeyTypeManager, DeriveKey
// fails properly.
TEST_F(RegistryTest, KeyManagerDeriveKeyFail) {
  std::string key_type = "type.googleapis.com/google.crypto.tink.AesGcmKey";
  ASSERT_THAT(Registry::RegisterKeyManager(
                  absl::make_unique<TestAeadKeyManager>(key_type),
                  /* new_key_allowed= */ true),
              IsOk());

  KeyTemplate key_template;
  key_template.set_type_url("type.googleapis.com/google.crypto.tink.AesGcmKey");

  EXPECT_THAT(
      RegistryImpl::GlobalInstance().DeriveKey(key_template, nullptr).status(),
      StatusIs(absl::StatusCode::kInvalidArgument, HasSubstr("cannot derive")));
}

TEST_F(RegistryTest, KeyManagerDeriveNotRegistered) {
  KeyTemplate key_template;
  key_template.set_type_url("some_inexistent_keytype");

  EXPECT_THAT(
      RegistryImpl::GlobalInstance().DeriveKey(key_template, nullptr).status(),
      StatusIs(absl::StatusCode::kNotFound, HasSubstr("No manager")));
}

TEST_F(RegistryTest, RegisterKeyTypeManagerTwiceMoreRestrictive) {
  if (kUseOnlyFips) {
    GTEST_SKIP() << "Not supported in FIPS-only mode";
  }

  EXPECT_THAT(Registry::RegisterKeyTypeManager(
                  absl::make_unique<ExampleKeyTypeManager>(), true),
              IsOk());
  EXPECT_THAT(Registry::RegisterKeyTypeManager(
                  absl::make_unique<ExampleKeyTypeManager>(), false),
              IsOk());
}

TEST_F(RegistryTest, RegisterKeyTypeManagerTwice) {
  if (kUseOnlyFips) {
    GTEST_SKIP() << "Not supported in FIPS-only mode";
  }

  EXPECT_THAT(Registry::RegisterKeyTypeManager(
                  absl::make_unique<ExampleKeyTypeManager>(), true),
              IsOk());
  EXPECT_THAT(Registry::RegisterKeyTypeManager(
                  absl::make_unique<ExampleKeyTypeManager>(), true),
              IsOk());
  EXPECT_THAT(Registry::RegisterKeyTypeManager(
                  absl::make_unique<ExampleKeyTypeManager>(), false),
              IsOk());
  EXPECT_THAT(Registry::RegisterKeyTypeManager(
                  absl::make_unique<ExampleKeyTypeManager>(), false),
              IsOk());
}

TEST_F(RegistryTest, RegisterKeyTypeManagerLessRestrictive) {
  if (kUseOnlyFips) {
    GTEST_SKIP() << "Not supported in FIPS-only mode";
  }

  EXPECT_THAT(Registry::RegisterKeyTypeManager(
                  absl::make_unique<ExampleKeyTypeManager>(), false),
              IsOk());
  EXPECT_THAT(Registry::RegisterKeyTypeManager(
                  absl::make_unique<ExampleKeyTypeManager>(), true),
              StatusIs(absl::StatusCode::kAlreadyExists));
}

TEST_F(RegistryTest, RegisterKeyTypeManagerBeforeKeyManager) {
  if (kUseOnlyFips) {
    GTEST_SKIP() << "Not supported in FIPS-only mode";
  }

  EXPECT_THAT(Registry::RegisterKeyTypeManager(
                  absl::make_unique<ExampleKeyTypeManager>(), true),
              IsOk());
  EXPECT_THAT(Registry::RegisterKeyManager(
                  absl::make_unique<TestAeadKeyManager>(
                      "type.googleapis.com/google.crypto.tink.AesGcmKey"),
                  true),
              StatusIs(absl::StatusCode::kAlreadyExists));
}

TEST_F(RegistryTest, RegisterKeyTypeManagerAfterKeyManager) {
  if (kUseOnlyFips) {
    GTEST_SKIP() << "Not supported in FIPS-only mode";
  }

  EXPECT_THAT(Registry::RegisterKeyManager(
                  absl::make_unique<TestAeadKeyManager>(
                      "type.googleapis.com/google.crypto.tink.AesGcmKey"),
                  true),
              IsOk());
  EXPECT_THAT(Registry::RegisterKeyTypeManager(
                  absl::make_unique<ExampleKeyTypeManager>(), true),
              StatusIs(absl::StatusCode::kAlreadyExists));
}

}  // namespace

// NOTE: These are outside of the anonymous namespace to allow compiling with
// MSVC.
class PrivatePrimitiveA {};
class PrivatePrimitiveB {};

namespace {

class TestPrivateKeyTypeManager
    : public PrivateKeyTypeManager<EcdsaPrivateKey, EcdsaKeyFormat,
                                   EcdsaPublicKey,
                                   List<PrivatePrimitiveA, PrivatePrimitiveB>> {
 public:
  class PrivatePrimitiveAFactory : public PrimitiveFactory<PrivatePrimitiveA> {
   public:
    crypto::tink::util::StatusOr<std::unique_ptr<PrivatePrimitiveA>> Create(
        const EcdsaPrivateKey& key) const override {
      return util::Status(absl::StatusCode::kUnimplemented, "Not implemented");
    }
  };
  class PrivatePrimitiveBFactory : public PrimitiveFactory<PrivatePrimitiveB> {
   public:
    crypto::tink::util::StatusOr<std::unique_ptr<PrivatePrimitiveB>> Create(
        const EcdsaPrivateKey& key) const override {
      return util::Status(absl::StatusCode::kUnimplemented, "Not implemented");
    }
  };

  TestPrivateKeyTypeManager()
      : PrivateKeyTypeManager(absl::make_unique<PrivatePrimitiveAFactory>(),
                              absl::make_unique<PrivatePrimitiveBFactory>()) {}

  google::crypto::tink::KeyData::KeyMaterialType key_material_type()
      const override {
    return google::crypto::tink::KeyData::ASYMMETRIC_PRIVATE;
  }

  uint32_t get_version() const override { return 0; }
  crypto::tink::util::Status ValidateKey(
      const EcdsaPrivateKey& key) const override {
    return crypto::tink::util::OkStatus();
  }
  crypto::tink::util::Status ValidateKeyFormat(
      const EcdsaKeyFormat& key) const override {
    return crypto::tink::util::OkStatus();
  }

  const std::string& get_key_type() const override { return kKeyType; }

  crypto::tink::util::StatusOr<EcdsaPrivateKey> CreateKey(
      const EcdsaKeyFormat& key_format) const override {
    EcdsaPublicKey public_key;
    *public_key.mutable_params() = key_format.params();
    EcdsaPrivateKey result;
    *result.mutable_public_key() = public_key;
    return result;
  }

  crypto::tink::util::StatusOr<EcdsaPublicKey> GetPublicKey(
      const EcdsaPrivateKey& private_key) const override {
    return private_key.public_key();
  }

  MOCK_METHOD(FipsCompatibility, FipsStatus, (), (const, override));

 private:
  const std::string kKeyType =
      "type.googleapis.com/google.crypto.tink.EcdsaPrivateKey";
};

}  // namespace

// NOTE: These are outside of the anonymous namespace to allow compiling with
// MSVC.
class PublicPrimitiveA {};
class PublicPrimitiveB {};

namespace {

class TestPublicKeyTypeManager
    : public KeyTypeManager<EcdsaPublicKey, void,
                            List<PublicPrimitiveA, PublicPrimitiveB>> {
 public:
  class PublicPrimitiveAFactory : public PrimitiveFactory<PublicPrimitiveA> {
   public:
    crypto::tink::util::StatusOr<std::unique_ptr<PublicPrimitiveA>> Create(
        const EcdsaPublicKey& key) const override {
      return util::Status(absl::StatusCode::kUnimplemented, "Not implemented");
    }
  };
  class PublicPrimitiveBFactory : public PrimitiveFactory<PublicPrimitiveB> {
   public:
    crypto::tink::util::StatusOr<std::unique_ptr<PublicPrimitiveB>> Create(
        const EcdsaPublicKey& key) const override {
      return util::Status(absl::StatusCode::kUnimplemented, "Not implemented");
    }
  };

  TestPublicKeyTypeManager()
      : KeyTypeManager(absl::make_unique<PublicPrimitiveAFactory>(),
                       absl::make_unique<PublicPrimitiveBFactory>()) {}

  google::crypto::tink::KeyData::KeyMaterialType key_material_type()
      const override {
    return google::crypto::tink::KeyData::ASYMMETRIC_PRIVATE;
  }

  uint32_t get_version() const override { return 0; }
  crypto::tink::util::Status ValidateKey(
      const EcdsaPublicKey& key) const override {
    return crypto::tink::util::OkStatus();
  }

  const std::string& get_key_type() const override { return kKeyType; }

  MOCK_METHOD(FipsCompatibility, FipsStatus, (), (const, override));

 private:
  const std::string kKeyType =
      "type.googleapis.com/google.crypto.tink.EcdsaPublicKey";
};

std::unique_ptr<TestPrivateKeyTypeManager>
CreateTestPrivateKeyManagerFipsCompatible() {
  auto private_key_manager = absl::make_unique<TestPrivateKeyTypeManager>();
  ON_CALL(*private_key_manager, FipsStatus())
      .WillByDefault(testing::Return(FipsCompatibility::kRequiresBoringCrypto));
  return private_key_manager;
}

std::unique_ptr<TestPublicKeyTypeManager>
CreateTestPublicKeyManagerFipsCompatible() {
  auto public_key_manager = absl::make_unique<TestPublicKeyTypeManager>();
  ON_CALL(*public_key_manager, FipsStatus())
      .WillByDefault(testing::Return(FipsCompatibility::kRequiresBoringCrypto));
  return public_key_manager;
}

TEST_F(RegistryTest, RegisterAsymmetricKeyManagers) {
  if (kUseOnlyFips && !IsFipsEnabledInSsl()) {
    GTEST_SKIP() << "Not supported if FIPS-mode is used and BoringCrypto is "
                    "not available";
  }

  crypto::tink::util::Status status = Registry::RegisterAsymmetricKeyManagers(
      CreateTestPrivateKeyManagerFipsCompatible(),
      CreateTestPublicKeyManagerFipsCompatible(), true);
  ASSERT_TRUE(status.ok()) << status;
}

TEST_F(RegistryTest, AsymmetricMoreRestrictiveNewKey) {
  if (kUseOnlyFips && !IsFipsEnabledInSsl()) {
    GTEST_SKIP() << "Not supported if FIPS-mode is used and BoringCrypto is "
                    "not available";
  }

  ASSERT_TRUE(Registry::RegisterAsymmetricKeyManagers(
                  CreateTestPrivateKeyManagerFipsCompatible(),
                  CreateTestPublicKeyManagerFipsCompatible(), true)
                  .ok());

  crypto::tink::util::Status status = Registry::RegisterAsymmetricKeyManagers(
      CreateTestPrivateKeyManagerFipsCompatible(),
      CreateTestPublicKeyManagerFipsCompatible(), false);
  ASSERT_TRUE(status.ok()) << status;
}

TEST_F(RegistryTest, AsymmetricSameNewKey) {
  if (kUseOnlyFips && !IsFipsEnabledInSsl()) {
    GTEST_SKIP() << "Not supported if FIPS-mode is used and BoringCrypto is "
                    "not available";
  }

  ASSERT_TRUE(Registry::RegisterAsymmetricKeyManagers(
                  CreateTestPrivateKeyManagerFipsCompatible(),
                  CreateTestPublicKeyManagerFipsCompatible(), true)
                  .ok());
  crypto::tink::util::Status status = Registry::RegisterAsymmetricKeyManagers(
      CreateTestPrivateKeyManagerFipsCompatible(),
      CreateTestPublicKeyManagerFipsCompatible(), true);
  ASSERT_TRUE(status.ok()) << status;

  ASSERT_TRUE(Registry::RegisterAsymmetricKeyManagers(
                  CreateTestPrivateKeyManagerFipsCompatible(),
                  CreateTestPublicKeyManagerFipsCompatible(), false)
                  .ok());
  status = Registry::RegisterAsymmetricKeyManagers(
      CreateTestPrivateKeyManagerFipsCompatible(),
      CreateTestPublicKeyManagerFipsCompatible(), false);
  ASSERT_TRUE(status.ok()) << status;
}

TEST_F(RegistryTest, AsymmetricLessRestrictiveGivesError) {
  if (kUseOnlyFips && !IsFipsEnabledInSsl()) {
    GTEST_SKIP() << "Not supported if FIPS-mode is used and BoringCrypto is "
                    "not available";
  }

  crypto::tink::util::Status status = Registry::RegisterAsymmetricKeyManagers(
      CreateTestPrivateKeyManagerFipsCompatible(),
      CreateTestPublicKeyManagerFipsCompatible(), false);
  ASSERT_TRUE(status.ok()) << status;
  EXPECT_THAT(Registry::RegisterAsymmetricKeyManagers(
                  CreateTestPrivateKeyManagerFipsCompatible(),
                  CreateTestPublicKeyManagerFipsCompatible(), true),
              StatusIs(absl::StatusCode::kAlreadyExists,
                       HasSubstr("forbidden new key operation")));
}

// Tests that if we register asymmetric key managers once more after a call to
// get_key_manager, the key manager previously obtained with "get_key_manager()"
// remains valid.

TEST_F(RegistryTest, RegisterAsymmetricKeyManagersGetKeyManagerStaysValid) {
  if (kUseOnlyFips && !IsFipsEnabledInSsl()) {
    GTEST_SKIP() << "Not supported if FIPS-mode is used and BoringCrypto is "
                    "not available";
  }

  ASSERT_THAT(Registry::RegisterAsymmetricKeyManagers(
                  CreateTestPrivateKeyManagerFipsCompatible(),
                  CreateTestPublicKeyManagerFipsCompatible(), true),
              IsOk());

  crypto::tink::util::StatusOr<const KeyManager<PrivatePrimitiveA>*>
      private_key_manager = Registry::get_key_manager<PrivatePrimitiveA>(
          TestPrivateKeyTypeManager().get_key_type());
  crypto::tink::util::StatusOr<const KeyManager<PublicPrimitiveA>*>
      public_key_manager = Registry::get_key_manager<PublicPrimitiveA>(
          TestPublicKeyTypeManager().get_key_type());

  ASSERT_THAT(Registry::RegisterAsymmetricKeyManagers(
                  CreateTestPrivateKeyManagerFipsCompatible(),
                  CreateTestPublicKeyManagerFipsCompatible(), true),
              IsOk());

  EXPECT_THAT(private_key_manager.value()->get_key_type(),
              Eq(TestPrivateKeyTypeManager().get_key_type()));
  EXPECT_THAT(public_key_manager.value()->get_key_type(),
              Eq(TestPublicKeyTypeManager().get_key_type()));
}

TEST_F(RegistryTest, AsymmetricPrivateRegisterAlone) {
  if (kUseOnlyFips && !IsFipsEnabledInSsl()) {
    GTEST_SKIP() << "Not supported if FIPS-mode is used and BoringCrypto is "
                    "not available";
  }

  ASSERT_TRUE(Registry::RegisterKeyTypeManager(
                  CreateTestPrivateKeyManagerFipsCompatible(), true)
                  .ok());
  ASSERT_TRUE(Registry::RegisterKeyTypeManager(
                  CreateTestPublicKeyManagerFipsCompatible(), true)
                  .ok());
  // Registering the same as asymmetric key managers must fail, because doing so
  // would mean we invalidate key managers previously obtained with
  // get_key_manager().
  ASSERT_FALSE(Registry::RegisterAsymmetricKeyManagers(
                   CreateTestPrivateKeyManagerFipsCompatible(),
                   CreateTestPublicKeyManagerFipsCompatible(), true)
                   .ok());
  ASSERT_TRUE(Registry::RegisterKeyTypeManager(
                  CreateTestPrivateKeyManagerFipsCompatible(), true)
                  .ok());
  ASSERT_TRUE(Registry::RegisterKeyTypeManager(
                  CreateTestPublicKeyManagerFipsCompatible(), true)
                  .ok());
}

TEST_F(RegistryTest, AsymmetricGetPrimitiveA) {
  if (kUseOnlyFips && !IsFipsEnabledInSsl()) {
    GTEST_SKIP() << "Not supported if FIPS-mode is used and BoringCrypto is "
                    "not available";
  }

  ASSERT_TRUE(Registry::RegisterAsymmetricKeyManagers(
                  CreateTestPrivateKeyManagerFipsCompatible(),
                  CreateTestPublicKeyManagerFipsCompatible(), true)
                  .ok());
  crypto::tink::util::StatusOr<const KeyManager<PrivatePrimitiveA>*> km =
      Registry::get_key_manager<PrivatePrimitiveA>(
          TestPrivateKeyTypeManager().get_key_type());
  ASSERT_TRUE(km.ok()) << km.status();
  EXPECT_THAT(km.value()->get_key_type(),
              Eq(TestPrivateKeyTypeManager().get_key_type()));
}

TEST_F(RegistryTest, AsymmetricGetPrimitiveB) {
  if (kUseOnlyFips && !IsFipsEnabledInSsl()) {
    GTEST_SKIP() << "Not supported if FIPS-mode is used and BoringCrypto is "
                    "not available";
  }

  ASSERT_TRUE(Registry::RegisterAsymmetricKeyManagers(
                  CreateTestPrivateKeyManagerFipsCompatible(),
                  CreateTestPublicKeyManagerFipsCompatible(), true)
                  .ok());
  crypto::tink::util::StatusOr<const KeyManager<PrivatePrimitiveB>*> km =
      Registry::get_key_manager<PrivatePrimitiveB>(
          TestPrivateKeyTypeManager().get_key_type());
  ASSERT_TRUE(km.ok()) << km.status();
  EXPECT_THAT(km.value()->get_key_type(),
              Eq(TestPrivateKeyTypeManager().get_key_type()));
}

TEST_F(RegistryTest, AsymmetricGetPublicPrimitiveA) {
  if (kUseOnlyFips && !IsFipsEnabledInSsl()) {
    GTEST_SKIP() << "Not supported if FIPS-mode is used and BoringCrypto is "
                    "not available";
  }

  ASSERT_TRUE(Registry::RegisterAsymmetricKeyManagers(
                  CreateTestPrivateKeyManagerFipsCompatible(),
                  CreateTestPublicKeyManagerFipsCompatible(), true)
                  .ok());
  crypto::tink::util::StatusOr<const KeyManager<PublicPrimitiveA>*> km =
      Registry::get_key_manager<PublicPrimitiveA>(
          TestPublicKeyTypeManager().get_key_type());
  ASSERT_TRUE(km.ok()) << km.status();
  EXPECT_THAT(km.value()->get_key_type(),
              Eq(TestPublicKeyTypeManager().get_key_type()));
}

TEST_F(RegistryTest, AsymmetricGetPublicPrimitiveB) {
  if (kUseOnlyFips && !IsFipsEnabledInSsl()) {
    GTEST_SKIP() << "Not supported if FIPS-mode is used and BoringCrypto is "
                    "not available";
  }

  ASSERT_TRUE(Registry::RegisterAsymmetricKeyManagers(
                  CreateTestPrivateKeyManagerFipsCompatible(),
                  CreateTestPublicKeyManagerFipsCompatible(), true)
                  .ok());
  crypto::tink::util::StatusOr<const KeyManager<PublicPrimitiveB>*> km =
      Registry::get_key_manager<PublicPrimitiveB>(
          TestPublicKeyTypeManager().get_key_type());
  ASSERT_TRUE(km.ok()) << km.status();
  EXPECT_THAT(km.value()->get_key_type(),
              Eq(TestPublicKeyTypeManager().get_key_type()));
}

TEST_F(RegistryTest, AsymmetricGetWrongPrimitiveError) {
  if (kUseOnlyFips && !IsFipsEnabledInSsl()) {
    GTEST_SKIP() << "Not supported if FIPS-mode is used and BoringCrypto is "
                    "not available";
  }

  ASSERT_TRUE(Registry::RegisterAsymmetricKeyManagers(
                  CreateTestPrivateKeyManagerFipsCompatible(),
                  CreateTestPublicKeyManagerFipsCompatible(), true)
                  .ok());
  crypto::tink::util::StatusOr<const KeyManager<PublicPrimitiveA>*> km =
      Registry::get_key_manager<PublicPrimitiveA>(
          TestPrivateKeyTypeManager().get_key_type());
  EXPECT_THAT(km.status(),
              StatusIs(absl::StatusCode::kInvalidArgument,
                       HasSubstr("not among supported primitives")));
}

class PrivateKeyManagerImplTest : public testing::Test {
  void SetUp() override { Registry::Reset(); }

  void TearDown() override {
    // Reset is needed here to ensure Mock objects get deleted and do not leak.
    Registry::Reset();
  }
};

TEST_F(PrivateKeyManagerImplTest, AsymmetricFactoryNewKeyFromMessage) {
  if (kUseOnlyFips && !IsFipsEnabledInSsl()) {
    GTEST_SKIP() << "Not supported if FIPS-mode is used and BoringCrypto is "
                    "not available";
  }

  ASSERT_TRUE(Registry::RegisterAsymmetricKeyManagers(
                  CreateTestPrivateKeyManagerFipsCompatible(),
                  CreateTestPublicKeyManagerFipsCompatible(), true)
                  .ok());

  EcdsaKeyFormat key_format;
  key_format.mutable_params()->set_encoding(EcdsaSignatureEncoding::DER);
  KeyTemplate key_template;
  key_template.set_type_url(TestPrivateKeyTypeManager().get_key_type());
  key_template.set_value(key_format.SerializeAsString());
  key_template.set_output_prefix_type(OutputPrefixType::TINK);
  std::unique_ptr<KeyData> key_data =
      Registry::NewKeyData(key_template).value();
  EXPECT_THAT(key_data->type_url(),
              Eq(TestPrivateKeyTypeManager().get_key_type()));
  EcdsaPrivateKey private_key;
  private_key.ParseFromString(key_data->value());
  EXPECT_THAT(private_key.public_key().params().encoding(),
              Eq(EcdsaSignatureEncoding::DER));
}

TEST_F(PrivateKeyManagerImplTest, AsymmetricNewKeyDisallowed) {
  if (kUseOnlyFips && !IsFipsEnabledInSsl()) {
    GTEST_SKIP() << "Not supported if FIPS-mode is used and BoringCrypto is "
                    "not available";
  }

  ASSERT_TRUE(Registry::RegisterAsymmetricKeyManagers(
                  CreateTestPrivateKeyManagerFipsCompatible(),
                  CreateTestPublicKeyManagerFipsCompatible(), true)
                  .ok());
  ASSERT_TRUE(Registry::RegisterAsymmetricKeyManagers(
                  CreateTestPrivateKeyManagerFipsCompatible(),
                  CreateTestPublicKeyManagerFipsCompatible(), false)
                  .ok());

  KeyTemplate key_template;
  key_template.set_type_url(TestPrivateKeyTypeManager().get_key_type());
  EXPECT_THAT(
      Registry::NewKeyData(key_template).status(),
      StatusIs(absl::StatusCode::kInvalidArgument, HasSubstr("not allow")));
}

TEST_F(RegistryTest, AsymmetricGetPublicKeyData) {
  if (kUseOnlyFips && !IsFipsEnabledInSsl()) {
    GTEST_SKIP() << "Not supported if FIPS-mode is used and BoringCrypto is "
                    "not available";
  }

  crypto::tink::util::Status status = Registry::RegisterAsymmetricKeyManagers(
      CreateTestPrivateKeyManagerFipsCompatible(),
      CreateTestPublicKeyManagerFipsCompatible(), true);
  EcdsaPrivateKey private_key;
  private_key.mutable_public_key()->mutable_params()->set_encoding(
      EcdsaSignatureEncoding::DER);

  std::unique_ptr<KeyData> key_data =
      Registry::GetPublicKeyData(TestPrivateKeyTypeManager().get_key_type(),
                                 private_key.SerializeAsString())
          .value();
  ASSERT_THAT(key_data->type_url(),
              Eq(TestPublicKeyTypeManager().get_key_type()));
  EcdsaPublicKey public_key;
  public_key.ParseFromString(key_data->value());
  EXPECT_THAT(public_key.params().encoding(), Eq(EcdsaSignatureEncoding::DER));
}

class TestPrivateKeyTypeManager2 : public TestPrivateKeyTypeManager {};
class TestPublicKeyTypeManager2 : public TestPublicKeyTypeManager {};

TEST_F(RegistryTest, RegisterAssymmetricReregistrationWithWrongClasses) {
  if (kUseOnlyFips) {
    GTEST_SKIP() << "Not supported in FIPS-only mode";
  }

  ASSERT_TRUE(Registry::RegisterAsymmetricKeyManagers(
                  absl::make_unique<TestPrivateKeyTypeManager>(),
                  absl::make_unique<TestPublicKeyTypeManager>(), true)
                  .ok());
  EXPECT_THAT(Registry::RegisterAsymmetricKeyManagers(
                  absl::make_unique<TestPrivateKeyTypeManager2>(),
                  absl::make_unique<TestPublicKeyTypeManager>(), true),
              StatusIs(absl::StatusCode::kAlreadyExists,
                       HasSubstr("already registered")));
  EXPECT_THAT(Registry::RegisterAsymmetricKeyManagers(
                  absl::make_unique<TestPrivateKeyTypeManager>(),
                  absl::make_unique<TestPublicKeyTypeManager2>(), true),
              StatusIs(absl::StatusCode::kAlreadyExists,
                       HasSubstr("already registered")));
  EXPECT_THAT(Registry::RegisterAsymmetricKeyManagers(
                  absl::make_unique<TestPrivateKeyTypeManager2>(),
                  absl::make_unique<TestPublicKeyTypeManager2>(), true),
              StatusIs(absl::StatusCode::kAlreadyExists,
                       HasSubstr("already registered")));
  EXPECT_THAT(Registry::RegisterKeyTypeManager(
                  absl::make_unique<TestPrivateKeyTypeManager2>(), true),
              StatusIs(absl::StatusCode::kAlreadyExists,
                       HasSubstr("already registered")));
  EXPECT_THAT(Registry::RegisterKeyTypeManager(
                  absl::make_unique<TestPublicKeyTypeManager2>(), true),
              StatusIs(absl::StatusCode::kAlreadyExists,
                       HasSubstr("already registered")));
}

class TestPublicKeyTypeManagerWithDifferentKeyType
    : public TestPublicKeyTypeManager {
  const std::string& get_key_type() const override { return kKeyType; }

 private:
  const std::string kKeyType = "bla";
};

TEST_F(RegistryTest, RegisterAssymmetricReregistrationWithNewKeyType) {
  if (kUseOnlyFips) {
    GTEST_SKIP() << "Not supported in FIPS-only mode";
  }

  ASSERT_TRUE(Registry::RegisterAsymmetricKeyManagers(
                  absl::make_unique<TestPrivateKeyTypeManager>(),
                  absl::make_unique<TestPublicKeyTypeManager>(), true)
                  .ok());
  EXPECT_THAT(
      Registry::RegisterAsymmetricKeyManagers(
          absl::make_unique<TestPrivateKeyTypeManager>(),
          absl::make_unique<TestPublicKeyTypeManagerWithDifferentKeyType>(),
          true),
      StatusIs(absl::StatusCode::kInvalidArgument,
               HasSubstr("impossible to register")));
}

// The DelegatingKeyTypeManager calls the registry
class DelegatingKeyTypeManager
    : public PrivateKeyTypeManager<EcdsaPrivateKey, EcdsaKeyFormat,
                                   EcdsaPublicKey, List<>> {
 public:
  DelegatingKeyTypeManager() : PrivateKeyTypeManager() {}

  void set_registry(RegistryImpl* registry) { registry_ = registry; }

  google::crypto::tink::KeyData::KeyMaterialType key_material_type()
      const override {
    return google::crypto::tink::KeyData::SYMMETRIC;
  }

  uint32_t get_version() const override { return kVersion; }

  const std::string& get_key_type() const override { return kKeyType; }

  crypto::tink::util::Status ValidateKey(
      const EcdsaPrivateKey& key) const override {
    return util::OkStatus();
  }

  crypto::tink::util::Status ValidateKeyFormat(
      const EcdsaKeyFormat& key_format) const override {
    return util::OkStatus();
  }

  crypto::tink::util::StatusOr<EcdsaPrivateKey> CreateKey(
      const EcdsaKeyFormat& key_format) const override {
    AesGcmKeyFormat format;
    KeyTemplate key_template;
    key_template.set_type_url(
        "type.googleapis.com/google.crypto.tink.AesGcmKey");
    key_template.set_value(format.SerializeAsString());
    auto result = registry_->NewKeyData(key_template);
    if (!result.ok()) return result.status();
    // Return a string we can check for.
    return util::Status(absl::StatusCode::kDeadlineExceeded,
                        "CreateKey worked");
  }

  crypto::tink::util::StatusOr<EcdsaPrivateKey> DeriveKey(
      const EcdsaKeyFormat& key_format,
      InputStream* input_stream) const override {
    AesGcmKeyFormat format;
    KeyTemplate key_template;
    key_template.set_type_url(
        "type.googleapis.com/google.crypto.tink.AesGcmKey");
    key_template.set_value(format.SerializeAsString());

    auto result = registry_->DeriveKey(key_template, input_stream);
    if (!result.ok()) return result.status();
    // Return a string we can check for.
    return util::Status(absl::StatusCode::kDeadlineExceeded,
                        "DeriveKey worked");
  }

  crypto::tink::util::StatusOr<EcdsaPublicKey> GetPublicKey(
      const EcdsaPrivateKey& private_key) const override {
    AesGcmKeyFormat format;
    KeyTemplate key_template;
    key_template.set_type_url(
        "type.googleapis.com/google.crypto.tink.AesGcmKey");
    key_template.set_value(format.SerializeAsString());
    auto result = registry_->NewKeyData(key_template);
    if (!result.ok()) return result.status();
    // Return a string we can check for.
    return util::Status(absl::StatusCode::kDeadlineExceeded,
                        "GetPublicKey worked");
  }

 private:
  RegistryImpl* registry_;

  static constexpr int kVersion = 0;
  const std::string kKeyType =
      "type.googleapis.com/google.crypto.tink.EcdsaPrivateKey";
};

class RegistryImplTest : public ::testing::Test {
 protected:
  void TearDown() override {
    // Calling RestrictToFipsIfEmpty() may call SetFipsRestricted(), which
    // set a global variable to true. We have to reset that after the test.
    UnSetFipsRestricted();
  }
};

// Check that we can call the registry again from within NewKeyData
TEST_F(RegistryImplTest, CanDelegateCreateKey) {
  if (kUseOnlyFips) {
    GTEST_SKIP() << "Not supported in FIPS-only mode";
  }

  RegistryImpl registry_impl;
  auto delegating_key_manager = absl::make_unique<DelegatingKeyTypeManager>();
  delegating_key_manager->set_registry(&registry_impl);
  auto status =
      registry_impl
          .RegisterKeyTypeManager<EcdsaPrivateKey, EcdsaKeyFormat, List<>>(
              std::move(delegating_key_manager), true);
  EXPECT_THAT(status, IsOk());
  status = registry_impl.RegisterKeyTypeManager<AesGcmKey, AesGcmKeyFormat,
                                                List<Aead, AeadVariant>>(
      absl::make_unique<ExampleKeyTypeManager>(), true);
  EXPECT_THAT(status, IsOk());

  EcdsaKeyFormat format;
  KeyTemplate key_template;
  key_template.set_type_url(
      "type.googleapis.com/google.crypto.tink.EcdsaPrivateKey");
  key_template.set_value(format.SerializeAsString());
  EXPECT_THAT(registry_impl.NewKeyData(key_template).status(),
              StatusIs(absl::StatusCode::kDeadlineExceeded,
                       HasSubstr("CreateKey worked")));
}

// Check that we can call the registry again from within NewKeyData
TEST_F(RegistryImplTest, CanDelegateDeriveKey) {
  if (kUseOnlyFips) {
    GTEST_SKIP() << "Not supported in FIPS-only mode";
  }

  RegistryImpl registry_impl;
  auto delegating_key_manager = absl::make_unique<DelegatingKeyTypeManager>();
  delegating_key_manager->set_registry(&registry_impl);
  auto status =
      registry_impl
          .RegisterKeyTypeManager<EcdsaPrivateKey, EcdsaKeyFormat, List<>>(
              std::move(delegating_key_manager), true);
  EXPECT_THAT(status, IsOk());
  status = registry_impl.RegisterKeyTypeManager<AesGcmKey, AesGcmKeyFormat,
                                                List<Aead, AeadVariant>>(
      absl::make_unique<ExampleKeyTypeManager>(), true);
  EXPECT_THAT(status, IsOk());

  EcdsaKeyFormat format;
  KeyTemplate key_template;
  key_template.set_type_url(
      "type.googleapis.com/google.crypto.tink.EcdsaPrivateKey");
  key_template.set_value(format.SerializeAsString());
  EXPECT_THAT(registry_impl.DeriveKey(key_template, nullptr).status(),
              StatusIs(absl::StatusCode::kDeadlineExceeded,
                       HasSubstr("DeriveKey worked")));
}

TEST_F(RegistryImplTest, CanDelegateGetPublicKey) {
  if (kUseOnlyFips) {
    GTEST_SKIP() << "Not supported in FIPS-only mode";
  }

  RegistryImpl registry_impl;
  auto delegating_key_manager = absl::make_unique<DelegatingKeyTypeManager>();
  delegating_key_manager->set_registry(&registry_impl);
  auto status = registry_impl.RegisterAsymmetricKeyManagers(
      delegating_key_manager.release(),
      absl::make_unique<TestPublicKeyTypeManager>().release(), true);
  EXPECT_THAT(status, IsOk());
  status = registry_impl.RegisterKeyTypeManager<AesGcmKey, AesGcmKeyFormat,
                                                List<Aead, AeadVariant>>(
      absl::make_unique<ExampleKeyTypeManager>(), true);
  EXPECT_THAT(status, IsOk());

  EcdsaPrivateKey private_key;
  private_key.mutable_public_key()->mutable_params()->set_encoding(
      EcdsaSignatureEncoding::DER);

  EXPECT_THAT(registry_impl
                  .GetPublicKeyData(DelegatingKeyTypeManager().get_key_type(),
                                    private_key.SerializeAsString())
                  .status(),
              StatusIs(absl::StatusCode::kDeadlineExceeded,
                       HasSubstr("GetPublicKey worked")));
}

TEST_F(RegistryImplTest, FipsRestrictionSucceedsOnEmptyRegistry) {
  RegistryImpl registry_impl;
  EXPECT_THAT(registry_impl.RestrictToFipsIfEmpty(), IsOk());
}

TEST_F(RegistryImplTest, FipsRestrictionSucceedsWhenSettingMultipleTimes) {
  RegistryImpl registry_impl;
  EXPECT_THAT(registry_impl.RestrictToFipsIfEmpty(), IsOk());
  EXPECT_THAT(registry_impl.RestrictToFipsIfEmpty(), IsOk());
  EXPECT_THAT(registry_impl.RestrictToFipsIfEmpty(), IsOk());
}

TEST_F(RegistryImplTest, FipsRestrictionSucceedsIfBuildInFipsMode) {
  if (!kUseOnlyFips) {
    GTEST_SKIP() << "Not supported when Tink is not built in FIPS mode.";
  }
  RegistryImpl registry_impl;
  EXPECT_THAT(registry_impl.RestrictToFipsIfEmpty(), IsOk());
}

TEST_F(RegistryImplTest, FipsFailsIfNotEmpty) {
  if (kUseOnlyFips) {
    GTEST_SKIP() << "Not supported in FIPS-only mode";
  }

  auto fips_key_manager = absl::make_unique<ExampleKeyTypeManager>();
  ON_CALL(*fips_key_manager, FipsStatus())
      .WillByDefault(testing::Return(FipsCompatibility::kRequiresBoringCrypto));

  RegistryImpl registry_impl;
  auto status = registry_impl.RegisterKeyTypeManager<AesGcmKey, AesGcmKeyFormat,
                                                     List<Aead, AeadVariant>>(
      std::move(fips_key_manager), true);
  EXPECT_THAT(status, IsOk());
  EXPECT_THAT(registry_impl.RestrictToFipsIfEmpty(),
              StatusIs(absl::StatusCode::kInternal));
}

TEST_F(RegistryImplTest, CanRegisterOnlyOneMonitoringFactory) {
  auto monitoring_client_factory =
      absl::make_unique<MockMonitoringClientFactory>();

  RegistryImpl registry_impl;
  EXPECT_THAT(registry_impl.RegisterMonitoringClientFactory(
                  std::move(monitoring_client_factory)),
              IsOk());
  ASSERT_THAT(registry_impl.GetMonitoringClientFactory(), Not(IsNull()));
  EXPECT_THAT(registry_impl.RegisterMonitoringClientFactory(
                  std::move(monitoring_client_factory)),
              StatusIs(absl::StatusCode::kAlreadyExists));
}

}  // namespace
}  // namespace internal
}  // namespace tink
}  // namespace crypto
