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 }