/* * (C) 2014,2015 Jack Lloyd * (C) 2015 Matej Kenda * * Botan is released under the Simplified BSD License (see license.txt) */ #ifndef BOTAN_ALGO_REGISTRY_H__ #define BOTAN_ALGO_REGISTRY_H__ #include #include #include #include #include #include #include #include #if defined(_MSC_VER) && (_MSC_VER <= 1800) #define BOTAN_WORKAROUND_GH_321 #define NOMINMAX 1 #define WIN32_LEAN_AND_MEAN 1 #include #endif namespace Botan { #if defined(BOTAN_WORKAROUND_GH_321) class WinCS_Mutex { public: WinCS_Mutex() { InitializeCriticalSection(&m_cs); } ~WinCS_Mutex() { DeleteCriticalSection(&m_cs); } void lock() { EnterCriticalSection(&m_cs); } void unlock() { LeaveCriticalSection(&m_cs); } private: CRITICAL_SECTION m_cs; }; #endif template class Algo_Registry { public: typedef typename T::Spec Spec; typedef std::function maker_fn; static Algo_Registry& global_registry() { static Algo_Registry g_registry; return g_registry; } void add(const std::string& name, const std::string& provider, maker_fn fn, byte pref) { std::lock_guard lock(m_mutex); if(!m_algo_info[name].add_provider(provider, fn, pref)) throw Exception("Duplicated registration of " + name + "/" + provider); } std::vector providers_of(const Spec& spec) { std::lock_guard lock(m_mutex); auto i = m_algo_info.find(spec.algo_name()); if(i != m_algo_info.end()) return i->second.providers(); return std::vector(); } void set_provider_preference(const Spec& spec, const std::string& provider, byte pref) { std::lock_guard lock(m_mutex); auto i = m_algo_info.find(spec.algo_name()); if(i != m_algo_info.end()) i->second.set_pref(provider, pref); } T* make(const Spec& spec, const std::string& provider = "") { const std::vector makers = get_makers(spec, provider); try { for(auto&& maker : makers) { if(T* t = maker(spec)) return t; } } catch(std::exception& e) { throw Exception("Creating '" + spec.as_string() + "' failed: " + e.what()); } return nullptr; } class Add { public: Add(const std::string& basename, maker_fn fn, const std::string& provider, byte pref) { Algo_Registry::global_registry().add(basename, provider, fn, pref); } Add(bool cond, const std::string& basename, maker_fn fn, const std::string& provider, byte pref) { if(cond) Algo_Registry::global_registry().add(basename, provider, fn, pref); } }; private: #if defined(BOTAN_WORKAROUND_GH_321) using mutex = WinCS_Mutex; #else using mutex = std::mutex; #endif Algo_Registry() { } std::vector get_makers(const Spec& spec, const std::string& provider) { std::lock_guard lock(m_mutex); return m_algo_info[spec.algo_name()].get_makers(provider); } struct Algo_Info { public: bool add_provider(const std::string& provider, maker_fn fn, byte pref) { if(m_maker_fns.count(provider) > 0) return false; m_maker_fns[provider] = fn; m_prefs.insert(std::make_pair(pref, provider)); return true; } std::vector providers() const { std::vector v; for(auto&& k : m_prefs) v.push_back(k.second); return v; } void set_pref(const std::string& provider, byte pref) { auto i = m_prefs.begin(); while(i != m_prefs.end()) { if(i->second == provider) i = m_prefs.erase(i); else ++i; } m_prefs.insert(std::make_pair(pref, provider)); } std::vector get_makers(const std::string& req_provider) { std::vector r; if(req_provider != "") { // find one explicit provider requested by user or fail auto i = m_maker_fns.find(req_provider); if(i != m_maker_fns.end()) r.push_back(i->second); } else { for(auto&& pref : m_prefs) r.push_back(m_maker_fns[pref.second]); } return r; } private: std::multimap> m_prefs; std::unordered_map m_maker_fns; }; mutex m_mutex; std::unordered_map m_algo_info; }; template T* make_a(const typename T::Spec& spec, const std::string provider = "") { return Algo_Registry::global_registry().make(spec, provider); } template std::vector providers_of(const typename T::Spec& spec) { return Algo_Registry::global_registry().providers_of(spec); } template T* make_new_T(const typename Algo_Registry::Spec& spec) { if(spec.arg_count() == 0) return new T; return nullptr; } template T* make_new_T_1len(const typename Algo_Registry::Spec& spec) { return new T(spec.arg_as_integer(0, DEF_VAL)); } template T* make_new_T_2len(const typename Algo_Registry::Spec& spec) { return new T(spec.arg_as_integer(0, DEF1), spec.arg_as_integer(1, DEF2)); } template T* make_new_T_1str(const typename Algo_Registry::Spec& spec, const std::string& def) { return new T(spec.arg(0, def)); } template T* make_new_T_1str_req(const typename Algo_Registry::Spec& spec) { return new T(spec.arg(0)); } template T* make_new_T_1X(const typename Algo_Registry::Spec& spec) { std::unique_ptr x(Algo_Registry::global_registry().make(spec.arg(0))); if(!x) throw Exception(spec.arg(0)); return new T(x.release()); } // Append to macros living outside of functions, so that invocations must end with a semicolon. // The struct is only declared to force the semicolon, it is never defined. #define BOTAN_FORCE_SEMICOLON struct BOTAN_DUMMY_STRUCT #define BOTAN_REGISTER_TYPE(T, type, name, maker, provider, pref) \ namespace { Algo_Registry::Add g_ ## type ## _reg(name, maker, provider, pref); } \ BOTAN_FORCE_SEMICOLON #define BOTAN_REGISTER_TYPE_COND(cond, T, type, name, maker, provider, pref) \ namespace { Algo_Registry::Add g_ ## type ## _reg(cond, name, maker, provider, pref); } \ BOTAN_FORCE_SEMICOLON #define BOTAN_DEFAULT_ALGORITHM_PRIO 100 #define BOTAN_SIMD_ALGORITHM_PRIO 110 #define BOTAN_REGISTER_NAMED_T(T, name, type, maker) \ BOTAN_REGISTER_TYPE(T, type, name, maker, "base", BOTAN_DEFAULT_ALGORITHM_PRIO) #define BOTAN_REGISTER_T(T, type, maker) \ BOTAN_REGISTER_TYPE(T, type, #type, maker, "base", BOTAN_DEFAULT_ALGORITHM_PRIO) #define BOTAN_REGISTER_T_NOARGS(T, type) \ BOTAN_REGISTER_TYPE(T, type, #type, make_new_T, "base", BOTAN_DEFAULT_ALGORITHM_PRIO) #define BOTAN_REGISTER_T_1LEN(T, type, def) \ BOTAN_REGISTER_TYPE(T, type, #type, (make_new_T_1len), "base", BOTAN_DEFAULT_ALGORITHM_PRIO) #define BOTAN_REGISTER_NAMED_T_NOARGS(T, type, name, provider) \ BOTAN_REGISTER_TYPE(T, type, name, make_new_T, provider, BOTAN_DEFAULT_ALGORITHM_PRIO) #define BOTAN_COND_REGISTER_NAMED_T_NOARGS(cond, T, type, name, provider, pref) \ BOTAN_REGISTER_TYPE_COND(cond, T, type, name, make_new_T, provider, pref) #define BOTAN_REGISTER_NAMED_T_2LEN(T, type, name, provider, len1, len2) \ BOTAN_REGISTER_TYPE(T, type, name, (make_new_T_2len), provider, BOTAN_DEFAULT_ALGORITHM_PRIO) // TODO move elsewhere: #define BOTAN_REGISTER_TRANSFORM(name, maker) BOTAN_REGISTER_T(Transform, name, maker) #define BOTAN_REGISTER_TRANSFORM_NOARGS(name) BOTAN_REGISTER_T_NOARGS(Transform, name) } #endif