blob: 2cb4c8ab7aaf1a168ae382b2aa5850f131a4674b [file] [log] [blame]
// Copyright 2018 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.
//
///////////////////////////////////////////////////////////////////////////////
#ifndef TINK_CORE_REGISTRY_IMPL_H_
#define TINK_CORE_REGISTRY_IMPL_H_
#include <typeinfo>
#include <unordered_map>
#include "absl/base/thread_annotations.h"
#include "absl/strings/str_cat.h"
#include "absl/synchronization/mutex.h"
#include "tink/catalogue.h"
#include "tink/core/registry_impl.h"
#include "tink/key_manager.h"
#include "tink/primitive_set.h"
#include "tink/primitive_wrapper.h"
#include "tink/util/errors.h"
#include "tink/util/protobuf_helper.h"
#include "tink/util/status.h"
#include "tink/util/validation.h"
#include "proto/tink.pb.h"
namespace crypto {
namespace tink {
class RegistryImpl {
public:
static RegistryImpl& GlobalInstance() {
static RegistryImpl* instance = new RegistryImpl();
return *instance;
}
template <class P>
crypto::tink::util::StatusOr<const Catalogue<P>*> get_catalogue(
const std::string& catalogue_name) const LOCKS_EXCLUDED(maps_mutex_);
template <class P>
crypto::tink::util::Status AddCatalogue(const std::string& catalogue_name,
Catalogue<P>* catalogue)
LOCKS_EXCLUDED(maps_mutex_);
// Registers the given 'manager' for the key type 'manager->get_key_type()'.
// Takes ownership of 'manager', which must be non-nullptr.
template <class P>
crypto::tink::util::Status RegisterKeyManager(KeyManager<P>* manager,
bool new_key_allowed)
LOCKS_EXCLUDED(maps_mutex_);
template <class P>
crypto::tink::util::Status RegisterKeyManager(KeyManager<P>* manager)
LOCKS_EXCLUDED(maps_mutex_) {
return RegisterKeyManager(manager, /* new_key_allowed= */ true);
}
template <class P>
crypto::tink::util::StatusOr<const KeyManager<P>*> get_key_manager(
const std::string& type_url) const LOCKS_EXCLUDED(maps_mutex_);
template <class P>
crypto::tink::util::Status RegisterPrimitiveWrapper(
PrimitiveWrapper<P>* wrapper) LOCKS_EXCLUDED(maps_mutex_);
template <class P>
crypto::tink::util::StatusOr<std::unique_ptr<P>> GetPrimitive(
const google::crypto::tink::KeyData& key_data)
const LOCKS_EXCLUDED(maps_mutex_);
template <class P>
crypto::tink::util::StatusOr<std::unique_ptr<P>> GetPrimitive(
const std::string& type_url, const portable_proto::MessageLite& key)
const LOCKS_EXCLUDED(maps_mutex_);
crypto::tink::util::StatusOr<std::unique_ptr<google::crypto::tink::KeyData>>
NewKeyData(const google::crypto::tink::KeyTemplate& key_template)
const LOCKS_EXCLUDED(maps_mutex_);
crypto::tink::util::StatusOr<std::unique_ptr<google::crypto::tink::KeyData>>
GetPublicKeyData(const std::string& type_url, const std::string& serialized_private_key)
const LOCKS_EXCLUDED(maps_mutex_);
template <class P>
crypto::tink::util::StatusOr<std::unique_ptr<P>> Wrap(
std::unique_ptr<PrimitiveSet<P>> primitive_set) const
LOCKS_EXCLUDED(maps_mutex_);
void Reset() LOCKS_EXCLUDED(maps_mutex_);
private:
// All information for a given type url.
struct KeyTypeInfo {
KeyTypeInfo(std::unique_ptr<void, void (*)(void*)> key_manager,
const char* type_id_name, bool new_key_allowed,
const KeyFactory& key_factory)
: key_manager(std::move(key_manager)),
type_id_name(type_id_name),
new_key_allowed(new_key_allowed),
key_factory(key_factory) {}
// A pointer to a KeyManager<P>. We cannot use a normal unique_ptr because
// we do not know P. Hence, we pass a custom deleter which knows how to
// delete the object.
const std::unique_ptr<void, void (*)(void*)> key_manager;
// TypeId of the primitive for which this key was inserted.
const char* type_id_name;
// Whether the key manager allows creating new keys.
bool new_key_allowed;
// The factory which can produce keys of this type.
const KeyFactory& key_factory;
};
// All information for a given primitive label.
struct LabelInfo {
LabelInfo(std::unique_ptr<void, void (*)(void*)> catalogue,
const char* type_id_name)
: catalogue(std::move(catalogue)), type_id_name(type_id_name) {}
// A pointer to the underlying Catalogue<P>.
const std::unique_ptr<void, void (*)(void*)> catalogue;
// TypeId of the primitive for which this key was inserted.
const char* type_id_name;
};
RegistryImpl() = default;
RegistryImpl(const RegistryImpl&) = delete;
RegistryImpl& operator=(const RegistryImpl&) = delete;
template <class P>
crypto::tink::util::StatusOr<const PrimitiveWrapper<P>*> get_wrapper()
const LOCKS_EXCLUDED(maps_mutex_);
mutable absl::Mutex maps_mutex_;
std::unordered_map<std::string, KeyTypeInfo> type_url_to_info_
GUARDED_BY(maps_mutex_);
std::unordered_map<std::string, std::unique_ptr<void, void (*)(void*)>>
primitive_to_wrapper_ GUARDED_BY(maps_mutex_);
std::unordered_map<std::string, LabelInfo> name_to_catalogue_map_
GUARDED_BY(maps_mutex_);
};
template <class P>
void delete_manager(void* t) {
delete static_cast<KeyManager<P>*>(t);
}
template <class P>
void delete_catalogue(void* t) {
delete static_cast<Catalogue<P>*>(t);
}
template <class Type>
std::unique_ptr<void, void (*)(void*)> WrapAsVoidUnique(Type* ptr) {
return std::unique_ptr<void, void (*)(void*)>(
static_cast<void*>(ptr), [](void* t) { delete static_cast<Type*>(t); });
}
template <class P>
crypto::tink::util::Status RegistryImpl::AddCatalogue(
const std::string& catalogue_name, Catalogue<P>* catalogue) {
if (catalogue == nullptr) {
return crypto::tink::util::Status(
crypto::tink::util::error::INVALID_ARGUMENT,
"Parameter 'catalogue' must be non-null.");
}
std::unique_ptr<void, void (*)(void*)> entry(catalogue, delete_catalogue<P>);
absl::MutexLock lock(&maps_mutex_);
auto curr_catalogue = name_to_catalogue_map_.find(catalogue_name);
if (curr_catalogue != name_to_catalogue_map_.end()) {
auto existing =
static_cast<Catalogue<P>*>(curr_catalogue->second.catalogue.get());
if (typeid(*existing).name() != typeid(*catalogue).name()) {
return ToStatusF(crypto::tink::util::error::ALREADY_EXISTS,
"A catalogue named '%s' has been already added.",
catalogue_name.c_str());
}
} else {
name_to_catalogue_map_.emplace(
std::piecewise_construct, std::forward_as_tuple(catalogue_name),
std::forward_as_tuple(std::move(entry), typeid(P).name()));
}
return crypto::tink::util::Status::OK;
}
template <class P>
crypto::tink::util::StatusOr<const Catalogue<P>*> RegistryImpl::get_catalogue(
const std::string& catalogue_name) const {
absl::MutexLock lock(&maps_mutex_);
auto catalogue_entry = name_to_catalogue_map_.find(catalogue_name);
if (catalogue_entry == name_to_catalogue_map_.end()) {
return ToStatusF(crypto::tink::util::error::NOT_FOUND,
"No catalogue named '%s' has been added.",
catalogue_name.c_str());
}
if (catalogue_entry->second.type_id_name != typeid(P).name()) {
return ToStatusF(crypto::tink::util::error::INVALID_ARGUMENT,
"Wrong Primitive type for catalogue named '%s': "
"got '%s', expected '%s'",
catalogue_name.c_str(), typeid(P).name(),
catalogue_entry->second.type_id_name);
}
return static_cast<Catalogue<P>*>(catalogue_entry->second.catalogue.get());
}
template <class P>
crypto::tink::util::Status RegistryImpl::RegisterKeyManager(
KeyManager<P>* manager, bool new_key_allowed) {
if (manager == nullptr) {
return crypto::tink::util::Status(
crypto::tink::util::error::INVALID_ARGUMENT,
"Parameter 'manager' must be non-null.");
}
std::unique_ptr<void, void (*)(void*)> entry(manager, delete_manager<P>);
std::string type_url = manager->get_key_type();
if (!manager->DoesSupport(type_url)) {
return ToStatusF(crypto::tink::util::error::INVALID_ARGUMENT,
"The manager does not support type '%s'.",
type_url.c_str());
}
absl::MutexLock lock(&maps_mutex_);
auto it = type_url_to_info_.find(type_url);
if (it != type_url_to_info_.end()) {
auto existing = static_cast<KeyManager<P>*>(it->second.key_manager.get());
if (typeid(*existing).name() != typeid(*manager).name()) {
return ToStatusF(crypto::tink::util::error::ALREADY_EXISTS,
"A manager for type '%s' has been already registered.",
type_url.c_str());
} else {
if (!it->second.new_key_allowed && new_key_allowed) {
return ToStatusF(crypto::tink::util::error::ALREADY_EXISTS,
"A manager for type '%s' has been already registered "
"with forbidden new key operation.",
type_url.c_str());
}
it->second.new_key_allowed = new_key_allowed;
}
} else {
type_url_to_info_.emplace(
std::piecewise_construct, std::forward_as_tuple(type_url),
std::forward_as_tuple(std::move(entry), typeid(P).name(),
new_key_allowed, manager->get_key_factory()));
}
return crypto::tink::util::Status::OK;
}
template <class P>
crypto::tink::util::Status RegistryImpl::RegisterPrimitiveWrapper(
PrimitiveWrapper<P>* wrapper) {
if (wrapper == nullptr) {
return crypto::tink::util::Status(
crypto::tink::util::error::INVALID_ARGUMENT,
"Parameter 'wrapper' must be non-null.");
}
std::unique_ptr<void, void (*)(void*)> entry = WrapAsVoidUnique(wrapper);
absl::MutexLock lock(&maps_mutex_);
auto it = primitive_to_wrapper_.find(typeid(P).name());
if (it != primitive_to_wrapper_.end()) {
if (typeid(*static_cast<PrimitiveWrapper<P>*>(it->second.get())).name() !=
typeid(*static_cast<PrimitiveWrapper<P>*>(entry.get())).name()) {
return ToStatusF(
crypto::tink::util::error::ALREADY_EXISTS,
"A wrapper named for this primitive has already been added.");
}
return crypto::tink::util::Status::OK;
}
primitive_to_wrapper_.insert(
std::make_pair(typeid(P).name(), std::move(entry)));
return crypto::tink::util::Status::OK;
}
template <class P>
crypto::tink::util::StatusOr<const KeyManager<P>*>
RegistryImpl::get_key_manager(const std::string& type_url) const {
absl::MutexLock lock(&maps_mutex_);
auto it = type_url_to_info_.find(type_url);
if (it == type_url_to_info_.end()) {
return ToStatusF(crypto::tink::util::error::NOT_FOUND,
"No manager for type '%s' has been registered.",
type_url.c_str());
}
if (it->second.type_id_name != typeid(P).name()) {
return ToStatusF(crypto::tink::util::error::INVALID_ARGUMENT,
"Wrong Primitive type for key type '%s': "
"got '%s', expected '%s'",
type_url.c_str(), typeid(P).name(),
it->second.type_id_name);
}
return static_cast<KeyManager<P>*>(it->second.key_manager.get());
}
template <class P>
crypto::tink::util::StatusOr<std::unique_ptr<P>> RegistryImpl::GetPrimitive(
const google::crypto::tink::KeyData& key_data) const {
auto key_manager_result = get_key_manager<P>(key_data.type_url());
if (key_manager_result.ok()) {
return key_manager_result.ValueOrDie()->GetPrimitive(key_data);
}
return key_manager_result.status();
}
template <class P>
crypto::tink::util::StatusOr<std::unique_ptr<P>> RegistryImpl::GetPrimitive(
const std::string& type_url, const portable_proto::MessageLite& key) const {
auto key_manager_result = get_key_manager<P>(type_url);
if (key_manager_result.ok()) {
return key_manager_result.ValueOrDie()->GetPrimitive(key);
}
return key_manager_result.status();
}
template <class P>
crypto::tink::util::StatusOr<const PrimitiveWrapper<P>*>
RegistryImpl::get_wrapper() const {
absl::MutexLock lock(&maps_mutex_);
auto it = primitive_to_wrapper_.find(typeid(P).name());
if (it == primitive_to_wrapper_.end()) {
return util::Status(
util::error::INVALID_ARGUMENT,
absl::StrCat("No wrapper registered for type ", typeid(P).name()));
}
return static_cast<PrimitiveWrapper<P>*>(it->second.get());
}
template <class P>
crypto::tink::util::StatusOr<std::unique_ptr<P>> RegistryImpl::Wrap(
std::unique_ptr<PrimitiveSet<P>> primitive_set) const {
if (primitive_set == nullptr) {
return crypto::tink::util::Status(
crypto::tink::util::error::INVALID_ARGUMENT,
"Parameter 'primitive_set' must be non-null.");
}
util::StatusOr<const PrimitiveWrapper<P>*> wrapper_result = get_wrapper<P>();
if (!wrapper_result.ok()) {
return wrapper_result.status();
}
crypto::tink::util::StatusOr<std::unique_ptr<P>> primitive_result =
wrapper_result.ValueOrDie()->Wrap(std::move(primitive_set));
return std::move(primitive_result);
}
} // namespace tink
} // namespace crypto
#endif // TINK_CORE_REGISTRY_IMPL_H_