/* * Copyright (C) 2006 Brad Hards <bradh@frogmouth.net> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * */ #include "pk11func.h" #include "nss.h" #include "hasht.h" #include <QtCrypto> #include <QDebug> #include <QtPlugin> #include <QStringList> //----------------------------------------------------------- class nssHashContext : public QCA::HashContext { Q_OBJECT public: nssHashContext( QCA::Provider *p, const QString &type) : QCA::HashContext(p, type) { SECStatus s; NSS_NoDB_Init("."); m_status = 0; /* Get a slot to use for the crypto operations */ m_slot = PK11_GetInternalKeySlot(); if (!m_slot) { qDebug() << "GetInternalKeySlot failed"; m_status = 1; return; } if ( QString("md2") == type ) { m_hashAlgo = SEC_OID_MD2; } else if ( QString("md5") == type ) { m_hashAlgo = SEC_OID_MD5; } else if ( QString("sha1") == type ) { m_hashAlgo = SEC_OID_SHA1; } else if ( QString("sha256") == type ) { m_hashAlgo = SEC_OID_SHA256; } else if ( QString("sha384") == type ) { m_hashAlgo = SEC_OID_SHA384; } else if ( QString("sha512") == type ) { m_hashAlgo = SEC_OID_SHA512; } else { qDebug() << "Unknown provider type: " << type; return; /* this will probably cause a segfault... */ } m_context = PK11_CreateDigestContext(m_hashAlgo); if (! m_context) { qDebug() << "CreateDigestContext failed"; return; } s = PK11_DigestBegin(m_context); if (s != SECSuccess) { qDebug() << "DigestBegin failed"; return; } } ~nssHashContext() { PK11_DestroyContext(m_context, PR_TRUE); if (m_slot) PK11_FreeSlot(m_slot); } Context *clone() const override { return new nssHashContext(provider(), type()); } void clear() override { SECStatus s; PK11_DestroyContext(m_context, PR_TRUE); m_context = PK11_CreateDigestContext(m_hashAlgo); if (! m_context) { qDebug() << "CreateDigestContext failed"; return; } s = PK11_DigestBegin(m_context); if (s != SECSuccess) { qDebug() << "DigestBegin failed"; return; } } void update(const QCA::MemoryRegion &a) override { PK11_DigestOp(m_context, (const unsigned char*)a.data(), a.size()); } QCA::MemoryRegion final() override { unsigned int len = 0; QCA::SecureArray a( 64 ); PK11_DigestFinal(m_context, (unsigned char*)a.data(), &len, a.size()); a.resize(len); return a; } private: PK11SlotInfo *m_slot; int m_status; PK11Context *m_context; SECOidTag m_hashAlgo; }; //----------------------------------------------------------- class nssHmacContext : public QCA::MACContext { Q_OBJECT public: nssHmacContext( QCA::Provider *p, const QString &type) : QCA::MACContext(p, type) { NSS_NoDB_Init("."); m_context = 0; m_status = 0; /* Get a slot to use for the crypto operations */ m_slot = PK11_GetInternalKeySlot(); if (!m_slot) { qDebug() << "GetInternalKeySlot failed"; m_status = 1; return; } if ( QString("hmac(md5)") == type ) { m_macAlgo = CKM_MD5_HMAC; } else if ( QString("hmac(sha1)") == type ) { m_macAlgo = CKM_SHA_1_HMAC; } else if ( QString("hmac(sha256)") == type ) { m_macAlgo = CKM_SHA256_HMAC; } else if ( QString("hmac(sha384)") == type ) { m_macAlgo = CKM_SHA384_HMAC; } else if ( QString("hmac(sha512)") == type ) { m_macAlgo = CKM_SHA512_HMAC; } else if ( QString("hmac(ripemd160)") == type ) { m_macAlgo = CKM_RIPEMD160_HMAC; } else { qDebug() << "Unknown provider type: " << type; return; /* this will probably cause a segfault... */ } } ~nssHmacContext() { if (m_context) PK11_DestroyContext(m_context, PR_TRUE); if (m_slot) PK11_FreeSlot(m_slot); } Context *clone() const override { return new nssHmacContext(provider(), type()); } void clear() { PK11_DestroyContext(m_context, PR_TRUE); SECItem noParams; noParams.data = 0; noParams.len = 0; m_context = PK11_CreateContextBySymKey(m_macAlgo, CKA_SIGN, m_nssKey, &noParams); if (! m_context) { qDebug() << "CreateContextBySymKey failed"; return; } SECStatus s = PK11_DigestBegin(m_context); if (s != SECSuccess) { qDebug() << "DigestBegin failed"; return; } } QCA::KeyLength keyLength() const override { return anyKeyLength(); } void setup(const QCA::SymmetricKey &key) override { /* turn the raw key into a SECItem */ SECItem keyItem; keyItem.data = (unsigned char*) key.data(); keyItem.len = key.size(); m_nssKey = PK11_ImportSymKey(m_slot, m_macAlgo, PK11_OriginUnwrap, CKA_SIGN, &keyItem, NULL); SECItem noParams; noParams.data = 0; noParams.len = 0; m_context = PK11_CreateContextBySymKey(m_macAlgo, CKA_SIGN, m_nssKey, &noParams); if (! m_context) { qDebug() << "CreateContextBySymKey failed"; return; } SECStatus s = PK11_DigestBegin(m_context); if (s != SECSuccess) { qDebug() << "DigestBegin failed"; return; } } void update(const QCA::MemoryRegion &a) override { PK11_DigestOp(m_context, (const unsigned char*)a.data(), a.size()); } void final( QCA::MemoryRegion *out) override { // NSS doesn't appear to be able to tell us how big the digest will // be for a given algorithm until after we finalise it, so we work // around the problem a bit. QCA::SecureArray sa( HASH_LENGTH_MAX, 0 ); // assume the biggest hash size we know unsigned int len = 0; PK11_DigestFinal(m_context, (unsigned char*)sa.data(), &len, sa.size()); sa.resize(len); // and fix it up later *out = sa; } private: PK11SlotInfo *m_slot; int m_status; PK11Context *m_context; CK_MECHANISM_TYPE m_macAlgo; PK11SymKey* m_nssKey; }; //----------------------------------------------------------- class nssCipherContext : public QCA::CipherContext { Q_OBJECT public: nssCipherContext( QCA::Provider *p, const QString &type) : QCA::CipherContext(p, type) { NSS_NoDB_Init("."); if ( QString("aes128-ecb") == type ) { m_cipherMechanism = CKM_AES_ECB; } else if ( QString("aes128-cbc") == type ) { m_cipherMechanism = CKM_AES_CBC; } else if ( QString("des-ecb") == type ) { m_cipherMechanism = CKM_DES_ECB; } else if ( QString("des-cbc") == type ) { m_cipherMechanism = CKM_DES_CBC; } else if ( QString("des-cbc-pkcs7") == type ) { m_cipherMechanism = CKM_DES_CBC_PAD; } else if ( QString("tripledes-ecb") == type ) { m_cipherMechanism = CKM_DES3_ECB; } else { qDebug() << "Unknown provider type: " << type; return; /* this will probably cause a segfault... */ } } ~nssCipherContext() { } void setup(QCA::Direction dir, const QCA::SymmetricKey &key, const QCA::InitializationVector &iv, const QCA::AuthTag &tag) override { Q_UNUSED(tag); /* Get a slot to use for the crypto operations */ m_slot = PK11_GetBestSlot( m_cipherMechanism, NULL ); if (!m_slot) { qDebug() << "GetBestSlot failed"; return; } /* turn the raw key into a SECItem */ SECItem keyItem; keyItem.data = (unsigned char*) key.data(); keyItem.len = key.size(); if (QCA::Encode == dir) { m_nssKey = PK11_ImportSymKey(m_slot, m_cipherMechanism, PK11_OriginUnwrap, CKA_ENCRYPT, &keyItem, NULL); } else { // decryption m_nssKey = PK11_ImportSymKey(m_slot, m_cipherMechanism, PK11_OriginUnwrap, CKA_DECRYPT, &keyItem, NULL); } SECItem ivItem; ivItem.data = (unsigned char*) iv.data(); ivItem.len = iv.size(); m_params = PK11_ParamFromIV(m_cipherMechanism, &ivItem); if (QCA::Encode == dir) { m_context = PK11_CreateContextBySymKey(m_cipherMechanism, CKA_ENCRYPT, m_nssKey, m_params); } else { // decryption m_context = PK11_CreateContextBySymKey(m_cipherMechanism, CKA_DECRYPT, m_nssKey, m_params); } if (! m_context) { qDebug() << "CreateContextBySymKey failed"; return; } } QCA::Provider::Context *clone() const override { return new nssCipherContext(*this); } int blockSize() const override { return PK11_GetBlockSize( m_cipherMechanism, m_params); } QCA::AuthTag tag() const override { // For future implementation return QCA::AuthTag(); } bool update( const QCA::SecureArray &in, QCA::SecureArray *out ) override { out->resize(in.size()+blockSize()); int resultLength; PK11_CipherOp(m_context, (unsigned char*)out->data(), &resultLength, out->size(), (unsigned char*)in.data(), in.size()); out->resize(resultLength); return true; } bool final( QCA::SecureArray *out ) override { out->resize(blockSize()); unsigned int resultLength; PK11_DigestFinal(m_context, (unsigned char*)out->data(), &resultLength, out->size()); out->resize(resultLength); return true; } QCA::KeyLength keyLength() const override { int min = 0; int max = 0; int multiple = 0; switch (m_cipherMechanism) { case CKM_AES_ECB: case CKM_AES_CBC: min = max = 16; multiple = 1; break; case CKM_DES_ECB: case CKM_DES_CBC: case CKM_DES_CBC_PAD: min = max = 8; multiple = 1; break; case CKM_DES3_ECB: min = 16; max = 24; multiple = 1; break; } return QCA::KeyLength(min, max, multiple); } private: PK11SymKey* m_nssKey; CK_MECHANISM_TYPE m_cipherMechanism; PK11SlotInfo *m_slot; PK11Context *m_context; SECItem* m_params; }; //========================================================== class nssProvider : public QCA::Provider { public: void init() override { } ~nssProvider() { } int qcaVersion() const override { return QCA_VERSION; } QString name() const override { return "qca-nss"; } QStringList features() const override { QStringList list; list += "md2"; list += "md5"; list += "sha1"; list += "sha256"; list += "sha384"; list += "sha512"; list += "hmac(md5)"; list += "hmac(sha1)"; list += "hmac(sha256)"; list += "hmac(sha384)"; list += "hmac(sha512)"; // appears to not be implemented in NSS yet // list += "hmac(ripemd160)"; list += "aes128-ecb"; list += "aes128-cbc"; list += "des-ecb"; list += "des-cbc"; list += "des-cbc-pkcs7"; list += "tripledes-ecb"; return list; } Context *createContext(const QString &type) override { if ( type == "md2" ) return new nssHashContext( this, type ); if ( type == "md5" ) return new nssHashContext( this, type ); if ( type == "sha1" ) return new nssHashContext( this, type ); if ( type == "sha256" ) return new nssHashContext( this, type ); if ( type == "sha384" ) return new nssHashContext( this, type ); if ( type == "sha512" ) return new nssHashContext( this, type ); if ( type == "hmac(md5)" ) return new nssHmacContext( this, type ); if ( type == "hmac(sha1)" ) return new nssHmacContext( this, type ); if ( type == "hmac(sha256)" ) return new nssHmacContext( this, type ); if ( type == "hmac(sha384)" ) return new nssHmacContext( this, type ); if ( type == "hmac(sha512)" ) return new nssHmacContext( this, type ); if ( type == "hmac(ripemd160)" ) return new nssHmacContext( this, type ); if ( type == "aes128-ecb" ) return new nssCipherContext( this, type); if ( type == "aes128-cbc" ) return new nssCipherContext( this, type); if ( type == "des-ecb" ) return new nssCipherContext( this, type); if ( type == "des-cbc" ) return new nssCipherContext( this, type); if ( type == "des-cbc-pkcs7" ) return new nssCipherContext( this, type); if ( type == "tripledes-ecb" ) return new nssCipherContext( this, type); else return 0; } }; class nssPlugin : public QObject, public QCAPlugin { Q_OBJECT Q_PLUGIN_METADATA(IID "com.affinix.qca.Plugin/1.0") Q_INTERFACES( QCAPlugin ) public: QCA::Provider *createProvider() override { return new nssProvider; } }; #include "qca-nss.moc"