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