1 /** 2 * Algorithm Factory 3 * 4 * Copyright: 5 * (C) 2008-2010 Jack Lloyd 6 * (C) 2014-2015 Etienne Cimon 7 * 8 * License: 9 * Botan is released under the Simplified BSD License (see LICENSE.md) 10 */ 11 module botan.algo_factory.algo_factory; 12 13 import botan.constants; 14 import botan.algo_factory.algo_cache; 15 import botan.engine.engine; 16 import botan.utils.exceptn; 17 18 import botan.block.block_cipher; 19 import botan.stream.stream_cipher; 20 import botan.hash.hash; 21 import botan.mac.mac; 22 import botan.pbkdf.pbkdf; 23 import botan.utils.types; 24 import std.algorithm; 25 26 /** 27 * Algorithm Factory 28 */ 29 final class AlgorithmFactory 30 { 31 public: 32 ~this() { 33 foreach(engine; m_engines) { 34 destroy(engine); 35 } 36 m_engines.clear(); 37 m_engines.destroy(); 38 39 } 40 41 /** 42 * Params: 43 * engine = the engine to add to $(D AlgorithmFactory) and gives ownership of it. 44 */ 45 void addEngine(Engine engine) 46 { 47 clearCaches(); 48 m_engines.pushBack(engine); 49 } 50 51 /** 52 * Clear out any cached objects 53 */ 54 void clearCaches() 55 { 56 m_block_cipher_cache.clearCache(); 57 m_stream_cipher_cache.clearCache(); 58 m_hash_cache.clearCache(); 59 m_mac_cache.clearCache(); 60 m_pbkdf_cache.clearCache(); 61 } 62 63 /** 64 * Possible providers of a request assuming you don't have 65 * different types by the same name 66 * 67 * Params: 68 * algo_spec = the algorithm we are querying 69 * 70 * Returns: list of providers of this algorithm 71 */ 72 Vector!string providersOf(in string algo_spec) 73 { 74 /* The checks with if (prototype_X(algo_spec)) have the effect of 75 forcing a full search, since otherwise there might not be any 76 providers at all in the cache. 77 */ 78 79 if (prototypeBlockCipher(algo_spec)) 80 return m_block_cipher_cache.providersOf(algo_spec); 81 else if (prototypeStreamCipher(algo_spec)) 82 return m_stream_cipher_cache.providersOf(algo_spec); 83 else if (prototypeHashFunction(algo_spec)) 84 return m_hash_cache.providersOf(algo_spec); 85 else if (prototypeMac(algo_spec)) 86 return m_mac_cache.providersOf(algo_spec); 87 else if (prototypePbkdf(algo_spec)) 88 return m_pbkdf_cache.providersOf(algo_spec); 89 else 90 return Vector!string(); 91 } 92 93 94 /** 95 * Set the preferred provider for an algorithm 96 * 97 * Params: 98 * algo_spec = the algorithm we are setting a provider for 99 * provider = the provider we would like to use 100 */ 101 void setPreferredProvider(in string algo_spec, in string provider = "") 102 { 103 if (prototypeBlockCipher(algo_spec)) 104 m_block_cipher_cache.setPreferredProvider(algo_spec, provider); 105 else if (prototypeStreamCipher(algo_spec)) 106 m_stream_cipher_cache.setPreferredProvider(algo_spec, provider); 107 else if (prototypeHashFunction(algo_spec)) 108 m_hash_cache.setPreferredProvider(algo_spec, provider); 109 else if (prototypeMac(algo_spec)) 110 m_mac_cache.setPreferredProvider(algo_spec, provider); 111 else if (prototypePbkdf(algo_spec)) 112 m_pbkdf_cache.setPreferredProvider(algo_spec, provider); 113 } 114 115 /** 116 * Prototypical block cipher retrieval by name, it must be cloned to be used 117 * 118 * Params: 119 * algo_spec = the algorithm we want 120 * provider = the provider we would like to use 121 * 122 * Returns: pointer to const prototype object, ready to $(D clone()), or NULL 123 */ 124 const(BlockCipher) prototypeBlockCipher(in string algo_spec, in string provider = "") 125 { 126 return factoryPrototype!BlockCipher(algo_spec, provider, engines, this, *m_block_cipher_cache); 127 } 128 129 /** 130 * Makes a ready-to-use block cipher according to its name 131 * Params: 132 * algo_spec = the algorithm we want 133 * provider = the provider we would like to use 134 * 135 * Returns: pointer to freshly created instance of the request algorithm 136 */ 137 BlockCipher makeBlockCipher(in string algo_spec, in string provider = "") 138 { 139 if (const BlockCipher proto = prototypeBlockCipher(algo_spec, provider)) 140 return proto.clone(); 141 throw new AlgorithmNotFound(algo_spec); 142 } 143 144 /** 145 * Add a new block cipher 146 * Params: 147 * block_cipher = the algorithm to add 148 * provider = the provider of this algorithm 149 */ 150 void addBlockCipher(BlockCipher block_cipher, in string provider = "") 151 { 152 m_block_cipher_cache.add(block_cipher, block_cipher.name, provider); 153 } 154 155 /** 156 * Return the prototypical stream cipher corresponding to this request 157 * 158 * Params: 159 * algo_spec = the algorithm we want 160 * provider = the provider we would like to use 161 * 162 * Returns: Pointer to const prototype object, ready to $(D clone()), or NULL 163 */ 164 const(StreamCipher) prototypeStreamCipher(in string algo_spec, in string provider = "") 165 { 166 return factoryPrototype!StreamCipher(algo_spec, provider, engines, this, *m_stream_cipher_cache); 167 } 168 169 170 /** 171 * Return a new stream cipher corresponding to this request 172 * 173 * Params: 174 * algo_spec = the algorithm we want 175 * provider = the provider we would like to use 176 * 177 * Returns: Pointer to freshly created instance of the request algorithm 178 */ 179 StreamCipher makeStreamCipher(in string algo_spec, in string provider = "") 180 { 181 if (const StreamCipher proto = prototypeStreamCipher(algo_spec, provider)) 182 return proto.clone(); 183 throw new AlgorithmNotFound(algo_spec); 184 } 185 186 187 /** 188 * Add a new stream cipher 189 * 190 * Params: 191 * stream_cipher = the algorithm to add 192 * provider = the provider of this algorithm 193 */ 194 void addStreamCipher(StreamCipher stream_cipher, in string provider = "") 195 { 196 m_stream_cipher_cache.add(stream_cipher, stream_cipher.name, provider); 197 } 198 199 /** 200 * Return the prototypical object corresponding to this request (if found) 201 * 202 * Params: 203 * algo_spec = the algorithm we want 204 * provider = the provider we would like to use 205 * 206 * Returns: pointer to const prototype object, ready to $(D clone()), or NULL 207 */ 208 const(HashFunction) prototypeHashFunction(in string algo_spec, in string provider = "") 209 { 210 return factoryPrototype!HashFunction(algo_spec, provider, engines, this, *m_hash_cache); 211 } 212 213 214 /** 215 * Return a new object corresponding to this request 216 * 217 * Params: 218 * algo_spec = the algorithm we want 219 * provider = the provider we would like to use 220 * Returns: pointer to freshly created instance of the request algorithm 221 */ 222 HashFunction makeHashFunction(in string algo_spec, in string provider = "") 223 { 224 if (const HashFunction proto = prototypeHashFunction(algo_spec, provider)) 225 return proto.clone(); 226 throw new AlgorithmNotFound(algo_spec); 227 } 228 229 /** 230 * Add a new hash 231 * 232 * Params: 233 * hash = the algorithm to add 234 * provider = the provider of this algorithm 235 */ 236 void addHashFunction(HashFunction hash, in string provider = "") 237 { 238 m_hash_cache.add(hash, hash.name, provider); 239 } 240 241 /** 242 * Return the prototypical object corresponding to this request 243 * 244 * Params: 245 * algo_spec = the algorithm we want 246 * provider = the provider we would like to use 247 * 248 * Returns: pointer to const prototype object, ready to $(D clone()), or NULL 249 */ 250 const(MessageAuthenticationCode) prototypeMac(in string algo_spec, in string provider = "") 251 { 252 return factoryPrototype!MessageAuthenticationCode(algo_spec, provider, engines, this, *m_mac_cache); 253 } 254 255 /** 256 * Return a new object corresponding to this request 257 * 258 * Params: 259 * algo_spec = the algorithm we want 260 * provider = the provider we would like to use 261 * 262 * Returns: pointer to freshly created instance of the request algorithm 263 */ 264 MessageAuthenticationCode makeMac(in string algo_spec, in string provider = "") 265 { 266 if (const MessageAuthenticationCode proto = prototypeMac(algo_spec, provider)) 267 return proto.clone(); 268 throw new AlgorithmNotFound(algo_spec); 269 } 270 271 272 /** 273 * Params: 274 * mac = the algorithm to add 275 * provider = the provider of this algorithm 276 */ 277 void addMac(MessageAuthenticationCode mac, in string provider = "") 278 { 279 m_mac_cache.add(mac, mac.name, provider); 280 } 281 282 283 /** 284 * Return the prototypical object corresponding to this request 285 * 286 * Params: 287 * algo_spec = the algorithm we want 288 * provider = the provider we would like to use 289 * 290 * Returns: pointer to const prototype object, ready to $(D clone()), or NULL 291 */ 292 const(PBKDF) prototypePbkdf(in string algo_spec, in string provider = "") 293 { 294 return factoryPrototype!PBKDF(algo_spec, provider, engines, this, *m_pbkdf_cache); 295 } 296 297 298 /** 299 * Returns a new Pbkdf object corresponding to this request 300 * 301 * Params: 302 * algo_spec = the algorithm we want 303 * provider = the provider we would like to use 304 * 305 * Returns: pointer to freshly created instance of the request algorithm 306 */ 307 PBKDF makePbkdf(in string algo_spec, in string provider = "") 308 { 309 if (const PBKDF proto = prototypePbkdf(algo_spec, provider)) 310 return proto.clone(); 311 throw new AlgorithmNotFound(algo_spec); 312 } 313 314 /** 315 * Add a new Pbkdf 316 * 317 * Params: 318 * pbkdf = the algorithm to add 319 * provider = the provider of this algorithm 320 */ 321 void addPbkdf(PBKDF pbkdf, in string provider = "") 322 { 323 m_pbkdf_cache.add(pbkdf, pbkdf.name, provider); 324 } 325 326 /// List of engines available 327 @property ref Vector!Engine engines() { 328 return m_engines; 329 } 330 331 this() { 332 m_block_cipher_cache = new AlgorithmCache!BlockCipher; 333 m_stream_cipher_cache = new AlgorithmCache!StreamCipher; 334 m_hash_cache = new AlgorithmCache!HashFunction; 335 m_mac_cache = new AlgorithmCache!MessageAuthenticationCode; 336 m_pbkdf_cache = new AlgorithmCache!PBKDF; 337 } 338 339 private: 340 Engine getEngineN(size_t n) const 341 { 342 // Get an engine out of the list 343 if (n >= m_engines.length) 344 return null; 345 return m_engines[n]; 346 } 347 348 Vector!Engine m_engines; 349 350 Unique!(AlgorithmCache!BlockCipher) m_block_cipher_cache; 351 Unique!(AlgorithmCache!StreamCipher) m_stream_cipher_cache; 352 Unique!(AlgorithmCache!HashFunction) m_hash_cache; 353 Unique!(AlgorithmCache!MessageAuthenticationCode) m_mac_cache; 354 Unique!(AlgorithmCache!PBKDF) m_pbkdf_cache; 355 } 356 357 private: 358 359 /* 360 * Template functions for the factory prototype/search algorithm 361 */ 362 T engineGetAlgo(T)(Engine, in SCANToken, AlgorithmFactory) 363 { static assert(false, "Invalid engine"); } 364 365 BlockCipher engineGetAlgo(T : BlockCipher)(Engine engine, 366 auto ref SCANToken request, 367 AlgorithmFactory af) 368 { return engine.findBlockCipher(request, af); } 369 370 StreamCipher engineGetAlgo(T : StreamCipher)(Engine engine, 371 auto ref SCANToken request, 372 AlgorithmFactory af) 373 { return engine.findStreamCipher(request, af); } 374 375 HashFunction engineGetAlgo(T : HashFunction)(Engine engine, 376 auto ref SCANToken request, 377 AlgorithmFactory af) 378 { return engine.findHash(request, af); } 379 380 MessageAuthenticationCode engineGetAlgo(T : MessageAuthenticationCode)(Engine engine, 381 auto ref SCANToken request, 382 AlgorithmFactory af) 383 { return engine.findMac(request, af); } 384 385 PBKDF engineGetAlgo(T : PBKDF)(Engine engine, 386 auto ref SCANToken request, 387 AlgorithmFactory af) 388 { return engine.findPbkdf(request, af); } 389 390 pragma(inline) 391 const(T) factoryPrototype(T)(in string algo_spec, 392 in string provider, 393 ref Vector!( Engine ) engines, 394 AlgorithmFactory af, 395 AlgorithmCache!T cache) { 396 397 //logTrace("Searching for algo ", algo_spec, " | engine: ", provider ? provider : "All"); 398 399 if (const T cache_hit = cache.get(algo_spec, provider)) 400 return cache_hit; 401 402 SCANToken scan_name = SCANToken(algo_spec); 403 404 if (scan_name.cipherMode() != "") 405 return null; 406 407 foreach (engine; engines[]) 408 { 409 if (provider == "" || engine.providerName() == provider) 410 if (T impl = engineGetAlgo!T(engine, scan_name, af)) 411 cache.add(impl, algo_spec, engine.providerName()); 412 413 } 414 415 return cache.get(algo_spec, provider); 416 }