1 /**
2 * Core Engine
3 * 
4 * Copyright:
5 * (C) 1999-2007 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.engine.core_engine;
12 
13 import botan.engine.engine;
14 import botan.rng.rng;
15 import botan.utils.parsing;
16 import botan.filters.filters;
17 import botan.algo_factory.algo_factory;
18 import botan.modes.mode_pad;
19 import botan.filters.transform_filter;
20 import botan.math.numbertheory.def_powm;
21 import botan.algo_base.scan_token;
22 import botan.algo_factory.algo_factory;
23 import std.algorithm : canFind;
24 import std.conv : to;
25 import memutils.scoped;
26 
27 import botan.constants;
28 static if (BOTAN_HAS_MODE_CFB)        import botan.modes.cfb;
29 static if (BOTAN_HAS_MODE_ECB)        import botan.modes.ecb;
30 static if (BOTAN_HAS_MODE_CBC)        import botan.modes.cbc;
31 static if (BOTAN_HAS_MODE_XTS)        import botan.modes.xts;
32 
33 static if (BOTAN_HAS_OFB)             import botan.stream.ofb;
34 static if (BOTAN_HAS_CTR_BE)          import botan.stream.ctr;
35 
36 static if (BOTAN_HAS_AEAD_FILTER)     import botan.filters.aead_filt;
37 static if (BOTAN_HAS_AEAD_CCM)        import botan.modes.aead.ccm;
38 static if (BOTAN_HAS_AEAD_EAX)        import botan.modes.aead.eax;    
39 static if (BOTAN_HAS_AEAD_OCB)        import botan.modes.aead.ocb;
40 static if (BOTAN_HAS_AEAD_GCM)        import botan.modes.aead.gcm;
41 static if (BOTAN_HAS_RSA)             import botan.pubkey.algo.rsa;
42 static if (BOTAN_HAS_RW)              import botan.pubkey.algo.rw;
43 static if (BOTAN_HAS_DSA)             import botan.pubkey.algo.dsa;
44 static if (BOTAN_HAS_ECDSA)           import botan.pubkey.algo.ecdsa;
45 static if (BOTAN_HAS_ELGAMAL)         import botan.pubkey.algo.elgamal;
46 static if (BOTAN_HAS_GOST_34_10_2001) import botan.pubkey.algo.gost_3410;
47 static if (BOTAN_HAS_NYBERG_RUEPPEL)  import  botan.pubkey.algo.nr;
48 static if (BOTAN_HAS_DIFFIE_HELLMAN)  import botan.pubkey.algo.dh;
49 static if (BOTAN_HAS_ECDH)            import botan.pubkey.algo.ecdh;
50 static if (BOTAN_HAS_CURVE25519)      import botan.pubkey.algo.curve25519;
51 /// Blocks
52 static if (BOTAN_HAS_AES)             import botan.block.aes;
53 static if (BOTAN_HAS_BLOWFISH)        import botan.block.blowfish;
54 static if (BOTAN_HAS_CAMELLIA)        import botan.block.camellia;
55 static if (BOTAN_HAS_CAST) {
56                                       import botan.block.cast128;
57                                       import botan.block.cast256;
58 }
59 static if (BOTAN_HAS_CASCADE)         import botan.block.cascade;
60 static if (BOTAN_HAS_DES){
61                                       import botan.block.des;
62                                       import botan.block.desx;
63 }
64 static if (BOTAN_HAS_GOST_28147_89)   import botan.block.gost_28147;
65 static if (BOTAN_HAS_IDEA)            import botan.block.idea;
66 static if (BOTAN_HAS_KASUMI)          import botan.block.kasumi;
67 static if (BOTAN_HAS_LION)            import botan.block.lion;
68 static if (BOTAN_HAS_MARS)            import botan.block.mars;
69 static if (BOTAN_HAS_MISTY1)          import botan.block.misty1;
70 static if (BOTAN_HAS_NOEKEON)         import botan.block.noekeon;
71 static if (BOTAN_HAS_RC2)             import botan.block.rc2;
72 static if (BOTAN_HAS_RC5)             import botan.block.rc5;
73 static if (BOTAN_HAS_RC6)             import botan.block.rc6;
74 static if (BOTAN_HAS_SAFER)           import botan.block.safer_sk;
75 static if (BOTAN_HAS_SEED)            import botan.block.seed;
76 static if (BOTAN_HAS_SERPENT)         import botan.block.serpent;
77 static if (BOTAN_HAS_TEA)             import botan.block.tea;
78 static if (BOTAN_HAS_TWOFISH)         import botan.block.twofish;
79 static if (BOTAN_HAS_THREEFISH_512)   import botan.block.threefish;
80 static if (BOTAN_HAS_XTEA)            import botan.block.xtea;
81 
82 //Hash
83 static if (BOTAN_HAS_ADLER32)         import botan.checksum.adler32;
84 static if (BOTAN_HAS_CRC24)           import botan.checksum.crc24;
85 static if (BOTAN_HAS_CRC32)           import botan.checksum.crc32;
86 static if (BOTAN_HAS_GOST_34_11)      import botan.hash.gost_3411;
87 static if (BOTAN_HAS_HAS_160)         import botan.hash.has160;
88 static if (BOTAN_HAS_KECCAK)          import botan.hash.keccak;
89 static if (BOTAN_HAS_MD2)             import botan.hash.md2;
90 static if (BOTAN_HAS_MD4)             import botan.hash.md4;
91 static if (BOTAN_HAS_MD5)             import botan.hash.md5;
92 static if (BOTAN_HAS_RIPEMD_128)      import botan.hash.rmd128;
93 static if (BOTAN_HAS_RIPEMD_160)      import botan.hash.rmd160;
94 static if (BOTAN_HAS_SHA1)            import botan.hash.sha160;
95 static if (BOTAN_HAS_SHA2_32)         import botan.hash.sha2_32;
96 static if (BOTAN_HAS_SHA2_64)         import botan.hash.sha2_64;
97 static if (BOTAN_HAS_SKEIN_512)       import botan.hash.skein_512;
98 static if (BOTAN_HAS_TIGER)           import botan.hash.tiger;
99 static if (BOTAN_HAS_WHIRLPOOL)       import botan.hash.whrlpool;
100 static if (BOTAN_HAS_PARALLEL_HASH)   import botan.hash.par_hash;
101 static if (BOTAN_HAS_COMB4P)          import botan.hash.comb4p;
102 
103 /// MAC
104 static if (BOTAN_HAS_POLY1305)        import botan.mac.poly1305;
105 static if (BOTAN_HAS_CBC_MAC)         import botan.mac.cbc_mac;
106 static if (BOTAN_HAS_CMAC)            import botan.mac.cmac;
107 static if (BOTAN_HAS_HMAC)            import botan.mac.hmac;
108 static if (BOTAN_HAS_SSL3_MAC)        import botan.mac.ssl3_mac;
109 static if (BOTAN_HAS_ANSI_X919_MAC)   import botan.mac.x919_mac;
110 
111 /// PBKDF
112 static if (BOTAN_HAS_PBKDF1)          import botan.pbkdf.pbkdf1;
113 static if (BOTAN_HAS_PBKDF2)          import botan.pbkdf.pbkdf2;
114 
115 /// STREAM
116 static if (BOTAN_HAS_RC4)             import botan.stream.rc4;
117 static if (BOTAN_HAS_CHACHA)          import botan.stream.chacha;
118 static if (BOTAN_HAS_SALSA20)         import botan.stream.salsa20;
119 
120 /**
121 * Core Engine
122 */
123 final class CoreEngine : Engine
124 {
125 public:
126     string providerName() const { return "core"; }
127 
128     override KeyedFilter getCipher(in string algo_spec,
129                           CipherDir direction,
130                           AlgorithmFactory af) const
131     {
132         Vector!string algo_parts = splitter(algo_spec, '/');
133         if (algo_parts.empty)
134             throw new InvalidAlgorithmName(algo_spec);
135         
136         const string cipher_name = algo_parts[0];
137         
138         // check if it is a stream cipher first (easy case)
139         const StreamCipher stream_cipher = af.prototypeStreamCipher(cipher_name);
140         if (stream_cipher)
141             return new StreamCipherFilter(stream_cipher.clone());
142         
143         const BlockCipher block_cipher = af.prototypeBlockCipher(cipher_name);
144         if (!block_cipher)
145             return null;
146         
147         if (algo_parts.length >= 4)
148             return null; // 4 part mode, not something we know about
149         
150         if (algo_parts.length < 2)
151             throw new LookupError("Cipher specification '" ~ algo_spec ~ "' is missing mode identifier");
152         
153         string mode = algo_parts[1];
154         
155         string padding;
156         if (algo_parts.length == 3)
157             padding = algo_parts[2];
158         else
159             padding = (mode == "CBC") ? "PKCS7" : "NoPadding";
160         
161         if (mode == "ECB" && padding == "CTS")
162             return null;
163         else if ((mode != "CBC" && mode != "ECB") && padding != "NoPadding")
164             throw new InvalidAlgorithmName(algo_spec);
165         
166         KeyedFilter filt = getCipherMode(block_cipher, direction, mode, padding);
167         if (filt)
168             return filt;
169         
170         if (padding != "NoPadding")
171             throw new AlgorithmNotFound(cipher_name ~ "/" ~ mode ~ "/" ~ padding);
172         else
173             throw new AlgorithmNotFound(cipher_name ~ "/" ~ mode);
174     }
175 
176     override BlockCipher findBlockCipher(in SCANToken request, AlgorithmFactory af) const
177     {
178         //logTrace("FindBlockCipher Core ", request.algoName);
179         
180         static if (BOTAN_HAS_AES) {
181             if (request.algoName == "AES-128")
182                 return new AES128;
183             if (request.algoName == "AES-192")
184                 return new AES192;
185             if (request.algoName == "AES-256")
186                 return new AES256;
187         }
188         
189         static if (BOTAN_HAS_BLOWFISH) {
190             if (request.algoName == "Blowfish")
191                 return new Blowfish;
192         }
193         
194         static if (BOTAN_HAS_CAMELLIA) {
195             if (request.algoName == "Camellia-128")
196                 return new Camellia128;
197             if (request.algoName == "Camellia-192")
198                 return new Camellia192;
199             if (request.algoName == "Camellia-256")
200                 return new Camellia256;
201         }
202         
203         static if (BOTAN_HAS_CAST) {
204             if (request.algoName == "CAST-128")
205                 return new CAST128;
206             if (request.algoName == "CAST-256")
207                 return new CAST256;
208         }
209         
210         static if (BOTAN_HAS_DES) {
211             if (request.algoName == "DES")
212                 return new DES;
213             if (request.algoName == "DESX")
214                 return new DESX;
215             if (request.algoName == "TripleDES")
216                 return new TripleDES;
217         }
218         
219         static if (BOTAN_HAS_GOST_28147_89) {
220             if (request.algoName == "GOST-28147-89")
221                 return new GOST_28147_89(request.arg(0, "R3411_94_TestParam"));
222         }
223         
224         static if (BOTAN_HAS_IDEA) {
225             if (request.algoName == "IDEA")
226                 return new IDEA;
227         }
228         
229         static if (BOTAN_HAS_KASUMI) {
230             if (request.algoName == "KASUMI")
231                 return new KASUMI;
232         }
233         
234         static if (BOTAN_HAS_MARS) {
235             if (request.algoName == "MARS")
236                     return new MARS;
237         }
238         
239         static if (BOTAN_HAS_MISTY1) {
240             if (request.algoName == "MISTY1")
241                 return new MISTY1(request.argAsInteger(0, 8));
242         }
243         
244         static if (BOTAN_HAS_NOEKEON) {
245             if (request.algoName == "Noekeon")
246                 return new Noekeon;
247         }
248         
249         static if (BOTAN_HAS_RC2) {
250             if (request.algoName == "RC2")
251                 return new RC2;
252         }
253         
254         static if (BOTAN_HAS_RC5) {
255             if (request.algoName == "RC5")
256                 return new RC5(request.argAsInteger(0, 12));
257         }
258         
259         static if (BOTAN_HAS_RC6) {
260             if (request.algoName == "RC6")
261                 return new RC6;
262         }
263         
264         static if (BOTAN_HAS_SAFER) {
265             if (request.algoName == "SAFER-SK")
266                 return new SAFERSK(request.argAsInteger(0, 10));
267         }
268         
269         static if (BOTAN_HAS_SEED) {
270             if (request.algoName == "SEED")
271                 return new SEED;
272         }
273         
274         static if (BOTAN_HAS_SERPENT) {
275             if (request.algoName == "Serpent")
276                 return new Serpent;
277         }
278         
279         static if (BOTAN_HAS_TEA) {
280             if (request.algoName == "TEA")
281                 return new TEA;
282         }
283         
284         static if (BOTAN_HAS_TWOFISH) {
285             if (request.algoName == "Twofish")
286                 return new Twofish;
287         }
288         
289         static if (BOTAN_HAS_TWOFISH) {
290             if (request.algoName == "Threefish-512")
291                 return new Threefish512;
292         }
293         
294         static if (BOTAN_HAS_XTEA) {
295             if (request.algoName == "XTEA")
296                 return new XTEA;
297         }
298         
299         static if (BOTAN_HAS_CASCADE) {
300             if (request.algoName == "Cascade" && request.argCount() == 2)
301             {
302                 const BlockCipher c1 = af.prototypeBlockCipher(request.arg(0));
303                 const BlockCipher c2 = af.prototypeBlockCipher(request.arg(1));
304                 
305                 if (c1 && c2)
306                     return new CascadeCipher(c1.clone(), c2.clone());
307             }
308         }
309         
310         static if (BOTAN_HAS_LION) {
311             if (request.algoName == "Lion" && request.argCountBetween(2, 3))
312             {
313                 const size_t block_size = request.argAsInteger(2, 1024);
314                 
315                 const HashFunction hash = af.prototypeHashFunction(request.arg(0));
316                 
317                 const StreamCipher stream_cipher = af.prototypeStreamCipher(request.arg(1));
318                 
319                 if (!hash || !stream_cipher)
320                     return null;
321                 
322                 return new Lion(hash.clone(), stream_cipher.clone(), block_size);
323             }
324         }
325         
326         return null;
327     }
328 
329     override StreamCipher findStreamCipher(in SCANToken request, AlgorithmFactory af) const
330     {
331         //logTrace("FindStreamCipher Core ", request.algoName);
332         static if (BOTAN_HAS_OFB) {
333             if (request.algoName == "OFB" && request.argCount() == 1)
334             {
335                 if (auto proto = af.prototypeBlockCipher(request.arg(0)))
336                     return new OFB(proto.clone());
337             }
338         }
339         
340         static if (BOTAN_HAS_CTR_BE) {
341             if (request.algoName == "CTR-BE" && request.argCount() == 1)
342             {
343                 if (auto proto = af.prototypeBlockCipher(request.arg(0)))
344                     return new CTRBE(proto.clone());
345             }
346         }
347         
348         static if (BOTAN_HAS_RC4) {
349             if (request.algoName == "RC4")
350                 return new RC4(request.argAsInteger(0, 0));
351             if (request.algoName == "RC4_drop")
352                 return new RC4(768);
353         }
354         
355         static if (BOTAN_HAS_CHACHA) {
356             if (request.algoName == "ChaCha")
357                 return new ChaCha;
358         }
359         
360         static if (BOTAN_HAS_SALSA20) {
361             if (request.algoName == "Salsa20")
362                 return new Salsa20;
363         }
364         
365         return null;
366     }
367 
368     override HashFunction findHash(in SCANToken request, AlgorithmFactory af) const
369     {
370         //logTrace("FindHash Core");
371         static if (BOTAN_HAS_ADLER32) {
372             if (request.algoName == "Adler32")
373                 return new Adler32;
374         }
375         
376         static if (BOTAN_HAS_CRC24) {
377             if (request.algoName == "CRC24")
378                 return new CRC24;
379         }
380         
381         static if (BOTAN_HAS_CRC32) {
382             if (request.algoName == "CRC32")
383                 return new CRC32;
384         }
385         
386         static if (BOTAN_HAS_GOST_34_11) {
387             if (request.algoName == "GOST-R-34.11-94")
388                 return new GOST3411;
389         }
390         
391         static if (BOTAN_HAS_HAS_160) {
392             if (request.algoName == "HAS-160")
393                 return new HAS160;
394         }
395         
396         static if (BOTAN_HAS_KECCAK) {
397             if (request.algoName == "Keccak-1600")
398                 return new Keccak1600(request.argAsInteger(0, 512));
399         }
400         
401         static if (BOTAN_HAS_MD2) {
402             if (request.algoName == "MD2")
403                 return new MD2;
404         }
405         
406         static if (BOTAN_HAS_MD4) {
407             if (request.algoName == "MD4")
408                 return new MD4;
409         }
410         
411         static if (BOTAN_HAS_MD5) {
412             if (request.algoName == "MD5")
413                 return new MD5;
414         }
415         
416         static if (BOTAN_HAS_RIPEMD_128) {
417             if (request.algoName == "RIPEMD-128")
418                 return new RIPEMD128;
419         }
420         
421         static if (BOTAN_HAS_RIPEMD_160) {
422             if (request.algoName == "RIPEMD-160")
423                 return new RIPEMD160;
424         }
425         
426         static if (BOTAN_HAS_SHA1) {
427             if (request.algoName == "SHA-160") 
428                 return new SHA160;
429 
430         }
431         
432         static if (BOTAN_HAS_SHA2_32) {
433             if (request.algoName == "SHA-224")
434                 return new SHA224;
435             if (request.algoName == "SHA-256")
436                 return new SHA256;
437         }
438         
439         static if (BOTAN_HAS_SHA2_64) {
440             if (request.algoName == "SHA-384")
441                 return new SHA384;
442             if (request.algoName == "SHA-512")
443                 return new SHA512;
444         }
445         
446         static if (BOTAN_HAS_TIGER) {
447             if (request.algoName == "Tiger")
448                 return new Tiger(request.argAsInteger(0, 24), // hash output
449                                  request.argAsInteger(1, 3)); // # passes
450         }
451         
452         static if (BOTAN_HAS_SKEIN_512) {
453             if (request.algoName == "Skein-512")
454                 return new Skein512(request.argAsInteger(0, 512),
455                                      request.arg(1, ""));
456         }
457         
458         static if (BOTAN_HAS_WHIRLPOOL) {
459             if (request.algoName == "Whirlpool")
460                 return new Whirlpool;
461         }
462         
463         static if (BOTAN_HAS_COMB4P) {
464             if (request.algoName == "Comb4P" && request.argCount() == 2)
465             {
466                 const HashFunction h1 = af.prototypeHashFunction(request.arg(0));
467                 const HashFunction h2 = af.prototypeHashFunction(request.arg(1));
468                 
469                 if (h1 && h2)
470                     return new Comb4P(h1.clone(), h2.clone());
471             }
472         }
473         
474         static if (BOTAN_HAS_PARALLEL_HASH) {
475             
476             if (request.algoName == "Parallel")
477             {
478                 Vector!HashFunction hash_prototypes;
479                 
480                 /* First pass, just get the prototypes (no memory allocation). Then
481                     if all were found, replace each prototype with a newly created clone
482                 */
483                 foreach (size_t i; 0 .. request.argCount())
484                 {
485                     HashFunction hash = cast(HashFunction)af.prototypeHashFunction(request.arg(i));
486                     if (!hash)
487                         return null;
488                     
489                     hash_prototypes.pushBack(hash);
490                 }
491                 
492                 Vector!HashFunction hashes;
493                 foreach (hash_prototype; hash_prototypes[])
494                     hashes.pushBack(hash_prototype.clone());
495                 
496                 return new Parallel(hashes.move());
497             }
498         }
499         
500         return null;
501         
502     }
503 
504     override MessageAuthenticationCode findMac(in SCANToken request, AlgorithmFactory af) const
505     {
506         //logTrace("FindMac Core");
507         
508         static if (BOTAN_HAS_CMAC) {
509             if (request.algoName == "CMAC" && request.argCount() == 1)
510                 return new CMAC(af.makeBlockCipher(request.arg(0)));
511         }
512         
513         static if (BOTAN_HAS_HMAC) {
514             if (request.algoName == "HMAC" && request.argCount() == 1) {
515                 return new HMAC(af.makeHashFunction(request.arg(0)));
516             }
517         }
518         
519 		static if (BOTAN_HAS_POLY1305) {
520 			if (request.algoName == "Poly1305") {
521 				return new Poly1305;
522 			}
523 		}
524 
525 		static if (BOTAN_HAS_CBC_MAC) {
526 			if (request.algoName == "CBC-MAC" && request.argCount() == 1)
527 				return new CBCMAC(af.makeBlockCipher(request.arg(0)));
528 		}
529 
530         static if (BOTAN_HAS_SSL3_MAC) {
531             if (request.algoName == "SSL3-MAC" && request.argCount() == 1)
532                 return new SSL3MAC(af.makeHashFunction(request.arg(0)));
533         }
534         
535         static if (BOTAN_HAS_ANSI_X919_MAC) {
536             if (request.algoName == "X9.19-MAC" && request.argCount() == 0)
537                 return new ANSIX919MAC(af.makeBlockCipher("DES"));
538         }
539         
540         return null;
541     }
542 
543 
544     override PBKDF findPbkdf(in SCANToken algo_spec, AlgorithmFactory af) const
545     {
546         //logTrace("FindPbkdf Core");
547         static if (BOTAN_HAS_PBKDF1) {
548             if (algo_spec.algoName == "PBKDF1" && algo_spec.argCount() == 1)
549                 return new PKCS5_PBKDF1(af.makeHashFunction(algo_spec.arg(0)));
550         }
551         
552         static if (BOTAN_HAS_PBKDF2) {
553             if (algo_spec.algoName == "PBKDF2" && algo_spec.argCount() == 1)
554             {
555                 if (const MessageAuthenticationCode mac_proto = af.prototypeMac(algo_spec.arg(0)))
556                     return new PKCS5_PBKDF2(mac_proto.clone());
557                 
558                 return new PKCS5_PBKDF2(af.makeMac("HMAC(" ~ algo_spec.arg(0) ~ ")"));
559             }
560         }
561         
562         return null;
563     }
564 
565     static if (BOTAN_HAS_PUBLIC_KEY_CRYPTO):
566 
567     override ModularExponentiator modExp(const ref BigInt n, PowerMod.UsageHints hints) const
568     {
569         if (n.isOdd()) {
570             //logTrace("Loading MontgomeryExponentiator");
571             return new MontgomeryExponentiator(n, hints);
572         }
573         //logTrace("Loading FixedWindowExponentiator");
574         return new FixedWindowExponentiator(n, hints);
575     }
576 
577     override KeyAgreement getKeyAgreementOp(in PrivateKey key, RandomNumberGenerator rng) const
578     {
579 		static if (BOTAN_HAS_CURVE25519) {
580 			if (Curve25519PrivateKey.algoName == key.algoName)
581 				return new Curve25519KAOperation(key);
582 		}
583 
584         static if (BOTAN_HAS_DIFFIE_HELLMAN) {
585             if (DHPrivateKey.algoName == key.algoName)
586                 return new DHKAOperation(key, rng);
587         }
588         
589         static if (BOTAN_HAS_ECDH) {
590             if (ECDHPrivateKey.algoName == key.algoName)
591                 return new ECDHKAOperation(key);
592         }
593         
594         return null;
595     }
596     
597     override Signature getSignatureOp(in PrivateKey key, RandomNumberGenerator rng) const
598     {
599         static if (BOTAN_HAS_RSA) {
600             if (RSAPrivateKey.algoName == key.algoName) {
601                 return new RSAPrivateOperation(key, rng);
602             }
603         }
604         
605         static if (BOTAN_HAS_RW) {
606             if (RWPrivateKey.algoName == key.algoName)
607                 return new RWSignatureOperation(key);
608         }
609         
610         static if (BOTAN_HAS_DSA) {
611             if (DSAPrivateKey.algoName == key.algoName)
612                 return new DSASignatureOperation(key);
613         }
614         
615         static if (BOTAN_HAS_ECDSA) {
616             if (ECDSAPrivateKey.algoName == key.algoName)
617                 return new ECDSASignatureOperation(key);
618         }
619         
620         static if (BOTAN_HAS_GOST_34_10_2001) {
621             if (GOST3410PrivateKey.algoName == key.algoName)
622                 return new GOST3410SignatureOperation(key);
623         }
624         
625         static if (BOTAN_HAS_NYBERG_RUEPPEL) {
626             if (NRPrivateKey.algoName == key.algoName)
627                 return new NRSignatureOperation(key);
628         }
629         
630         return null;
631     }
632     
633     override Verification getVerifyOp(in PublicKey key, RandomNumberGenerator rng) const
634     {
635         static if (BOTAN_HAS_RSA) {
636             if (RSAPublicKey.algoName == key.algoName)
637                 return new RSAPublicOperation(key);
638         }
639         
640         static if (BOTAN_HAS_RW) {
641             if (RWPublicKey.algoName == key.algoName)
642                 return new RWVerificationOperation(key);
643         }
644         
645         static if (BOTAN_HAS_DSA) {
646             if (DSAPublicKey.algoName == key.algoName)
647                 return new DSAVerificationOperation(key);
648         }
649         
650         static if (BOTAN_HAS_ECDSA) {
651             if (ECDSAPublicKey.algoName == key.algoName)
652                 return new ECDSAVerificationOperation(key);
653         }
654         
655         static if (BOTAN_HAS_GOST_34_10_2001) {
656             if (GOST3410PublicKey.algoName == key.algoName)
657                 return new GOST3410VerificationOperation(key);
658         }
659         
660         static if (BOTAN_HAS_NYBERG_RUEPPEL) {
661             if (NRPublicKey.algoName == key.algoName)
662                 return new NRVerificationOperation(key);
663         }
664         
665         return null;
666     }
667     
668     
669     override Encryption getEncryptionOp(in PublicKey key, RandomNumberGenerator) const
670     {
671         static if (BOTAN_HAS_RSA) {
672             if (RSAPublicKey.algoName == key.algoName)
673                 return new RSAPublicOperation(key);
674         }
675         
676         static if (BOTAN_HAS_ELGAMAL) {
677             if (ElGamalPublicKey.algoName == key.algoName)
678                 return new ElGamalEncryptionOperation(key);
679         }
680         
681         return null;
682     }
683     
684     override Decryption getDecryptionOp(in PrivateKey key, RandomNumberGenerator rng) const
685     {
686         static if (BOTAN_HAS_RSA) {
687             if (RSAPrivateKey.algoName == key.algoName)
688                 return new RSAPrivateOperation(key, rng);
689         }
690         
691         static if (BOTAN_HAS_ELGAMAL) {
692             if (ElGamalPrivateKey.algoName == key.algoName)
693                 return new ElGamalDecryptionOperation(key, rng);
694         }
695         
696         return null;
697     }
698 }
699 
700 /**
701 * Create a cipher mode filter object
702 * Params:
703 *  block_cipher = a block cipher object
704 *  direction = are we encrypting or decrypting?
705 *  mode = the name of the cipher mode to use
706 *  padding = the mode padding to use (only used for ECB, CBC)
707 */
708 KeyedFilter getCipherMode(const BlockCipher block_cipher,
709                           CipherDir direction,
710                           in string mode,
711                           in string padding)
712 {
713     static if (BOTAN_HAS_OFB) {
714         if (mode == "OFB")
715             return new StreamCipherFilter(new OFB(block_cipher.clone()));
716     }
717         
718     static if (BOTAN_HAS_CTR_BE) {
719         if (mode == "CTR-BE")
720             return new StreamCipherFilter(new CTRBE(block_cipher.clone()));
721     }
722         
723     static if (BOTAN_HAS_MODE_ECB) {
724         if (mode == "ECB" || mode == "")
725         {
726             if (direction == ENCRYPTION)
727                 return new TransformationFilter(
728                     new ECBEncryption(block_cipher.clone(), getBcPad(padding, "NoPadding")));
729             else
730                 return new TransformationFilter(
731                     new ECBDecryption(block_cipher.clone(), getBcPad(padding, "NoPadding")));
732         }
733     }
734     
735     if (mode == "CBC")
736     {
737         static if (BOTAN_HAS_MODE_CBC) {
738                 if (padding == "CTS")
739                 {
740                     if (direction == ENCRYPTION)
741                         return new TransformationFilter(new CTSEncryption(block_cipher.clone()));
742                     else
743                         return new TransformationFilter(new CTSDecryption(block_cipher.clone()));
744                 }
745                 
746                 if (direction == ENCRYPTION)
747                     return new TransformationFilter(
748                         new CBCEncryption(block_cipher.clone(), getBcPad(padding, "PKCS7")));
749                 else
750                     return new TransformationFilter(
751                         new CBCDecryption(block_cipher.clone(), getBcPad(padding, "PKCS7")));
752         } else {
753                 return null;
754         }
755     }
756     
757     static if (BOTAN_HAS_MODE_XTS) {
758         if (mode == "XTS")
759         {
760             if (direction == ENCRYPTION)
761                 return new TransformationFilter(new XTSEncryption(block_cipher.clone()));
762             else
763                 return new TransformationFilter(new XTSDecryption(block_cipher.clone()));
764         }
765     }
766     
767     if (mode.canFind("CFB") ||
768         mode.canFind("EAX") ||
769         mode.canFind("GCM") ||
770         mode.canFind("OCB") ||
771         mode.canFind("CCM"))
772     {
773         Vector!string algo_info = parseAlgorithmName(mode);
774         const string mode_name = algo_info[0];
775         
776         size_t bits = 8 * block_cipher.blockSize();
777         if (algo_info.length > 1)
778             bits = to!uint(algo_info[1]);
779         
780         static if (BOTAN_HAS_MODE_CFB) {
781                 if (mode_name == "CFB")
782                 {
783                     if (direction == ENCRYPTION)
784                         return new TransformationFilter(new CFBEncryption(block_cipher.clone(), bits));
785                     else
786                         return new TransformationFilter(new CFBDecryption(block_cipher.clone(), bits));
787                 }
788         }
789                 
790         if (bits % 8 != 0)
791             throw new InvalidArgument("AEAD interface does not support non-octet length tags");
792         
793         static if (BOTAN_HAS_AEAD_FILTER) {
794         
795             const size_t tag_size = bits / 8;
796             
797             static if (BOTAN_HAS_AEAD_CCM) {
798                     if (mode_name == "CCM")
799                     {
800                         const size_t L = (algo_info.length == 3) ? to!uint(algo_info[2]) : 3;
801                         if (direction == ENCRYPTION)
802                             return new AEADFilter(new CCMEncryption(block_cipher.clone(), tag_size, L));
803                         else
804                             return new AEADFilter(new CCMDecryption(block_cipher.clone(), tag_size, L));
805                     }
806             }
807                     
808             static if (BOTAN_HAS_AEAD_EAX) {
809                     if (mode_name == "EAX")
810                     {
811                         if (direction == ENCRYPTION)
812                             return new AEADFilter(new EAXEncryption(block_cipher.clone(), tag_size));
813                         else
814                             return new AEADFilter(new EAXDecryption(block_cipher.clone(), tag_size));
815                     }
816             }
817                     
818             static if (BOTAN_HAS_AEAD_OCB) {
819                     if (mode_name == "OCB")
820                     {
821                         if (direction == ENCRYPTION)
822                             return new AEADFilter(new OCBEncryption(block_cipher.clone(), tag_size));
823                         else
824                             return new AEADFilter(new OCBDecryption(block_cipher.clone(), tag_size));
825                     }
826             }
827                     
828             static if (BOTAN_HAS_AEAD_GCM) {
829                     if (mode_name == "GCM")
830                     {
831                         if (direction == ENCRYPTION)
832                             return new AEADFilter(new GCMEncryption(block_cipher.clone(), tag_size));
833                         else
834                             return new AEADFilter(new GCMDecryption(block_cipher.clone(), tag_size));
835                     }
836             }
837             
838         }
839     }
840     
841     return null;
842 }
843 
844 private {
845     
846     /**
847     * Get a block cipher padding method by name
848     */
849     BlockCipherModePaddingMethod getBcPad(in string algo_spec, in string def_if_empty)
850     {
851         static if (BOTAN_HAS_CIPHER_MODE_PADDING) {
852             if (algo_spec == "NoPadding" || (algo_spec == "" && def_if_empty == "NoPadding"))
853                 return new NullPadding;
854             
855             if (algo_spec == "PKCS7" || (algo_spec == "" && def_if_empty == "PKCS7"))
856                 return new PKCS7Padding;
857             
858             if (algo_spec == "OneAndZeros")
859                 return new OneAndZerosPadding;
860             
861             if (algo_spec == "X9.23")
862                 return new ANSIX923Padding;
863             
864         }
865         
866         throw new AlgorithmNotFound(algo_spec);
867     }
868     
869 }