File indexing completed on 2024-04-28 04:43:46

0001 /*
0002  * Copyright (C) 2006  Brad Hards <bradh@frogmouth.net>
0003  *
0004  * This library is free software; you can redistribute it and/or
0005  * modify it under the terms of the GNU Lesser General Public
0006  * License as published by the Free Software Foundation; either
0007  * version 2.1 of the License, or (at your option) any later version.
0008  *
0009  * This library is distributed in the hope that it will be useful,
0010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0012  * Lesser General Public License for more details.
0013  *
0014  * You should have received a copy of the GNU Lesser General Public
0015  * License along with this library; if not, write to the Free Software
0016  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
0017  *
0018  */
0019 #include "hasht.h"
0020 #include "nss.h"
0021 #include "pk11func.h"
0022 
0023 #include <QtCrypto>
0024 
0025 #include <QDebug>
0026 #include <QStringList>
0027 #include <QtPlugin>
0028 
0029 //-----------------------------------------------------------
0030 class nssHashContext : public QCA::HashContext
0031 {
0032     Q_OBJECT
0033 public:
0034     nssHashContext(QCA::Provider *p, const QString &type)
0035         : QCA::HashContext(p, type)
0036     {
0037         SECStatus s;
0038 
0039         NSS_NoDB_Init(".");
0040 
0041         m_status = 0;
0042 
0043         /* Get a slot to use for the crypto operations */
0044         m_slot = PK11_GetInternalKeySlot();
0045         if (!m_slot) {
0046             qDebug() << "GetInternalKeySlot failed";
0047             m_status = 1;
0048             return;
0049         }
0050 
0051         if (QLatin1String("md2") == type) {
0052             m_hashAlgo = SEC_OID_MD2;
0053         } else if (QLatin1String("md5") == type) {
0054             m_hashAlgo = SEC_OID_MD5;
0055         } else if (QLatin1String("sha1") == type) {
0056             m_hashAlgo = SEC_OID_SHA1;
0057         } else if (QLatin1String("sha256") == type) {
0058             m_hashAlgo = SEC_OID_SHA256;
0059         } else if (QLatin1String("sha384") == type) {
0060             m_hashAlgo = SEC_OID_SHA384;
0061         } else if (QLatin1String("sha512") == type) {
0062             m_hashAlgo = SEC_OID_SHA512;
0063         } else {
0064             qDebug() << "Unknown provider type: " << type;
0065             return; /* this will probably cause a segfault... */
0066         }
0067 
0068         m_context = PK11_CreateDigestContext(m_hashAlgo);
0069         if (!m_context) {
0070             qDebug() << "CreateDigestContext failed";
0071             return;
0072         }
0073 
0074         s = PK11_DigestBegin(m_context);
0075         if (s != SECSuccess) {
0076             qDebug() << "DigestBegin failed";
0077             return;
0078         }
0079     }
0080 
0081     ~nssHashContext() override
0082     {
0083         PK11_DestroyContext(m_context, PR_TRUE);
0084         if (m_slot)
0085             PK11_FreeSlot(m_slot);
0086     }
0087 
0088     Context *clone() const override
0089     {
0090         return new nssHashContext(provider(), type());
0091     }
0092 
0093     void clear() override
0094     {
0095         SECStatus s;
0096 
0097         PK11_DestroyContext(m_context, PR_TRUE);
0098 
0099         m_context = PK11_CreateDigestContext(m_hashAlgo);
0100         if (!m_context) {
0101             qDebug() << "CreateDigestContext failed";
0102             return;
0103         }
0104 
0105         s = PK11_DigestBegin(m_context);
0106         if (s != SECSuccess) {
0107             qDebug() << "DigestBegin failed";
0108             return;
0109         }
0110     }
0111 
0112     void update(const QCA::MemoryRegion &a) override
0113     {
0114         PK11_DigestOp(m_context, (const unsigned char *)a.data(), a.size());
0115     }
0116 
0117     QCA::MemoryRegion final() override
0118     {
0119         unsigned int     len = 0;
0120         QCA::SecureArray a(64);
0121         PK11_DigestFinal(m_context, (unsigned char *)a.data(), &len, a.size());
0122         a.resize(len);
0123         return a;
0124     }
0125 
0126 private:
0127     PK11SlotInfo *m_slot;
0128     int           m_status;
0129     PK11Context  *m_context;
0130     SECOidTag     m_hashAlgo;
0131 };
0132 
0133 //-----------------------------------------------------------
0134 class nssHmacContext : public QCA::MACContext
0135 {
0136     Q_OBJECT
0137 public:
0138     nssHmacContext(QCA::Provider *p, const QString &type)
0139         : QCA::MACContext(p, type)
0140     {
0141         NSS_NoDB_Init(".");
0142 
0143         m_context = nullptr;
0144         m_status  = 0;
0145 
0146         /* Get a slot to use for the crypto operations */
0147         m_slot = PK11_GetInternalKeySlot();
0148         if (!m_slot) {
0149             qDebug() << "GetInternalKeySlot failed";
0150             m_status = 1;
0151             return;
0152         }
0153 
0154         if (QLatin1String("hmac(md5)") == type) {
0155             m_macAlgo = CKM_MD5_HMAC;
0156         } else if (QLatin1String("hmac(sha1)") == type) {
0157             m_macAlgo = CKM_SHA_1_HMAC;
0158         } else if (QLatin1String("hmac(sha256)") == type) {
0159             m_macAlgo = CKM_SHA256_HMAC;
0160         } else if (QLatin1String("hmac(sha384)") == type) {
0161             m_macAlgo = CKM_SHA384_HMAC;
0162         } else if (QLatin1String("hmac(sha512)") == type) {
0163             m_macAlgo = CKM_SHA512_HMAC;
0164         } else if (QLatin1String("hmac(ripemd160)") == type) {
0165             m_macAlgo = CKM_RIPEMD160_HMAC;
0166         } else {
0167             qDebug() << "Unknown provider type: " << type;
0168             return; /* this will probably cause a segfault... */
0169         }
0170     }
0171 
0172     ~nssHmacContext() override
0173     {
0174         if (m_context)
0175             PK11_DestroyContext(m_context, PR_TRUE);
0176         if (m_slot)
0177             PK11_FreeSlot(m_slot);
0178     }
0179 
0180     Context *clone() const override
0181     {
0182         return new nssHmacContext(provider(), type());
0183     }
0184 
0185     void clear()
0186     {
0187         PK11_DestroyContext(m_context, PR_TRUE);
0188 
0189         SECItem noParams;
0190         noParams.data = nullptr;
0191         noParams.len  = 0;
0192 
0193         m_context = PK11_CreateContextBySymKey(m_macAlgo, CKA_SIGN, m_nssKey, &noParams);
0194         if (!m_context) {
0195             qDebug() << "CreateContextBySymKey failed";
0196             return;
0197         }
0198 
0199         SECStatus s = PK11_DigestBegin(m_context);
0200         if (s != SECSuccess) {
0201             qDebug() << "DigestBegin failed";
0202             return;
0203         }
0204     }
0205 
0206     QCA::KeyLength keyLength() const override
0207     {
0208         return anyKeyLength();
0209     }
0210 
0211     void setup(const QCA::SymmetricKey &key) override
0212     {
0213         /* turn the raw key into a SECItem */
0214         SECItem keyItem;
0215         keyItem.data = (unsigned char *)key.data();
0216         keyItem.len  = key.size();
0217 
0218         m_nssKey = PK11_ImportSymKey(m_slot, m_macAlgo, PK11_OriginUnwrap, CKA_SIGN, &keyItem, nullptr);
0219 
0220         SECItem noParams;
0221         noParams.data = nullptr;
0222         noParams.len  = 0;
0223 
0224         m_context = PK11_CreateContextBySymKey(m_macAlgo, CKA_SIGN, m_nssKey, &noParams);
0225         if (!m_context) {
0226             qDebug() << "CreateContextBySymKey failed";
0227             return;
0228         }
0229 
0230         SECStatus s = PK11_DigestBegin(m_context);
0231         if (s != SECSuccess) {
0232             qDebug() << "DigestBegin failed";
0233             return;
0234         }
0235     }
0236 
0237     void update(const QCA::MemoryRegion &a) override
0238     {
0239         PK11_DigestOp(m_context, (const unsigned char *)a.data(), a.size());
0240     }
0241 
0242     void final(QCA::MemoryRegion *out) override
0243     {
0244         // NSS doesn't appear to be able to tell us how big the digest will
0245         // be for a given algorithm until after we finalise it, so we work
0246         // around the problem a bit.
0247         QCA::SecureArray sa(HASH_LENGTH_MAX, 0); // assume the biggest hash size we know
0248         unsigned int     len = 0;
0249         PK11_DigestFinal(m_context, (unsigned char *)sa.data(), &len, sa.size());
0250         sa.resize(len); // and fix it up later
0251         *out = sa;
0252     }
0253 
0254 private:
0255     PK11SlotInfo     *m_slot;
0256     int               m_status;
0257     PK11Context      *m_context;
0258     CK_MECHANISM_TYPE m_macAlgo;
0259     PK11SymKey       *m_nssKey;
0260 };
0261 
0262 //-----------------------------------------------------------
0263 class nssCipherContext : public QCA::CipherContext
0264 {
0265     Q_OBJECT
0266 public:
0267     nssCipherContext(QCA::Provider *p, const QString &type)
0268         : QCA::CipherContext(p, type)
0269     {
0270         NSS_NoDB_Init(".");
0271 
0272         if (QLatin1String("aes128-ecb") == type) {
0273             m_cipherMechanism = CKM_AES_ECB;
0274         } else if (QLatin1String("aes128-cbc") == type) {
0275             m_cipherMechanism = CKM_AES_CBC;
0276         } else if (QLatin1String("des-ecb") == type) {
0277             m_cipherMechanism = CKM_DES_ECB;
0278         } else if (QLatin1String("des-cbc") == type) {
0279             m_cipherMechanism = CKM_DES_CBC;
0280         } else if (QLatin1String("des-cbc-pkcs7") == type) {
0281             m_cipherMechanism = CKM_DES_CBC_PAD;
0282         } else if (QLatin1String("tripledes-ecb") == type) {
0283             m_cipherMechanism = CKM_DES3_ECB;
0284         } else {
0285             qDebug() << "Unknown provider type: " << type;
0286             return; /* this will probably cause a segfault... */
0287         }
0288     }
0289 
0290     ~nssCipherContext() override
0291     {
0292     }
0293 
0294     void setup(QCA::Direction                   dir,
0295                const QCA::SymmetricKey         &key,
0296                const QCA::InitializationVector &iv,
0297                const QCA::AuthTag              &tag) override
0298     {
0299         Q_UNUSED(tag);
0300         /* Get a slot to use for the crypto operations */
0301         m_slot = PK11_GetBestSlot(m_cipherMechanism, nullptr);
0302         if (!m_slot) {
0303             qDebug() << "GetBestSlot failed";
0304             return;
0305         }
0306 
0307         /* turn the raw key into a SECItem */
0308         SECItem keyItem;
0309         keyItem.data = (unsigned char *)key.data();
0310         keyItem.len  = key.size();
0311 
0312         if (QCA::Encode == dir) {
0313             m_nssKey = PK11_ImportSymKey(m_slot, m_cipherMechanism, PK11_OriginUnwrap, CKA_ENCRYPT, &keyItem, nullptr);
0314         } else {
0315             // decryption
0316             m_nssKey = PK11_ImportSymKey(m_slot, m_cipherMechanism, PK11_OriginUnwrap, CKA_DECRYPT, &keyItem, nullptr);
0317         }
0318 
0319         SECItem ivItem;
0320         ivItem.data = (unsigned char *)iv.data();
0321         ivItem.len  = iv.size();
0322 
0323         m_params = PK11_ParamFromIV(m_cipherMechanism, &ivItem);
0324 
0325         if (QCA::Encode == dir) {
0326             m_context = PK11_CreateContextBySymKey(m_cipherMechanism, CKA_ENCRYPT, m_nssKey, m_params);
0327         } else {
0328             // decryption
0329             m_context = PK11_CreateContextBySymKey(m_cipherMechanism, CKA_DECRYPT, m_nssKey, m_params);
0330         }
0331 
0332         if (!m_context) {
0333             qDebug() << "CreateContextBySymKey failed";
0334             return;
0335         }
0336     }
0337 
0338     QCA::Provider::Context *clone() const override
0339     {
0340         return new nssCipherContext(*this);
0341     }
0342 
0343     int blockSize() const override
0344     {
0345         return PK11_GetBlockSize(m_cipherMechanism, m_params);
0346     }
0347 
0348     QCA::AuthTag tag() const override
0349     {
0350         // For future implementation
0351         return QCA::AuthTag();
0352     }
0353 
0354     bool update(const QCA::SecureArray &in, QCA::SecureArray *out) override
0355     {
0356         out->resize(in.size() + blockSize());
0357         int resultLength;
0358 
0359         PK11_CipherOp(
0360             m_context, (unsigned char *)out->data(), &resultLength, out->size(), (unsigned char *)in.data(), in.size());
0361         out->resize(resultLength);
0362 
0363         return true;
0364     }
0365 
0366     bool final(QCA::SecureArray *out) override
0367     {
0368         out->resize(blockSize());
0369         unsigned int resultLength;
0370 
0371         PK11_DigestFinal(m_context, (unsigned char *)out->data(), &resultLength, out->size());
0372         out->resize(resultLength);
0373 
0374         return true;
0375     }
0376 
0377     QCA::KeyLength keyLength() const override
0378     {
0379         int min      = 0;
0380         int max      = 0;
0381         int multiple = 0;
0382 
0383         switch (m_cipherMechanism) {
0384         case CKM_AES_ECB:
0385         case CKM_AES_CBC:
0386             min = max = 16;
0387             multiple  = 1;
0388             break;
0389 
0390         case CKM_DES_ECB:
0391         case CKM_DES_CBC:
0392         case CKM_DES_CBC_PAD:
0393             min = max = 8;
0394             multiple  = 1;
0395             break;
0396 
0397         case CKM_DES3_ECB:
0398             min      = 16;
0399             max      = 24;
0400             multiple = 1;
0401             break;
0402         }
0403 
0404         return QCA::KeyLength(min, max, multiple);
0405     }
0406 
0407 private:
0408     PK11SymKey       *m_nssKey;
0409     CK_MECHANISM_TYPE m_cipherMechanism;
0410     PK11SlotInfo     *m_slot;
0411     PK11Context      *m_context;
0412     SECItem          *m_params;
0413 };
0414 
0415 //==========================================================
0416 class nssProvider : public QCA::Provider
0417 {
0418 public:
0419     void init() override
0420     {
0421     }
0422 
0423     ~nssProvider() override
0424     {
0425     }
0426 
0427     int qcaVersion() const override
0428     {
0429         return QCA_VERSION;
0430     }
0431 
0432     QString name() const override
0433     {
0434         return QStringLiteral("qca-nss");
0435     }
0436 
0437     QStringList features() const override
0438     {
0439         QStringList list;
0440 
0441         list += QStringLiteral("md2");
0442         list += QStringLiteral("md5");
0443         list += QStringLiteral("sha1");
0444         list += QStringLiteral("sha256");
0445         list += QStringLiteral("sha384");
0446         list += QStringLiteral("sha512");
0447 
0448         list += QStringLiteral("hmac(md5)");
0449         list += QStringLiteral("hmac(sha1)");
0450         list += QStringLiteral("hmac(sha256)");
0451         list += QStringLiteral("hmac(sha384)");
0452         list += QStringLiteral("hmac(sha512)");
0453         // appears to not be implemented in NSS yet
0454         // list += QStringLiteral("hmac(ripemd160)");
0455 
0456         list += QStringLiteral("aes128-ecb");
0457         list += QStringLiteral("aes128-cbc");
0458         list += QStringLiteral("des-ecb");
0459         list += QStringLiteral("des-cbc");
0460         list += QStringLiteral("des-cbc-pkcs7");
0461         list += QStringLiteral("tripledes-ecb");
0462 
0463         return list;
0464     }
0465 
0466     Context *createContext(const QString &type) override
0467     {
0468         if (type == QLatin1String("md2"))
0469             return new nssHashContext(this, type);
0470         if (type == QLatin1String("md5"))
0471             return new nssHashContext(this, type);
0472         if (type == QLatin1String("sha1"))
0473             return new nssHashContext(this, type);
0474         if (type == QLatin1String("sha256"))
0475             return new nssHashContext(this, type);
0476         if (type == QLatin1String("sha384"))
0477             return new nssHashContext(this, type);
0478         if (type == QLatin1String("sha512"))
0479             return new nssHashContext(this, type);
0480 
0481         if (type == QLatin1String("hmac(md5)"))
0482             return new nssHmacContext(this, type);
0483         if (type == QLatin1String("hmac(sha1)"))
0484             return new nssHmacContext(this, type);
0485         if (type == QLatin1String("hmac(sha256)"))
0486             return new nssHmacContext(this, type);
0487         if (type == QLatin1String("hmac(sha384)"))
0488             return new nssHmacContext(this, type);
0489         if (type == QLatin1String("hmac(sha512)"))
0490             return new nssHmacContext(this, type);
0491         if (type == QLatin1String("hmac(ripemd160)"))
0492             return new nssHmacContext(this, type);
0493 
0494         if (type == QLatin1String("aes128-ecb"))
0495             return new nssCipherContext(this, type);
0496         if (type == QLatin1String("aes128-cbc"))
0497             return new nssCipherContext(this, type);
0498         if (type == QLatin1String("des-ecb"))
0499             return new nssCipherContext(this, type);
0500         if (type == QLatin1String("des-cbc"))
0501             return new nssCipherContext(this, type);
0502         if (type == QLatin1String("des-cbc-pkcs7"))
0503             return new nssCipherContext(this, type);
0504         if (type == QLatin1String("tripledes-ecb"))
0505             return new nssCipherContext(this, type);
0506         else
0507             return nullptr;
0508     }
0509 };
0510 
0511 class nssPlugin : public QObject, public QCAPlugin
0512 {
0513     Q_OBJECT
0514     Q_PLUGIN_METADATA(IID "com.affinix.qca.Plugin/1.0")
0515     Q_INTERFACES(QCAPlugin)
0516 public:
0517     QCA::Provider *createProvider() override
0518     {
0519         return new nssProvider;
0520     }
0521 };
0522 
0523 #include "qca-nss.moc"