diff options
Diffstat (limited to 'src/lib/algo_factory')
-rw-r--r-- | src/lib/algo_factory/algo_cache.h | 239 | ||||
-rw-r--r-- | src/lib/algo_factory/algo_factory.cpp | 352 | ||||
-rw-r--r-- | src/lib/algo_factory/algo_factory.h | 225 | ||||
-rw-r--r-- | src/lib/algo_factory/info.txt | 24 | ||||
-rw-r--r-- | src/lib/algo_factory/prov_weight.cpp | 34 |
5 files changed, 874 insertions, 0 deletions
diff --git a/src/lib/algo_factory/algo_cache.h b/src/lib/algo_factory/algo_cache.h new file mode 100644 index 000000000..3bd9f0031 --- /dev/null +++ b/src/lib/algo_factory/algo_cache.h @@ -0,0 +1,239 @@ +/* +* An algorithm cache (used by Algorithm_Factory) +* (C) 2008-2009,2011 Jack Lloyd +* +* Distributed under the terms of the Botan license +*/ + +#ifndef BOTAN_ALGORITHM_CACHE_TEMPLATE_H__ +#define BOTAN_ALGORITHM_CACHE_TEMPLATE_H__ + +#include <botan/types.h> +#include <botan/internal/stl_util.h> +#include <mutex> +#include <string> +#include <vector> +#include <map> + +namespace Botan { + +/** +* @param prov_name a provider name +* @return weight for this provider +*/ +size_t static_provider_weight(const std::string& prov_name); + +/** +* Algorithm_Cache (used by Algorithm_Factory) +*/ +template<typename T> +class Algorithm_Cache + { + public: + /** + * @param algo_spec names the requested algorithm + * @param pref_provider suggests a preferred provider + * @return prototype object, or NULL + */ + const T* get(const std::string& algo_spec, + const std::string& pref_provider); + + /** + * Add a new algorithm implementation to the cache + * @param algo the algorithm prototype object + * @param requested_name how this name will be requested + * @param provider_name is the name of the provider of this prototype + */ + void add(T* algo, + const std::string& requested_name, + const std::string& provider_name); + + /** + * Set the preferred provider + * @param algo_spec names the algorithm + * @param provider names the preferred provider + */ + void set_preferred_provider(const std::string& algo_spec, + const std::string& provider); + + /** + * Return the list of providers of this algorithm + * @param algo_name names the algorithm + * @return list of providers of this algorithm + */ + std::vector<std::string> providers_of(const std::string& algo_name); + + /** + * Clear the cache + */ + void clear_cache(); + + ~Algorithm_Cache() { clear_cache(); } + private: + typename std::map<std::string, std::map<std::string, T*> >::const_iterator + find_algorithm(const std::string& algo_spec); + + std::mutex mutex; + std::map<std::string, std::string> aliases; + std::map<std::string, std::string> pref_providers; + std::map<std::string, std::map<std::string, T*> > algorithms; + }; + +/* +* Look for an algorithm implementation in the cache, also checking aliases +* Assumes object lock is held +*/ +template<typename T> +typename std::map<std::string, std::map<std::string, T*> >::const_iterator +Algorithm_Cache<T>::find_algorithm(const std::string& algo_spec) + { + auto algo = algorithms.find(algo_spec); + + // Not found? Check if a known alias + if(algo == algorithms.end()) + { + auto alias = aliases.find(algo_spec); + + if(alias != aliases.end()) + algo = algorithms.find(alias->second); + } + + return algo; + } + +/* +* Look for an algorithm implementation by a particular provider +*/ +template<typename T> +const T* Algorithm_Cache<T>::get(const std::string& algo_spec, + const std::string& requested_provider) + { + std::lock_guard<std::mutex> lock(mutex); + + auto algo = find_algorithm(algo_spec); + if(algo == algorithms.end()) // algo not found at all (no providers) + return nullptr; + + // If a provider is requested specifically, return it or fail entirely + if(requested_provider != "") + { + auto prov = algo->second.find(requested_provider); + if(prov != algo->second.end()) + return prov->second; + return nullptr; + } + + const T* prototype = nullptr; + std::string prototype_provider; + size_t prototype_prov_weight = 0; + + const std::string pref_provider = search_map(pref_providers, algo_spec); + + for(auto i = algo->second.begin(); i != algo->second.end(); ++i) + { + // preferred prov exists, return immediately + if(i->first == pref_provider) + return i->second; + + const size_t prov_weight = static_provider_weight(i->first); + + if(prototype == nullptr || prov_weight > prototype_prov_weight) + { + prototype = i->second; + prototype_provider = i->first; + prototype_prov_weight = prov_weight; + } + } + + return prototype; + } + +/* +* Add an implementation to the cache +*/ +template<typename T> +void Algorithm_Cache<T>::add(T* algo, + const std::string& requested_name, + const std::string& provider) + { + if(!algo) + return; + + std::lock_guard<std::mutex> lock(mutex); + + if(algo->name() != requested_name && + aliases.find(requested_name) == aliases.end()) + { + aliases[requested_name] = algo->name(); + } + + if(!algorithms[algo->name()][provider]) + algorithms[algo->name()][provider] = algo; + else + delete algo; + } + +/* +* Find the providers of this algo (if any) +*/ +template<typename T> std::vector<std::string> +Algorithm_Cache<T>::providers_of(const std::string& algo_name) + { + std::lock_guard<std::mutex> lock(mutex); + + std::vector<std::string> providers; + + auto algo = find_algorithm(algo_name); + if(algo != algorithms.end()) + { + auto provider = algo->second.begin(); + + while(provider != algo->second.end()) + { + providers.push_back(provider->first); + ++provider; + } + } + + return providers; + } + +/* +* Set the preferred provider for an algorithm +*/ +template<typename T> +void Algorithm_Cache<T>::set_preferred_provider(const std::string& algo_spec, + const std::string& provider) + { + std::lock_guard<std::mutex> lock(mutex); + + pref_providers[algo_spec] = provider; + } + +/* +* Clear out the cache +*/ +template<typename T> +void Algorithm_Cache<T>::clear_cache() + { + auto algo = algorithms.begin(); + + while(algo != algorithms.end()) + { + auto provider = algo->second.begin(); + + while(provider != algo->second.end()) + { + delete provider->second; + ++provider; + } + + ++algo; + } + + algorithms.clear(); + } + +} + +#endif diff --git a/src/lib/algo_factory/algo_factory.cpp b/src/lib/algo_factory/algo_factory.cpp new file mode 100644 index 000000000..1683648bd --- /dev/null +++ b/src/lib/algo_factory/algo_factory.cpp @@ -0,0 +1,352 @@ +/* +* Algorithm Factory +* (C) 2008-2010 Jack Lloyd +* +* Distributed under the terms of the Botan license +*/ + +#include <botan/algo_factory.h> +#include <botan/internal/algo_cache.h> +#include <botan/internal/stl_util.h> +#include <botan/engine.h> +#include <botan/exceptn.h> + +#include <botan/block_cipher.h> +#include <botan/stream_cipher.h> +#include <botan/hash.h> +#include <botan/mac.h> +#include <botan/pbkdf.h> + +#include <algorithm> + +namespace Botan { + +namespace { + +/* +* Template functions for the factory prototype/search algorithm +*/ +template<typename T> +T* engine_get_algo(Engine*, + const SCAN_Name&, + Algorithm_Factory&) + { return nullptr; } + +template<> +BlockCipher* engine_get_algo(Engine* engine, + const SCAN_Name& request, + Algorithm_Factory& af) + { return engine->find_block_cipher(request, af); } + +template<> +StreamCipher* engine_get_algo(Engine* engine, + const SCAN_Name& request, + Algorithm_Factory& af) + { return engine->find_stream_cipher(request, af); } + +template<> +HashFunction* engine_get_algo(Engine* engine, + const SCAN_Name& request, + Algorithm_Factory& af) + { return engine->find_hash(request, af); } + +template<> +MessageAuthenticationCode* engine_get_algo(Engine* engine, + const SCAN_Name& request, + Algorithm_Factory& af) + { return engine->find_mac(request, af); } + +template<> +PBKDF* engine_get_algo(Engine* engine, + const SCAN_Name& request, + Algorithm_Factory& af) + { return engine->find_pbkdf(request, af); } + +template<typename T> +const T* factory_prototype(const std::string& algo_spec, + const std::string& provider, + const std::vector<Engine*>& engines, + Algorithm_Factory& af, + Algorithm_Cache<T>* cache) + { + if(const T* cache_hit = cache->get(algo_spec, provider)) + return cache_hit; + + SCAN_Name scan_name(algo_spec); + + if(scan_name.cipher_mode() != "") + return nullptr; + + for(size_t i = 0; i != engines.size(); ++i) + { + if(provider == "" || engines[i]->provider_name() == provider) + { + if(T* impl = engine_get_algo<T>(engines[i], scan_name, af)) + cache->add(impl, algo_spec, engines[i]->provider_name()); + } + } + + return cache->get(algo_spec, provider); + } + +} + +/* +* Setup caches +*/ +Algorithm_Factory::Algorithm_Factory() + { + block_cipher_cache = new Algorithm_Cache<BlockCipher>(); + stream_cipher_cache = new Algorithm_Cache<StreamCipher>(); + hash_cache = new Algorithm_Cache<HashFunction>(); + mac_cache = new Algorithm_Cache<MessageAuthenticationCode>(); + pbkdf_cache = new Algorithm_Cache<PBKDF>(); + } + +/* +* Delete all engines +*/ +Algorithm_Factory::~Algorithm_Factory() + { + delete block_cipher_cache; + delete stream_cipher_cache; + delete hash_cache; + delete mac_cache; + delete pbkdf_cache; + + for(auto i = engines.begin(); i != engines.end(); ++i) + delete *i; + } + +void Algorithm_Factory::clear_caches() + { + block_cipher_cache->clear_cache(); + stream_cipher_cache->clear_cache(); + hash_cache->clear_cache(); + mac_cache->clear_cache(); + pbkdf_cache->clear_cache(); + } + +void Algorithm_Factory::add_engine(Engine* engine) + { + clear_caches(); + engines.push_back(engine); + } + +/* +* Set the preferred provider for an algorithm +*/ +void Algorithm_Factory::set_preferred_provider(const std::string& algo_spec, + const std::string& provider) + { + if(prototype_block_cipher(algo_spec)) + block_cipher_cache->set_preferred_provider(algo_spec, provider); + else if(prototype_stream_cipher(algo_spec)) + stream_cipher_cache->set_preferred_provider(algo_spec, provider); + else if(prototype_hash_function(algo_spec)) + hash_cache->set_preferred_provider(algo_spec, provider); + else if(prototype_mac(algo_spec)) + mac_cache->set_preferred_provider(algo_spec, provider); + else if(prototype_pbkdf(algo_spec)) + pbkdf_cache->set_preferred_provider(algo_spec, provider); + } + +/* +* Get an engine out of the list +*/ +Engine* Algorithm_Factory::get_engine_n(size_t n) const + { + if(n >= engines.size()) + return nullptr; + return engines[n]; + } + +/* +* Return the possible providers of a request +* Note: assumes you don't have different types by the same name +*/ +std::vector<std::string> +Algorithm_Factory::providers_of(const std::string& algo_spec) + { + /* The checks with if(prototype_X(algo_spec)) have the effect of + forcing a full search, since otherwise there might not be any + providers at all in the cache. + */ + + if(prototype_block_cipher(algo_spec)) + return block_cipher_cache->providers_of(algo_spec); + else if(prototype_stream_cipher(algo_spec)) + return stream_cipher_cache->providers_of(algo_spec); + else if(prototype_hash_function(algo_spec)) + return hash_cache->providers_of(algo_spec); + else if(prototype_mac(algo_spec)) + return mac_cache->providers_of(algo_spec); + else if(prototype_pbkdf(algo_spec)) + return pbkdf_cache->providers_of(algo_spec); + else + return std::vector<std::string>(); + } + +/* +* Return the prototypical block cipher corresponding to this request +*/ +const BlockCipher* +Algorithm_Factory::prototype_block_cipher(const std::string& algo_spec, + const std::string& provider) + { + return factory_prototype<BlockCipher>(algo_spec, provider, engines, + *this, block_cipher_cache); + } + +/* +* Return the prototypical stream cipher corresponding to this request +*/ +const StreamCipher* +Algorithm_Factory::prototype_stream_cipher(const std::string& algo_spec, + const std::string& provider) + { + return factory_prototype<StreamCipher>(algo_spec, provider, engines, + *this, stream_cipher_cache); + } + +/* +* Return the prototypical object corresponding to this request (if found) +*/ +const HashFunction* +Algorithm_Factory::prototype_hash_function(const std::string& algo_spec, + const std::string& provider) + { + return factory_prototype<HashFunction>(algo_spec, provider, engines, + *this, hash_cache); + } + +/* +* Return the prototypical object corresponding to this request +*/ +const MessageAuthenticationCode* +Algorithm_Factory::prototype_mac(const std::string& algo_spec, + const std::string& provider) + { + return factory_prototype<MessageAuthenticationCode>(algo_spec, provider, + engines, + *this, mac_cache); + } + +/* +* Return the prototypical object corresponding to this request +*/ +const PBKDF* +Algorithm_Factory::prototype_pbkdf(const std::string& algo_spec, + const std::string& provider) + { + return factory_prototype<PBKDF>(algo_spec, provider, + engines, + *this, pbkdf_cache); + } + +/* +* Return a new block cipher corresponding to this request +*/ +BlockCipher* +Algorithm_Factory::make_block_cipher(const std::string& algo_spec, + const std::string& provider) + { + if(const BlockCipher* proto = prototype_block_cipher(algo_spec, provider)) + return proto->clone(); + throw Algorithm_Not_Found(algo_spec); + } + +/* +* Return a new stream cipher corresponding to this request +*/ +StreamCipher* +Algorithm_Factory::make_stream_cipher(const std::string& algo_spec, + const std::string& provider) + { + if(const StreamCipher* proto = prototype_stream_cipher(algo_spec, provider)) + return proto->clone(); + throw Algorithm_Not_Found(algo_spec); + } + +/* +* Return a new object corresponding to this request +*/ +HashFunction* +Algorithm_Factory::make_hash_function(const std::string& algo_spec, + const std::string& provider) + { + if(const HashFunction* proto = prototype_hash_function(algo_spec, provider)) + return proto->clone(); + throw Algorithm_Not_Found(algo_spec); + } + +/* +* Return a new object corresponding to this request +*/ +MessageAuthenticationCode* +Algorithm_Factory::make_mac(const std::string& algo_spec, + const std::string& provider) + { + if(const MessageAuthenticationCode* proto = prototype_mac(algo_spec, provider)) + return proto->clone(); + throw Algorithm_Not_Found(algo_spec); + } + +/* +* Return a new object corresponding to this request +*/ +PBKDF* +Algorithm_Factory::make_pbkdf(const std::string& algo_spec, + const std::string& provider) + { + if(const PBKDF* proto = prototype_pbkdf(algo_spec, provider)) + return proto->clone(); + throw Algorithm_Not_Found(algo_spec); + } + +/* +* Add a new block cipher +*/ +void Algorithm_Factory::add_block_cipher(BlockCipher* block_cipher, + const std::string& provider) + { + block_cipher_cache->add(block_cipher, block_cipher->name(), provider); + } + +/* +* Add a new stream cipher +*/ +void Algorithm_Factory::add_stream_cipher(StreamCipher* stream_cipher, + const std::string& provider) + { + stream_cipher_cache->add(stream_cipher, stream_cipher->name(), provider); + } + +/* +* Add a new hash +*/ +void Algorithm_Factory::add_hash_function(HashFunction* hash, + const std::string& provider) + { + hash_cache->add(hash, hash->name(), provider); + } + +/* +* Add a new mac +*/ +void Algorithm_Factory::add_mac(MessageAuthenticationCode* mac, + const std::string& provider) + { + mac_cache->add(mac, mac->name(), provider); + } + +/* +* Add a new PBKDF +*/ +void Algorithm_Factory::add_pbkdf(PBKDF* pbkdf, + const std::string& provider) + { + pbkdf_cache->add(pbkdf, pbkdf->name(), provider); + } + +} diff --git a/src/lib/algo_factory/algo_factory.h b/src/lib/algo_factory/algo_factory.h new file mode 100644 index 000000000..201982766 --- /dev/null +++ b/src/lib/algo_factory/algo_factory.h @@ -0,0 +1,225 @@ +/* +* Algorithm Factory +* (C) 2008 Jack Lloyd +* +* Distributed under the terms of the Botan license +*/ + +#ifndef BOTAN_ALGORITHM_FACTORY_H__ +#define BOTAN_ALGORITHM_FACTORY_H__ + +#include <botan/types.h> +#include <string> +#include <vector> + +namespace Botan { + +/** +* Forward declarations (don't need full definitions here) +*/ +class BlockCipher; +class StreamCipher; +class HashFunction; +class MessageAuthenticationCode; +class PBKDF; + +template<typename T> class Algorithm_Cache; + +class Engine; + +/** +* Algorithm Factory +*/ +class BOTAN_DLL Algorithm_Factory + { + public: + /** + * Constructor + */ + Algorithm_Factory(); + + /** + * Destructor + */ + ~Algorithm_Factory(); + + /** + * @param engine to add (Algorithm_Factory takes ownership) + */ + void add_engine(Engine* engine); + + /** + * Clear out any cached objects + */ + void clear_caches(); + + /** + * @param algo_spec the algorithm we are querying + * @returns list of providers of this algorithm + */ + std::vector<std::string> providers_of(const std::string& algo_spec); + + /** + * @param algo_spec the algorithm we are setting a provider for + * @param provider the provider we would like to use + */ + void set_preferred_provider(const std::string& algo_spec, + const std::string& provider); + + /** + * @param algo_spec the algorithm we want + * @param provider the provider we would like to use + * @returns pointer to const prototype object, ready to clone(), or NULL + */ + const BlockCipher* + prototype_block_cipher(const std::string& algo_spec, + const std::string& provider = ""); + + /** + * @param algo_spec the algorithm we want + * @param provider the provider we would like to use + * @returns pointer to freshly created instance of the request algorithm + */ + BlockCipher* make_block_cipher(const std::string& algo_spec, + const std::string& provider = ""); + + /** + * @param algo the algorithm to add + * @param provider the provider of this algorithm + */ + void add_block_cipher(BlockCipher* algo, const std::string& provider); + + /** + * @param algo_spec the algorithm we want + * @param provider the provider we would like to use + * @returns pointer to const prototype object, ready to clone(), or NULL + */ + const StreamCipher* + prototype_stream_cipher(const std::string& algo_spec, + const std::string& provider = ""); + + /** + * @param algo_spec the algorithm we want + * @param provider the provider we would like to use + * @returns pointer to freshly created instance of the request algorithm + */ + StreamCipher* make_stream_cipher(const std::string& algo_spec, + const std::string& provider = ""); + + /** + * @param algo the algorithm to add + * @param provider the provider of this algorithm + */ + void add_stream_cipher(StreamCipher* algo, const std::string& provider); + + /** + * @param algo_spec the algorithm we want + * @param provider the provider we would like to use + * @returns pointer to const prototype object, ready to clone(), or NULL + */ + const HashFunction* + prototype_hash_function(const std::string& algo_spec, + const std::string& provider = ""); + + /** + * @param algo_spec the algorithm we want + * @param provider the provider we would like to use + * @returns pointer to freshly created instance of the request algorithm + */ + HashFunction* make_hash_function(const std::string& algo_spec, + const std::string& provider = ""); + + /** + * @param algo the algorithm to add + * @param provider the provider of this algorithm + */ + void add_hash_function(HashFunction* algo, const std::string& provider); + + /** + * @param algo_spec the algorithm we want + * @param provider the provider we would like to use + * @returns pointer to const prototype object, ready to clone(), or NULL + */ + const MessageAuthenticationCode* + prototype_mac(const std::string& algo_spec, + const std::string& provider = ""); + + /** + * @param algo_spec the algorithm we want + * @param provider the provider we would like to use + * @returns pointer to freshly created instance of the request algorithm + */ + MessageAuthenticationCode* make_mac(const std::string& algo_spec, + const std::string& provider = ""); + + /** + * @param algo the algorithm to add + * @param provider the provider of this algorithm + */ + void add_mac(MessageAuthenticationCode* algo, + const std::string& provider); + + /** + * @param algo_spec the algorithm we want + * @param provider the provider we would like to use + * @returns pointer to const prototype object, ready to clone(), or NULL + */ + const PBKDF* prototype_pbkdf(const std::string& algo_spec, + const std::string& provider = ""); + + /** + * @param algo_spec the algorithm we want + * @param provider the provider we would like to use + * @returns pointer to freshly created instance of the request algorithm + */ + PBKDF* make_pbkdf(const std::string& algo_spec, + const std::string& provider = ""); + + /** + * @param algo the algorithm to add + * @param provider the provider of this algorithm + */ + void add_pbkdf(PBKDF* algo, const std::string& provider); + + /** + * An iterator for the engines in this factory + * @deprecated Avoid in new code + */ + class BOTAN_DLL Engine_Iterator + { + public: + /** + * @return next engine in the sequence + */ + Engine* next() { return af.get_engine_n(n++); } + + /** + * @param a an algorithm factory + */ + Engine_Iterator(const Algorithm_Factory& a) : + af(a) { n = 0; } + private: + const Algorithm_Factory& af; + size_t n; + }; + friend class Engine_Iterator; + + private: + Algorithm_Factory(const Algorithm_Factory&) {} + Algorithm_Factory& operator=(const Algorithm_Factory&) + { return (*this); } + + Engine* get_engine_n(size_t n) const; + + std::vector<Engine*> engines; + + Algorithm_Cache<BlockCipher>* block_cipher_cache; + Algorithm_Cache<StreamCipher>* stream_cipher_cache; + Algorithm_Cache<HashFunction>* hash_cache; + Algorithm_Cache<MessageAuthenticationCode>* mac_cache; + Algorithm_Cache<PBKDF>* pbkdf_cache; + }; + +} + +#endif diff --git a/src/lib/algo_factory/info.txt b/src/lib/algo_factory/info.txt new file mode 100644 index 000000000..837ced1d0 --- /dev/null +++ b/src/lib/algo_factory/info.txt @@ -0,0 +1,24 @@ +load_on auto + +define ALGORITHM_FACTORY 20131128 + +<header:public> +algo_factory.h +</header:public> + +<header:internal> +algo_cache.h +</header:internal> + +<source> +algo_factory.cpp +prov_weight.cpp +</source> + +<requires> +block +engine +hash +mac +stream +</requires> diff --git a/src/lib/algo_factory/prov_weight.cpp b/src/lib/algo_factory/prov_weight.cpp new file mode 100644 index 000000000..fca791333 --- /dev/null +++ b/src/lib/algo_factory/prov_weight.cpp @@ -0,0 +1,34 @@ +/* +* Default provider weights for Algorithm_Cache +* (C) 2008 Jack Lloyd +* +* Distributed under the terms of the Botan license +*/ + +#include <botan/internal/algo_cache.h> + +namespace Botan { + +/** +* Return a static provider weighing +*/ +size_t static_provider_weight(const std::string& prov_name) + { + /* + * Prefer asm over C++, but prefer anything over OpenSSL or GNU MP; to use + * them, set the provider explicitly for the algorithms you want + */ + + if(prov_name == "aes_isa") return 9; + if(prov_name == "simd") return 8; + if(prov_name == "asm") return 7; + + if(prov_name == "core") return 5; + + if(prov_name == "openssl") return 2; + if(prov_name == "gmp") return 1; + + return 0; // other/unknown + } + +} |