File indexing completed on 2024-05-05 04:45:02

0001 /*
0002  * Copyright (C) 2004  Justin Karneges <justin@affinix.com>
0003  * Copyright (C) 2004-2006  Brad Hards <bradh@frogmouth.net>
0004  *
0005  * This library is free software; you can redistribute it and/or
0006  * modify it under the terms of the GNU Lesser General Public
0007  * License as published by the Free Software Foundation; either
0008  * version 2.1 of the License, or (at your option) any later version.
0009  *
0010  * This library is distributed in the hope that it will be useful,
0011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0013  * Lesser General Public License for more details.
0014  *
0015  * You should have received a copy of the GNU Lesser General Public
0016  * License along with this library; if not, write to the Free Software
0017  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
0018  *
0019  */
0020 #include <QElapsedTimer>
0021 #include <QtCrypto>
0022 #include <QtPlugin>
0023 
0024 #include <qstringlist.h>
0025 
0026 #include <botan/auto_rng.h>
0027 #include <botan/block_cipher.h>
0028 #include <botan/filters.h>
0029 #include <botan/hash.h>
0030 #include <botan/kdf.h>
0031 #include <botan/pbkdf.h>
0032 #include <botan/stream_cipher.h>
0033 #include <botan/version.h>
0034 
0035 #include <cstdlib>
0036 #include <iostream>
0037 
0038 //-----------------------------------------------------------
0039 class botanRandomContext : public QCA::RandomContext
0040 {
0041     Q_OBJECT
0042 public:
0043     botanRandomContext(QCA::Provider *p)
0044         : RandomContext(p)
0045     {
0046     }
0047 
0048     Context *clone() const override
0049     {
0050         return new botanRandomContext(*this);
0051     }
0052 
0053     QCA::SecureArray nextBytes(int size) override
0054     {
0055         QCA::SecureArray      buf(size);
0056         Botan::AutoSeeded_RNG rng;
0057         rng.randomize(reinterpret_cast<Botan::byte *>(buf.data()), buf.size());
0058         return buf;
0059     }
0060 };
0061 
0062 static QString qcaHashToBotanHash(const QString &type)
0063 {
0064     if (type == QLatin1String("md2"))
0065         return QStringLiteral("MD2");
0066     else if (type == QLatin1String("md4"))
0067         return QStringLiteral("MD4");
0068     else if (type == QLatin1String("md5"))
0069         return QStringLiteral("MD5");
0070     else if (type == QLatin1String("sha1"))
0071         return QStringLiteral("SHA-1");
0072     else if (type == QLatin1String("sha256"))
0073         return QStringLiteral("SHA-256");
0074     else if (type == QLatin1String("sha384"))
0075         return QStringLiteral("SHA-384");
0076     else if (type == QLatin1String("sha512"))
0077         return QStringLiteral("SHA-512");
0078     else if (type == QLatin1String("ripemd160"))
0079         return QStringLiteral("RIPEMD-160");
0080 
0081     return {};
0082 }
0083 
0084 //-----------------------------------------------------------
0085 class BotanHashContext : public QCA::HashContext
0086 {
0087     Q_OBJECT
0088 public:
0089     BotanHashContext(QCA::Provider *p, const QString &type)
0090         : QCA::HashContext(p, type)
0091     {
0092         const QString hashName = qcaHashToBotanHash(type);
0093         m_hashObj              = Botan::HashFunction::create(hashName.toStdString()).release();
0094     }
0095 
0096     ~BotanHashContext() override
0097     {
0098         delete m_hashObj;
0099     }
0100 
0101     bool isOk() const
0102     {
0103         return m_hashObj;
0104     }
0105 
0106     Context *clone() const override
0107     {
0108         return new BotanHashContext(provider(), type());
0109     }
0110 
0111     void clear() override
0112     {
0113         m_hashObj->clear();
0114     }
0115 
0116     void update(const QCA::MemoryRegion &a) override
0117     {
0118         m_hashObj->update((const Botan::byte *)a.data(), a.size());
0119     }
0120 
0121     QCA::MemoryRegion final() override
0122     {
0123         QCA::SecureArray a(m_hashObj->output_length());
0124         m_hashObj->final((Botan::byte *)a.data());
0125         return a;
0126     }
0127 
0128 private:
0129     Botan::HashFunction *m_hashObj;
0130 };
0131 
0132 static QString qcaHmacToBotanHmac(const QString &type)
0133 {
0134     if (type == QLatin1String("hmac(md5)"))
0135         return QStringLiteral("HMAC(MD5)");
0136     else if (type == QLatin1String("hmac(sha1)"))
0137         return QStringLiteral("HMAC(SHA-1)");
0138     else if (type == QLatin1String("hmac(sha224)"))
0139         return QStringLiteral("HMAC(SHA-224)");
0140     else if (type == QLatin1String("hmac(sha256)"))
0141         return QStringLiteral("HMAC(SHA-256)");
0142     else if (type == QLatin1String("hmac(sha384)"))
0143         return QStringLiteral("HMAC(SHA-384)");
0144     else if (type == QLatin1String("hmac(sha512)"))
0145         return QStringLiteral("HMAC(SHA-512)");
0146     else if (type == QLatin1String("hmac(ripemd160)"))
0147         return QStringLiteral("HMAC(RIPEMD-160)");
0148 
0149     return {};
0150 }
0151 
0152 //-----------------------------------------------------------
0153 class BotanHMACContext : public QCA::MACContext
0154 {
0155     Q_OBJECT
0156 public:
0157     BotanHMACContext(QCA::Provider *p, const QString &type)
0158         : QCA::MACContext(p, type)
0159         , m_hashObj(Botan::MessageAuthenticationCode::create(qcaHmacToBotanHmac(type).toStdString()))
0160     {
0161         if (nullptr == m_hashObj) {
0162             std::cout << "null context object " << qcaHmacToBotanHmac(type).toStdString() << std::endl;
0163         }
0164     }
0165 
0166     ~BotanHMACContext() override
0167     {
0168     }
0169 
0170     void setup(const QCA::SymmetricKey &key) override
0171     {
0172         // this often gets called with an empty key, because that is the default
0173         // in the QCA MessageAuthenticationCode constructor. Botan doesn't like
0174         // that happening.
0175         if (key.size() > 0) {
0176             m_hashObj->set_key((const Botan::byte *)key.data(), key.size());
0177         }
0178     }
0179 
0180     Context *clone() const override
0181     {
0182         return new BotanHMACContext(provider(), type());
0183     }
0184 
0185     void clear()
0186     {
0187         m_hashObj->clear();
0188     }
0189 
0190     QCA::KeyLength keyLength() const override
0191     {
0192         return anyKeyLength();
0193     }
0194 
0195     void update(const QCA::MemoryRegion &a) override
0196     {
0197         m_hashObj->update((const Botan::byte *)a.data(), a.size());
0198     }
0199 
0200     void final(QCA::MemoryRegion *out) override
0201     {
0202         QCA::SecureArray sa(m_hashObj->output_length(), 0);
0203         m_hashObj->final((Botan::byte *)sa.data());
0204         *out = sa;
0205     }
0206 
0207 protected:
0208     std::unique_ptr<Botan::MessageAuthenticationCode> m_hashObj;
0209 };
0210 
0211 static QString qcaPbkdfToBotanPbkdf(const QString &pbkdf)
0212 {
0213     if (pbkdf == QLatin1String("pbkdf1(sha1)"))
0214         return QStringLiteral("PBKDF1(SHA-1)");
0215     else if (pbkdf == QLatin1String("pbkdf1(md2)"))
0216         return QStringLiteral("PBKDF1(MD2)");
0217     else if (pbkdf == QLatin1String("pbkdf2(sha1)"))
0218         return QStringLiteral("PBKDF2(SHA-1)");
0219 
0220     return {};
0221 }
0222 
0223 //-----------------------------------------------------------
0224 class BotanPBKDFContext : public QCA::KDFContext
0225 {
0226     Q_OBJECT
0227 public:
0228     BotanPBKDFContext(QCA::Provider *p, const QString &type)
0229         : QCA::KDFContext(p, type)
0230     {
0231         try {
0232             const QString kdfName = qcaPbkdfToBotanPbkdf(type);
0233             m_s2k                 = Botan::get_s2k(kdfName.toStdString());
0234         } catch (Botan::Exception &e) {
0235             m_s2k = nullptr;
0236         }
0237     }
0238 
0239     ~BotanPBKDFContext() override
0240     {
0241         delete m_s2k;
0242     }
0243 
0244     bool isOk() const
0245     {
0246         return m_s2k;
0247     }
0248 
0249     Context *clone() const override
0250     {
0251         return new BotanPBKDFContext(provider(), type());
0252     }
0253 
0254     QCA::SymmetricKey makeKey(const QCA::SecureArray          &secret,
0255                               const QCA::InitializationVector &salt,
0256                               unsigned int                     keyLength,
0257                               unsigned int                     iterationCount) override
0258     {
0259         if (!m_s2k)
0260             return {};
0261 
0262         const std::string        secretString(secret.data(), secret.size());
0263         const Botan::OctetString key =
0264             m_s2k->derive_key(keyLength, secretString, (const Botan::byte *)salt.data(), salt.size(), iterationCount);
0265         const QCA::SecureArray retval(QByteArray((const char *)key.begin(), key.length()));
0266         return QCA::SymmetricKey(retval);
0267     }
0268 
0269     QCA::SymmetricKey makeKey(const QCA::SecureArray          &secret,
0270                               const QCA::InitializationVector &salt,
0271                               unsigned int                     keyLength,
0272                               int                              msecInterval,
0273                               unsigned int                    *iterationCount) override
0274     {
0275         Q_ASSERT(iterationCount != nullptr);
0276         Botan::OctetString key;
0277         QElapsedTimer      timer;
0278         const std::string  secretString(secret.data(), secret.size());
0279 
0280         *iterationCount = 0;
0281         timer.start();
0282         while (timer.elapsed() < msecInterval) {
0283             key = m_s2k->derive_key(keyLength, secretString, (const Botan::byte *)salt.data(), salt.size(), 1);
0284             ++(*iterationCount);
0285         }
0286         return makeKey(secret, salt, keyLength, *iterationCount);
0287     }
0288 
0289 protected:
0290     Botan::S2K *m_s2k;
0291 };
0292 
0293 static QString qcaHkdfToBotanHkdf(const QString &type)
0294 {
0295     if (type == QLatin1String("hkdf(sha256)"))
0296         return QStringLiteral("HKDF(SHA-256)");
0297 
0298     return {};
0299 }
0300 
0301 //-----------------------------------------------------------
0302 class BotanHKDFContext : public QCA::HKDFContext
0303 {
0304     Q_OBJECT
0305 public:
0306     BotanHKDFContext(QCA::Provider *p, const QString &type)
0307         : QCA::HKDFContext(p, type)
0308         , m_hkdf(Botan::KDF::create(qcaHkdfToBotanHkdf(type).toStdString()))
0309     {
0310     }
0311 
0312     ~BotanHKDFContext() override
0313     {
0314     }
0315 
0316     Context *clone() const override
0317     {
0318         return new BotanHKDFContext(provider(), type());
0319     }
0320 
0321     QCA::SymmetricKey makeKey(const QCA::SecureArray          &secret,
0322                               const QCA::InitializationVector &salt,
0323                               const QCA::InitializationVector &info,
0324                               unsigned int                     keyLength) override
0325     {
0326         Botan::secure_vector<uint8_t> key(keyLength);
0327         m_hkdf->kdf(key.data(),
0328                     keyLength,
0329                     reinterpret_cast<const Botan::byte *>(secret.data()),
0330                     secret.size(),
0331                     reinterpret_cast<const Botan::byte *>(salt.data()),
0332                     salt.size(),
0333                     reinterpret_cast<const Botan::byte *>(info.data()),
0334                     info.size());
0335         QCA::SecureArray retval(QByteArray::fromRawData(reinterpret_cast<const char *>(key.data()), key.size()));
0336         return QCA::SymmetricKey(retval);
0337     }
0338 
0339 protected:
0340     std::unique_ptr<Botan::KDF> m_hkdf;
0341 };
0342 
0343 static void
0344 qcaCipherToBotanCipher(const QString &type, std::string *algoName, std::string *algoMode, std::string *algoPadding)
0345 {
0346     if (type == QLatin1String("aes128-ecb")) {
0347         *algoName    = "AES-128";
0348         *algoMode    = "ECB";
0349         *algoPadding = "NoPadding";
0350     } else if (type == QLatin1String("aes128-cbc")) {
0351         *algoName    = "AES-128";
0352         *algoMode    = "CBC";
0353         *algoPadding = "NoPadding";
0354     } else if (type == QLatin1String("aes128-cfb")) {
0355         *algoName    = "AES-128";
0356         *algoMode    = "CFB";
0357         *algoPadding = "NoPadding";
0358     } else if (type == QLatin1String("aes128-ofb")) {
0359         *algoName    = "AES-128";
0360         *algoMode    = "OFB";
0361         *algoPadding = "NoPadding";
0362     } else if (type == QLatin1String("aes192-ecb")) {
0363         *algoName    = "AES-192";
0364         *algoMode    = "ECB";
0365         *algoPadding = "NoPadding";
0366     } else if (type == QLatin1String("aes192-cbc")) {
0367         *algoName    = "AES-192";
0368         *algoMode    = "CBC";
0369         *algoPadding = "NoPadding";
0370     } else if (type == QLatin1String("aes192-cfb")) {
0371         *algoName    = "AES-192";
0372         *algoMode    = "CFB";
0373         *algoPadding = "NoPadding";
0374     } else if (type == QLatin1String("aes192-ofb")) {
0375         *algoName    = "AES-192";
0376         *algoMode    = "OFB";
0377         *algoPadding = "NoPadding";
0378     } else if (type == QLatin1String("aes256-ecb")) {
0379         *algoName    = "AES-256";
0380         *algoMode    = "ECB";
0381         *algoPadding = "NoPadding";
0382     } else if (type == QLatin1String("aes256-cbc")) {
0383         *algoName    = "AES-256";
0384         *algoMode    = "CBC";
0385         *algoPadding = "NoPadding";
0386     } else if (type == QLatin1String("aes256-cfb")) {
0387         *algoName    = "AES-256";
0388         *algoMode    = "CFB";
0389         *algoPadding = "NoPadding";
0390     } else if (type == QLatin1String("aes256-ofb")) {
0391         *algoName    = "AES-256";
0392         *algoMode    = "OFB";
0393         *algoPadding = "NoPadding";
0394     } else if (type == QLatin1String("blowfish-ecb")) {
0395         *algoName    = "Blowfish";
0396         *algoMode    = "ECB";
0397         *algoPadding = "NoPadding";
0398     } else if (type == QLatin1String("blowfish-cbc")) {
0399         *algoName    = "Blowfish";
0400         *algoMode    = "CBC";
0401         *algoPadding = "NoPadding";
0402     } else if (type == QLatin1String("blowfish-cbc-pkcs7")) {
0403         *algoName    = "Blowfish";
0404         *algoMode    = "CBC";
0405         *algoPadding = "PKCS7";
0406     } else if (type == QLatin1String("blowfish-cfb")) {
0407         *algoName    = "Blowfish";
0408         *algoMode    = "CFB";
0409         *algoPadding = "NoPadding";
0410     } else if (type == QLatin1String("blowfish-ofb")) {
0411         *algoName    = "Blowfish";
0412         *algoMode    = "OFB";
0413         *algoPadding = "NoPadding";
0414     } else if (type == QLatin1String("des-ecb")) {
0415         *algoName    = "DES";
0416         *algoMode    = "ECB";
0417         *algoPadding = "NoPadding";
0418     } else if (type == QLatin1String("des-ecb-pkcs7")) {
0419         *algoName    = "DES";
0420         *algoMode    = "ECB";
0421         *algoPadding = "PKCS7";
0422     } else if (type == QLatin1String("des-cbc")) {
0423         *algoName    = "DES";
0424         *algoMode    = "CBC";
0425         *algoPadding = "NoPadding";
0426     } else if (type == QLatin1String("des-cbc-pkcs7")) {
0427         *algoName    = "DES";
0428         *algoMode    = "CBC";
0429         *algoPadding = "PKCS7";
0430     } else if (type == QLatin1String("des-cfb")) {
0431         *algoName    = "DES";
0432         *algoMode    = "CFB";
0433         *algoPadding = "NoPadding";
0434     } else if (type == QLatin1String("des-ofb")) {
0435         *algoName    = "DES";
0436         *algoMode    = "OFB";
0437         *algoPadding = "NoPadding";
0438     } else if (type == QLatin1String("tripledes-ecb")) {
0439         *algoName    = "TripleDES";
0440         *algoMode    = "ECB";
0441         *algoPadding = "NoPadding";
0442     }
0443 }
0444 
0445 static std::string qcaCipherToBotanCipher(const QString &qcaCipher)
0446 {
0447     std::string algoName, algoMode, algoPadding;
0448     qcaCipherToBotanCipher(qcaCipher, &algoName, &algoMode, &algoPadding);
0449     return algoName + '/' + algoMode + '/' + algoPadding; // NOLINT(performance-inefficient-string-concatenation)
0450 }
0451 
0452 #if BOTAN_VERSION_MAJOR == 2
0453 #define BOTAN_CIPHER_DIR_ENCRYPTION Botan::ENCRYPTION
0454 #define BOTAN_CIPHER_DIR_DECRYPTION Botan::DECRYPTION
0455 #elif BOTAN_VERSION_MAJOR == 3
0456 #define BOTAN_CIPHER_DIR_ENCRYPTION Botan::Cipher_Dir::Encryption
0457 #define BOTAN_CIPHER_DIR_DECRYPTION Botan::Cipher_Dir::Decryption
0458 #endif
0459 
0460 //-----------------------------------------------------------
0461 class BotanCipherContext : public QCA::CipherContext
0462 {
0463     Q_OBJECT
0464 public:
0465     BotanCipherContext(QCA::Provider *p, const QString &type)
0466         : QCA::CipherContext(p, type)
0467     {
0468         qcaCipherToBotanCipher(type, &m_algoName, &m_algoMode, &m_algoPadding);
0469     }
0470 
0471     void setup(QCA::Direction                   dir,
0472                const QCA::SymmetricKey         &key,
0473                const QCA::InitializationVector &iv,
0474                const QCA::AuthTag              &tag) override
0475     {
0476         Q_UNUSED(tag);
0477         try {
0478             m_dir = dir;
0479             const Botan::SymmetricKey keyCopy((Botan::byte *)key.data(), key.size());
0480 
0481             if (iv.size() == 0) {
0482                 if (QCA::Encode == dir) {
0483                     m_crypter = new Botan::Pipe(Botan::get_cipher(
0484                         m_algoName + '/' + m_algoMode + '/' + m_algoPadding, keyCopy, BOTAN_CIPHER_DIR_ENCRYPTION));
0485                 } else {
0486                     m_crypter = new Botan::Pipe(Botan::get_cipher(
0487                         m_algoName + '/' + m_algoMode + '/' + m_algoPadding, keyCopy, BOTAN_CIPHER_DIR_DECRYPTION));
0488                 }
0489             } else {
0490                 const Botan::InitializationVector ivCopy((Botan::byte *)iv.data(), iv.size());
0491                 if (QCA::Encode == dir) {
0492                     m_crypter = new Botan::Pipe(Botan::get_cipher(m_algoName + '/' + m_algoMode + '/' + m_algoPadding,
0493                                                                   keyCopy,
0494                                                                   ivCopy,
0495                                                                   BOTAN_CIPHER_DIR_ENCRYPTION));
0496                 } else {
0497                     m_crypter = new Botan::Pipe(Botan::get_cipher(m_algoName + '/' + m_algoMode + '/' + m_algoPadding,
0498                                                                   keyCopy,
0499                                                                   ivCopy,
0500                                                                   BOTAN_CIPHER_DIR_DECRYPTION));
0501                 }
0502             }
0503             m_crypter->start_msg();
0504         } catch (Botan::Exception &e) {
0505             m_crypter = nullptr;
0506             std::cout << "caught: " << e.what() << std::endl;
0507         }
0508     }
0509 
0510     Context *clone() const override
0511     {
0512         return new BotanCipherContext(*this);
0513     }
0514 
0515     int blockSize() const override
0516     {
0517         if (const std::unique_ptr<Botan::BlockCipher> bc = Botan::BlockCipher::create(m_algoName))
0518             return bc->block_size();
0519 
0520         throw Botan::Algorithm_Not_Found(m_algoName);
0521     }
0522 
0523     QCA::AuthTag tag() const override
0524     {
0525         // For future implementation
0526         return QCA::AuthTag();
0527     }
0528 
0529     bool update(const QCA::SecureArray &in, QCA::SecureArray *out) override
0530     {
0531         if (!m_crypter)
0532             return false;
0533         m_crypter->write((Botan::byte *)in.data(), in.size());
0534         QCA::SecureArray result(m_crypter->remaining());
0535         // Perhaps bytes_read is redundant and can be dropped
0536         const size_t bytes_read = m_crypter->read((Botan::byte *)result.data(), result.size());
0537         result.resize(bytes_read);
0538         *out = result;
0539         return true;
0540     }
0541 
0542     bool final(QCA::SecureArray *out) override
0543     {
0544         m_crypter->end_msg();
0545         QCA::SecureArray result(m_crypter->remaining());
0546         // Perhaps bytes_read is redundant and can be dropped
0547         const size_t bytes_read = m_crypter->read((Botan::byte *)result.data(), result.size());
0548         result.resize(bytes_read);
0549         *out = result;
0550         return true;
0551     }
0552 
0553     QCA::KeyLength keyLength() const override
0554     {
0555         Botan::Key_Length_Specification kls(0);
0556         if (const std::unique_ptr<Botan::BlockCipher> bc = Botan::BlockCipher::create(m_algoName))
0557             kls = bc->key_spec();
0558         else if (const std::unique_ptr<Botan::StreamCipher> sc = Botan::StreamCipher::create(m_algoName))
0559             kls = sc->key_spec();
0560         else if (const std::unique_ptr<Botan::MessageAuthenticationCode> mac =
0561                      Botan::MessageAuthenticationCode::create(m_algoName))
0562             kls = mac->key_spec();
0563         return QCA::KeyLength(kls.minimum_keylength(), kls.maximum_keylength(), kls.keylength_multiple());
0564     }
0565 
0566     ~BotanCipherContext() override
0567     {
0568         delete m_crypter;
0569     }
0570 
0571 protected:
0572     QCA::Direction       m_dir;
0573     std::string          m_algoName;
0574     std::string          m_algoMode;
0575     std::string          m_algoPadding;
0576     Botan::Keyed_Filter *m_cipher;
0577     Botan::Pipe         *m_crypter;
0578 };
0579 
0580 //==========================================================
0581 class botanProvider : public QCA::Provider
0582 {
0583 public:
0584     void init() override
0585     {
0586     }
0587 
0588     ~botanProvider() override
0589     {
0590         // We should be cleaning up there, but
0591         // this causes the unit tests to segfault
0592         // delete m_init;
0593     }
0594 
0595     int qcaVersion() const override
0596     {
0597         return QCA_VERSION;
0598     }
0599 
0600     QString name() const override
0601     {
0602         return QStringLiteral("qca-botan");
0603     }
0604 
0605     const QStringList &pbkdfTypes() const
0606     {
0607         static QStringList list;
0608         if (list.isEmpty()) {
0609             static const QStringList allTypes = {
0610                 QStringLiteral("pbkdf1(sha1)"), QStringLiteral("pbkdf1(md2)"), QStringLiteral("pbkdf2(sha1)")};
0611             for (const QString &type : allTypes) {
0612                 std::unique_ptr<BotanPBKDFContext> pbkdf(new BotanPBKDFContext(nullptr, type));
0613                 if (pbkdf->isOk())
0614                     list += type;
0615             }
0616         }
0617         return list;
0618     }
0619 
0620     const QStringList &hashTypes() const
0621     {
0622         static QStringList supported;
0623         if (supported.isEmpty()) {
0624             QStringList list;
0625             list += QStringLiteral("md2");
0626             list += QStringLiteral("md4");
0627             list += QStringLiteral("md5");
0628             list += QStringLiteral("sha1");
0629             list += QStringLiteral("sha256");
0630             list += QStringLiteral("sha384");
0631             list += QStringLiteral("sha512");
0632             list += QStringLiteral("ripemd160");
0633 
0634             for (const QString &hash : qAsConst(list)) {
0635                 std::unique_ptr<BotanHashContext> hashContext(new BotanHashContext(nullptr, hash));
0636                 if (hashContext->isOk()) {
0637                     supported << hash;
0638                 }
0639             }
0640         }
0641         return supported;
0642     }
0643 
0644     const QStringList &cipherTypes() const
0645     {
0646         static QStringList supported;
0647         if (supported.isEmpty()) {
0648             QStringList list;
0649             list += QStringLiteral("aes128-ecb");
0650             list += QStringLiteral("aes128-cbc");
0651             list += QStringLiteral("aes128-cfb");
0652             list += QStringLiteral("aes128-ofb");
0653             list += QStringLiteral("aes192-ecb");
0654             list += QStringLiteral("aes192-cbc");
0655             list += QStringLiteral("aes192-cfb");
0656             list += QStringLiteral("aes192-ofb");
0657             list += QStringLiteral("aes256-ecb");
0658             list += QStringLiteral("aes256-cbc");
0659             list += QStringLiteral("aes256-cfb");
0660             list += QStringLiteral("aes256-ofb");
0661             list += QStringLiteral("des-ecb");
0662             list += QStringLiteral("des-ecb-pkcs7");
0663             list += QStringLiteral("des-cbc");
0664             list += QStringLiteral("des-cbc-pkcs7");
0665             list += QStringLiteral("des-cfb");
0666             list += QStringLiteral("des-ofb");
0667             list += QStringLiteral("tripledes-ecb");
0668             list += QStringLiteral("blowfish-ecb");
0669             list += QStringLiteral("blowfish-cbc");
0670             list += QStringLiteral("blowfish-cbc-pkcs7");
0671             list += QStringLiteral("blowfish-cfb");
0672             list += QStringLiteral("blowfish-ofb");
0673 
0674             for (const QString &cipher : qAsConst(list)) {
0675                 const std::string bothanCipher = qcaCipherToBotanCipher(cipher);
0676                 try {
0677                     std::unique_ptr<Botan::Keyed_Filter> enc(
0678                         Botan::get_cipher(bothanCipher, BOTAN_CIPHER_DIR_ENCRYPTION));
0679                     std::unique_ptr<Botan::Keyed_Filter> dec(
0680                         Botan::get_cipher(bothanCipher, BOTAN_CIPHER_DIR_DECRYPTION));
0681                     supported += cipher;
0682                 } catch (Botan::Exception &e) {
0683                 }
0684             }
0685         }
0686         return supported;
0687     }
0688 
0689     const QStringList &hmacTypes() const
0690     {
0691         static QStringList list;
0692         if (list.isEmpty()) {
0693             list += QStringLiteral("hmac(md5)");
0694             list += QStringLiteral("hmac(sha1)");
0695             // HMAC with SHA2 doesn't appear to work correctly in Botan.
0696             list += QStringLiteral("hmac(sha224)");
0697             list += QStringLiteral("hmac(sha256)");
0698             list += QStringLiteral("hmac(sha384)");
0699             list += QStringLiteral("hmac(sha512)");
0700             list += QStringLiteral("hmac(ripemd160)");
0701         }
0702         return list;
0703     }
0704 
0705     QStringList hkdfTypes() const
0706     {
0707         static QStringList list;
0708         if (list.isEmpty()) {
0709             list += QStringLiteral("hkdf(sha256)");
0710         }
0711         return list;
0712     }
0713 
0714     QStringList features() const override
0715     {
0716         static QStringList list;
0717         if (list.isEmpty()) {
0718             list += QStringLiteral("random");
0719             list += hmacTypes();
0720             list += pbkdfTypes();
0721             list += hkdfTypes();
0722             list += cipherTypes();
0723             list += hashTypes();
0724         }
0725         return list;
0726     }
0727 
0728     Context *createContext(const QString &type) override
0729     {
0730         if (type == QLatin1String("random"))
0731             return new botanRandomContext(this);
0732         else if (hashTypes().contains(type))
0733             return new BotanHashContext(this, type);
0734         else if (hmacTypes().contains(type))
0735             return new BotanHMACContext(this, type);
0736         else if (pbkdfTypes().contains(type))
0737             return new BotanPBKDFContext(this, type);
0738         else if (hkdfTypes().contains(type))
0739             return new BotanHKDFContext(this, type);
0740         else if (cipherTypes().contains(type))
0741             return new BotanCipherContext(this, type);
0742         else
0743             return nullptr;
0744     }
0745 
0746 private:
0747 };
0748 
0749 class botanPlugin : public QObject, public QCAPlugin
0750 {
0751     Q_OBJECT
0752     Q_PLUGIN_METADATA(IID "com.affinix.qca.Plugin/1.0")
0753     Q_INTERFACES(QCAPlugin)
0754 public:
0755     QCA::Provider *createProvider() override
0756     {
0757         return new botanProvider;
0758     }
0759 };
0760 
0761 #include "qca-botan.moc"