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 }