qca/plugins/qca-nss/qca-nss.cpp
Albert Astals Cid dce16514fe Enable a clazy CI
Starting with only missing-qobject-macro check enabled
2020-01-29 14:31:55 +01:00

551 lines
12 KiB
C++

/*
* 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"