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 const(T) factoryPrototype(T)(in string algo_spec,
391                              in string provider,
392                              ref Vector!( Engine ) engines,
393                              AlgorithmFactory af,
394                              AlgorithmCache!T cache) {
395 
396     logTrace("Searching for algo ", algo_spec, " | engine: ", provider ? provider : "All");
397 
398     if (const T cache_hit = cache.get(algo_spec, provider)) 
399         return cache_hit;
400 
401     SCANToken scan_name = SCANToken(algo_spec);
402 
403     if (scan_name.cipherMode() != "")
404         return null;
405 
406     foreach (engine; engines[])
407     {
408         if (provider == "" || engine.providerName() == provider)
409             if (T impl = engineGetAlgo!T(engine, scan_name, af))
410                 cache.add(impl, algo_spec, engine.providerName());
411         
412     }
413 
414     return cache.get(algo_spec, provider);
415 }